Commit 78735dca authored by Igor Babaev's avatar Igor Babaev

MDEV-26108 Crash with query referencing twice CTE that uses embedded recursive CTE

This bug could affect queries that had at least two references to a CTE that
used an embedded recursive CTE.
Starting from version 10.4 some code in With_element::clone_parsed_spec()
that assumed a certain order of selects after parsing the specification of
a CTE became not valid anymore. It could lead to global select lists where
some selects were missing. If a missing CTE happened to belong to the
recursive part of a recursive CTE some recursive table references were not
set as references to materialized derived tables and this caused a crash of
the server.

Approved by Oleksandr Byelkin <sanja@mariadb.com>
parent e56fe393
...@@ -1126,7 +1126,7 @@ NULL UNION RESULT <union4,5> ALL NULL NULL NULL NULL NULL NULL ...@@ -1126,7 +1126,7 @@ NULL UNION RESULT <union4,5> ALL NULL NULL NULL NULL NULL NULL
NULL UNION RESULT <union11,12> ALL NULL NULL NULL NULL NULL NULL NULL UNION RESULT <union11,12> ALL NULL NULL NULL NULL NULL NULL
NULL UNION RESULT <union1,6> ALL NULL NULL NULL NULL NULL NULL NULL UNION RESULT <union1,6> ALL NULL NULL NULL NULL NULL NULL
Warnings: Warnings:
Note 1003 with cte_e as (with cte_o as (with cte_i as (select `test`.`t1`.`a` AS `a` from `test`.`t1` where `test`.`t1`.`a` < 7)select `test`.`t1`.`a` AS `a` from `test`.`t1` where `test`.`t1`.`a` > 1)select `test`.`t1`.`a` AS `a` from `test`.`t1` where `test`.`t1`.`a` < 3 and `test`.`t1`.`a` > 1 and `test`.`t1`.`a` < 7 and `test`.`t1`.`a` > 1 union select `test`.`t1`.`a` AS `a` from `test`.`t1` where `test`.`t1`.`a` > 4 and `test`.`t1`.`a` > 1 and `test`.`t1`.`a` < 7 and `test`.`t1`.`a` > 1)select `cte_e1`.`a` AS `a` from `cte_e` `cte_e1` where `cte_e1`.`a` > 1 union select `cte_e2`.`a` AS `a` from `cte_e` `cte_e2` Note 1003 with cte_e as (with cte_o as (with cte_i as (/* select#2 */ select `test`.`t1`.`a` AS `a` from `test`.`t1` where `test`.`t1`.`a` < 7)/* select#3 */ select `test`.`t1`.`a` AS `a` from `test`.`t1` where `test`.`t1`.`a` > 1)/* select#4 */ select `test`.`t1`.`a` AS `a` from `test`.`t1` where `test`.`t1`.`a` < 3 and `test`.`t1`.`a` > 1 and `test`.`t1`.`a` < 7 and `test`.`t1`.`a` > 1 union /* select#5 */ select `test`.`t1`.`a` AS `a` from `test`.`t1` where `test`.`t1`.`a` > 4 and `test`.`t1`.`a` > 1 and `test`.`t1`.`a` < 7 and `test`.`t1`.`a` > 1)/* select#1 */ select `cte_e1`.`a` AS `a` from `cte_e` `cte_e1` where `cte_e1`.`a` > 1 union /* select#6 */ select `cte_e2`.`a` AS `a` from `cte_e` `cte_e2`
drop table t1; drop table t1;
# #
# MDEV-13753: embedded CTE in a VIEW created in prepared statement # MDEV-13753: embedded CTE in a VIEW created in prepared statement
......
...@@ -4791,3 +4791,22 @@ a ...@@ -4791,3 +4791,22 @@ a
NULL NULL
DROP TABLE t1; DROP TABLE t1;
# End of 10.3 tests # End of 10.3 tests
#
# MDEV-26108: Recursive CTE embedded into another CTE which is used twice
#
create table t1 (a int);
insert into t1 values (5), (7);
with cte_e as (
with recursive cte_r as (
select a from t1 union select a+1 as a from cte_r r where a < 10
) select * from cte_r
) select * from cte_e s1, cte_e s2 where s1.a=s2.a;
a a
5 5
7 7
6 6
8 8
9 9
10 10
drop table t1;
# End of 10.4 tests
...@@ -3087,3 +3087,20 @@ SELECT * FROM cte; ...@@ -3087,3 +3087,20 @@ SELECT * FROM cte;
DROP TABLE t1; DROP TABLE t1;
--echo # End of 10.3 tests --echo # End of 10.3 tests
--echo #
--echo # MDEV-26108: Recursive CTE embedded into another CTE which is used twice
--echo #
create table t1 (a int);
insert into t1 values (5), (7);
with cte_e as (
with recursive cte_r as (
select a from t1 union select a+1 as a from cte_r r where a < 10
) select * from cte_r
) select * from cte_e s1, cte_e s2 where s1.a=s2.a;
drop table t1;
--echo # End of 10.4 tests
...@@ -1038,6 +1038,7 @@ st_select_lex_unit *With_element::clone_parsed_spec(LEX *old_lex, ...@@ -1038,6 +1038,7 @@ st_select_lex_unit *With_element::clone_parsed_spec(LEX *old_lex,
bool parse_status= false; bool parse_status= false;
st_select_lex *with_select; st_select_lex *with_select;
st_select_lex *last_clone_select;
char save_end= unparsed_spec.str[unparsed_spec.length]; char save_end= unparsed_spec.str[unparsed_spec.length];
((char*) &unparsed_spec.str[unparsed_spec.length])[0]= '\0'; ((char*) &unparsed_spec.str[unparsed_spec.length])[0]= '\0';
...@@ -1124,11 +1125,14 @@ st_select_lex_unit *With_element::clone_parsed_spec(LEX *old_lex, ...@@ -1124,11 +1125,14 @@ st_select_lex_unit *With_element::clone_parsed_spec(LEX *old_lex,
lex->unit.include_down(with_table->select_lex); lex->unit.include_down(with_table->select_lex);
lex->unit.set_slave(with_select); lex->unit.set_slave(with_select);
lex->unit.cloned_from= spec; lex->unit.cloned_from= spec;
last_clone_select= lex->all_selects_list;
while (last_clone_select->next_select_in_list())
last_clone_select= last_clone_select->next_select_in_list();
old_lex->all_selects_list= old_lex->all_selects_list=
(st_select_lex*) (lex->all_selects_list-> (st_select_lex*) (lex->all_selects_list->
insert_chain_before( insert_chain_before(
(st_select_lex_node **) &(old_lex->all_selects_list), (st_select_lex_node **) &(old_lex->all_selects_list),
with_select)); last_clone_select));
/* /*
Now all references to the CTE defined outside of the cloned specification Now all references to the CTE defined outside of the cloned specification
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment