[Commits] 78735dc: MDEV-26108 Crash with query referencing twice CTE that uses embedded recursive CTE

IgorBabaev igor at mariadb.com
Fri Jul 9 03:47:17 EEST 2021


revision-id: 78735dcaf757cd71c8f0ff3d21071b0f89018150 (mariadb-10.4.20-31-g78735dc)
parent(s): e56fe393104960eb62043c3777ce7d21de9362f4
author: Igor Babaev
committer: Igor Babaev
timestamp: 2021-07-08 17:47:17 -0700
message:

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 at mariadb.com>

---
 mysql-test/main/cte_nonrecursive.result |  2 +-
 mysql-test/main/cte_recursive.result    | 19 +++++++++++++++++++
 mysql-test/main/cte_recursive.test      | 17 +++++++++++++++++
 sql/sql_cte.cc                          | 10 +++++++---
 4 files changed, 44 insertions(+), 4 deletions(-)

diff --git a/mysql-test/main/cte_nonrecursive.result b/mysql-test/main/cte_nonrecursive.result
index 040afdf..4cd466a 100644
--- a/mysql-test/main/cte_nonrecursive.result
+++ b/mysql-test/main/cte_nonrecursive.result
@@ -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	<union1,6>	ALL	NULL	NULL	NULL	NULL	NULL	NULL	
 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;
 #
 # MDEV-13753: embedded CTE in a VIEW created in prepared statement
diff --git a/mysql-test/main/cte_recursive.result b/mysql-test/main/cte_recursive.result
index 6f30de3..74b450f 100644
--- a/mysql-test/main/cte_recursive.result
+++ b/mysql-test/main/cte_recursive.result
@@ -4791,3 +4791,22 @@ a
 NULL
 DROP TABLE t1;
 # 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
diff --git a/mysql-test/main/cte_recursive.test b/mysql-test/main/cte_recursive.test
index c3537e5..3b140b3 100644
--- a/mysql-test/main/cte_recursive.test
+++ b/mysql-test/main/cte_recursive.test
@@ -3087,3 +3087,20 @@ SELECT * FROM cte;
 DROP TABLE t1;
 
 --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
diff --git a/sql/sql_cte.cc b/sql/sql_cte.cc
index dfcb4e1..c5dcc15 100644
--- a/sql/sql_cte.cc
+++ b/sql/sql_cte.cc
@@ -1038,6 +1038,7 @@ st_select_lex_unit *With_element::clone_parsed_spec(LEX *old_lex,
 
   bool parse_status= false;
   st_select_lex *with_select;
+  st_select_lex *last_clone_select;
 
   char save_end= unparsed_spec.str[unparsed_spec.length];
   ((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,
   lex->unit.include_down(with_table->select_lex);
   lex->unit.set_slave(with_select);
   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=
     (st_select_lex*) (lex->all_selects_list->
-		      insert_chain_before(
-			(st_select_lex_node **) &(old_lex->all_selects_list),
-                        with_select));
+                     insert_chain_before(
+                       (st_select_lex_node **) &(old_lex->all_selects_list),
+                       last_clone_select));
 
   /*
     Now all references to the CTE defined outside of the cloned specification


More information about the commits mailing list