summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIgor Babaev <igor@askmonty.org>2018-05-17 22:56:27 -0700
committerIgor Babaev <igor@askmonty.org>2018-05-17 22:58:21 -0700
commitde86997160ea5e02e7fc6eb877d5823e96b64523 (patch)
treed841fc4cad41d13437fbce025151b5b68cdeccd3
parent52c98bf830cc14948f6a950961c77d64d20677a6 (diff)
downloadmariadb-git-de86997160ea5e02e7fc6eb877d5823e96b64523.tar.gz
MDEV-15581 Incorrect result (missing row) with UNION DISTINCT in anchor parts
The current code does not support recursive CTEs whose specifications contain a mix of ALL UNION and DISTINCT UNION operations. This patch catches such specifications and reports errors for them.
-rw-r--r--mysql-test/r/cte_recursive.result20
-rw-r--r--mysql-test/t/cte_recursive.test24
-rw-r--r--sql/sql_cte.cc12
-rw-r--r--sql/sql_lex.cc1
-rw-r--r--sql/sql_lex.h1
-rw-r--r--sql/sql_union.cc17
-rw-r--r--sql/sql_yacc.yy1
7 files changed, 72 insertions, 4 deletions
diff --git a/mysql-test/r/cte_recursive.result b/mysql-test/r/cte_recursive.result
index b7516b88f9a..2e93e9f6ff8 100644
--- a/mysql-test/r/cte_recursive.result
+++ b/mysql-test/r/cte_recursive.result
@@ -3243,3 +3243,23 @@ SELECT 1 FROM qn
ORDER BY (SELECT * FROM qn))
SELECT count(*) FROM qn;
ERROR 42000: This version of MariaDB doesn't yet support 'global ORDER_BY/LIMIT in recursive CTE spec'
+#
+# MDEV-15581: mix of ALL and DISTINCT UNION in recursive CTE
+#
+create table t1(a int);
+insert into t1 values(1),(2);
+insert into t1 values(1),(2);
+set @c=0, @d=0;
+WITH RECURSIVE qn AS
+(
+select 1,0 as col from t1
+union distinct
+select 1,0 from t1
+union all
+select 3, 0*(@c:=@c+1) from qn where @c<1
+union all
+select 3, 0*(@d:=@d+1) from qn where @d<1
+)
+select * from qn;
+ERROR 42000: This version of MariaDB doesn't yet support 'mix of ALL and DISTINCT UNION operations in recursive CTE spec'
+drop table t1;
diff --git a/mysql-test/t/cte_recursive.test b/mysql-test/t/cte_recursive.test
index b3276475fa4..32a82c494e0 100644
--- a/mysql-test/t/cte_recursive.test
+++ b/mysql-test/t/cte_recursive.test
@@ -2258,3 +2258,27 @@ SELECT 1 FROM dual UNION ALL
SELECT 1 FROM qn
ORDER BY (SELECT * FROM qn))
SELECT count(*) FROM qn;
+
+--echo #
+--echo # MDEV-15581: mix of ALL and DISTINCT UNION in recursive CTE
+--echo #
+
+create table t1(a int);
+insert into t1 values(1),(2);
+insert into t1 values(1),(2);
+
+set @c=0, @d=0;
+--error ER_NOT_SUPPORTED_YET
+WITH RECURSIVE qn AS
+(
+select 1,0 as col from t1
+union distinct
+select 1,0 from t1
+union all
+select 3, 0*(@c:=@c+1) from qn where @c<1
+union all
+select 3, 0*(@d:=@d+1) from qn where @d<1
+)
+select * from qn;
+
+drop table t1;
diff --git a/sql/sql_cte.cc b/sql/sql_cte.cc
index cd2516c2beb..fdd6943af93 100644
--- a/sql/sql_cte.cc
+++ b/sql/sql_cte.cc
@@ -665,7 +665,6 @@ void With_element::move_anchors_ahead()
{
st_select_lex *next_sl;
st_select_lex *new_pos= spec->first_select();
- st_select_lex *UNINIT_VAR(last_sl);
new_pos->linkage= UNION_TYPE;
for (st_select_lex *sl= new_pos; sl; sl= next_sl)
{
@@ -673,6 +672,14 @@ void With_element::move_anchors_ahead()
if (is_anchor(sl))
{
sl->move_node(new_pos);
+ if (new_pos == spec->first_select())
+ {
+ enum sub_select_type type= new_pos->linkage;
+ new_pos->linkage= sl->linkage;
+ sl->linkage= type;
+ new_pos->with_all_modifier= sl->with_all_modifier;
+ sl->with_all_modifier= false;
+ }
new_pos= sl->next_select();
}
else if (!sq_rec_ref && no_rec_ref_on_top_level())
@@ -680,10 +687,7 @@ void With_element::move_anchors_ahead()
sq_rec_ref= find_first_sq_rec_ref_in_select(sl);
DBUG_ASSERT(sq_rec_ref != NULL);
}
- last_sl= sl;
}
- if (spec->union_distinct)
- spec->union_distinct= last_sl;
first_recursive= new_pos;
}
diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc
index 1752689b385..7c963be695a 100644
--- a/sql/sql_lex.cc
+++ b/sql/sql_lex.cc
@@ -2171,6 +2171,7 @@ void st_select_lex::init_select()
select_limit= 0; /* denotes the default limit = HA_POS_ERROR */
offset_limit= 0; /* denotes the default offset = 0 */
with_sum_func= 0;
+ with_all_modifier= 0;
is_correlated= 0;
cur_pos_in_select_list= UNDEF_POS;
cond_value= having_value= Item::COND_UNDEF;
diff --git a/sql/sql_lex.h b/sql/sql_lex.h
index 68e1885f5e9..4acb1e441c1 100644
--- a/sql/sql_lex.h
+++ b/sql/sql_lex.h
@@ -907,6 +907,7 @@ public:
*/
bool subquery_in_having;
/* TRUE <=> this SELECT is correlated w.r.t. some ancestor select */
+ bool with_all_modifier; /* used for selects in union */
bool is_correlated;
/*
This variable is required to ensure proper work of subqueries and
diff --git a/sql/sql_union.cc b/sql/sql_union.cc
index 39e7bcdf051..13c19dae342 100644
--- a/sql/sql_union.cc
+++ b/sql/sql_union.cc
@@ -453,6 +453,23 @@ bool st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result,
DBUG_ASSERT(thd == thd_arg);
DBUG_ASSERT(thd == current_thd);
+ if (is_recursive && (sl= first_sl->next_select()))
+ {
+ SELECT_LEX *next_sl;
+ for ( ; ; sl= next_sl)
+ {
+ next_sl= sl->next_select();
+ if (!next_sl)
+ break;
+ if (next_sl->with_all_modifier != sl->with_all_modifier)
+ {
+ my_error(ER_NOT_SUPPORTED_YET, MYF(0),
+ "mix of ALL and DISTINCT UNION operations in recursive CTE spec");
+ DBUG_RETURN(TRUE);
+ }
+ }
+ }
+
describe= additional_options & SELECT_DESCRIBE;
/*
diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy
index b2088b0e640..47e5f2f9402 100644
--- a/sql/sql_yacc.yy
+++ b/sql/sql_yacc.yy
@@ -707,6 +707,7 @@ bool add_select_to_union_list(LEX *lex, bool is_union_distinct,
return TRUE;
mysql_init_select(lex);
lex->current_select->linkage=UNION_TYPE;
+ lex->current_select->with_all_modifier= !is_union_distinct;
if (is_union_distinct) /* UNION DISTINCT - remember position */
lex->current_select->master_unit()->union_distinct=
lex->current_select;