Skip to content

Commit de86997

Browse files
committed
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.
1 parent 52c98bf commit de86997

File tree

7 files changed

+72
-4
lines changed

7 files changed

+72
-4
lines changed

mysql-test/r/cte_recursive.result

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3243,3 +3243,23 @@ SELECT 1 FROM qn
32433243
ORDER BY (SELECT * FROM qn))
32443244
SELECT count(*) FROM qn;
32453245
ERROR 42000: This version of MariaDB doesn't yet support 'global ORDER_BY/LIMIT in recursive CTE spec'
3246+
#
3247+
# MDEV-15581: mix of ALL and DISTINCT UNION in recursive CTE
3248+
#
3249+
create table t1(a int);
3250+
insert into t1 values(1),(2);
3251+
insert into t1 values(1),(2);
3252+
set @c=0, @d=0;
3253+
WITH RECURSIVE qn AS
3254+
(
3255+
select 1,0 as col from t1
3256+
union distinct
3257+
select 1,0 from t1
3258+
union all
3259+
select 3, 0*(@c:=@c+1) from qn where @c<1
3260+
union all
3261+
select 3, 0*(@d:=@d+1) from qn where @d<1
3262+
)
3263+
select * from qn;
3264+
ERROR 42000: This version of MariaDB doesn't yet support 'mix of ALL and DISTINCT UNION operations in recursive CTE spec'
3265+
drop table t1;

mysql-test/t/cte_recursive.test

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2258,3 +2258,27 @@ SELECT 1 FROM dual UNION ALL
22582258
SELECT 1 FROM qn
22592259
ORDER BY (SELECT * FROM qn))
22602260
SELECT count(*) FROM qn;
2261+
2262+
--echo #
2263+
--echo # MDEV-15581: mix of ALL and DISTINCT UNION in recursive CTE
2264+
--echo #
2265+
2266+
create table t1(a int);
2267+
insert into t1 values(1),(2);
2268+
insert into t1 values(1),(2);
2269+
2270+
set @c=0, @d=0;
2271+
--error ER_NOT_SUPPORTED_YET
2272+
WITH RECURSIVE qn AS
2273+
(
2274+
select 1,0 as col from t1
2275+
union distinct
2276+
select 1,0 from t1
2277+
union all
2278+
select 3, 0*(@c:=@c+1) from qn where @c<1
2279+
union all
2280+
select 3, 0*(@d:=@d+1) from qn where @d<1
2281+
)
2282+
select * from qn;
2283+
2284+
drop table t1;

sql/sql_cte.cc

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -665,25 +665,29 @@ void With_element::move_anchors_ahead()
665665
{
666666
st_select_lex *next_sl;
667667
st_select_lex *new_pos= spec->first_select();
668-
st_select_lex *UNINIT_VAR(last_sl);
669668
new_pos->linkage= UNION_TYPE;
670669
for (st_select_lex *sl= new_pos; sl; sl= next_sl)
671670
{
672671
next_sl= sl->next_select();
673672
if (is_anchor(sl))
674673
{
675674
sl->move_node(new_pos);
675+
if (new_pos == spec->first_select())
676+
{
677+
enum sub_select_type type= new_pos->linkage;
678+
new_pos->linkage= sl->linkage;
679+
sl->linkage= type;
680+
new_pos->with_all_modifier= sl->with_all_modifier;
681+
sl->with_all_modifier= false;
682+
}
676683
new_pos= sl->next_select();
677684
}
678685
else if (!sq_rec_ref && no_rec_ref_on_top_level())
679686
{
680687
sq_rec_ref= find_first_sq_rec_ref_in_select(sl);
681688
DBUG_ASSERT(sq_rec_ref != NULL);
682689
}
683-
last_sl= sl;
684690
}
685-
if (spec->union_distinct)
686-
spec->union_distinct= last_sl;
687691
first_recursive= new_pos;
688692
}
689693

sql/sql_lex.cc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2171,6 +2171,7 @@ void st_select_lex::init_select()
21712171
select_limit= 0; /* denotes the default limit = HA_POS_ERROR */
21722172
offset_limit= 0; /* denotes the default offset = 0 */
21732173
with_sum_func= 0;
2174+
with_all_modifier= 0;
21742175
is_correlated= 0;
21752176
cur_pos_in_select_list= UNDEF_POS;
21762177
cond_value= having_value= Item::COND_UNDEF;

sql/sql_lex.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -907,6 +907,7 @@ class st_select_lex: public st_select_lex_node
907907
*/
908908
bool subquery_in_having;
909909
/* TRUE <=> this SELECT is correlated w.r.t. some ancestor select */
910+
bool with_all_modifier; /* used for selects in union */
910911
bool is_correlated;
911912
/*
912913
This variable is required to ensure proper work of subqueries and

sql/sql_union.cc

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -453,6 +453,23 @@ bool st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result,
453453
DBUG_ASSERT(thd == thd_arg);
454454
DBUG_ASSERT(thd == current_thd);
455455

456+
if (is_recursive && (sl= first_sl->next_select()))
457+
{
458+
SELECT_LEX *next_sl;
459+
for ( ; ; sl= next_sl)
460+
{
461+
next_sl= sl->next_select();
462+
if (!next_sl)
463+
break;
464+
if (next_sl->with_all_modifier != sl->with_all_modifier)
465+
{
466+
my_error(ER_NOT_SUPPORTED_YET, MYF(0),
467+
"mix of ALL and DISTINCT UNION operations in recursive CTE spec");
468+
DBUG_RETURN(TRUE);
469+
}
470+
}
471+
}
472+
456473
describe= additional_options & SELECT_DESCRIBE;
457474

458475
/*

sql/sql_yacc.yy

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -707,6 +707,7 @@ bool add_select_to_union_list(LEX *lex, bool is_union_distinct,
707707
return TRUE;
708708
mysql_init_select(lex);
709709
lex->current_select->linkage=UNION_TYPE;
710+
lex->current_select->with_all_modifier= !is_union_distinct;
710711
if (is_union_distinct) /* UNION DISTINCT - remember position */
711712
lex->current_select->master_unit()->union_distinct=
712713
lex->current_select;

0 commit comments

Comments
 (0)