diff --git a/query_classifier/test/CMakeLists.txt b/query_classifier/test/CMakeLists.txt index eda63761f..a44e39981 100644 --- a/query_classifier/test/CMakeLists.txt +++ b/query_classifier/test/CMakeLists.txt @@ -48,8 +48,12 @@ if (BUILD_QC_MYSQLEMBEDDED) add_test(TestQC_version_sensitivity version_sensitivity) if(NOT (MYSQL_EMBEDDED_VERSION VERSION_LESS 10.2)) - add_test(TestQC_cte_simple compare -v 2 ${CMAKE_CURRENT_SOURCE_DIR}/cte_simple.test) + add_test(TestQC_cte_simple compare -v 2 ${CMAKE_CURRENT_SOURCE_DIR}/cte_simple.test) + add_test(TestQC_cte_grant compare -v 2 ${CMAKE_CURRENT_SOURCE_DIR}/cte_grant.test) + add_test(TestQC_cte_nonrecursive compare -v 2 ${CMAKE_CURRENT_SOURCE_DIR}/cte_nonrecursive.test) + add_test(TestQC_cte_recursive compare -v 2 ${CMAKE_CURRENT_SOURCE_DIR}/cte_recursive.test) endif() + if(NOT (MYSQL_EMBEDDED_VERSION VERSION_LESS 10.3)) add_test(TestQC_Oracle-binlog_stm_ps compare -v 2 ${CMAKE_CURRENT_SOURCE_DIR}/oracle/binlog_stm_ps.test) add_test(TestQC_Oracle-binlog_stm_sp compare -v 2 ${CMAKE_CURRENT_SOURCE_DIR}/oracle/binlog_stm_sp.test) diff --git a/query_classifier/test/cte_grant.test b/query_classifier/test/cte_grant.test new file mode 100644 index 000000000..44fd4a0bc --- /dev/null +++ b/query_classifier/test/cte_grant.test @@ -0,0 +1,79 @@ +# Can't test with embedded server +-- source include/not_embedded.inc + +# Save the initial number of concurrent sessions +--source include/count_sessions.inc + +connect (root,localhost,root,,test); +connection root; + +--disable_warnings +create database mysqltest; +--enable_warnings + +create user mysqltest_1@localhost; +connect (user1,localhost,mysqltest_1,,test); +connection user1; + +connection root; + +create table mysqltest.t1 (a int, b int); +insert into mysqltest.t1 values (2,10), (1,30); +create table mysqltest.t2 (c int, d char(32)); +insert into mysqltest.t2 values (1,'xxx'), (1,'zzz'); + +grant select on mysqltest.t1 to mysqltest_1@localhost; +grant select (c) on mysqltest.t2 to mysqltest_1@localhost; + +connection user1; +with t as (select c from mysqltest.t2 where c < 2) +select t.c,t1.b from t,mysqltest.t1 where t.c=t1.a; +--error ER_COLUMNACCESS_DENIED_ERROR +select t.c,t.d,t1.b +from (select c,d from mysqltest.t2 where c < 2) as t, mysqltest.t1 +where t.c=t1.a; +--error ER_COLUMNACCESS_DENIED_ERROR +with t as (select c,d from mysqltest.t2 where c < 2) +select t.c,t.d,t1.b from t,mysqltest.t1 where t.c=t1.a; + +connection root; + +create view mysqltest.v1(f1,f2) as +with t as (select c from mysqltest.t2 where c < 2) +select t.c,t1.b from t,mysqltest.t1 where t.c=t1.a; +create view mysqltest.v2(c,d) as +with t as (select a from mysqltest.t1 where a>=3) +select t.a,b from t,mysqltest.t1 where mysqltest.t1.a = t.a; + +grant select on mysqltest.v1 to mysqltest_1@localhost; +grant select (c) on mysqltest.v2 to mysqltest_1@localhost; +grant create view on mysqltest.* to mysqltest_1@localhost; + +connection user1; + +create view mysqltest.v3(c,d) as +with t as (select c from mysqltest.t2 where c < 2) +select t.c,t1.b from t,mysqltest.t1 where t.c=t1.a; +--error ER_COLUMNACCESS_DENIED_ERROR +create view mysqltest.v4(f1,f2,f3) as +with t as (select c,d from mysqltest.t2 where c < 2) +select t.c,t.d,t1.b from t,mysqltest.t1 where t.c=t1.a; + +select * from mysqltest.v1; + +select c from mysqltest.v2; +# there are no privileges on column 'd' +--error ER_COLUMNACCESS_DENIED_ERROR +select d from mysqltest.v2; + +--error ER_TABLEACCESS_DENIED_ERROR +select * from mysqltest.v3; +connection root; +grant select on mysqltest.v3 to mysqltest_1@localhost; +connection user1; +select * from mysqltest.v3; + +connection root; +revoke all privileges on mysqltest.v1 from mysqltest_1@localhost; +drop user mysqltest_1@localhost; +drop database mysqltest; \ No newline at end of file diff --git a/query_classifier/test/cte_nonrecursive.test b/query_classifier/test/cte_nonrecursive.test new file mode 100644 index 000000000..0cc63104f --- /dev/null +++ b/query_classifier/test/cte_nonrecursive.test @@ -0,0 +1,683 @@ +create table t1 (a int, b varchar(32)); +insert into t1 values + (4,'aaaa' ), (7,'bb'), (1,'ccc'), (4,'dd'); +insert into t1 values + (3,'eee'), (7,'bb'), (1,'fff'), (4,'ggg'); +create table t2 (c int); +insert into t2 values + (2), (4), (5), (3); + +--echo # select certain field in the specification of t +with t as (select a from t1 where b >= 'c') + select * from t2,t where t2.c=t.a; +select * from t2, (select a from t1 where b >= 'c') as t + where t2.c=t.a; +explain +with t as (select a from t1 where b >= 'c') + select * from t2,t where t2.c=t.a; +explain +select * from t2, (select a from t1 where b >= 'c') as t + where t2.c=t.a; + +--echo # select '*' in the specification of t +with t as (select * from t1 where b >= 'c') + select * from t2,t where t2.c=t.a; +select * from t2, (select * from t1 where b >= 'c') as t + where t2.c=t.a; +explain +with t as (select * from t1 where b >= 'c') + select * from t2,t where t2.c=t.a; +explain +select * from t2, (select * from t1 where b >= 'c') as t + where t2.c=t.a; + +--echo # rename fields returned by the specication when defining t +with t(f1,f2) as (select * from t1 where b >= 'c') + select * from t2,t where t2.c=t.f1; +explain +with t(f1,f2) as (select * from t1 where b >= 'c') + select * from t2,t where t2.c=t.f1; + +--echo # materialized query specifying t +with t as (select a, count(*) from t1 where b >= 'c' group by a) + select * from t2,t where t2.c=t.a; +select * from t2, (select a, count(*) from t1 where b >= 'c' group by a) as t + where t2.c=t.a; +explain +with t as (select a, count(*) from t1 where b >= 'c' group by a) + select * from t2,t where t2.c=t.a; +explain +select * from t2, (select a, count(*) from t1 where b >= 'c' group by a) as t + where t2.c=t.a; + +--echo # specivication of t contains having +with t as (select a, count(*) from t1 where b >= 'c' + group by a having count(*)=1 ) + select * from t2,t where t2.c=t.a; +select * from t2, (select a, count(*) from t1 where b >= 'c' + group by a having count(*)=1) t + where t2.c=t.a; + +--echo # main query contains having +with t as (select * from t2 where c <= 4) + select a, count(*) from t1,t where t1.a=t.c group by a having count(*)=1; +select a, count(*) from t1, (select * from t2 where c <= 4) t + where t1.a=t.c group by a having count(*)=1; + +--echo # main query contains group by + order by +with t as (select * from t2 where c <= 4 ) + select a, count(*) from t1,t where t1.a=t.c group by a order by count(*); +select a, count(*) from t1, (select * from t2 where c <= 4 ) t + where t1.a=t.c group by a order by count(*); + +--echo # main query contains group by + order by + limit +with t as (select * from t2 where c <= 4 ) + select a, count(*) from t1,t + where t1.a=t.c group by a order by count(*) desc limit 1; +select a, count(*) from t1, (select * from t2 where c <= 4 ) t + where t1.a=t.c group by a order by count(*) desc limit 1; + + +--echo # t is used in a subquery +with t as (select a from t1 where a<5) + select * from t2 where c in (select a from t); +select * from t2 + where c in (select a from (select a from t1 where a<5) as t); +explain +with t as (select a from t1 where a<5) + select * from t2 where c in (select a from t); +explain +select * from t2 + where c in (select a from (select a from t1 where a<5) as t); + +--echo # materialized t is used in a subquery +with t as (select count(*) as c from t1 where b >= 'c' group by a) + select * from t2 where c in (select c from t); +select * from t2 + where c in (select c from (select count(*) as c from t1 + where b >= 'c' group by a) as t); +explain +with t as (select count(*) as c from t1 where b >= 'c' group by a) + select * from t2 where c in (select c from t); +explain +select * from t2 + where c in (select c from (select count(*) as c from t1 + where b >= 'c' group by a) as t); + +--echo # two references to t specified by a query +--echo # selecting a field: both in main query +with t as (select a from t1 where b >= 'c') + select * from t as r1, t as r2 where r1.a=r2.a; +select * from (select a from t1 where b >= 'c') as r1, + (select a from t1 where b >= 'c') as r2 + where r1.a=r2.a; +explain +with t as (select a from t1 where b >= 'c') + select * from t as r1, t as r2 where r1.a=r2.a; +explain +select * from (select a from t1 where b >= 'c') as r1, + (select a from t1 where b >= 'c') as r2 + where r1.a=r2.a; + +--echo # two references to materialized t: both in main query +with t as (select distinct a from t1 where b >= 'c') + select * from t as r1, t as r2 where r1.a=r2.a; +select * from (select distinct a from t1 where b >= 'c') as r1, + (select distinct a from t1 where b >= 'c') as r2 + where r1.a=r2.a; +explain +with t as (select distinct a from t1 where b >= 'c') + select * from t as r1, t as r2 where r1.a=r2.a; +explain +select * from (select distinct a from t1 where b >= 'c') as r1, + (select distinct a from t1 where b >= 'c') as r2 + where r1.a=r2.a; + +--echo # two references to t specified by a query +--echo # selecting all fields: both in main query +with t as (select * from t1 where b >= 'c') + select * from t as r1, t as r2 where r1.a=r2.a; +select * from (select * from t1 where b >= 'c') as r1, + (select * from t1 where b >= 'c') as r2 + where r1.a=r2.a; +explain +with t as (select * from t1 where b >= 'c') + select * from t as r1, t as r2 where r1.a=r2.a; +explain +select * from (select * from t1 where b >= 'c') as r1, + (select * from t1 where b >= 'c') as r2 + where r1.a=r2.a; + +--echo # two references to t specifying explicitly column names +with t(c) as (select a from t1 where b >= 'c') + select * from t r1, t r2 where r1.c=r2.c; + +--echo # t two references of t used in different parts of a union +with t as (select a from t1 where b >= 'c') + select * from t where a < 2 + union + select * from t where a >= 4; +select * from (select a from t1 where b >= 'c') as t + where t.a < 2 +union +select * from (select a from t1 where b >= 'c') as t + where t.a >= 4; +explain +with t as (select a from t1 where b >= 'c') + select * from t where a < 2 + union + select * from t where a >= 4; +explain +select * from (select a from t1 where b >= 'c') as t + where t.a < 2 +union +select * from (select a from t1 where b >= 'c') as t + where t.a >= 4; + +--echo # specification of t contains union +with t as (select a from t1 where b >= 'f' + union + select c as a from t2 where c < 4) + select * from t2,t where t2.c=t.a; +select * from t2, + (select a from t1 where b >= 'f' + union + select c as a from t2 where c < 4) as t + where t2.c=t.a; +explain +with t as (select a from t1 where b >= 'f' + union + select c as a from t2 where c < 4) + select * from t2,t where t2.c=t.a; +explain +select * from t2, + (select a from t1 where b >= 'f' + union + select c as a from t2 where c < 4) as t + where t2.c=t.a; + +--echo # t is defined in the with clause of a subquery +select t1.a,t1.b from t1,t2 + where t1.a>t2.c and + t2.c in (with t as (select * from t1 where t1.a<5) + select t2.c from t2,t where t2.c=t.a); +select t1.a,t1.b from t1,t2 + where t1.a>t2.c and + t2.c in (select t2.c + from t2,(select * from t1 where t1.a<5) as t + where t2.c=t.a); +explain +select t1.a,t1.b from t1,t2 + where t1.a>t2.c and + t2.c in (with t as (select * from t1 where t1.a<5) + select t2.c from t2,t where t2.c=t.a); +explain +select t1.a,t1.b from t1,t2 + where t1.a>t2.c and + t2.c in (select t2.c + from t2,(select * from t1 where t1.a<5) as t + where t2.c=t.a); + +--echo # two different definitions of t: one in the with clause of the main query, +--echo # the other in the with clause of a subquery +with t as (select c from t2 where c >= 4) + select t1.a,t1.b from t1,t + where t1.a=t.c and + t.c in (with t as (select * from t1 where t1.a<5) + select t2.c from t2,t where t2.c=t.a); +select t1.a,t1.b from t1, (select c from t2 where c >= 4) as t + where t1.a=t.c and + t.c in (select t2.c from t2, (select * from t1 where t1.a<5) as t + where t2.c=t.a); +explain +with t as (select c from t2 where c >= 4) + select t1.a,t1.b from t1,t + where t1.a=t.c and + t.c in (with t as (select * from t1 where t1.a<5) + select t2.c from t2,t where t2.c=t.a); +explain +select t1.a,t1.b from t1, (select c from t2 where c >= 4) as t + where t1.a=t.c and + t.c in (select t2.c from t2, (select * from t1 where t1.a<5) as t + where t2.c=t.a); + +--echo # another with table tt is defined in the with clause of a subquery +--echo # from the specification of t +with t as (select * from t1 + where a>2 and + b in (with tt as (select * from t2 where t2.c<5) + select t1.b from t1,tt where t1.a=tt.c)) + select t.a, count(*) from t1,t where t1.a=t.a group by t.a; +select t.a, count(*) + from t1, + (select * from t1 + where a>2 and + b in (select t1.b + from t1, + (select * from t2 where t2.c<5) as tt + where t1.a=tt.c)) as t + where t1.a=t.a group by t.a; +explain +with t as (select * from t1 + where a>2 and + b in (with tt as (select * from t2 where t2.c<5) + select t1.b from t1,tt where t1.a=tt.c)) + select t.a, count(*) from t1,t where t1.a=t.a group by t.a; +explain +select t.a, count(*) + from t1, + (select * from t1 + where a>2 and + b in (select t1.b + from t1, + (select * from t2 where t2.c<5) as tt + where t1.a=tt.c)) as t + where t1.a=t.a group by t.a; + +--echo # with clause in the specification of a derived table +select * + from t1, + (with t as (select a from t1 where b >= 'c') + select * from t2,t where t2.c=t.a) as tt + where t1.b > 'f' and tt.a=t1.a; +select * + from t1, + (select * from t2, + (select a from t1 where b >= 'c') as t + where t2.c=t.a) as tt + where t1.b > 'f' and tt.a=t1.a; +explain +select * + from t1, + (with t as (select a from t1 where b >= 'c') + select * from t2,t where t2.c=t.a) as tt + where t1.b > 'f' and tt.a=t1.a; +explain +select * + from t1, + (select * from t2, + (select a from t1 where b >= 'c') as t + where t2.c=t.a) as tt + where t1.b > 'f' and tt.a=t1.a; + +--echo # with claused in the specification of a view +create view v1 as +with t as (select a from t1 where b >= 'c') + select * from t2,t where t2.c=t.a; +show create view v1; +select * from v1; +explain +select * from v1; + +--echo # with claused in the specification of a materialized view +create view v2 as +with t as (select a, count(*) from t1 where b >= 'c' group by a) + select * from t2,t where t2.c=t.a; +show create view v2; +select * from v2; +explain +select * from v2; + +--echo # with clause in the specification of a view that whose definition +--echo # table alias for a with table +create view v3 as +with t(c) as (select a from t1 where b >= 'c') +select * from t r1 where r1.c=4; +show create view v3; +select * from v3; + +--echo # with clause in the specification of a view that whose definition +--echo # two table aliases for for the same with table +create view v4(c,d) as +with t(c) as (select a from t1 where b >= 'c') +select * from t r1, t r2 where r1.c=r2.c and r2.c=4; +show create view v4; +select * from v4; +explain +select * from v4; + +drop view v1,v2,v3,v4; + + +--echo # currently any views containing with clause are not updatable +create view v1(a) as +with t as (select a from t1 where b >= 'c') + select t.a from t2,t where t2.c=t.a; +--error ER_NON_UPDATABLE_TABLE +update v1 set a=0 where a > 4; +drop view v1; + + +--echo # prepare of a query containing a definition of a with table t +prepare stmt1 from " +with t as (select a from t1 where b >= 'c') + select * from t2,t where t2.c=t.a; +"; +execute stmt1; +execute stmt1; +deallocate prepare stmt1; + +--echo # prepare of a query containing a definition of a materialized t +prepare stmt1 from " +with t as (select a, count(*) from t1 where b >= 'c' group by a) + select * from t2,t where t2.c=t.a; +"; +execute stmt1; +execute stmt1; +deallocate prepare stmt1; + +--echo # prepare of a query containing two references to with table t +prepare stmt1 from " +with t as (select * from t1 where b >= 'c') + select * from t as r1, t as r2 where r1.a=r2.a; +"; +execute stmt1; +execute stmt1; +deallocate prepare stmt1; + +--ERROR ER_WITH_COL_WRONG_LIST +with t(f) as (select * from t1 where b >= 'c') + select * from t2,t where t2.c=t.f1; + +--ERROR ER_DUP_FIELDNAME +with t(f1,f1) as (select * from t1 where b >= 'c') + select * from t2,t where t2.c=t.f1; + +--ERROR ER_DUP_QUERY_NAME +with t as (select * from t2 where c>3), + t as (select a from t1 where a>2) + select * from t,t1 where t1.a=t.c; + +--ERROR ER_NO_SUCH_TABLE +with t as (select a from s where a<5), + s as (select a from t1 where b>='d') + select * from t,s where t.a=s.a; + +with recursive + t as (select a from s where a<5), + s as (select a from t1 where b>='d') + select * from t,s where t.a=s.a; + +--ERROR ER_RECURSIVE_WITHOUT_ANCHORS +with recursive t as (select * from s where a>2), + s as (select a from t1,r where t1.a>r.c), + r as (select c from t,t2 where t.a=t2.c) + select * from r where r.c<7; + +--ERROR ER_RECURSIVE_WITHOUT_ANCHORS +with recursive + t as (select * from s where a>2), + s as (select a from t1,r where t1.a>r.c), + r as (select c from t,t2 where t.a=t2.c) + select * from r where r.c<7; + +--ERROR ER_RECURSIVE_WITHOUT_ANCHORS +with recursive + t as (select * from t1 + where a in (select c from s where b<='ccc') and b>'b'), + s as (select * from t1,t2 + where t1.a=t2.c and t1.c in (select a from t where a<5)) + select * from s where s.b>'aaa'; + +--ERROR ER_RECURSIVE_WITHOUT_ANCHORS +with recursive + t as (select * from t1 where b>'aaa' and b <='d') + select t.b from t,t2 + where t.a=t2.c and + t2.c in (with recursive + s as (select t1.a from s,t1 where t1.a=s.a and t1.b<'c') + select * from s); +--echo #erroneous definition of unreferenced with table t +--ERROR ER_BAD_FIELD_ERROR +with t as (select count(*) from t1 where d>='f' group by a) + select t1.b from t2,t1 where t1.a = t2.c; + +with t as (select count(*) from t1 where b>='f' group by a) + select t1.b from t2,t1 where t1.a = t2.c; + +--echo #erroneous definition of s referring to unreferenced t +--ERROR ER_BAD_FIELD_ERROR +with t(d) as (select count(*) from t1 where b<='ccc' group by b), + s as (select * from t1 where a in (select t2.d from t2,t where t2.c=t.d)) + select t1.b from t1,t2 where t1.a=t2.c; +--ERROR ER_BAD_FIELD_ERROR +with t(d) as (select count(*) from t1 where b<='ccc' group by b), + s as (select * from t1 where a in (select t2.c from t2,t where t2.c=t.c)) + select t1.b from t1,t2 where t1.a=t2.c; + +with t(d) as (select count(*) from t1 where b<='ccc' group by b), + s as (select * from t1 where a in (select t2.c from t2,t where t2.c=t.d)) + select t1.b from t1,t2 where t1.a=t2.c; + +--echo #erroneous definition of unreferenced with table t +--ERROR ER_WITH_COL_WRONG_LIST +with t(f) as (select * from t1 where b >= 'c') + select t1.b from t2,t1 where t1.a = t2.c; + +--echo #erroneous definition of unreferenced with table t +--ERROR ER_DUP_FIELDNAME +with t(f1,f1) as (select * from t1 where b >= 'c') + select t1.b from t2,t1 where t1.a = t2.c; + +--echo # explain for query with unreferenced with table + +explain +with t as (select a from t1 where b >= 'c') + select t1.b from t2,t1 where t1.a = t2.c; + +explain +with t as (select a, count(*) from t1 where b >= 'c' group by a) + select t1.b from t2,t1 where t1.a = t2.c; + +--echo # too many with elements in with clause +let $m= 65; +let $i= $m; +dec $i; +let $q= with s$m as (select * from t1); +while ($i) +{ + let $q= $q, s$i as (select * from t1) ; + dec $i; + } +let $q= $q select * from s$m; +--ERROR ER_TOO_MANY_DEFINITIONS_IN_WITH_CLAUSE +eval $q; + +drop table t1,t2; + +--echo # +--echo # Bug mdev-9937: View used in the specification of with table +--echo # refers to the base table with the same name +--echo # + +create table t1 (a int); +insert into t1 values (20), (30), (10); +create view v1 as select * from t1 where a > 10; + +with t1 as (select * from v1) select * from t1; + +drop view v1; +drop table t1; + +--echo # +--echo # Bug mdev-10058: Invalid derived table with WITH clause +--echo # + +CREATE TABLE t1 (a INT); +CREATE TABLE t2 (a INT); +CREATE TABLE t3 (a INT); +INSERT INTO t1 VALUES (1),(2),(3); +INSERT INTO t2 VALUES (1),(2),(3); +INSERT INTO t3 VALUES (1),(2),(3); + +--ERROR ER_PARSE_ERROR +SELECT * FROM (WITH a AS (SELECT * FROM t1) (t2 NATURAL JOIN t3)); + +SELECT * FROM (WITH a AS (SELECT * FROM t1) SELECT * FROM t2 NATURAL JOIN t3) AS d1; + +DROP TABLE t1,t2,t3; + +--echo # +--echo # Bug mdev-10344: the WITH clause of the query refers to a view that uses +--echo # a base table with the same name as a CTE table from the clause +--echo # + + +create table ten(a int primary key); +insert into ten values (0),(1),(2),(3),(4),(5),(6),(7),(8),(9); + +create table one_k(a int primary key); +insert into one_k select A.a + B.a* 10 + C.a * 100 from ten A, ten B, ten C; + +create view v1 as select * from ten; + +select * from v1; + +drop view v1; +drop table ten, one_k; + +--echo # +--echo # MDEV-10057 : Crash with EXPLAIN + WITH + constant query +--echo # +CREATE TABLE t1 (a INT); +INSERT INTO t1 VALUES (1),(2),(3); +SELECT * FROM (WITH a AS (SELECT * FROM t1) SELECT 1) AS t1; +EXPLAIN SELECT * FROM (WITH a AS (SELECT * FROM t1) SELECT 1) AS t1; +DROP TABLE t1; + +--echo # +--echo # MDEV-10058: Suspicious EXPLAIN output for a derived table + WITH + joined table +--echo # +CREATE TABLE t1 (a INT); +CREATE TABLE t2 (a INT); +CREATE TABLE t3 (a INT); +INSERT INTO t1 VALUES (1),(2),(3); +INSERT INTO t2 VALUES (1),(2),(3); +INSERT INTO t3 VALUES (1),(2),(3); +--error ER_PARSE_ERROR +EXPLAIN SELECT * FROM (WITH a AS (SELECT * FROM t1) (t2 NATURAL JOIN t3)); +explain SELECT * FROM (WITH a AS (SELECT * FROM t1) SELECT * FROM t2 NATURAL JOIN t3) AS d1; +DROP TABLE t1,t2,t3; + +--echo # +--echo # MDEV-10729: Server crashes in st_select_lex::set_explain_type +--echo # +CREATE TABLE t1 (i1 INT, KEY(i1)) ENGINE=MyISAM; +INSERT INTO t1 VALUES (4),(8); + +CREATE TABLE t2 (a2 INT, b2 INT, KEY(b2)) ENGINE=MyISAM; +INSERT INTO t2 VALUES (8,7); + +CREATE TABLE t3 (i3 INT) ENGINE=MyISAM; +INSERT INTO t3 VALUES (2),(6); + +SELECT * FROM t1, t2 WHERE a2 = i1 and b2 >= i1 AND i1 IN ( SELECT i3 FROM t3 ) +UNION +SELECT * FROM t1, t2 WHERE a2 = i1 and b2 >= i1 AND i1 IN ( SELECT i3 FROM t3 ) +; +DROP TABLE t1,t2,t3; + +--echo # +--echo # MDEV-10923: mergeable CTE used twice in the query +--echo # + +create table employees ( + name varchar(32), + dept varchar(32), + country varchar(8) +); + +insert into employees +values +('Sergei Golubchik', 'Development', 'DE'), +('Claudio Nanni', 'Support', 'ES'), +('Sergei Petrunia', 'Development', 'RU'); + +with eng as +( + select * from employees + where dept in ('Development','Support') +), +eu_eng as +( + select * from eng where country IN ('DE','ES','RU') +) +select * from eu_eng T1 +where + not exists (select 1 from eu_eng T2 + where T2.country=T1.country + and T2.name <> T1.name); + +drop table employees; + +--echo # +--echo # MDEV-11818: EXPLAIN EXTENDED for a query with optimized away CTE table +--echo # + +CREATE TABLE t1 (i INT, c VARCHAR(3)); +INSERT INTO t1 VALUES (1,'foo'); + +EXPLAIN EXTENDED +WITH cte AS ( SELECT * FROM t1 ) SELECT i FROM cte; + +DROP TABLE t1; + +--echo # +--echo # MDEV-12185: view defintion contains WITH clause with +--echo # several specifications of CTE +--echo # + +with + alias1 as (select 1 as one), + alias2 as (select 2 as two) +select one, two from alias1, alias2; + +create view v1 as +with + alias1 as (select 1 as one), + alias2 as (select 2 as two) +select one, two from alias1, alias2; + +select * from v1; +show create view v1; + +drop view v1; + +--echo # +--echo # MDEV-12440: the same CTE table is used twice +--echo # + +create table t1 (a int, b varchar(32)); +insert into t1 values + (4,'aaaa' ), (7,'bb'), (1,'ccc'), (4,'dd'); + +--echo # cte2 is used in the main query and in the spec for ct3 +with +cte1 as (select * from t1 where b >= 'c'), +cte2 as (select * from cte1 where a < 7), +cte3 as (select * from cte2 where a > 1) +select * from cte2, cte3 where cte2.a = cte3.a; + +--echo # cte2 is used twice in the spec for ct3 +with +cte1 as (select * from t1 where b >= 'b'), +cte2 as (select * from cte1 where b > 'c'), +cte3 as (select * from cte2 where a > 1 union select * from cte2 where a > 1) +select * from cte3; + +drop table t1; + +--echo # +--echo # MDEV-12558: CTE with the same name as temporary table +--echo # + +CREATE TABLE t ENGINE=MyISAM AS SELECT 1 AS i; +CREATE TEMPORARY TABLE cte ENGINE=MyISAM AS SELECT 2 AS f; + +WITH cte AS ( SELECT i FROM t ) SELECT * FROM cte; +WITH cte AS ( SELECT i FROM t GROUP BY i) SELECT * FROM cte; + +SELECT * FROM cte; + +DROP TABLE cte; +DROP TABLE t; diff --git a/query_classifier/test/cte_recursive.test b/query_classifier/test/cte_recursive.test new file mode 100644 index 000000000..5701ee896 --- /dev/null +++ b/query_classifier/test/cte_recursive.test @@ -0,0 +1,1930 @@ +create table t1 (a int, b varchar(32)); +insert into t1 values +(4,'aaaa' ), (7,'bb'), (1,'ccc'), (4,'dd'); +insert into t1 values +(3,'eee'), (7,'bb'), (1,'fff'), (4,'ggg'); + +--ERROR ER_UNACCEPTABLE_MUTUAL_RECURSION +with recursive +t as +( + select * from t1 where t1.b >= 'c' + union + select * from r +), +r as +( + select * from t + union + select t1.* from t1,r where r.a+1 = t1.a +) +select * from r; + + +--ERROR ER_UNACCEPTABLE_MUTUAL_RECURSION +with recursive +a1(a,b) as +(select * from t1 where t1.a>3 +union +select * from b1 where b1.a >3 +union +select * from c1 where c1.a>3), +b1(a,b) as +(select * from a1 where a1.b > 'ccc' +union +select * from c1 where c1.b > 'ddd'), +c1(a,b) as +(select * from a1 where a1.a<6 and a1.b< 'zz' +union +select * from b1 where b1.b > 'auu') +select * from c1; + +drop table t1; + + +--echo # WITH RECURSIVE vs just WITH + +create table t1 (a int); +insert into t1 values + (0), (1), (2), (3), (4); +create table t2 (a int); +insert into t2 values + (1), (2), (3), (4), (5); + + +--echo # just WITH : s refers to t defined after s +--ERROR ER_NO_SUCH_TABLE +with + s(a) as (select t.a + 10 from t), + t(a) as (select t1.a from t1) +select * from s; + +--echo # WITH RECURSIVE: s refers to t defined after s +with recursive + s(a) as (select t.a + 10 from t), + t(a) as (select t1.a from t1) +select * from s; + +--echo # just WITH : defined t1 is non-recursive and uses base tables t1,t2 +with +t1 as +( +select a from t2 where t2.a=3 +union +select t2.a from t1,t2 where t1.a+1=t2.a +) +select * from t1; + +explain +with +t1 as +( +select a from t2 where t2.a=3 +union +select t2.a from t1,t2 where t1.a+1=t2.a +) +select * from t1; + + +--echo #WITH RECURSIVE : defined t1 is recursive and uses only base table t2 +with recursive +t1 as +( +select a from t2 where t2.a=3 +union +select t2.a from t1,t2 where t1.a+1=t2.a +) +select * from t1; + +explain +with recursive +t1 as +( +select a from t2 where t2.a=3 +union +select t2.a from t1,t2 where t1.a+1=t2.a +) +select * from t1; + +--echo # just WITH : types of t1 columns are determined by all parts of union + +create view v1 as +with +t1 as +( +select a from t2 where t2.a=3 +union +select t2.a+1 from t1,t2 where t1.a=t2.a +) +select * from t1; + +show columns from v1; + + +--echo # WITH RECURSIVE : types of t1 columns are determined by anchor parts + +create view v2 as +with recursive +t1 as +( +select a from t2 where t2.a=3 +union +select t2.a+1 from t1,t2 where t1.a=t2.a +) +select * from t1; + +show columns from v2; + +drop view v1,v2; + +drop table t1,t2; + + +create table folks(id int, name char(32), dob date, father int, mother int); + +insert into folks values +(100, 'Me', '2000-01-01', 20, 30), +(20, 'Dad', '1970-02-02', 10, 9), +(30, 'Mom', '1975-03-03', 8, 7), +(10, 'Grandpa Bill', '1940-04-05', null, null), +(9, 'Grandma Ann', '1941-10-15', null, null), +(25, 'Uncle Jim', '1968-11-18', 8, 7), +(98, 'Sister Amy', '2001-06-20', 20, 30), +(7, 'Grandma Sally', '1943-08-23', null, 6), +(8, 'Grandpa Ben', '1940-10-21', null, null), +(6, 'Grandgrandma Martha', '1923-05-17', null, null), +(67, 'Cousin Eddie', '1992-02-28', 25, 27), +(27, 'Auntie Melinda', '1971-03-29', null, null); + +--echo # simple recursion with one anchor and one recursive select +--echo # the anchor is the first select in the specification +with recursive +ancestors +as +( + select * + from folks + where name = 'Me' and dob = '2000-01-01' + union + select p.id, p.name, p.dob, p.father, p.mother + from folks as p, ancestors AS a + where p.id = a.father or p.id = a.mother +) +select * from ancestors; + +--echo # simple recursion with one anchor and one recursive select +--echo # the anchor is the last select in the specification +with recursive +ancestors +as +( + select p.* + from folks as p, ancestors AS a + where p.id = a.father or p.id = a.mother + union + select * + from folks + where name = 'Me' and dob = '2000-01-01' +) +select * from ancestors; + +--echo # simple recursion with one anchor and one recursive select +--echo # the anchor is the first select in the specification +with recursive +ancestors +as +( + select * + from folks + where name = 'Cousin Eddie' + union + select p.* + from folks as p, ancestors as a + where p.id = a.father or p.id = a.mother +) +select * from ancestors; + +--echo # simple recursion with or in anchor and or in recursive part +with recursive +ancestors +as +( + select * + from folks + where name = 'Me' or name='Sister Amy' + union + select p.* + from folks as p, ancestors as a + where p.id = a.father or p.id = a.mother +) +select * from ancestors; + +--echo # two recursive definition, one uses another +with recursive +prev_gen +as +( + select folks.* + from folks, prev_gen + where folks.id=prev_gen.father or folks.id=prev_gen.mother + union + select * + from folks + where name='Me' +), +ancestors +as +( + select * + from folks + where name='Me' + union + select * + from ancestors + union + select * + from prev_gen +) +select ancestors.name, ancestors.dob from ancestors; + +--echo # recursive definition with two attached non-recursive +with recursive +ancestors(id,name,dob) +as +( + with + father(child_id,id,name,dob) + as + ( + select folks.id, f.id, f.name, f.dob + from folks, folks f + where folks.father=f.id + + ), + mother(child_id,id,name,dob) + as + ( + select folks.id, m.id, m.name, m.dob + from folks, folks m + where folks.mother=m.id + + ) + select folks.id, folks.name, folks.dob + from folks + where name='Me' + union + select f.id, f.name, f.dob + from ancestors a, father f + where f.child_id=a.id + union + select m.id, m.name, m.dob + from ancestors a, mother m + where m.child_id=a.id + +) +select ancestors.name, ancestors.dob from ancestors; + +--echo # simple recursion with one anchor and one recursive select +--echo # the anchor is the first select in the specification +with recursive +descendants +as +( + select * + from folks + where name = 'Grandpa Bill' + union + select folks.* + from folks, descendants as d + where d.id=folks.father or d.id=folks.mother +) +select * from descendants; + +--echo # simple recursion with one anchor and one recursive select +--echo # the anchor is the first select in the specification +with recursive +descendants +as +( + select * + from folks + where name = 'Grandma Sally' + union + select folks.* + from folks, descendants as d + where d.id=folks.father or d.id=folks.mother +) +select * from descendants; + + +--echo # simple recursive table used three times in the main query +with recursive +ancestors +as +( + select * + from folks + where name = 'Me' and dob = '2000-01-01' + union + select p.* + from folks as p, ancestors AS a + where p.id = a.father OR p.id = a.mother +) +select * + from ancestors t1, ancestors t2 + where exists (select * from ancestors a + where a.father=t1.id AND a.mother=t2.id); + + +--echo # simple recursive table used three times in the main query +with +ancestor_couples(husband, h_dob, wife, w_dob) +as +( +with recursive +ancestors +as +( + select * + from folks + where name = 'Me' + union + select p.* + from folks as p, ancestors AS a + where p.id = a.father OR p.id = a.mother +) +select t1.name, t1.dob, t2.name, t2.dob + from ancestors t1, ancestors t2 + where exists (select * from ancestors a + where a.father=t1.id AND a.mother=t2.id) +) +select * from ancestor_couples; + + +--echo # simple recursion with two selects in recursive part +with recursive +ancestors +as +( + select * + from folks + where name = 'Me' + union + select p.* + from folks as p, ancestors as fa + where p.id = fa.father + union + select p.* + from folks as p, ancestors as ma + where p.id = ma.mother +) +select * from ancestors; + + +--echo # mutual recursion with renaming +with recursive +ancestor_couples(h_id, h_name, h_dob, h_father, h_mother, + w_id, w_name, w_dob, w_father, w_mother) +as +( + select h.*, w.* + from folks h, folks w, coupled_ancestors a + where a.father = h.id AND a.mother = w.id + union + select h.*, w.* + from folks v, folks h, folks w + where v.name = 'Me' and + (v.father = h.id AND v.mother= w.id) +), +coupled_ancestors (id, name, dob, father, mother) +as +( + select h_id, h_name, h_dob, h_father, h_mother + from ancestor_couples + union + select w_id, w_name, w_dob, w_father, w_mother + from ancestor_couples +) +select h_name, h_dob, w_name, w_dob + from ancestor_couples; + + +--echo # mutual recursion with union all +with recursive +ancestor_couples(h_id, h_name, h_dob, h_father, h_mother, + w_id, w_name, w_dob, w_father, w_mother) +as +( + select h.*, w.* + from folks h, folks w, coupled_ancestors a + where a.father = h.id AND a.mother = w.id + union + select h.*, w.* + from folks v, folks h, folks w + where v.name = 'Me' and + (v.father = h.id AND v.mother= w.id) +), +coupled_ancestors (id, name, dob, father, mother) +as +( + select h_id, h_name, h_dob, h_father, h_mother + from ancestor_couples + union all + select w_id, w_name, w_dob, w_father, w_mother + from ancestor_couples +) +select h_name, h_dob, w_name, w_dob + from ancestor_couples; + + +--echo # mutual recursion with renaming +with recursive +ancestor_couples(h_id, h_name, h_dob, h_father, h_mother, + w_id, w_name, w_dob, w_father, w_mother) +as +( + select h.*, w.* + from folks h, folks w, coupled_ancestors a + where a.father = h.id AND a.mother = w.id + union + select h.*, w.* + from folks v, folks h, folks w + where v.name = 'Me' and + (v.father = h.id AND v.mother= w.id) +), +coupled_ancestors (id, name, dob, father, mother) +as +( + select h_id, h_name, h_dob, h_father, h_mother + from ancestor_couples + union + select w_id, w_name, w_dob, w_father, w_mother + from ancestor_couples +) +select h_name, h_dob, w_name, w_dob + from ancestor_couples; + + +--echo # mutual recursion with union all +with recursive +ancestor_couples(h_id, h_name, h_dob, h_father, h_mother, + w_id, w_name, w_dob, w_father, w_mother) +as +( + select h.*, w.* + from folks h, folks w, coupled_ancestors a + where a.father = h.id AND a.mother = w.id +), +coupled_ancestors (id, name, dob, father, mother) +as +( + select * + from folks + where name = 'Me' + union all + select h_id, h_name, h_dob, h_father, h_mother + from ancestor_couples + union all + select w_id, w_name, w_dob, w_father, w_mother + from ancestor_couples +) +select h_name, h_dob, w_name, w_dob + from ancestor_couples; + +--echo # mutual recursion with one select in the first definition +with recursive +ancestor_couple_ids(h_id, w_id) +as +( + select a.father, a.mother + from coupled_ancestors a + where a.father is not null and a.mother is not null +), +coupled_ancestors (id, name, dob, father, mother) +as +( + select * + from folks + where name = 'Me' + union all + select p.* + from folks p, ancestor_couple_ids fa + where p.id = fa.h_id + union all + select p.* + from folks p, ancestor_couple_ids ma + where p.id = ma.w_id +) +select * + from ancestor_couple_ids; + + +--echo # join of a mutually recursive table with base tables +with recursive +ancestor_couple_ids(h_id, w_id) +as +( + select a.father, a.mother + from coupled_ancestors a + where a.father is not null and a.mother is not null +), +coupled_ancestors (id, name, dob, father, mother) +as +( + select * + from folks + where name = 'Me' + union all + select p.* + from folks p, ancestor_couple_ids fa + where p.id = fa.h_id + union all + select p.* + from folks p, ancestor_couple_ids ma + where p.id = ma.w_id +) +select h.name, h.dob, w.name, w.dob + from ancestor_couple_ids c, folks h, folks w + where c.h_id = h.id and c.w_id= w.id; + + +--echo # join of two mutually recursive tables +with recursive +ancestor_couple_ids(h_id, w_id) +as +( + select a.father, a.mother + from coupled_ancestors a + where a.father is not null and a.mother is not null +), +coupled_ancestors (id, name, dob, father, mother) +as +( + select * + from folks + where name = 'Me' + union all + select p.* + from folks p, ancestor_couple_ids fa + where p.id = fa.h_id + union all + select p.* + from folks p, ancestor_couple_ids ma + where p.id = ma.w_id +) +select h.name, h.dob, w.name, w.dob + from ancestor_couple_ids c, coupled_ancestors h, coupled_ancestors w + where c.h_id = h.id and c.w_id= w.id; + +explain extended +with recursive +ancestor_couple_ids(h_id, w_id) +as +( + select a.father, a.mother + from coupled_ancestors a + where a.father is not null and a.mother is not null +), +coupled_ancestors (id, name, dob, father, mother) +as +( + select * + from folks + where name = 'Me' + union all + select p.* + from folks p, ancestor_couple_ids fa + where p.id = fa.h_id + union all + select p.* + from folks p, ancestor_couple_ids ma + where p.id = ma.w_id +) +select h.name, h.dob, w.name, w.dob + from ancestor_couple_ids c, coupled_ancestors h, coupled_ancestors w + where c.h_id = h.id and c.w_id= w.id; + + +--echo # simple mutual recursion +with recursive +ancestor_couple_ids(h_id, w_id) +as +( + select a.father, a.mother + from coupled_ancestors a +), +coupled_ancestors (id, name, dob, father, mother) +as +( + select * + from folks + where name = 'Me' + union all + select p.* + from folks p, ancestor_couple_ids fa + where p.id = fa.h_id + union all + select p.* + from folks p, ancestor_couple_ids ma + where p.id = ma.w_id +) +select * + from ancestor_couple_ids; + + +--echo # join of two mutually recursive tables +with recursive +ancestor_couple_ids(h_id, w_id) +as +( + select a.father, a.mother + from coupled_ancestors a +), +coupled_ancestors (id, name, dob, father, mother) +as +( + select * + from folks + where name = 'Me' + union all + select p.* + from folks p, ancestor_couple_ids fa + where p.id = fa.h_id + union all + select p.* + from folks p, ancestor_couple_ids ma + where p.id = ma.w_id +) +select h.name, h.dob, w.name, w.dob + from ancestor_couple_ids c, coupled_ancestors h, coupled_ancestors w + where c.h_id = h.id and c.w_id= w.id; + + +--echo # execution of prepared query using a recursive table +prepare stmt1 from " +with recursive +ancestors +as +( + select * + from folks + where name = 'Me' and dob = '2000-01-01' + union + select p.id, p.name, p.dob, p.father, p.mother + from folks as p, ancestors AS a + where p.id = a.father or p.id = a.mother +) +select * from ancestors; +"; + +execute stmt1; +execute stmt1; + +deallocate prepare stmt1; + + +--echo # view using a recursive table +create view v1 as +with recursive +ancestors +as +( + select * + from folks + where name = 'Me' and dob = '2000-01-01' + union + select p.id, p.name, p.dob, p.father, p.mother + from folks as p, ancestors AS a + where p.id = a.father or p.id = a.mother +) +select * from ancestors; + +show create view v1; + +select * from v1; + +create view v2 as +with recursive +ancestors +as +( + select * + from folks + where name = 'Me' + union + select p.* + from folks as p, ancestors as fa + where p.id = fa.father + union + select p.* + from folks as p, ancestors as ma + where p.id = ma.mother +) +select * from ancestors; + +show create view v2; + +select * from v2; + +drop view v1,v2; + + +explain extended +with recursive +ancestors +as +( + select * + from folks + where name = 'Me' and dob = '2000-01-01' + union + select p.id, p.name, p.dob, p.father, p.mother + from folks as p, ancestors AS a + where p.id = a.father or p.id = a.mother +) +select * from ancestors; + + +--echo # recursive spec with two anchor selects and two recursive ones +with recursive +ancestor_ids (id) +as +( + select father from folks where name = 'Me' + union + select mother from folks where name = 'Me' + union + select father from folks, ancestor_ids a where folks.id = a.id + union + select mother from folks, ancestor_ids a where folks.id = a.id +), +ancestors +as +( + select p.* from folks as p, ancestor_ids as a + where p.id = a.id +) +select * from ancestors; + + +--echo # recursive spec using union all +with recursive +ancestors +as +( + select * + from folks + where name = 'Me' + union all + select p.* + from folks as p, ancestors as fa + where p.id = fa.father + union all + select p.* + from folks as p, ancestors as ma + where p.id = ma.mother +) +select * from ancestors; + + +--ERROR ER_NOT_STANDARD_COMPLIANT_RECURSIVE +with recursive +ancestor_ids (id, generation) +as +( + select father, 1 from folks where name = 'Me' and father is not null + union all + select mother, 1 from folks where name = 'Me' and mother is not null + union all + select father, fa.generation+1 from folks, ancestor_ids fa + where folks.id = fa.id and (father not in (select id from ancestor_ids)) + union all + select mother, ma.generation+1 from folks, ancestor_ids ma + where folks.id = ma.id and (mother not in (select id from ancestor_ids)) +) +select generation, name from ancestor_ids a, folks + where a.id = folks.id; + +set standard_compliant_cte=0; + +--ERROR ER_WITH_COL_WRONG_LIST +with recursive +ancestor_ids (id, generation) +as +( + select father from folks where name = 'Me' and father is not null + union all + select mother from folks where name = 'Me' and mother is not null + union all + select father, fa.generation+1 from folks, ancestor_ids fa + where folks.id = fa.id and (father not in (select id from ancestor_ids)) + union all + select mother, ma.generation+1 from folks, ancestor_ids ma + where folks.id = ma.id and (mother not in (select id from ancestor_ids)) +) +select generation, name from ancestor_ids a, folks + where a.id = folks.id; + +with recursive +ancestor_ids (id, generation) +as +( + select father, 1 from folks where name = 'Me' and father is not null + union all + select mother, 1 from folks where name = 'Me' and mother is not null + union all + select father, fa.generation+1 from folks, ancestor_ids fa + where folks.id = fa.id and father is not null and + (father not in (select id from ancestor_ids)) + union all + select mother, ma.generation+1 from folks, ancestor_ids ma + where folks.id = ma.id and mother is not null and + (mother not in (select id from ancestor_ids)) +) +select generation, name from ancestor_ids a, folks + where a.id = folks.id; + +set standard_compliant_cte=1; + +--ERROR ER_NOT_STANDARD_COMPLIANT_RECURSIVE +with recursive +coupled_ancestor_ids (id) +as +( + select father from folks where name = 'Me' and father is not null + union + select mother from folks where name = 'Me' and mother is not null + union + select n.father + from folks, coupled_ancestor_ids fa, coupled_ancestor_ids ma, folks n + where folks.father = fa.id and folks.mother = ma.id and + (fa.id = n.id or ma.id = n.id) and + n.father is not null and n.mother is not null + union + select n.mother + from folks, coupled_ancestor_ids fa, coupled_ancestor_ids ma, folks n + where folks.father = fa.id and folks.mother = ma.id and + (fa.id = n.id or ma.id = n.id) and + n.father is not null and n.mother is not null +) +select p.* from coupled_ancestor_ids a, folks p + where a.id = p.id; + +set statement standard_compliant_cte=0 for +with recursive +coupled_ancestor_ids (id) +as +( + select father from folks where name = 'Me' and father is not null + union + select mother from folks where name = 'Me' and mother is not null + union + select n.father + from folks, coupled_ancestor_ids fa, coupled_ancestor_ids ma, folks n + where folks.father = fa.id and folks.mother = ma.id and + (fa.id = n.id or ma.id = n.id) and + n.father is not null and n.mother is not null + union + select n.mother + from folks, coupled_ancestor_ids fa, coupled_ancestor_ids ma, folks n + where folks.father = fa.id and folks.mother = ma.id and + (fa.id = n.id or ma.id = n.id) and + n.father is not null and n.mother is not null +) +select p.* from coupled_ancestor_ids a, folks p + where a.id = p.id; + +--ERROR ER_NOT_STANDARD_COMPLIANT_RECURSIVE +with recursive +ancestor_ids (id) +as +( + select father from folks where name = 'Me' + union + select mother from folks where name = 'Me' + union + select father from folks left join ancestor_ids a on folks.id = a.id + union + select mother from folks left join ancestor_ids a on folks.id = a.id +), +ancestors +as +( + select p.* from folks as p, ancestor_ids as a + where p.id = a.id +) +select * from ancestors; + +set statement standard_compliant_cte=0 for +with recursive +ancestor_ids (id) +as +( + select father from folks where name = 'Me' + union + select mother from folks where name = 'Me' + union + select father from folks left join ancestor_ids a on folks.id = a.id + union + select mother from folks left join ancestor_ids a on folks.id = a.id +), +ancestors +as +( + select p.* from folks as p, ancestor_ids as a + where p.id = a.id +) +select * from ancestors; + +with recursive +ancestor_ids (id, generation) +as +( + select father, 1 from folks where name = 'Me' + union + select mother, 1 from folks where name = 'Me' + union + select father, a.generation+1 from folks, ancestor_ids a + where folks.id = a.id + union + select mother, a.generation+1 from folks, ancestor_ids a + where folks.id = a.id +), +ancestors +as +( + select generation, name from folks as p, ancestor_ids as a + where p.id = a.id +) +select * from ancestors; + +--ERROR ER_NOT_STANDARD_COMPLIANT_RECURSIVE +with recursive +ancestor_ids (id, generation) +as +( + select father, 1 from folks where name = 'Me' + union + select mother, 1 from folks where name = 'Me' + union + select max(father), max(a.generation)+1 from folks, ancestor_ids a + where folks.id = a.id + group by a.generation + union + select max(mother), max(a.generation)+1 from folks, ancestor_ids a + where folks.id = a.id + group by a.generation +), +ancestors +as +( + select generation, name from folks as p, ancestor_ids as a + where p.id = a.id +) +select * from ancestors; + +set statement standard_compliant_cte=0 for +with recursive +ancestor_ids (id, generation) +as +( + select father, 1 from folks where name = 'Me' + union + select mother, 1 from folks where name = 'Me' + union + select max(father), a.generation+1 from folks, ancestor_ids a + where folks.id = a.id + group by a.generation + union + select max(mother), a.generation+1 from folks, ancestor_ids a + where folks.id = a.id + group by a.generation +), +ancestors +as +( + select generation, name from folks as p, ancestor_ids as a + where p.id = a.id +) +select * from ancestors; + +set statement max_recursive_iterations=1 for +with recursive +ancestor_ids (id, generation) +as +( + select father, 1 from folks where name = 'Me' + union + select mother, 1 from folks where name = 'Me' + union + select father, a.generation+1 from folks, ancestor_ids a + where folks.id = a.id + union + select mother, a.generation+1 from folks, ancestor_ids a + where folks.id = a.id +), +ancestors +as +( + select generation, name from folks as p, ancestor_ids as a + where p.id = a.id +) +select * from ancestors; + +--echo # query with recursive tables using key access + +alter table folks add primary key (id); + +explain +with recursive +ancestors +as +( + select * + from folks + where name = 'Me' + union + select p.* + from folks as p, ancestors as fa + where p.id = fa.father + union + select p.* + from folks as p, ancestors as ma + where p.id = ma.mother +) +select * from ancestors; + + +with recursive +ancestors +as +( + select * + from folks + where name = 'Me' + union + select p.* + from folks as p, ancestors as fa + where p.id = fa.father + union + select p.* + from folks as p, ancestors as ma + where p.id = ma.mother +) +select * from ancestors; + + +--echo # +--echo # EXPLAIN FORMAT=JSON on a query where one recursive CTE uses another: +--echo # +explain +with recursive +prev_gen +as +( + select folks.* + from folks, prev_gen + where folks.id=prev_gen.father or folks.id=prev_gen.mother + union + select * + from folks + where name='Me' +), +ancestors +as +( + select * + from folks + where name='Me' + union + select * + from ancestors + union + select * + from prev_gen +) +select ancestors.name, ancestors.dob from ancestors; + +explain FORMAT=JSON +with recursive +prev_gen +as +( + select folks.* + from folks, prev_gen + where folks.id=prev_gen.father or folks.id=prev_gen.mother + union + select * + from folks + where name='Me' +), +ancestors +as +( + select * + from folks + where name='Me2' + union + select * + from ancestors where id < 234 + union + select * + from prev_gen where id < 345 +) +select ancestors.name, ancestors.dob from ancestors; + +--echo # +explain format=json +with recursive +ancestor_couples(h_id, h_name, h_dob, h_father, h_mother, + w_id, w_name, w_dob, w_father, w_mother) +as +( + select h.*, w.* + from folks h, folks w, coupled_ancestors a + where a.father = h.id AND a.mother = w.id + union + select h.*, w.* + from folks v, folks h, folks w + where v.name = 'Me' and + (v.father = h.id AND v.mother= w.id) +), +coupled_ancestors (id, name, dob, father, mother) +as +( + select h_id, h_name, h_dob, h_father, h_mother + from ancestor_couples + union all + select w_id, w_name, w_dob, w_father, w_mother + from ancestor_couples +) +select h_name, h_dob, w_name, w_dob + from ancestor_couples; + + +create table my_ancestors +with recursive +ancestor_ids (id) +as +( + select father from folks where name = 'Me' + union + select mother from folks where name = 'Me' + union + select father from folks, ancestor_ids a where folks.id = a.id + union + select mother from folks, ancestor_ids a where folks.id = a.id +) +select p.* from folks as p, ancestor_ids as a where p.id = a.id; + +select * from my_ancestors; + +delete from my_ancestors; + +insert into my_ancestors +with recursive +ancestor_ids (id) +as +( + select father from folks where name = 'Me' + union + select mother from folks where name = 'Me' + union + select father from folks, ancestor_ids a where folks.id = a.id + union + select mother from folks, ancestor_ids a where folks.id = a.id +) +select p.* from folks as p, ancestor_ids as a where p.id = a.id; + +select * from my_ancestors; + +drop table my_ancestors; + +--echo # +--echo # MDEV-10883: execution of prepared statement from SELECT +--echo # with recursive CTE that renames columns +--echo # + +prepare stmt from" +with recursive +ancestor_ids (id) +as +( + select father from folks where name = 'Me' + union + select mother from folks where name = 'Me' + union + select father from folks, ancestor_ids a where folks.id = a.id + union + select mother from folks, ancestor_ids a where folks.id = a.id +) +select p.* from folks as p, ancestor_ids as a where p.id = a.id; +"; +execute stmt; +deallocate prepare stmt; + +--echo # +--echo # MDEV-10881: execution of prepared statement from +--echo # CREATE ... SELECT, INSERT ... SELECT +--echo # + +prepare stmt from" +create table my_ancestors +with recursive +ancestor_ids (id) +as +( + select father from folks where name = 'Me' + union + select mother from folks where name = 'Me' + union + select father from folks, ancestor_ids a where folks.id = a.id + union + select mother from folks, ancestor_ids a where folks.id = a.id +) +select p.* from folks as p, ancestor_ids as a where p.id = a.id; +"; +execute stmt; +deallocate prepare stmt; +select * from my_ancestors; + +delete from my_ancestors; + +prepare stmt from" +insert into my_ancestors +with recursive +ancestor_ids (id) +as +( + select father from folks where name = 'Me' + union + select mother from folks where name = 'Me' + union + select father from folks, ancestor_ids a where folks.id = a.id + union + select mother from folks, ancestor_ids a where folks.id = a.id +) +select p.* from folks as p, ancestor_ids as a where p.id = a.id; +"; +execute stmt; +deallocate prepare stmt; +select * from my_ancestors; + +drop table my_ancestors; + +--echo # +--echo # MDEV-10933: WITH clause together with SELECT in parenthesis +--echo # CREATE SELECT +--echo # + +create table my_ancestors +( +with recursive +ancestor_ids (id) +as +( + select father from folks where name = 'Me' + union + select mother from folks where name = 'Me' + union + select father from folks, ancestor_ids a where folks.id = a.id + union + select mother from folks, ancestor_ids a where folks.id = a.id +) +select p.* from folks as p, ancestor_ids as a where p.id = a.id +); +select * from my_ancestors; +drop table my_ancestors; + +drop table folks; + +--echo # +--echo # MDEV-10372: [bb-10.2-mdev9864 tree] EXPLAIN with recursive CTE enters endless recursion +--echo # +create table t1(a int); +insert into t1 values (0),(1),(2),(3),(4),(5),(6),(7),(8),(9); + +explain format=json +with recursive t as (select a from t1 union select a+10 from t where a < 1000) +select * from t; + +drop table t1; + + +--echo # +--echo # MDEV-10737: recursive union with several anchors at the end +--echo # + +WITH RECURSIVE cte(n) AS + ( SELECT n+1 FROM cte WHERE n < 5 UNION SELECT 1 UNION SELECT 1 ) +SELECT * FROM cte; + +--echo # +--echo # MDEV-10736: recursive definition with anchor over a table with blob +--echo # + +CREATE TABLE t1 (f VARCHAR(1024)); +WITH RECURSIVE cte(f) AS + (SELECT t1.f FROM t1 UNION ALL SELECT cte.f FROM cte) +SELECT * FROM cte as t; +DROP TABLE t1; + +--echo # +--echo # MDEV-10899: mergeable derived in the spec of recursive CTE +--echo # + +create table t1 (a int); +insert into t1 values + (0), (1), (2), (3), (4); +create table t2 (a int); +insert into t2 values + (1), (2), (3), (4), (5); + +with recursive +t1 as +( +select x.a from (select a from t2 where t2.a=3) x +union +select t2.a from t1,t2 where t1.a+1=t2.a +) +select * from t1; + +explain +with recursive +t1 as +( +select x.a from (select a from t2 where t2.a=3) x +union +select t2.a from t1,t2 where t1.a+1=t2.a +) +select * from t1; + +drop table t1,t2; + +--echo # +--echo # MDEV-11278: non-mergeable view in the spec of recursive CTE +--echo # + +create table t1 (a int); +insert into t1 values + (0), (1), (2), (3), (4); +create table t2 (a int); +insert into t2 values + (1), (2), (3), (4), (5); + +create view v1 as + select a from t2 where a < 3 + union + select a from t2 where a > 4; + +with recursive +t1 as +( +select a from v1 where a=1 +union +select v1.a from t1,v1 where t1.a+1=v1.a +) +select * from t1; + +drop view v1; +drop table t1,t2; + + +--echo # +--echo # MDEV-11259: recursive CTE with concatenation operation +--echo # + +DROP TABLE IF EXISTS edges; +CREATE TABLE edges( + a int(10) unsigned NOT NULL, + b int(10) unsigned NOT NULL, + PRIMARY KEY (a,b), + KEY b(b) +); + +INSERT INTO edges + VALUES (1,3),(2,1),(2,4),(3,4),(3,5),(3,6),(4,7),(5,1),(5,6),(6,1); + +DROP TABLE IF EXISTS edges2; +CREATE VIEW edges2 (a, b) AS + SELECT a, b FROM edges UNION ALL SELECT b, a FROM edges; + +--sorted_result +WITH RECURSIVE transitive_closure(a, b, distance, path_string) AS +( SELECT a, b, 1 AS distance, + concat(a, '.', b, '.') AS path_string + FROM edges + + UNION ALL + + SELECT tc.a, e.b, tc.distance + 1, + concat(tc.path_string, e.b, '.') AS path_string + FROM edges AS e + JOIN transitive_closure AS tc + ON e.a = tc.b + WHERE tc.path_string NOT LIKE concat('%', e.b, '.%') +) +SELECT * FROM transitive_closure +ORDER BY a, b, distance; + +--sorted_result +WITH RECURSIVE transitive_closure(a, b, distance, path_string) AS +( SELECT a, b, 1 AS distance, + concat(a, '.', b, '.') AS path_string + FROM edges + WHERE a = 1 -- source + + UNION ALL + + SELECT tc.a, e.b, tc.distance + 1, + concat(tc.path_string, e.b, '.') AS path_string + FROM edges AS e + JOIN transitive_closure AS tc ON e.a = tc.b + WHERE tc.path_string NOT LIKE concat('%', e.b, '.%') +) + SELECT * FROM transitive_closure + WHERE b = 6 -- destination +ORDER BY a, b, distance; + +--sorted_result +WITH RECURSIVE transitive_closure(a, b, distance, path_string) AS +( SELECT a, b, 1 AS distance, + concat(a, '.', b, '.') AS path_string + FROM edges2 + + UNION ALL + + SELECT tc.a, e.b, tc.distance + 1, + concat(tc.path_string, e.b, '.') AS path_string + FROM edges2 AS e + JOIN transitive_closure AS tc ON e.a = tc.b + WHERE tc.path_string NOT LIKE concat('%', e.b, '.%') +) +SELECT * FROM transitive_closure +ORDER BY a, b, distance; + +--sorted_result +WITH RECURSIVE transitive_closure(a, b, distance, path_string) +AS +( SELECT a, b, 1 AS distance, + concat(a, '.', b, '.') AS path_string + FROM edges2 + + UNION ALL + + SELECT tc.a, e.b, tc.distance + 1, + concat(tc.path_string, e.b, '.') AS path_string + FROM edges2 AS e + JOIN transitive_closure AS tc ON e.a = tc.b + WHERE tc.path_string NOT LIKE concat('%', e.b, '.%') +) +SELECT a, b, min(distance) AS dist FROM transitive_closure +GROUP BY a, b +ORDER BY a, dist, b; + +DROP VIEW edges2; +DROP TABLE edges; + + +--echo # +--echo # MDEV-11674: recursive CTE table that cannot be stored +--echo # in a heap table +--echo # + +create table t1 (id int, test_data varchar(36)); + +insert into t1(id, test_data) +select id, test_data + from ( + with recursive data_generator(id, test_data) as ( + select 1 as id, uuid() as test_data + union all + select id + 1, uuid() from data_generator where id < 150000 + ) + select * from data_generator + ) as a; + +drop table t1; + +--echo # +--echo # MDEV-10773: ANALYZE for query with recursive CTE +--echo # + +--source include/analyze-format.inc +analyze format=json +with recursive src(counter) as +(select 1 + union + select counter+1 from src where counter<10 +) select * from src; + +--echo # +--echo # mdev-12360: recursive reference in left operand of LEFT JOIN +--echo # + +create table folks(id int, name char(32), dob date, father int, mother int); + +insert into folks values +(100, 'Me', '2000-01-01', 20, 30), +(20, 'Dad', '1970-02-02', 10, 9), +(30, 'Mom', '1975-03-03', 8, 7), +(10, 'Grandpa Bill', '1940-04-05', null, null), +(9, 'Grandma Ann', '1941-10-15', null, null), +(25, 'Uncle Jim', '1968-11-18', 8, 7), +(98, 'Sister Amy', '2001-06-20', 20, 30), +(7, 'Grandma Sally', '1943-08-23', null, 6), +(8, 'Grandpa Ben', '1940-10-21', null, null), +(6, 'Grandgrandma Martha', '1923-05-17', null, null), +(67, 'Cousin Eddie', '1992-02-28', 25, 27), +(27, 'Auntie Melinda', '1971-03-29', null, null); + +with recursive +ancestor_ids (id) +as +( + select father from folks where name = 'Me' + union + select mother from folks where name = 'Me' + union + select father from ancestor_ids as a left join folks on folks.id = a.id + union + select mother from ancestor_ids as a left join folks on folks.id = a.id +), +ancestors +as +( + select p.* from folks as p, ancestor_ids as a + where p.id = a.id +) +select * from ancestors; + +drop table folks; + +--echo # +--echo # mdev-12368: crash with mutually recursive CTE +--echo # that arenot Standard compliant +--echo # + +create table value_nodes (v char(4)); +create table module_nodes(m char(4)); +create table module_arguments(m char(4), v char(4)); +create table module_results(m char(4), v char(4)); + +--ERROR ER_NOT_STANDARD_COMPLIANT_RECURSIVE +with recursive +reached_values as +( + select v from value_nodes where v in ('v3','v7','v9') + union + select module_results.v from module_results, applied_modules + where module_results.m = applied_modules.m +), +applied_modules as +( + select module_nodes.m + from + module_nodes + left join + ( + module_arguments + left join + reached_values + on module_arguments.v = reached_values.v + ) + on reached_values.v is null and + module_nodes.m = module_arguments.m + where module_arguments.m is null +) +select * from reached_values; + +drop table value_nodes, module_nodes, module_arguments, module_results; + +--echo # +--echo # mdev-12375: query using one of two mutually recursive CTEs +--echo # whose non-recursive part returns an empty set +--echo # + +create table value_nodes (v char(4)); +insert into value_nodes values + ('v1'), ('v2'), ('v3'), ('v4'), ('v5'), ('v6'), ('v7'), ('v8'), ('v9'), + ('v10'), ('v11'), ('v12'), ('v13'), ('v14'), ('v15'), ('v16'); +create table module_nodes(m char(4)); +insert into module_nodes values + ('m1'), ('m2'), ('m3'), ('m4'), ('m5'), ('m6'), ('m7'); +create table module_arguments(m char(4), v char(4)); +insert into module_arguments values + ('m1','v3'), ('m1','v9'), + ('m2','v4'), ('m2','v3'), ('m2','v7'), + ('m3','v6'), + ('m4','v4'), ('m4','v1'), + ('m5','v10'), ('m5','v8'), ('m5','v3'), + ('m6','v8'), ('m6','v1'), + ('m7','v11'), ('m7','v12'); +create table module_results(m char(4), v char(4)); +insert into module_results values + ('m1','v4'), + ('m2','v1'), ('m2','v6'), + ('m3','v10'), + ('m4','v8'), + ('m5','v11'), ('m5','v9'), + ('m6','v12'), ('m6','v4'), + ('m7','v2'); + +set statement max_recursive_iterations=2, standard_compliant_cte=0 for +with recursive +reached_values as +( + select v from value_nodes where v in ('v3','v7','v9') + union + select module_results.v from module_results, applied_modules + where module_results.m = applied_modules.m +), +applied_modules as +( + select * from module_nodes where 1=0 + union + select module_nodes.m + from + module_nodes + left join + ( + module_arguments + left join + reached_values + on module_arguments.v = reached_values.v + ) + on reached_values.v is null and + module_nodes.m = module_arguments.m + where module_arguments.m is null +) +select * from applied_modules; + +drop table value_nodes, module_nodes, module_arguments, module_results; + +--echo # +--echo # mdev-12519: recursive references in subqueries +--echo # + +create table t1 (lp char(4) not null, rp char(4) not null); +insert into t1 values + ('p1','p2'), ('p2','p3'), ('p3','p4'), ('p4','p5'), + ('p2','p7'), ('p7','p8'), ('p8','p3'), ('p8','p4'); + +set standard_compliant_cte=0; + +with recursive +reachables(p) as +( + select lp from t1 where lp = 'p1' + union + select t1.rp from reachables, t1 + where t1.lp = reachables.p +) +select * from reachables; + +with recursive +reachables(p) as +( + select lp from t1 where lp = 'p1' + union + select t1.rp from reachables, t1 + where 'p3' not in (select * from reachables) and + t1.lp = reachables.p +) +select * from reachables; + +with recursive +reachables(p) as +( + select lp from t1 where lp = 'p1' + union + select t1.rp from reachables, t1 + where 'p3' not in (select p from reachables where p <= 'p5' + union + select p from reachables where p > 'p5') and + t1.lp = reachables.p +) +select * from reachables; + +prepare stmt from " +with recursive +reachables(p) as +( + select lp from t1 where lp = 'p1' + union + select t1.rp from reachables, t1 + where 'p3' not in (select p from reachables where p <= 'p5' + union + select p from reachables where p > 'p5') and + t1.lp = reachables.p +) +select * from reachables; +"; + +execute stmt; +execute stmt; + +deallocate prepare stmt; + +drop table t1; + +create table objects(v char(4) not null); +insert into objects values + ('v1'), ('v2'), ('v3'), ('v4'), ('v5'), + ('v6'), ('v7'), ('v8'), ('v9'), ('v10'); + +create table modules(m char(4) not null); +insert into modules values + ('m1'), ('m2'), ('m3'), ('m4'); + +create table module_arguments(m char(4) not null, v char(4) not null); +insert into module_arguments values + ('m1','v3'), ('m1','v9'), + ('m2','v4'), ('m2','v7'), + ('m3','v6'), ('m4','v2'); + +create table module_results(m char(4) not null, v char(4) not null); +insert into module_results values + ('m1','v4'), + ('m2','v1'), ('m2','v6'), + ('m3','v10'), ('m4','v7'); + +set standard_compliant_cte=0; + +with recursive +reached_objects as +( + select v, 'init' as m from objects where v in ('v3','v7','v9') + union + select module_results.v, module_results.m from module_results, applied_modules + where module_results.m = applied_modules.m +), +applied_modules as +( + select * from modules where 1=0 + union + select modules.m + from + modules + where + not exists (select * from module_arguments + where module_arguments.m = modules.m and + module_arguments.v not in + (select v from reached_objects)) +) +select * from reached_objects; + +with recursive +reached_objects as +( + select v, 'init' as m from objects where v in ('v3','v7','v9') + union + select module_results.v, module_results.m from module_results, applied_modules + where module_results.m = applied_modules.m +), +applied_modules as +( + select * from modules where 1=0 + union + select modules.m + from + modules + where + 'v6' not in (select v from reached_objects) and + not exists (select * from module_arguments + where module_arguments.m = modules.m and + module_arguments.v not in + (select v from reached_objects)) +) +select * from reached_objects; + +prepare stmt from " +with recursive +reached_objects as +( + select v, 'init' as m from objects where v in ('v3','v7','v9') + union + select module_results.v, module_results.m from module_results, applied_modules + where module_results.m = applied_modules.m +), +applied_modules as +( + select * from modules where 1=0 + union + select modules.m + from + modules + where + 'v6' not in (select v from reached_objects) and + not exists (select * from module_arguments + where module_arguments.m = modules.m and + module_arguments.v not in + (select v from reached_objects)) +) +select * from reached_objects; +"; + +execute stmt; +execute stmt; + +deallocate prepare stmt; + +drop table objects, modules, module_arguments, module_results; + +set standard_compliant_cte=default; +select @@standard_compliant_cte; + +--echo # +--echo # mdev-12554: impossible where in recursive select +--echo # + +CREATE TABLE t1 (i int); +INSERT INTO t1 VALUES (1),(2); + +WITH RECURSIVE +cte(f) AS ( SELECT i FROM t1 UNION SELECT f FROM t1, cte WHERE 1=0 ) +SELECT * FROM cte; + +DROP TABLE t1; + +--echo # +--echo # mdev-12556: recursive execution uses Aria temporary tables +--echo # + +CREATE TABLE t (c1 varchar(255), c2 tinytext); +INSERT INTO t VALUES ('a','a'),('b','b'),('c','c'),('d','d'); + +let $q1= +WITH RECURSIVE cte(f) AS ( + SELECT c1 FROM t + UNION + SELECT c1 FROM t, cte +) SELECT COUNT(*) FROM cte; + +let $q2= +WITH RECURSIVE cte(f) AS ( + SELECT c2 FROM t + UNION + SELECT c2 FROM t, cte +) SELECT COUNT(*) FROM cte; + +eval ANALYZE $q1; +eval $q1; + +eval ANALYZE $q2; +eval $q2; + +DROP TABLE t; + +--echo # +--echo # mdev-12563: no recursive references on the top level of the CTE spec +--echo # + +CREATE TABLE t (i int); +INSERT INTO t VALUES (3), (1),(2); + +SET standard_compliant_cte=0; + +WITH RECURSIVE cte(f) AS ( + SELECT i FROM t + UNION + SELECT i FROM t WHERE i NOT IN ( SELECT * FROM cte ) +) SELECT * FROM cte; + +WITH RECURSIVE cte(f) AS ( + SELECT i FROM t + UNION + SELECT i FROM t WHERE i NOT IN ( SELECT * FROM cte WHERE i < 2 ) + UNION + SELECT i FROM t WHERE i NOT IN ( SELECT * FROM cte WHERE i > 2 ) +) SELECT * FROM cte; + +WITH RECURSIVE cte(f) AS ( + SELECT i FROM t + UNION + SELECT i FROM t + WHERE i NOT IN ( SELECT * FROM cte WHERE i < 2 + UNION + SELECT * FROM cte WHERE i > 2) +) SELECT * FROM cte; + +WITH RECURSIVE cte(f) AS ( + SELECT i FROM t + UNION + SELECT i FROM t + WHERE i NOT IN ( SELECT * FROM t + WHERE i IN ( SELECT * FROM cte ) GROUP BY i ) +) SELECT * FROM cte; + +WITH RECURSIVE cte(f) AS ( + SELECT i FROM t + UNION + SELECT i FROM t WHERE i NOT IN ( SELECT * FROM cte ) + UNION + SELECT * FROM cte WHERE f > 2 +) SELECT * FROM cte; + +set standard_compliant_cte=default; + +DROP TABLE t; +