[Commits] 044db90: MDEV-29361 Infinite recursive calls when detecting CTE dependencies
revision-id: 044db90ecdd69c9d959d6a7149a8cfd0f116fd54 (mariadb-10.3.35-158-g044db90) parent(s): 667df98c3e0f32d391af4eb65c618043720b6a2f author: Igor Babaev committer: Igor Babaev timestamp: 2022-09-22 21:40:33 -0700 message: MDEV-29361 Infinite recursive calls when detecting CTE dependencies This patch resolves the problem of improper name resolution of table references to embedded CTEs for some queries. This improper binding could lead to - infinite sequence of calls of recursive functions - crashes due to resolution of null pointers - wrong result sets returned by queries - bogus error messages If the definition of a CTE contains with clauses then such CTE is called embedding CTE while CTEs from the with clauses are called embedded CTEs. If a table reference used in the definition of an embedded CTE cannot be resolved within the unit that contains this reference it still may be resolved against a CTE definition from the with clause with one of the embedding CTEs. A table reference can be resolved against a CTE definition if it used in the the scope of this definition and it refers to the name of the CTE. Table reference t is in the scope of the CTE definition of CTE cte if - the definition of cte is an element of a with clause declared as RECURSIVE and the reference t belongs either to the unit to which this with clause is attached or to one of the elements of this clause - the definition of cte is an element of a with clause without RECURSIVE specifier and the reference t belongs either to the unit to which this with clause is attached or to one of the elements from this clause that are placed before the definition of cte. If a table reference can be resolved against several CTE definitions then it is bound to the most embedded. The code before this patch not always resolved table references used in embedded CTE according to the above rules. Approved by Oleksandr Byelkin <sanja@mariadb.com> --- mysql-test/main/cte_recursive.result | 586 +++++++++++++++++++++++++++++++ mysql-test/main/cte_recursive.test | 664 +++++++++++++++++++++++++++++++++++ sql/sql_cte.cc | 100 ++++-- sql/sql_cte.h | 20 +- sql/sql_yacc.yy | 3 +- sql/sql_yacc_ora.yy | 3 +- 6 files changed, 1341 insertions(+), 35 deletions(-) diff --git a/mysql-test/main/cte_recursive.result b/mysql-test/main/cte_recursive.result index 7bf8501..1ca1341 100644 --- a/mysql-test/main/cte_recursive.result +++ b/mysql-test/main/cte_recursive.result @@ -5017,5 +5017,591 @@ t2 CREATE TABLE `t2` ( set @@sql_mode=default; drop table t1,t2; # +# MDEV-29361: Embedded recursive / non-recursive CTE within +# the scope of another embedded CTE with the same name +# +create table t1 (a int); +insert into t1 values (4), (5); +create table t2 (a int); +insert into t2 values (6), (8); +create table t3 (a int); +insert into t3 values (1), (9); +with recursive +x as +( +select a from t1 union select a+1 from x as r1 where a < 7 +) +select * from x as s1; +a +4 +5 +6 +7 +with recursive +x as +( +select a from t2 +union +select a+2 from x as r2 where a < 10 +) +select a from x as s2; +a +6 +8 +10 +with +cte as +( +with recursive +x as +( +select a from t1 union select a+1 from x as r1 where a < 7 +) +select * from x as s1 +where s1.a in ( +with recursive +x as +( +select a from t2 +union +select a+2 from x as r2 where a < 10 +) +select a from x as s2 +) +) +select * from cte; +a +6 +with +cte as +( +with recursive +x(a) as +( +select a from t1 union select a+1 from x as r1 where a < 7 +) +select s1.a from x as s1, x +where s1.a = x.a and +x.a in ( +with recursive +x(a) as +( +select a from t2 +union +select a+2 from x as r2 where a < 10 +) +select a from x as s2 +) +) +select * from cte; +a +6 +with +cte as +( +with +x as +( +select a from t1 union select a+1 from x as r1 where a < 7 +) +select * from x as s1 +where s1.a in ( +with recursive +x as +( +select a from t2 +union +select a+2 from x as r2 where a < 10 +) +select a from x as s2 +) +) +select * from cte; +ERROR 42S02: Table 'test.x' doesn't exist +with +cte as +( +with recursive +x as +( +select a from t1 union select a+1 from x as r1 where a < 7 +) +select * from x as s1 +where s1.a in ( +with +x as +( +select a from t2 +union +select a+2 from x as r2 where a < 10 +) +select a from x as s2 +) +) +select * from cte; +a +6 +7 +with +cte as +( +with +x as +( +select a from t1 union select a+1 from x as r1 where a < 7 +) +select * from x as s1 +where s1.a in ( +with +x as +( +select a from t2 +union +select a+2 from x as r2 where a < 10 +) +select a from x as s2 +) +) +select * from cte; +ERROR 42S02: Table 'test.x' doesn't exist +with +cte as +( +with recursive +y as +( +select a from t1 union select a+1 from y as r1 where a < 7 +) +select * from y as s1 +where s1.a in ( +with +x as +( +select a from t2 +union +select a+2 from x as r2 where a < 10 +) +select a from x as s2 +) +) +select * from cte; +ERROR 42S02: Table 'test.x' doesn't exist +with +cte as +( +with +y(a) as +( +select a+5 from t1 +) +select * from y as s1 +where s1.a in ( +with +x as +( +select a from t2 +union +select a+2 from x as r2 where a < 10 +) +select a from x as s2 +) +) +select * from cte; +ERROR 42S02: Table 'test.x' doesn't exist +with +cte as +( +select ( +with +x as +( +select a from x as r1 +) +select * from x as s1 +where s1.a in ( +with recursive +x as +( +select a from t2 +union +select a+2 from x as r2 where a < 10 +) +select a from x as s2 +) +) as r +from t3 +) +select * from cte; +ERROR 42S02: Table 'test.x' doesn't exist +with +cte as +( +select ( +with +x as +( +select a from x as r1 +) +select * from x as s1 +where s1.a < 5 and +s1.a in ( +with +x as +( +select a from t2 +union +select a+2 from x as r2 where a < 10 +) +select a from x as s2 +) +) as r +from t3 +) +select * from cte; +ERROR 42S02: Table 'test.x' doesn't exist +with +cte as +( +select ( +with recursive +x(a) as +( +select a+3 from t1 union select a+1 from x as r1 where a < 7 +) +select * from x as s1 +where s1.a < 8 and +s1.a in ( +with recursive +x(a) as +( +select a-2 from t2 +union +select a+1 from x as r2 where a < 10 +) +select a from x as s2 +) +) as r +from t3 +) +select * from cte; +r +7 +7 +with +cte as +( +select ( +with recursive +x as +( +select a from t1 union select a+1 from x as r1 where a < 7 +) +select * from x as s1 +where s1.a in ( +with recursive +x as +( +select a from t2 +union +select a+2 from x as r2 where a < 10 +) +select a from x as s2 +) +) as r +from t3 +) +select * from cte; +r +6 +6 +create table x (a int); +insert into x values (3), (7), (1), (5), (6); +with +cte as +( +select ( +with +x as +( +select ( select a from x as r1 ) as a from t1 +) +select * from x as s1 +where s1.a in ( +with recursive +x as +( +select a from t2 +union +select a+2 from x as r2 where a < 10 +) +select a from x s2 +) +) as r +from t3 +) +select * from cte; +ERROR 21000: Subquery returns more than 1 row +with +cte as +( +select ( +with +x as +( +select ( select a from x ) as a from t1 +) +select exists ( +with recursive +x as +( +select a from t2 +union +select a+2 from x as r2 where a < 10 +) +select a from x +) +) as r +from t3 +) +select * from cte; +r +1 +1 +with +cte_e as +( +with +cte as +( +select ( +with +x as +( +select ( select a from x ) from t1 +) +select exists ( +with recursive +x as +( +select a from t2 +union +select a+2 from x as r2 where a < 10 +) +select a from x +) +) as r +from t3 +) +select * from cte +) +select s1.*, s2.* from cte_e as s1, cte_e as s2; +r r +1 1 +1 1 +1 1 +1 1 +with +x as +( +select a from t1 union select a+1 from x as r1 where a < 7 +) +select * from x as s1; +a +4 +5 +2 +6 +7 +with +x as +( +select a from t2 +union +select a+2 from x as r2 where a < 10 +) +select a from x as s2; +a +6 +8 +5 +9 +3 +7 +with recursive +x as +( +select a from t1 union select a+1 from x as r1 where a < 7 +) +select * from x as s1; +a +4 +5 +6 +7 +with recursive +x as +( +select a from t2 +union +select a+2 from x as r2 where a < 10 +) +select a from x as s2; +a +6 +8 +10 +with +cte as +( +with +x as +( +select a from t1 union select a+1 from x as r1 where a < 7 +) +select * from x as s1 +where s1.a in ( +with recursive +x as +( +select a from t2 +union +select a+2 from x as r2 where a < 10 +) +select a from x as s2 +) +) +select * from cte; +a +6 +with +cte as +( +with +x as +( +select a from t1 union select a+1 from x as r1 where a < 7 +) +select * from x as s1 +where s1.a in ( +with +x as +( +select a from t2 +union +select a+2 from x as r2 where a < 10 +) +select a from x as s2 +) +) +select * from cte; +a +4 +6 +7 +with +cte as +( +with recursive +y as +( +select a from t1 union select a+1 from y as r1 where a < 7 +) +select * from y as s1 +where s1.a in ( +with +x as +( +select a from t2 +union +select a+2 from x as r2 where a < 10 +) +select a from x as s2 +) +) +select * from cte; +a +5 +6 +7 +with +cte as +( +with +y(a) as +( +select a+5 from t1 +) +select * from y as s1 +where s1.a in ( +with +x as +( +select a from t2 +union +select a+2 from x as r2 where a < 10 +) +select a from x as s2 +) +) +select * from cte; +a +9 +with +cte as +( +select ( +with +x as +( +select a from x as r1 +) +select * from x as s1 +where s1.a in ( +with +recursive x as +( +select a from t2 +union +select a+2 from x as r2 where a < 10 +) +select a from x as s2 +) +) as r +from t3 +) +select * from cte; +r +6 +6 +with +cte as +( +select ( +with +x as +( +select a from x as r1 +) +select * from x as s1 +where s1.a < 5 and +s1.a in ( +with +x as +( +select a from t2 +union +select a+2 from x as r2 where a < 10 +) +select a from x as s2 +) +) as r +from t3 +) +select * from cte; +r +3 +3 +drop table t1,t2,t3,x; +# # End of 10.3 tests # diff --git a/mysql-test/main/cte_recursive.test b/mysql-test/main/cte_recursive.test index ca97c2d..f5babc6 100644 --- a/mysql-test/main/cte_recursive.test +++ b/mysql-test/main/cte_recursive.test @@ -3207,5 +3207,669 @@ set @@sql_mode=default; drop table t1,t2; --echo # +--echo # MDEV-29361: Embedded recursive / non-recursive CTE within +--echo # the scope of another embedded CTE with the same name +--echo # + +create table t1 (a int); +insert into t1 values (4), (5); +create table t2 (a int); +insert into t2 values (6), (8); +create table t3 (a int); +insert into t3 values (1), (9); + + +with recursive +x as +( + select a from t1 union select a+1 from x as r1 where a < 7 +) +select * from x as s1; + +with recursive +x as +( + select a from t2 + union + select a+2 from x as r2 where a < 10 +) +select a from x as s2; + +# All recursive CTEs with name x are embedded in in the definition of 'cte', +# without this embedding CTE the bug could not be reproduced + +# two recursive CTEs with name x, the second CTE is in the scope +# of the first one, but does not use it +# before fix of this bug: wrong result set + +with +cte as +( + with recursive + x as + ( + select a from t1 union select a+1 from x as r1 where a < 7 + ) + select * from x as s1 + where s1.a in ( + with recursive + x as + ( + select a from t2 + union + select a+2 from x as r2 where a < 10 + ) + select a from x as s2 + ) +) +select * from cte; + + +# two recursive CTEs with name x, the second CTE is in the scope of the first +# one, but does not use it; there are two non-recursive references to the latter +# before fix of this bug: wrong result set + +with +cte as +( + with recursive + x(a) as + ( + select a from t1 union select a+1 from x as r1 where a < 7 + ) + select s1.a from x as s1, x + where s1.a = x.a and + x.a in ( + with recursive + x(a) as + ( + select a from t2 + union + select a+2 from x as r2 where a < 10 + ) + select a from x as s2 + ) +) +select * from cte; + + +# x as r1 belongs to the definition of CTE x from non-RECURSIVE with clause +# before fix of this bug: infinite sequence of recursive calls + +--error ER_NO_SUCH_TABLE +with +cte as +( + with + x as + ( + select a from t1 union select a+1 from x as r1 where a < 7 + ) + select * from x as s1 + where s1.a in ( + with recursive + x as + ( + select a from t2 + union + select a+2 from x as r2 where a < 10 + ) + select a from x as s2 + ) +) +select * from cte; + + +# x as r2 belongs to the definition of CTE x from non-RECURSIVE with clause +# yet it is in the scope of another CTE with the same name +# before fix of this bug: crash in With_element::get_name() + +with +cte as +( + with recursive + x as + ( + select a from t1 union select a+1 from x as r1 where a < 7 + ) + select * from x as s1 + where s1.a in ( + with + x as + ( + select a from t2 + union + select a+2 from x as r2 where a < 10 + ) + select a from x as s2 + ) +) +select * from cte; + + +# x as r1 is in the definition of CTE x from non-RECURSIVE with clause, thus +# although x as r2 is in the scope of the first CTE x an error is expected +# before fix of this bug: crash in With_element::get_name() + +--error ER_NO_SUCH_TABLE +with +cte as +( + with + x as + ( + select a from t1 union select a+1 from x as r1 where a < 7 + ) + select * from x as s1 + where s1.a in ( + with + x as + ( + select a from t2 + union + select a+2 from x as r2 where a < 10 + ) + select a from x as s2 + ) +) +select * from cte; + + +# x as r2 belongs to the definition of CTE x from non-RECURSIVE with clause +# and in the scope of recursive CTE y, but does not use the latter +# before fix of this bug: crash in With_element::get_name() + +--error ER_NO_SUCH_TABLE +with +cte as +( + with recursive + y as + ( + select a from t1 union select a+1 from y as r1 where a < 7 + ) + select * from y as s1 + where s1.a in ( + with + x as + ( + select a from t2 + union + select a+2 from x as r2 where a < 10 + ) + select a from x as s2 + ) +) +select * from cte; + + +# x as r2 belongs to the definition of CTE x from non-RECURSIVE with clause +# and in the scope of non-recursive CTE y, but does not use the latter +# before fix of this bug: crash in With_element::get_name() + +--error ER_NO_SUCH_TABLE +with +cte as +( + with + y(a) as + ( + select a+5 from t1 + ) + select * from y as s1 + where s1.a in ( + with + x as + ( + select a from t2 + union + select a+2 from x as r2 where a < 10 + ) + select a from x as s2 + ) +) +select * from cte; + + +# in the subquery of the embedding CTE cte: +# x as r1 is in the definition of CTE x from non-RECURSIVE with clause; +# x as t2 is in the definition of CTE x from RECURSIVE with clause; +# an error is expected to be reported for x as r1 +# before fix of this bug: infinite sequence of recursive calls + +--error ER_NO_SUCH_TABLE +with +cte as +( + select ( + with + x as + ( + select a from x as r1 + ) + select * from x as s1 + where s1.a in ( + with recursive + x as + ( + select a from t2 + union + select a+2 from x as r2 where a < 10 + ) + select a from x as s2 + ) + ) as r + from t3 +) +select * from cte; + + +# in the subquery of the embedding CTE cte: +# x as r1 is in the definition of CTE x from non-RECURSIVE with clause, thus +# although x as r2 is in the scope of the first CTE x an error is expected +# before fix of this bug: crash in With_element::get_name() + +--error ER_NO_SUCH_TABLE +with +cte as +( + select ( + with + x as + ( + select a from x as r1 + ) + select * from x as s1 + where s1.a < 5 and + s1.a in ( + with + x as + ( + select a from t2 + union + select a+2 from x as r2 where a < 10 + ) + select a from x as s2 + ) + ) as r + from t3 +) +select * from cte; + + +# in the subquery of the embedding CTE cte: +# two recursive CTEs with name x, the second CTE is in the scope +# of the first one, but does not use it +# before fix of this bug: wrong result set + +with +cte as +( + select ( + with recursive + x(a) as + ( + select a+3 from t1 union select a+1 from x as r1 where a < 7 + ) + select * from x as s1 + where s1.a < 8 and + s1.a in ( + with recursive + x(a) as + ( + select a-2 from t2 + union + select a+1 from x as r2 where a < 10 + ) + select a from x as s2 + ) + ) as r + from t3 +) +select * from cte; + + +# in the subquery of the embedding CTE cte: +# two recursive CTEs with name x, the second CTE is in the scope +# of the first one, but does not use it +# before fix of this bug: Subquery returns more than 1 row + +with +cte as +( + select ( + with recursive + x as + ( + select a from t1 union select a+1 from x as r1 where a < 7 + ) + select * from x as s1 + where s1.a in ( + with recursive + x as + ( + select a from t2 + union + select a+2 from x as r2 where a < 10 + ) + select a from x as s2 + ) + ) as r + from t3 +) +select * from cte; + + +create table x (a int); +insert into x values (3), (7), (1), (5), (6); + + +# in the subquery of the embedding CTE cte: +# one non-recursive CTEs with name x using table t in a subquery, the second +# CTE x is recursive and is in the scope of the first one, but does not use it; +# the query uses both CTE with name x. +# before fix of this bug: infinite sequence of recursive calls + +--error ER_SUBQUERY_NO_1_ROW +with +cte as +( + select ( + with + x as + ( + select ( select a from x as r1 ) as a from t1 + ) + select * from x as s1 + where s1.a in ( + with recursive + x as + ( + select a from t2 + union + select a+2 from x as r2 where a < 10 + ) + select a from x s2 + ) + ) as r + from t3 +) +select * from cte; + + +# in the subquery of the embedding CTE cte: +# one non-recursive CTEs with name x using table t, the second CTE x is +# recursive and is in the scope of the first one, but does not use it; +# the query uses only the second CTE with name x. +# before fix of this bug: Subquery returns more than 1 row + +with +cte as +( + select ( + with + x as + ( + select ( select a from x ) as a from t1 + ) + select exists ( + with recursive + x as + ( + select a from t2 + union + select a+2 from x as r2 where a < 10 + ) + select a from x + ) + ) as r + from t3 +) +select * from cte; + + +# in the subquery of the embedding CTE cte embedded in the CTE cte_e +# one non-recursive CTEs with name x uses table t1, the second CTE x is +# recursive and is in the scope of the first one, but does not use it; +# CTE cte uses only the second CTE with name x; +# the query has two refeences to cte_e +# before fix of this bug: infinite sequence of recursive calls + +with +cte_e as +( + with + cte as + ( + select ( + with + x as + ( + select ( select a from x ) from t1 + ) + select exists ( + with recursive + x as + ( + select a from t2 + union + select a+2 from x as r2 where a < 10 + ) + select a from x + ) + ) as r + from t3 + ) + select * from cte +) +select s1.*, s2.* from cte_e as s1, cte_e as s2; + + +# check : with base table x all queries abobe that returned error +# message ER_NO_SUCH_TABLE now return proper result sets + +with +x as +( + select a from t1 union select a+1 from x as r1 where a < 7 +) +select * from x as s1; + +with +x as +( + select a from t2 + union + select a+2 from x as r2 where a < 10 +) +select a from x as s2; + +with recursive +x as +( + select a from t1 union select a+1 from x as r1 where a < 7 +) +select * from x as s1; + +with recursive +x as +( + select a from t2 + union + select a+2 from x as r2 where a < 10 +) +select a from x as s2; + + +# x as r1 is bound to table x, x as s1 is bound to the first CTE x +# x as r2 and x as s2 are bound to the second CTE x +# before fix of this bug: infinite sequence of recursive calls + +with +cte as +( + with + x as + ( + select a from t1 union select a+1 from x as r1 where a < 7 + ) + select * from x as s1 + where s1.a in ( + with recursive + x as + ( + select a from t2 + union + select a+2 from x as r2 where a < 10 + ) + select a from x as s2 + ) +) +select * from cte; + + +# x as r1 is bound to table x, x as s1 is bound to the first CTE x +# x as r1 is bound to the first CTE x, x as s2 is bound to the second CTE x +# before fix of this bug: crash in With_element::get_name() + +with +cte as +( + with + x as + ( + select a from t1 union select a+1 from x as r1 where a < 7 + ) + select * from x as s1 + where s1.a in ( + with + x as + ( + select a from t2 + union + select a+2 from x as r2 where a < 10 + ) + select a from x as s2 + ) +) +select * from cte; + + +# x as r2 is bound to table x, x as s2 is bound to CTE x +# before fix of this bug: crash in With_element::get_name() + +with +cte as +( + with recursive + y as + ( + select a from t1 union select a+1 from y as r1 where a < 7 + ) + select * from y as s1 + where s1.a in ( + with + x as + ( + select a from t2 + union + select a+2 from x as r2 where a < 10 + ) + select a from x as s2 + ) +) +select * from cte; + + +# x as r2 is bound to table x, x as s2 is bound to CTE x +# before fix of this bug: crash in With_element::get_name() + +with +cte as +( + with + y(a) as + ( + select a+5 from t1 + ) + select * from y as s1 + where s1.a in ( + with + x as + ( + select a from t2 + union + select a+2 from x as r2 where a < 10 + ) + select a from x as s2 + ) +) +select * from cte; + + +# x as r1 is bound to table x, x as s1 is bound to the first CTE x +# x as r2 and x as s2 are bound to the second CTE x +# before fix of this bug: infinite sequence of recursive calls + +with +cte as +( + select ( + with + x as + ( + select a from x as r1 + ) + select * from x as s1 + where s1.a in ( + with + recursive x as + ( + select a from t2 + union + select a+2 from x as r2 where a < 10 + ) + select a from x as s2 + ) + ) as r + from t3 +) +select * from cte; + + +# x as r1 is bound to table x, x as s1 is bound to the first CTE x +# x as r2 is bound to the first CTE x, x as s2 is bound to the second CTE x +# before fix of this bug: crash in With_element::get_name() + +with +cte as +( + select ( + with + x as + ( + select a from x as r1 + ) + select * from x as s1 + where s1.a < 5 and + s1.a in ( + with + x as + ( + select a from t2 + union + select a+2 from x as r2 where a < 10 + ) + select a from x as s2 + ) + ) as r + from t3 +) +select * from cte; + + +drop table t1,t2,t3,x; + +--echo # --echo # End of 10.3 tests --echo # diff --git a/sql/sql_cte.cc b/sql/sql_cte.cc index 9acdf22..d7e3eec 100644 --- a/sql/sql_cte.cc +++ b/sql/sql_cte.cc @@ -400,9 +400,17 @@ bool With_element::check_dependencies_in_spec() { for (st_select_lex *sl= spec->first_select(); sl; sl= sl->next_select()) { - st_unit_ctxt_elem ctxt0= {NULL, owner->owner}; - st_unit_ctxt_elem ctxt1= {&ctxt0, spec}; - check_dependencies_in_select(sl, &ctxt1, false, &sl->with_dep); + if (owner->with_recursive) + { + st_unit_ctxt_elem ctxt0= {NULL, owner->owner}; + st_unit_ctxt_elem ctxt1= {&ctxt0, spec}; + check_dependencies_in_select(sl, &ctxt1, false, &sl->with_dep); + } + else + { + st_unit_ctxt_elem ctxt= {NULL, spec}; + check_dependencies_in_select(sl, &ctxt, false, &sl->with_dep); + } base_dep_map|= sl->with_dep; } return false; @@ -470,29 +478,50 @@ With_element *With_clause::find_table_def(TABLE_LIST *table, With_element *find_table_def_in_with_clauses(TABLE_LIST *tbl, st_unit_ctxt_elem *ctxt) { - With_element *barrier= NULL; + With_element *found= 0; for (st_unit_ctxt_elem *unit_ctxt_elem= ctxt; unit_ctxt_elem; unit_ctxt_elem= unit_ctxt_elem->prev) { st_select_lex_unit *unit= unit_ctxt_elem->unit; With_clause *with_clause= unit->with_clause; - if (with_clause && - (tbl->with= with_clause->find_table_def(tbl, barrier))) - return tbl->with; - barrier= NULL; - if (unit->with_element && !unit->with_element->get_owner()->with_recursive) + /* + First look for the table definition in the with clause attached to 'unit' + if there is any such clause. + */ + if (with_clause) { - /* - This unit is the specification if the with element unit->with_element. - The with element belongs to a with clause without the specifier RECURSIVE. - So when searching for the matching definition of tbl this with clause must - be looked up to this with element - */ - barrier= unit->with_element; + found= with_clause->find_table_def(tbl, NULL); + if (found) + break; + } + /* + If 'unit' is the unit that defines a with element then reset 'unit' + to the unit whose attached with clause contains this with element. + */ + With_element *with_elem= unit->with_element; + if (with_elem) + { + if (!(unit_ctxt_elem= unit_ctxt_elem->prev)) + break; + unit= unit_ctxt_elem->unit; + } + with_clause= unit->with_clause; + /* + Now look for the table definition in this with clause. If the with clause + contains RECURSIVE the search is performed through all CTE definitions in + clause, otherwise up to the definition of 'with_elem' unless it is NULL. + */ + if (with_clause) + { + found= with_clause->find_table_def(tbl, + with_clause->with_recursive ? + NULL : with_elem); + if (found) + break; } } - return NULL; + return found; } @@ -522,22 +551,30 @@ void With_element::check_dependencies_in_select(st_select_lex *sl, bool in_subq, table_map *dep_map) { - With_clause *with_clause= sl->get_with_clause(); + bool is_spec_select= sl->get_with_element() == this; + for (TABLE_LIST *tbl= sl->table_list.first; tbl; tbl= tbl->next_local) { - if (tbl->derived || tbl->nested_join) + if (tbl->with || tbl->derived || tbl->nested_join) continue; tbl->with_internal_reference_map= 0; /* - If there is a with clause attached to the unit containing sl - look first for the definition of tbl in this with clause. - If such definition is not found there look in the with - clauses of the upper levels. + Look first for the definition of tbl in the with clause to which + this with element belongs. If such definition is not found there + look in the with clauses of the upper levels via the context + chain of embedding with elements. If the definition of tbl is found somewhere in with clauses - then tbl->with is set to point to this definition + then tbl->with is set to point to this definition. */ - if (with_clause && !tbl->with) - tbl->with= with_clause->find_table_def(tbl, NULL); + if (is_spec_select) + { + With_clause *with_clause= sl->master_unit()->with_clause; + if (with_clause) + tbl->with= with_clause->find_table_def(tbl, NULL); + if (!tbl->with) + tbl->with= owner->find_table_def(tbl, + owner->with_recursive ? NULL : this); + } if (!tbl->with) tbl->with= find_table_def_in_with_clauses(tbl, ctxt); @@ -564,8 +601,7 @@ void With_element::check_dependencies_in_select(st_select_lex *sl, st_select_lex_unit *inner_unit= sl->first_inner_unit(); for (; inner_unit; inner_unit= inner_unit->next_unit()) { - if (!inner_unit->with_element) - check_dependencies_in_unit(inner_unit, ctxt, in_subq, dep_map); + check_dependencies_in_unit(inner_unit, ctxt, in_subq, dep_map); } } @@ -643,10 +679,14 @@ void With_element::check_dependencies_in_unit(st_select_lex_unit *unit, bool in_subq, table_map *dep_map) { + st_unit_ctxt_elem unit_ctxt_elem= {ctxt, unit}; if (unit->with_clause) - check_dependencies_in_with_clause(unit->with_clause, ctxt, in_subq, dep_map); + { + (void) unit->with_clause->check_dependencies(); + check_dependencies_in_with_clause(unit->with_clause, &unit_ctxt_elem, + in_subq, dep_map); + } in_subq |= unit->item != NULL; - st_unit_ctxt_elem unit_ctxt_elem= {ctxt, unit}; st_select_lex *sl= unit->first_select(); for (; sl; sl= sl->next_select()) { diff --git a/sql/sql_cte.h b/sql/sql_cte.h index 47071dd..b270818 100644 --- a/sql/sql_cte.h +++ b/sql/sql_cte.h @@ -391,10 +391,24 @@ class With_clause : public Sql_alloc bool add_with_element(With_element *elem); /* Add this with clause to the list of with clauses used in the statement */ - void add_to_list(With_clause ** &last_next) + void add_to_list(With_clause **ptr, With_clause ** &last_next) { - *last_next= this; - last_next= &this->next_with_clause; + if (embedding_with_clause) + { + /* + An embedded with clause is always placed before the embedding one + in the list of with clauses used in the query. + */ + while (*ptr != embedding_with_clause) + ptr= &(*ptr)->next_with_clause; + *ptr= this; + next_with_clause= embedding_with_clause; + } + else + { + *last_next= this; + last_next= &this->next_with_clause; + } } void set_owner(st_select_lex_unit *unit) { owner= unit; } diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 5774057..e457036 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -15374,7 +15374,8 @@ with_clause: Lex->derived_tables|= DERIVED_WITH; Lex->with_cte_resolution= true; Lex->curr_with_clause= with_clause; - with_clause->add_to_list(Lex->with_clauses_list_last_next); + with_clause->add_to_list(&Lex->with_clauses_list, + Lex->with_clauses_list_last_next); } with_list { diff --git a/sql/sql_yacc_ora.yy b/sql/sql_yacc_ora.yy index 2a42abe..bcb6c98 100644 --- a/sql/sql_yacc_ora.yy +++ b/sql/sql_yacc_ora.yy @@ -15352,7 +15352,8 @@ with_clause: Lex->derived_tables|= DERIVED_WITH; Lex->with_cte_resolution= true; Lex->curr_with_clause= with_clause; - with_clause->add_to_list(Lex->with_clauses_list_last_next); + with_clause->add_to_list(&Lex->with_clauses_list, + Lex->with_clauses_list_last_next); } with_list {
participants (1)
-
IgorBabaev