From 0a948f26523174deb6ef5758ac2b812b6fdf04ee Mon Sep 17 00:00:00 2001 From: Rex Johnston Date: Tue, 28 Oct 2025 14:43:33 +1200 Subject: [PATCH] MDEV-37220 Allow UPDATE/DELETE to read from a CTE We extend from the SQL standard to match the functionality of other databases that allow the inclusion of a CTE definition prior to update and delete statements. These CTEs are currently read only, like other derived tables, so cannot have their columns updated in updates set clause, nor have rows removed in the delete statement. --- mysql-test/main/cte_update_delete.result | 982 +++++++++++++++++++++++ mysql-test/main/cte_update_delete.test | 402 ++++++++++ sql/sql_cte.cc | 12 + sql/sql_lex.h | 8 +- sql/sql_parse.cc | 7 + sql/sql_select.cc | 3 +- sql/sql_update.cc | 11 +- sql/sql_yacc.yy | 25 +- sql/table.cc | 3 +- 9 files changed, 1439 insertions(+), 14 deletions(-) create mode 100644 mysql-test/main/cte_update_delete.result create mode 100644 mysql-test/main/cte_update_delete.test diff --git a/mysql-test/main/cte_update_delete.result b/mysql-test/main/cte_update_delete.result new file mode 100644 index 0000000000000..bcc89cea11469 --- /dev/null +++ b/mysql-test/main/cte_update_delete.result @@ -0,0 +1,982 @@ +# +# MDEV-37220 Allow UPDATE/DELETE to read from a CTE +# +create table t1 +( +a int not null, +b int, +c int, +d int, +amount decimal, +key t1_ix1 (a,b) +); +insert into t1 values (0, NULL, 0, NULL, 10.0000), (1, 1, 1, 1, 10.0000), +(2, 2, 2, 2, 20.0000), (3, 3, 3, 3, 30.0000), (4, 4, 4, 4, 40.0000), +(5, 5, 5, 5, NULL), (6, 6, 6, 6, NULL), (7, 7, 7, 7, 70.0000), +(8, 8, 8, 8, 80.0000); +create table t2 +( +a int NOT NULL, +b int, +name varchar(50), +key t2_ix1 (a,b) +); +insert into t2 values (0, NULL, 'a'), (1, NULL, 'A'), (2, 2, 'B'), (3,3, 'C'), +(4,4, 'D'), (5,5, NULL), (6,6, NULL), (7,7, 'E'), (8,8, 'F'), (9,9, 'G'), +(10,10,'H'), (11,11, NULL), (12,12, NULL); +create table t3 +( +a int, +b int, +description varchar(50) +); +create table t4 as select * from t1; +create table t5 as select * from t2; +insert into t3 values (1, 1, 'iron'),(2,2,'wood'),(0,NULL, 'gold'), +(3, 3, 'silver'), (4, 4, 'lead'), (5, 5, 'tin'), (6, 6, 'platinum'), +(7, 7, 'aluminium'); +select * from t3; +a b description +1 1 iron +2 2 wood +0 NULL gold +3 3 silver +4 4 lead +5 5 tin +6 6 platinum +7 7 aluminium +# update, single table syntax, subq in set +explain with cte as (select * from t1 where c < 5), +cte2 as (select * from t2 where b < 3) +update t3 +set t3.a = (select a from cte where b in (select b from cte2) and d = t3.a); +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY t3 ALL NULL NULL NULL NULL 8 +4 DEPENDENT SUBQUERY t1 ALL NULL NULL NULL NULL 9 Using where +4 DEPENDENT SUBQUERY t2 index NULL t2_ix1 9 NULL 13 Using where; Using index; FirstMatch(t1); Using join buffer (flat, BNL join) +with cte as (select * from t1 where c < 5), +cte2 as (select * from t2 where b < 3) +update t3 +set t3.a = (select a from cte where b in (select b from cte2) and d = t3.a); +select * from t3; +a b description +NULL 1 iron +2 2 wood +NULL NULL gold +NULL 3 silver +NULL 4 lead +NULL 5 tin +NULL 6 platinum +NULL 7 aluminium +truncate t3; +insert into t3 values (1, 1, 'iron'),(2,2,'wood'),(0,NULL, 'gold'), +(3, 3, 'silver'), (4, 4, 'lead'), (5, 5, 'tin'), (6, 6, 'platinum'), +(7, 7, 'aluminium'); +create procedure p() with cte as (select * from t1 where c < 5), +cte2 as (select * from t2 where b < 3) +update t3 +set t3.a = (select a from cte where b in (select b from cte2) and d = t3.a); +call p(); +drop procedure p; +select * from t3; +a b description +NULL 1 iron +2 2 wood +NULL NULL gold +NULL 3 silver +NULL 4 lead +NULL 5 tin +NULL 6 platinum +NULL 7 aluminium +truncate t3; +insert into t3 values (1, 1, 'iron'),(2,2,'wood'),(0,NULL, 'gold'), +(3, 3, 'silver'), (4, 4, 'lead'), (5, 5, 'tin'), (6, 6, 'platinum'), +(7, 7, 'aluminium'); +prepare s from 'with cte as (select * from t1 where c < 5), +cte2 as (select * from t2 where b < 3) +update t3 +set t3.a = (select a from cte where b in (select b from cte2) and d = t3.a)'; +execute s; +select * from t3; +a b description +NULL 1 iron +2 2 wood +NULL NULL gold +NULL 3 silver +NULL 4 lead +NULL 5 tin +NULL 6 platinum +NULL 7 aluminium +truncate t3; +insert into t3 values (1, 1, 'iron'),(2,2,'wood'),(0,NULL, 'gold'), +(3, 3, 'silver'), (4, 4, 'lead'), (5, 5, 'tin'), (6, 6, 'platinum'), +(7, 7, 'aluminium'); +execute s; +select * from t3; +a b description +NULL 1 iron +2 2 wood +NULL NULL gold +NULL 3 silver +NULL 4 lead +NULL 5 tin +NULL 6 platinum +NULL 7 aluminium +truncate t3; +insert into t3 values (1, 1, 'iron'),(2,2,'wood'),(0,NULL, 'gold'), +(3, 3, 'silver'), (4, 4, 'lead'), (5, 5, 'tin'), (6, 6, 'platinum'), +(7, 7, 'aluminium'); +drop prepare s; +# update, single table syntax, subq in set, cte used twice +explain with cte as (select * from t1 where c < 5), +cte2 as (select * from t2 where b < 3) +update t3 +set t3.a = (select a from cte +where +(b in (select b from cte2 where cte2.a = cte.a) or +b in (select b from cte2 where cte2.b = cte.a)) and +d = t3.a +); +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY t3 ALL NULL NULL NULL NULL 8 +4 DEPENDENT SUBQUERY t1 ALL NULL NULL NULL NULL 9 Using where +6 DEPENDENT SUBQUERY t2 index t2_ix1 t2_ix1 9 NULL 13 Using where; Using index +5 DEPENDENT SUBQUERY t2 ref t2_ix1 t2_ix1 9 test.t1.a,func 1 Using where; Using index +with cte as (select * from t1 where c < 5), +cte2 as (select * from t2 where b < 3) +update t3 +set t3.a = (select a from cte +where +(b in (select b from cte2 where cte2.a = cte.a) or +b in (select b from cte2 where cte2.b = cte.a)) and +d = t3.a +); +select * from t3; +a b description +NULL 1 iron +2 2 wood +NULL NULL gold +NULL 3 silver +NULL 4 lead +NULL 5 tin +NULL 6 platinum +NULL 7 aluminium +truncate t3; +insert into t3 values (1, 1, 'iron'),(2,2,'wood'),(0,NULL, 'gold'), +(3, 3, 'silver'), (4, 4, 'lead'), (5, 5, 'tin'), (6, 6, 'platinum'), +(7, 7, 'aluminium'); +create procedure p() with cte as (select * from t1 where c < 5), +cte2 as (select * from t2 where b < 3) +update t3 +set t3.a = (select a from cte +where +(b in (select b from cte2 where cte2.a = cte.a) or +b in (select b from cte2 where cte2.b = cte.a)) and +d = t3.a +); +call p(); +drop procedure p; +select * from t3; +a b description +NULL 1 iron +2 2 wood +NULL NULL gold +NULL 3 silver +NULL 4 lead +NULL 5 tin +NULL 6 platinum +NULL 7 aluminium +truncate t3; +insert into t3 values (1, 1, 'iron'),(2,2,'wood'),(0,NULL, 'gold'), +(3, 3, 'silver'), (4, 4, 'lead'), (5, 5, 'tin'), (6, 6, 'platinum'), +(7, 7, 'aluminium'); +prepare s from 'with cte as (select * from t1 where c < 5), +cte2 as (select * from t2 where b < 3) +update t3 +set t3.a = (select a from cte +where +(b in (select b from cte2 where cte2.a = cte.a) or +b in (select b from cte2 where cte2.b = cte.a)) and +d = t3.a +)'; +execute s; +select * from t3; +a b description +NULL 1 iron +2 2 wood +NULL NULL gold +NULL 3 silver +NULL 4 lead +NULL 5 tin +NULL 6 platinum +NULL 7 aluminium +truncate t3; +insert into t3 values (1, 1, 'iron'),(2,2,'wood'),(0,NULL, 'gold'), +(3, 3, 'silver'), (4, 4, 'lead'), (5, 5, 'tin'), (6, 6, 'platinum'), +(7, 7, 'aluminium'); +execute s; +select * from t3; +a b description +NULL 1 iron +2 2 wood +NULL NULL gold +NULL 3 silver +NULL 4 lead +NULL 5 tin +NULL 6 platinum +NULL 7 aluminium +truncate t3; +insert into t3 values (1, 1, 'iron'),(2,2,'wood'),(0,NULL, 'gold'), +(3, 3, 'silver'), (4, 4, 'lead'), (5, 5, 'tin'), (6, 6, 'platinum'), +(7, 7, 'aluminium'); +drop prepare s; +# multi table syntax, subq in set and where +explain with cte as (select * from t1 where c < 5), +cte2 as (select * from t2 where b < 3) +update t3,cte +set t3.a = cte.a+1 where cte.b in (select b from cte2) and d = t3.a; +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY t3 ALL NULL NULL NULL NULL 8 Using where +1 PRIMARY ref key0 key0 5 test.t3.a 1 Using where +1 PRIMARY t2 index NULL t2_ix1 9 NULL 13 Using where; Using index; FirstMatch() +2 DERIVED t1 ALL NULL NULL NULL NULL 9 Using where +with cte as (select * from t1 where c < 5), +cte2 as (select * from t2 where b < 3) +update t3,cte +set t3.a = cte.a+1 where cte.b in (select b from cte2) and d = t3.a; +select * from t3; +a b description +1 1 iron +3 2 wood +0 NULL gold +3 3 silver +4 4 lead +5 5 tin +6 6 platinum +7 7 aluminium +truncate t3; +insert into t3 values (1, 1, 'iron'),(2,2,'wood'),(0,NULL, 'gold'), +(3, 3, 'silver'), (4, 4, 'lead'), (5, 5, 'tin'), (6, 6, 'platinum'), +(7, 7, 'aluminium'); +create procedure p() with cte as (select * from t1 where c < 5), +cte2 as (select * from t2 where b < 3) +update t3,cte +set t3.a = cte.a+1 where cte.b in (select b from cte2) and d = t3.a; +call p(); +drop procedure p; +select * from t3; +a b description +1 1 iron +3 2 wood +0 NULL gold +3 3 silver +4 4 lead +5 5 tin +6 6 platinum +7 7 aluminium +truncate t3; +insert into t3 values (1, 1, 'iron'),(2,2,'wood'),(0,NULL, 'gold'), +(3, 3, 'silver'), (4, 4, 'lead'), (5, 5, 'tin'), (6, 6, 'platinum'), +(7, 7, 'aluminium'); +# multi table syntax, subq in set and where, more complex CTEs +explain with cte as (select t1.* from t1 left join t4 on t1.d = t4.d where t1.c < 5), +cte2 as (select t2.* from t2, t5 where t2.b < 3 and t2.name = t5.name limit 1) +update t3,cte, t4 +set t3.a = cte.a+1, t4.a = cte.a+2 +where cte.b in (select b from cte2) and cte.d = t3.a and cte.b = t4.a; +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY t3 ALL NULL NULL NULL NULL 8 Using where +1 PRIMARY ref key0 key0 5 test.t3.a 8 Using where +1 PRIMARY ref key0 key0 5 cte.b 1 FirstMatch() +1 PRIMARY t4 ALL NULL NULL NULL NULL 9 Using where +3 DERIVED t2 ALL NULL NULL NULL NULL 13 Using where +3 DERIVED t5 ALL NULL NULL NULL NULL 13 Using where; Using join buffer (flat, BNL join) +2 DERIVED t1 ALL NULL NULL NULL NULL 9 Using where +2 DERIVED t4 ALL NULL NULL NULL NULL 9 Using where; Using join buffer (flat, BNL join) +with cte as (select t1.* from t1 left join t4 on t1.d = t4.d where t1.c < 5), +cte2 as (select t2.* from t2, t5 where t2.b < 3 and t2.name = t5.name limit 1) +update t3,cte, t4 +set t3.a = cte.a+1, t4.a = cte.a+2 +where cte.b in (select b from cte2) and cte.d = t3.a and cte.b = t4.a; +select * from t3; +a b description +1 1 iron +3 2 wood +0 NULL gold +3 3 silver +4 4 lead +5 5 tin +6 6 platinum +7 7 aluminium +select * from t4; +a b c d amount +0 NULL 0 NULL 10 +1 1 1 1 10 +4 2 2 2 20 +3 3 3 3 30 +4 4 4 4 40 +5 5 5 5 NULL +6 6 6 6 NULL +7 7 7 7 70 +8 8 8 8 80 +truncate t3; +insert into t3 values (1, 1, 'iron'),(2,2,'wood'),(0,NULL, 'gold'), +(3, 3, 'silver'), (4, 4, 'lead'), (5, 5, 'tin'), (6, 6, 'platinum'), +(7, 7, 'aluminium'); +drop table t4; +create table t4 as select * from t1; +create procedure p() with cte as (select t1.* from t1 left join t4 on t1.d = t4.d where t1.c < 5), +cte2 as (select t2.* from t2, t5 where t2.b < 3 and t2.name = t5.name limit 1) +update t3,cte, t4 +set t3.a = cte.a+1, t4.a = cte.a+2 +where cte.b in (select b from cte2) and cte.d = t3.a and cte.b = t4.a; +call p(); +drop procedure p; +select * from t3; +a b description +1 1 iron +3 2 wood +0 NULL gold +3 3 silver +4 4 lead +5 5 tin +6 6 platinum +7 7 aluminium +select * from t4; +a b c d amount +0 NULL 0 NULL 10 +1 1 1 1 10 +4 2 2 2 20 +3 3 3 3 30 +4 4 4 4 40 +5 5 5 5 NULL +6 6 6 6 NULL +7 7 7 7 70 +8 8 8 8 80 +truncate t3; +insert into t3 values (1, 1, 'iron'),(2,2,'wood'),(0,NULL, 'gold'), +(3, 3, 'silver'), (4, 4, 'lead'), (5, 5, 'tin'), (6, 6, 'platinum'), +(7, 7, 'aluminium'); +drop table t4; +create table t4 as select * from t1; +prepare s from 'with cte as (select t1.* from t1 left join t4 on t1.d = t4.d where t1.c < 5), +cte2 as (select t2.* from t2, t5 where t2.b < 3 and t2.name = t5.name limit 1) +update t3,cte, t4 +set t3.a = cte.a+1, t4.a = cte.a+2 +where cte.b in (select b from cte2) and cte.d = t3.a and cte.b = t4.a'; +execute s; +select * from t3; +a b description +1 1 iron +3 2 wood +0 NULL gold +3 3 silver +4 4 lead +5 5 tin +6 6 platinum +7 7 aluminium +select * from t4; +a b c d amount +0 NULL 0 NULL 10 +1 1 1 1 10 +4 2 2 2 20 +3 3 3 3 30 +4 4 4 4 40 +5 5 5 5 NULL +6 6 6 6 NULL +7 7 7 7 70 +8 8 8 8 80 +truncate t3; +insert into t3 values (1, 1, 'iron'),(2,2,'wood'),(0,NULL, 'gold'), +(3, 3, 'silver'), (4, 4, 'lead'), (5, 5, 'tin'), (6, 6, 'platinum'), +(7, 7, 'aluminium'); +drop table t4; +create table t4 as select * from t1; +execute s; +select * from t3; +a b description +1 1 iron +3 2 wood +0 NULL gold +3 3 silver +4 4 lead +5 5 tin +6 6 platinum +7 7 aluminium +select * from t4; +a b c d amount +0 NULL 0 NULL 10 +1 1 1 1 10 +4 2 2 2 20 +3 3 3 3 30 +4 4 4 4 40 +5 5 5 5 NULL +6 6 6 6 NULL +7 7 7 7 70 +8 8 8 8 80 +truncate t3; +insert into t3 values (1, 1, 'iron'),(2,2,'wood'),(0,NULL, 'gold'), +(3, 3, 'silver'), (4, 4, 'lead'), (5, 5, 'tin'), (6, 6, 'platinum'), +(7, 7, 'aluminium'); +drop prepare s; +drop table t4; +create table t4 as select * from t1; +explain with cte as (select * from t1 where c < 5), +cte2 as (select * from t2 where b < 3) +update t3 left join cte on t3.b = cte.b +set t3.a = cte.a+1 where cte.b in (select b from cte2) and d = t3.a; +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY t3 ALL NULL NULL NULL NULL 8 Using where +1 PRIMARY ref key0 key0 10 test.t3.b,test.t3.a 1 +1 PRIMARY t2 index NULL t2_ix1 9 NULL 13 Using where; Using index; FirstMatch() +2 DERIVED t1 ALL NULL NULL NULL NULL 9 Using where +with cte as (select * from t1 where c < 5), +cte2 as (select * from t2 where b < 3) +update t3 left join cte on t3.b = cte.b +set t3.a = cte.a+1 where cte.b in (select b from cte2) and d = t3.a; +select * from t3; +a b description +1 1 iron +3 2 wood +0 NULL gold +3 3 silver +4 4 lead +5 5 tin +6 6 platinum +7 7 aluminium +truncate t3; +insert into t3 values (1, 1, 'iron'),(2,2,'wood'),(0,NULL, 'gold'), +(3, 3, 'silver'), (4, 4, 'lead'), (5, 5, 'tin'), (6, 6, 'platinum'), +(7, 7, 'aluminium'); +explain with cte as (select * from t1 where c < 5), +cte2 as (select * from t2 where b < 3) +update t3, cte, cte2 set t3.a = cte.a+2 where cte.b = cte2.b and d = t3.a; +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY t3 ALL NULL NULL NULL NULL 8 Using where +1 PRIMARY ref key0 key0 5 test.t3.a 1 Using where +1 PRIMARY ref key0 key0 5 cte.b 1 +2 DERIVED t1 ALL NULL NULL NULL NULL 9 Using where +3 DERIVED t2 ALL NULL NULL NULL NULL 13 Using where +with cte as (select * from t1 where c < 5), +cte2 as (select * from t2 where b < 3) +update t3, cte, cte2 set t3.a = cte.a+2 where cte.b = cte2.b and d = t3.a; +select * from t3; +a b description +1 1 iron +4 2 wood +0 NULL gold +3 3 silver +4 4 lead +5 5 tin +6 6 platinum +7 7 aluminium +truncate t3; +insert into t3 values (1, 1, 'iron'),(2,2,'wood'),(0,NULL, 'gold'), +(3, 3, 'silver'), (4, 4, 'lead'), (5, 5, 'tin'), (6, 6, 'platinum'), +(7, 7, 'aluminium'); +explain with cte as (select * from t1 where c < 5), +cte2 as (select * from t2 where b < 3) +update cte, cte2, t3 set t3.a = cte.a+2 where cte.b = cte2.b and d = t3.a; +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY t3 ALL NULL NULL NULL NULL 8 Using where +1 PRIMARY ref key1 key1 5 test.t3.a 1 Using where +1 PRIMARY ref key0 key0 5 cte.b 1 +2 DERIVED t1 ALL NULL NULL NULL NULL 9 Using where +3 DERIVED t2 ALL NULL NULL NULL NULL 13 Using where +with cte as (select * from t1 where c < 5), +cte2 as (select * from t2 where b < 3) +update cte, cte2, t3 set t3.a = cte.a+2 where cte.b = cte2.b and d = t3.a; +select * from t3; +a b description +1 1 iron +4 2 wood +0 NULL gold +3 3 silver +4 4 lead +5 5 tin +6 6 platinum +7 7 aluminium +truncate t3; +insert into t3 values (1, 1, 'iron'),(2,2,'wood'),(0,NULL, 'gold'), +(3, 3, 'silver'), (4, 4, 'lead'), (5, 5, 'tin'), (6, 6, 'platinum'), +(7, 7, 'aluminium'); +explain with cte as (select * from t1 where c < 5 group by a), +cte2 as (select * from t2 where b < 3 group by a) +update cte, cte2, t3 set t3.a = cte.a+2 where cte.b = cte2.b and d = t3.a; +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY t3 ALL NULL NULL NULL NULL 8 Using where +1 PRIMARY ref key1 key1 5 test.t3.a 1 Using where +1 PRIMARY ref key0 key0 5 cte.b 1 +2 DERIVED t1 ALL NULL NULL NULL NULL 9 Using where; Using temporary; Using filesort +3 DERIVED t2 ALL NULL NULL NULL NULL 13 Using where; Using temporary; Using filesort +with cte as (select * from t1 where c < 5 group by a), +cte2 as (select * from t2 where b < 3 group by a) +update cte, cte2, t3 set t3.a = cte.a+2 where cte.b = cte2.b and d = t3.a; +select * from t3; +a b description +1 1 iron +4 2 wood +0 NULL gold +3 3 silver +4 4 lead +5 5 tin +6 6 platinum +7 7 aluminium +truncate t3; +insert into t3 values (1, 1, 'iron'),(2,2,'wood'),(0,NULL, 'gold'), +(3, 3, 'silver'), (4, 4, 'lead'), (5, 5, 'tin'), (6, 6, 'platinum'), +(7, 7, 'aluminium'); +create view v1 as select * from t1; +explain with cte as (select * from v1 where c < 5 group by a), +cte2 as (select * from t2 where b < 3 group by a) +update cte, cte2, t3 set t3.a = cte.a+2 where cte.b = cte2.b and d = t3.a; +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY t3 ALL NULL NULL NULL NULL 8 Using where +1 PRIMARY ref key1 key1 5 test.t3.a 1 Using where +1 PRIMARY ref key0 key0 5 cte.b 1 +2 DERIVED t1 ALL NULL NULL NULL NULL 9 Using where; Using temporary; Using filesort +3 DERIVED t2 ALL NULL NULL NULL NULL 13 Using where; Using temporary; Using filesort +with cte as (select * from v1 where c < 5 group by a), +cte2 as (select * from t2 where b < 3 group by a) +update cte, cte2, t3 set t3.a = cte.a+2 where cte.b = cte2.b and d = t3.a; +select * from t3; +a b description +1 1 iron +4 2 wood +0 NULL gold +3 3 silver +4 4 lead +5 5 tin +6 6 platinum +7 7 aluminium +truncate t3; +insert into t3 values (1, 1, 'iron'),(2,2,'wood'),(0,NULL, 'gold'), +(3, 3, 'silver'), (4, 4, 'lead'), (5, 5, 'tin'), (6, 6, 'platinum'), +(7, 7, 'aluminium'); +with T as (select * from t1) update T set T.a=3; +ERROR HY000: The target table T of the UPDATE is not updatable +with T as (select * from v1) update T set T.a=3; +ERROR HY000: The target table T of the UPDATE is not updatable +with T as (select * from t1) delete from T where a=3; +ERROR HY000: The target table T of the DELETE is not updatable +with T as (select * from v1) delete from T where a=3; +ERROR HY000: The target table T of the DELETE is not updatable +drop view v1; +create view v1 as select * from t1 where b IS NOT NULL limit 1; +explain with cte as (select * from v1 where c < 5 group by a), +cte2 as (select * from t2 where b < 3 group by a) +update cte, cte2, t3 set t3.a = cte.a+2 where d = t3.a; +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY t3 ALL NULL NULL NULL NULL 8 Using where +1 PRIMARY ref key0 key0 5 test.t3.a 1 +1 PRIMARY ALL NULL NULL NULL NULL 13 +2 DERIVED ALL NULL NULL NULL NULL 2 Using where; Using temporary; Using filesort +4 DERIVED t1 ALL NULL NULL NULL NULL 9 Using where +3 DERIVED t2 ALL NULL NULL NULL NULL 13 Using where; Using temporary; Using filesort +with cte as (select * from v1 where c < 5 group by a), +cte2 as (select * from t2 where b < 3 group by a) +update cte, cte2, t3 set t3.a = cte.a+2 where d = t3.a; +select * from t3; +a b description +3 1 iron +2 2 wood +0 NULL gold +3 3 silver +4 4 lead +5 5 tin +6 6 platinum +7 7 aluminium +truncate t3; +insert into t3 values (1, 1, 'iron'),(2,2,'wood'),(0,NULL, 'gold'), +(3, 3, 'silver'), (4, 4, 'lead'), (5, 5, 'tin'), (6, 6, 'platinum'), +(7, 7, 'aluminium'); +drop view v1; +CREATE FUNCTION f1 () RETURNS BLOB RETURN 1; +CREATE TABLE tf (f1 DATE); +INSERT INTO tf VALUES('2001-01-01'); +explain with cte as (select f1 as a, f1() as b, 2 as d from tf), +cte2 as (select * from t2 where b < 3 group by a) +update cte, cte2, t3 set t3.a = cte.a+2 where cte.b = cte2.b and d = t3.a; +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY system NULL NULL NULL NULL 1 +1 PRIMARY ref key0 key0 5 const 1 Using where +1 PRIMARY t3 ALL NULL NULL NULL NULL 8 Using where +2 DERIVED tf system NULL NULL NULL NULL 1 +3 DERIVED t2 ALL NULL NULL NULL NULL 13 Using where; Using temporary; Using filesort +with cte as (select f1 as a, f1() as b, 2 as d from tf), +cte2 as (select * from t2 where b < 3 group by a) +update cte, cte2, t3 set t3.a = cte.a+2 where cte.b = cte2.b and d = t3.a; +select * from t3; +a b description +1 1 iron +2 2 wood +0 NULL gold +3 3 silver +4 4 lead +5 5 tin +6 6 platinum +7 7 aluminium +truncate t3; +insert into t3 values (1, 1, 'iron'),(2,2,'wood'),(0,NULL, 'gold'), +(3, 3, 'silver'), (4, 4, 'lead'), (5, 5, 'tin'), (6, 6, 'platinum'), +(7, 7, 'aluminium'); +drop function f1; +drop table tf; +# delete single table syntax +explain with cte as (select * from t1 where c < 5), +cte2 as (select * from t2 where b < 3) +delete from t3 +where t3.a = (select a from cte where b in (select b from cte2)); +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY t3 ALL NULL NULL NULL NULL 8 Using where +4 SUBQUERY t1 ALL NULL NULL NULL NULL 9 Using where +4 SUBQUERY t2 index NULL t2_ix1 9 NULL 13 Using where; Using index; FirstMatch(t1); Using join buffer (flat, BNL join) +with cte as (select * from t1 where c < 5), +cte2 as (select * from t2 where b < 3) +delete from t3 +where t3.a = (select a from cte where b in (select b from cte2)); +select * from t3; +a b description +1 1 iron +0 NULL gold +3 3 silver +4 4 lead +5 5 tin +6 6 platinum +7 7 aluminium +truncate t3; +insert into t3 values (1, 1, 'iron'),(2,2,'wood'),(0,NULL, 'gold'), +(3, 3, 'silver'), (4, 4, 'lead'), (5, 5, 'tin'), (6, 6, 'platinum'), +(7, 7, 'aluminium'); +create procedure p() with cte as (select * from t1 where c < 5), +cte2 as (select * from t2 where b < 3) +delete from t3 +where t3.a = (select a from cte where b in (select b from cte2)); +call p(); +drop procedure p; +select * from t3; +a b description +1 1 iron +0 NULL gold +3 3 silver +4 4 lead +5 5 tin +6 6 platinum +7 7 aluminium +truncate t3; +insert into t3 values (1, 1, 'iron'),(2,2,'wood'),(0,NULL, 'gold'), +(3, 3, 'silver'), (4, 4, 'lead'), (5, 5, 'tin'), (6, 6, 'platinum'), +(7, 7, 'aluminium'); +# delete multi table syntax +explain with cte as (select * from t1 where c < 5), +cte2 as (select * from t2 where b < 3) +delete from t3 +using t3, cte +where t3.a = cte.a and cte.b in (select b from cte2); +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY t3 ALL NULL NULL NULL NULL 8 Using where +1 PRIMARY ref key0 key0 4 test.t3.a 1 Using where +1 PRIMARY t2 index NULL t2_ix1 9 NULL 13 Using where; Using index; FirstMatch() +2 DERIVED t1 ALL NULL NULL NULL NULL 9 Using where +with cte as (select * from t1 where c < 5), +cte2 as (select * from t2 where b < 3) +delete from t3 +using t3, cte +where t3.a = cte.a and cte.b in (select b from cte2); +select * from t3; +a b description +1 1 iron +0 NULL gold +3 3 silver +4 4 lead +5 5 tin +6 6 platinum +7 7 aluminium +truncate t3; +insert into t3 values (1, 1, 'iron'),(2,2,'wood'),(0,NULL, 'gold'), +(3, 3, 'silver'), (4, 4, 'lead'), (5, 5, 'tin'), (6, 6, 'platinum'), +(7, 7, 'aluminium'); +create procedure p() with cte as (select * from t1 where c < 5), +cte2 as (select * from t2 where b < 3) +delete from t3 +using t3, cte +where t3.a = cte.a and cte.b in (select b from cte2); +call p(); +drop procedure p; +select * from t3; +a b description +1 1 iron +0 NULL gold +3 3 silver +4 4 lead +5 5 tin +6 6 platinum +7 7 aluminium +truncate t3; +insert into t3 values (1, 1, 'iron'),(2,2,'wood'),(0,NULL, 'gold'), +(3, 3, 'silver'), (4, 4, 'lead'), (5, 5, 'tin'), (6, 6, 'platinum'), +(7, 7, 'aluminium'); +# delete multi table syntax, multi table delete +explain with cte as (select * from t1 where c < 5), +cte2 as (select * from t2 where b < 3) +delete from t3, t4 +using t3, t4, cte +where t3.a = cte.a and cte.b in (select b from cte2) +and t4.a = t3.a; +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY t3 ALL NULL NULL NULL NULL 8 Using where +1 PRIMARY ref key0 key0 4 test.t3.a 1 Using where +1 PRIMARY t2 index NULL t2_ix1 9 NULL 13 Using where; Using index; FirstMatch() +1 PRIMARY t4 ALL NULL NULL NULL NULL 9 Using where +2 DERIVED t1 ALL NULL NULL NULL NULL 9 Using where +with cte as (select * from t1 where c < 5), +cte2 as (select * from t2 where b < 3) +delete from t3, t4 +using t3, t4, cte +where t3.a = cte.a and cte.b in (select b from cte2) +and t4.a = t3.a; +select * from t3; +a b description +1 1 iron +0 NULL gold +3 3 silver +4 4 lead +5 5 tin +6 6 platinum +7 7 aluminium +select * from t4; +a b c d amount +0 NULL 0 NULL 10 +1 1 1 1 10 +3 3 3 3 30 +4 4 4 4 40 +5 5 5 5 NULL +6 6 6 6 NULL +7 7 7 7 70 +8 8 8 8 80 +truncate t3; +insert into t3 values (1, 1, 'iron'),(2,2,'wood'),(0,NULL, 'gold'), +(3, 3, 'silver'), (4, 4, 'lead'), (5, 5, 'tin'), (6, 6, 'platinum'), +(7, 7, 'aluminium'); +create procedure p() with cte as (select * from t1 where c < 5), +cte2 as (select * from t2 where b < 3) +delete from t3, t4 +using t3, t4, cte +where t3.a = cte.a and cte.b in (select b from cte2) +and t4.a = t3.a; +call p(); +drop procedure p; +select * from t3; +a b description +1 1 iron +2 2 wood +0 NULL gold +3 3 silver +4 4 lead +5 5 tin +6 6 platinum +7 7 aluminium +truncate t3; +insert into t3 values (1, 1, 'iron'),(2,2,'wood'),(0,NULL, 'gold'), +(3, 3, 'silver'), (4, 4, 'lead'), (5, 5, 'tin'), (6, 6, 'platinum'), +(7, 7, 'aluminium'); +# delete multi table syntax, multi table CTEs +explain with cte as (select t1.* from t1 left join t4 on t1.d = t4.d where t1.c < 5), +cte2 as (select t2.* from t2, t5 where t2.b < 3 and t2.name = t5.name limit 1) +delete from t3 +using t3, cte +where t3.a = cte.a and cte.b in (select b from cte2); +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY t3 ALL NULL NULL NULL NULL 8 Using where +1 PRIMARY ref key0 key0 4 test.t3.a 8 Using where +1 PRIMARY ref key0 key0 5 cte.b 1 FirstMatch() +3 DERIVED t2 ALL NULL NULL NULL NULL 13 Using where +3 DERIVED t5 ALL NULL NULL NULL NULL 13 Using where; Using join buffer (flat, BNL join) +2 DERIVED t1 ALL NULL NULL NULL NULL 9 Using where +2 DERIVED t4 ALL NULL NULL NULL NULL 8 Using where; Using join buffer (flat, BNL join) +with cte as (select t1.* from t1 left join t4 on t1.d = t4.d where t1.c < 5), +cte2 as (select t2.* from t2, t5 where t2.b < 3 and t2.name = t5.name limit 1) +delete from t3 +using t3, cte +where t3.a = cte.a and cte.b in (select b from cte2); +select * from t3; +a b description +1 1 iron +0 NULL gold +3 3 silver +4 4 lead +5 5 tin +6 6 platinum +7 7 aluminium +truncate t3; +insert into t3 values (1, 1, 'iron'),(2,2,'wood'),(0,NULL, 'gold'), +(3, 3, 'silver'), (4, 4, 'lead'), (5, 5, 'tin'), (6, 6, 'platinum'), +(7, 7, 'aluminium'); +create procedure p() with cte as (select t1.* from t1 left join t4 on t1.d = t4.d where t1.c < 5), +cte2 as (select t2.* from t2, t5 where t2.b < 3 and t2.name = t5.name limit 1) +delete from t3 +using t3, cte +where t3.a = cte.a and cte.b in (select b from cte2); +call p(); +drop procedure p; +select * from t3; +a b description +1 1 iron +0 NULL gold +3 3 silver +4 4 lead +5 5 tin +6 6 platinum +7 7 aluminium +truncate t3; +insert into t3 values (1, 1, 'iron'),(2,2,'wood'),(0,NULL, 'gold'), +(3, 3, 'silver'), (4, 4, 'lead'), (5, 5, 'tin'), (6, 6, 'platinum'), +(7, 7, 'aluminium'); +prepare s from 'with cte as (select t1.* from t1 left join t4 on t1.d = t4.d where t1.c < 5), +cte2 as (select t2.* from t2, t5 where t2.b < 3 and t2.name = t5.name limit 1) +delete from t3 +using t3, cte +where t3.a = cte.a and cte.b in (select b from cte2)'; +execute s; +select * from t3; +a b description +1 1 iron +0 NULL gold +3 3 silver +4 4 lead +5 5 tin +6 6 platinum +7 7 aluminium +truncate t3; +insert into t3 values (1, 1, 'iron'),(2,2,'wood'),(0,NULL, 'gold'), +(3, 3, 'silver'), (4, 4, 'lead'), (5, 5, 'tin'), (6, 6, 'platinum'), +(7, 7, 'aluminium'); +execute s; +select * from t3; +a b description +1 1 iron +0 NULL gold +3 3 silver +4 4 lead +5 5 tin +6 6 platinum +7 7 aluminium +truncate t3; +insert into t3 values (1, 1, 'iron'),(2,2,'wood'),(0,NULL, 'gold'), +(3, 3, 'silver'), (4, 4, 'lead'), (5, 5, 'tin'), (6, 6, 'platinum'), +(7, 7, 'aluminium'); +drop prepare s; +# delete multi table syntax, limit clause in delete +explain with cte as (select * from t1 where c < 5), +cte2 as (select * from t2 where b < 3) +delete from t3 +using t3, cte +where t3.a = cte.a and cte.b in (select b from cte2) limit 1; +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY t3 ALL NULL NULL NULL NULL 8 Using where +1 PRIMARY ref key0 key0 4 test.t3.a 1 Using where +1 PRIMARY t2 index NULL t2_ix1 9 NULL 13 Using where; Using index; FirstMatch() +2 DERIVED t1 ALL NULL NULL NULL NULL 9 Using where +with cte as (select * from t1 where c < 5), +cte2 as (select * from t2 where b < 3) +delete from t3 +using t3, cte +where t3.a = cte.a and cte.b in (select b from cte2) limit 1; +select * from t3; +a b description +1 1 iron +0 NULL gold +3 3 silver +4 4 lead +5 5 tin +6 6 platinum +7 7 aluminium +truncate t3; +insert into t3 values (1, 1, 'iron'),(2,2,'wood'),(0,NULL, 'gold'), +(3, 3, 'silver'), (4, 4, 'lead'), (5, 5, 'tin'), (6, 6, 'platinum'), +(7, 7, 'aluminium'); +create procedure p() with cte as (select * from t1 where c < 5), +cte2 as (select * from t2 where b < 3) +delete from t3 +using t3, cte +where t3.a = cte.a and cte.b in (select b from cte2) limit 1; +call p(); +drop procedure p; +select * from t3; +a b description +1 1 iron +0 NULL gold +3 3 silver +4 4 lead +5 5 tin +6 6 platinum +7 7 aluminium +truncate t3; +insert into t3 values (1, 1, 'iron'),(2,2,'wood'),(0,NULL, 'gold'), +(3, 3, 'silver'), (4, 4, 'lead'), (5, 5, 'tin'), (6, 6, 'platinum'), +(7, 7, 'aluminium'); +prepare s from 'with cte as (select * from t1 where c < 5), +cte2 as (select * from t2 where b < 3) +delete from t3 +using t3, cte +where t3.a = cte.a and cte.b in (select b from cte2) limit 1'; +execute s; +select * from t3; +a b description +1 1 iron +0 NULL gold +3 3 silver +4 4 lead +5 5 tin +6 6 platinum +7 7 aluminium +truncate t3; +insert into t3 values (1, 1, 'iron'),(2,2,'wood'),(0,NULL, 'gold'), +(3, 3, 'silver'), (4, 4, 'lead'), (5, 5, 'tin'), (6, 6, 'platinum'), +(7, 7, 'aluminium'); +execute s; +select * from t3; +a b description +1 1 iron +0 NULL gold +3 3 silver +4 4 lead +5 5 tin +6 6 platinum +7 7 aluminium +truncate t3; +insert into t3 values (1, 1, 'iron'),(2,2,'wood'),(0,NULL, 'gold'), +(3, 3, 'silver'), (4, 4, 'lead'), (5, 5, 'tin'), (6, 6, 'platinum'), +(7, 7, 'aluminium'); +drop prepare s; +explain with cte as (select * from t1 where c < 5), +cte2 as (select * from t2 where b < 3) +delete from t3 +using t3, cte, cte2 +where t3.a = cte.a and cte.b = cte2.b; +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY t3 ALL NULL NULL NULL NULL 8 Using where +1 PRIMARY ref key0 key0 4 test.t3.a 1 Using where +1 PRIMARY ref key0 key0 5 cte.b 1 +2 DERIVED t1 ALL NULL NULL NULL NULL 9 Using where +3 DERIVED t2 ALL NULL NULL NULL NULL 13 Using where +with cte as (select * from t1 where c < 5), +cte2 as (select * from t2 where b < 3) +delete from t3 +using t3, cte, cte2 +where t3.a = cte.a and cte.b = cte2.b; +select * from t3; +a b description +1 1 iron +0 NULL gold +3 3 silver +4 4 lead +5 5 tin +6 6 platinum +7 7 aluminium +truncate t3; +insert into t3 values (1, 1, 'iron'),(2,2,'wood'),(0,NULL, 'gold'), +(3, 3, 'silver'), (4, 4, 'lead'), (5, 5, 'tin'), (6, 6, 'platinum'), +(7, 7, 'aluminium'); +drop table t1, t2, t3, t4, t5; +# End of 12.2 tests diff --git a/mysql-test/main/cte_update_delete.test b/mysql-test/main/cte_update_delete.test new file mode 100644 index 0000000000000..3d5fa62d2de5d --- /dev/null +++ b/mysql-test/main/cte_update_delete.test @@ -0,0 +1,402 @@ +--echo # +--echo # MDEV-37220 Allow UPDATE/DELETE to read from a CTE +--echo # + +create table t1 +( + a int not null, + b int, + c int, + d int, + amount decimal, + key t1_ix1 (a,b) +); + +insert into t1 values (0, NULL, 0, NULL, 10.0000), (1, 1, 1, 1, 10.0000), +(2, 2, 2, 2, 20.0000), (3, 3, 3, 3, 30.0000), (4, 4, 4, 4, 40.0000), +(5, 5, 5, 5, NULL), (6, 6, 6, 6, NULL), (7, 7, 7, 7, 70.0000), +(8, 8, 8, 8, 80.0000); + +create table t2 +( + a int NOT NULL, + b int, + name varchar(50), + key t2_ix1 (a,b) +); + +insert into t2 values (0, NULL, 'a'), (1, NULL, 'A'), (2, 2, 'B'), (3,3, 'C'), +(4,4, 'D'), (5,5, NULL), (6,6, NULL), (7,7, 'E'), (8,8, 'F'), (9,9, 'G'), +(10,10,'H'), (11,11, NULL), (12,12, NULL); + +create table t3 +( + a int, + b int, + description varchar(50) +); + +let $empty_t3= +truncate t3; +let $fill_t3= +insert into t3 values (1, 1, 'iron'),(2,2,'wood'),(0,NULL, 'gold'), +(3, 3, 'silver'), (4, 4, 'lead'), (5, 5, 'tin'), (6, 6, 'platinum'), +(7, 7, 'aluminium'); + +create table t4 as select * from t1; +create table t5 as select * from t2; + +eval $fill_t3; + +select * from t3; + +--echo # update, single table syntax, subq in set +let $q= +with cte as (select * from t1 where c < 5), + cte2 as (select * from t2 where b < 3) +update t3 + set t3.a = (select a from cte where b in (select b from cte2) and d = t3.a); +eval explain $q; +eval $q; +select * from t3; +eval $empty_t3; +eval $fill_t3; +eval create procedure p() $q; +call p(); +drop procedure p; +select * from t3; +eval $empty_t3; +eval $fill_t3; +eval prepare s from '$q'; +execute s; +select * from t3; +eval $empty_t3; +eval $fill_t3; +execute s; +select * from t3; +eval $empty_t3; +eval $fill_t3; +drop prepare s; + +--echo # update, single table syntax, subq in set, cte used twice +let $q= +with cte as (select * from t1 where c < 5), + cte2 as (select * from t2 where b < 3) +update t3 + set t3.a = (select a from cte + where + (b in (select b from cte2 where cte2.a = cte.a) or + b in (select b from cte2 where cte2.b = cte.a)) and + d = t3.a + ); +eval explain $q; +eval $q; +select * from t3; +eval $empty_t3; +eval $fill_t3; +eval create procedure p() $q; +call p(); +drop procedure p; +select * from t3; +eval $empty_t3; +eval $fill_t3; +eval prepare s from '$q'; +execute s; +select * from t3; +eval $empty_t3; +eval $fill_t3; +execute s; +select * from t3; +eval $empty_t3; +eval $fill_t3; +drop prepare s; + +--echo # multi table syntax, subq in set and where +let $q= +with cte as (select * from t1 where c < 5), + cte2 as (select * from t2 where b < 3) +update t3,cte + set t3.a = cte.a+1 where cte.b in (select b from cte2) and d = t3.a; +eval explain $q; +eval $q; +select * from t3; +eval $empty_t3; +eval $fill_t3; +eval create procedure p() $q; +call p(); +drop procedure p; +select * from t3; +eval $empty_t3; +eval $fill_t3; + +--echo # multi table syntax, subq in set and where, more complex CTEs +let $q= +with cte as (select t1.* from t1 left join t4 on t1.d = t4.d where t1.c < 5), + cte2 as (select t2.* from t2, t5 where t2.b < 3 and t2.name = t5.name limit 1) +update t3,cte, t4 + set t3.a = cte.a+1, t4.a = cte.a+2 + where cte.b in (select b from cte2) and cte.d = t3.a and cte.b = t4.a; +eval explain $q; +eval $q; +select * from t3; +select * from t4; +eval $empty_t3; +eval $fill_t3; +drop table t4; +create table t4 as select * from t1; + +eval create procedure p() $q; +call p(); +drop procedure p; +select * from t3; +select * from t4; +eval $empty_t3; +eval $fill_t3; +drop table t4; +create table t4 as select * from t1; +eval prepare s from '$q'; +execute s; +select * from t3; +select * from t4; +eval $empty_t3; +eval $fill_t3; +drop table t4; +create table t4 as select * from t1; +execute s; +select * from t3; +select * from t4; +eval $empty_t3; +eval $fill_t3; +drop prepare s; + +drop table t4; +create table t4 as select * from t1; + +let $q= +with cte as (select * from t1 where c < 5), + cte2 as (select * from t2 where b < 3) +update t3 left join cte on t3.b = cte.b + set t3.a = cte.a+1 where cte.b in (select b from cte2) and d = t3.a; +eval explain $q; +eval $q; +select * from t3; +eval $empty_t3; +eval $fill_t3; + +let $q= +with cte as (select * from t1 where c < 5), + cte2 as (select * from t2 where b < 3) +update t3, cte, cte2 set t3.a = cte.a+2 where cte.b = cte2.b and d = t3.a; +eval explain $q; +eval $q; +select * from t3; +eval $empty_t3; +eval $fill_t3; + +let $q= +with cte as (select * from t1 where c < 5), + cte2 as (select * from t2 where b < 3) +update cte, cte2, t3 set t3.a = cte.a+2 where cte.b = cte2.b and d = t3.a; +eval explain $q; +eval $q; +select * from t3; +eval $empty_t3; +eval $fill_t3; + +let $q= +with cte as (select * from t1 where c < 5 group by a), + cte2 as (select * from t2 where b < 3 group by a) +update cte, cte2, t3 set t3.a = cte.a+2 where cte.b = cte2.b and d = t3.a; +eval explain $q; +eval $q; +select * from t3; +eval $empty_t3; +eval $fill_t3; + +create view v1 as select * from t1; +let $q= +with cte as (select * from v1 where c < 5 group by a), + cte2 as (select * from t2 where b < 3 group by a) +update cte, cte2, t3 set t3.a = cte.a+2 where cte.b = cte2.b and d = t3.a; +eval explain $q; +eval $q; +select * from t3; +eval $empty_t3; +eval $fill_t3; + +--error 1288 +with T as (select * from t1) update T set T.a=3; + +--error 1288 +with T as (select * from v1) update T set T.a=3; + +--error 1288 +with T as (select * from t1) delete from T where a=3; + +--error 1288 +with T as (select * from v1) delete from T where a=3; + +drop view v1; + +create view v1 as select * from t1 where b IS NOT NULL limit 1; +let $q= +with cte as (select * from v1 where c < 5 group by a), + cte2 as (select * from t2 where b < 3 group by a) +update cte, cte2, t3 set t3.a = cte.a+2 where d = t3.a; +eval explain $q; +eval $q; +select * from t3; +eval $empty_t3; +eval $fill_t3; +drop view v1; + +CREATE FUNCTION f1 () RETURNS BLOB RETURN 1; +CREATE TABLE tf (f1 DATE); +INSERT INTO tf VALUES('2001-01-01'); +let $q= +with cte as (select f1 as a, f1() as b, 2 as d from tf), + cte2 as (select * from t2 where b < 3 group by a) +update cte, cte2, t3 set t3.a = cte.a+2 where cte.b = cte2.b and d = t3.a; +eval explain $q; +eval $q; +select * from t3; +eval $empty_t3; +eval $fill_t3; +drop function f1; +drop table tf; + +--echo # delete single table syntax +let $q= +with cte as (select * from t1 where c < 5), + cte2 as (select * from t2 where b < 3) +delete from t3 + where t3.a = (select a from cte where b in (select b from cte2)); + +eval explain $q; +eval $q; +select * from t3; +eval $empty_t3; +eval $fill_t3; +eval create procedure p() $q; +call p(); +drop procedure p; +select * from t3; +eval $empty_t3; +eval $fill_t3; + + +--echo # delete multi table syntax +let $q= +with cte as (select * from t1 where c < 5), + cte2 as (select * from t2 where b < 3) +delete from t3 + using t3, cte + where t3.a = cte.a and cte.b in (select b from cte2); +eval explain $q; +eval $q; +select * from t3; +eval $empty_t3; +eval $fill_t3; +eval create procedure p() $q; +call p(); +drop procedure p; +select * from t3; +eval $empty_t3; +eval $fill_t3; + +--echo # delete multi table syntax, multi table delete +let $q= +with cte as (select * from t1 where c < 5), + cte2 as (select * from t2 where b < 3) +delete from t3, t4 + using t3, t4, cte + where t3.a = cte.a and cte.b in (select b from cte2) + and t4.a = t3.a; +eval explain $q; +eval $q; +select * from t3; +select * from t4; +eval $empty_t3; +eval $fill_t3; +eval create procedure p() $q; +call p(); +drop procedure p; +select * from t3; +eval $empty_t3; +eval $fill_t3; + +--echo # delete multi table syntax, multi table CTEs +let $q= +with cte as (select t1.* from t1 left join t4 on t1.d = t4.d where t1.c < 5), + cte2 as (select t2.* from t2, t5 where t2.b < 3 and t2.name = t5.name limit 1) +delete from t3 + using t3, cte + where t3.a = cte.a and cte.b in (select b from cte2); +eval explain $q; +eval $q; +select * from t3; +eval $empty_t3; +eval $fill_t3; +eval create procedure p() $q; +call p(); +drop procedure p; +select * from t3; +eval $empty_t3; +eval $fill_t3; +eval prepare s from '$q'; +execute s; +select * from t3; +eval $empty_t3; +eval $fill_t3; +execute s; +select * from t3; +eval $empty_t3; +eval $fill_t3; +drop prepare s; + + + +--echo # delete multi table syntax, limit clause in delete +let $q= +with cte as (select * from t1 where c < 5), + cte2 as (select * from t2 where b < 3) +delete from t3 + using t3, cte + where t3.a = cte.a and cte.b in (select b from cte2) limit 1; +eval explain $q; +eval $q; +select * from t3; +eval $empty_t3; +eval $fill_t3; +eval create procedure p() $q; +call p(); +drop procedure p; +select * from t3; +eval $empty_t3; +eval $fill_t3; +eval prepare s from '$q'; +execute s; +select * from t3; +eval $empty_t3; +eval $fill_t3; +execute s; +select * from t3; +eval $empty_t3; +eval $fill_t3; +drop prepare s; + +let $q= +with cte as (select * from t1 where c < 5), + cte2 as (select * from t2 where b < 3) +delete from t3 + using t3, cte, cte2 + where t3.a = cte.a and cte.b = cte2.b; +eval explain $q; +eval $q; +select * from t3; +eval $empty_t3; +eval $fill_t3; + +drop table t1, t2, t3, t4, t5; + +--echo # End of 12.2 tests diff --git a/sql/sql_cte.cc b/sql/sql_cte.cc index d5cb96dc55bea..db8b47c55726f 100644 --- a/sql/sql_cte.cc +++ b/sql/sql_cte.cc @@ -153,6 +153,18 @@ bool LEX::resolve_references_to_cte(TABLE_LIST *tables, { With_element *with_elem= 0; + /* + Add back the tables collected during definition of the CTEs, but cleared + during mysql_init_delete + */ + if (save_list.elements) + { + for (TABLE_LIST *saved= save_list.first; saved; saved= saved->next_global) + add_to_query_tables(saved); + save_list.empty(); + last_table()->next_global= nullptr; + } + for (TABLE_LIST *tbl= tables; tbl != *tables_last; tbl= tbl->next_global) { if (tbl->derived) diff --git a/sql/sql_lex.h b/sql/sql_lex.h index 83b3c5ae43c8f..bbf4ac009db12 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -3348,7 +3348,13 @@ struct LEX: public Query_tables_list uint select_stack_outer_barrier; SQL_I_List proc_list; - SQL_I_List auxiliary_table_list, save_list; + SQL_I_List auxiliary_table_list; + /* + save_list is used by + - Parsing CREATE TABLE t0 (...) UNION = (t1, t2, t3) + - CTEs for DELETE, see mysql_init_delete(). + */ + SQL_I_List save_list; Column_definition *last_field; Table_function_json_table *json_table; Item_sum *in_sum_func; diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index ef4a7e37eb67d..55a1206ad6d76 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -6070,6 +6070,7 @@ mysql_execute_command(THD *thd, bool is_called_from_prepared_stmt) thd->variables.default_master_connection.str) thd->lex->mi.connection_name= null_clex_str; + lex->save_list.empty(); if (lex->sql_command != SQLCOM_SET_OPTION) DEBUG_SYNC(thd, "end_of_statement"); DBUG_RETURN(res || thd->is_error()); @@ -7619,6 +7620,12 @@ void create_select_for_variable(THD *thd, LEX_CSTRING *var_name) void mysql_init_delete(LEX *lex) { + if (lex->with_cte_resolution) + { + // Save and clear lex->query_tables. + lex->save_list.insert(lex->query_tables, &lex->query_tables); + lex->query_tables_last= &lex->query_tables; + } lex->init_select(); lex->first_select_lex()->limit_params.clear(); lex->unit.lim.clear(); diff --git a/sql/sql_select.cc b/sql/sql_select.cc index edb8088e6d8cf..db10a59d762bc 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -23812,7 +23812,8 @@ free_tmp_table(THD *thd, TABLE *entry) if (entry->pos_in_table_list && entry->pos_in_table_list->table) { - DBUG_ASSERT(entry->pos_in_table_list->table == entry); + DBUG_ASSERT(entry->pos_in_table_list->table == entry || + entry->pos_in_table_list->merged_for_insert); entry->pos_in_table_list->table= NULL; } diff --git a/sql/sql_update.cc b/sql/sql_update.cc index 0e26b379f2d3b..754f696272b0e 100644 --- a/sql/sql_update.cc +++ b/sql/sql_update.cc @@ -1886,7 +1886,7 @@ int multi_update::prepare(List ¬_used_values, { TABLE_LIST *table_ref; SQL_I_List update_list; - Item_field *item; + Item *item; List_iterator_fast field_it(*fields); List_iterator_fast value_it(*values); uint i, max_fields; @@ -1975,15 +1975,16 @@ int multi_update::prepare(List ¬_used_values, /* Split fields into fields_for_table[] and values_by_table[] */ - while ((item= (Item_field *) field_it++)) + while ((item= field_it++)) { + Item_field *item_field= (Item_field *)item->real_item(); Item *value= value_it++; - uint offset= item->field->table->pos_in_table_list->shared; + uint offset= item_field->field->table->pos_in_table_list->shared; - if (value->associate_with_target_field(thd, item)) + if (value->associate_with_target_field(thd, item_field)) DBUG_RETURN(1); - fields_for_table[offset]->push_back(item, thd->mem_root); + fields_for_table[offset]->push_back(item_field, thd->mem_root); values_for_table[offset]->push_back(value, thd->mem_root); } if (unlikely(thd->is_fatal_error)) diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index dd742bc47f523..5aed54f243c7d 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -1974,7 +1974,7 @@ rule: '-' '+' '*' '/' '%' '(' ')' ',' '!' '{' '}' '&' '|' -%type with_clause +%type with_clause opt_with_clause %type with_element_head @@ -14092,15 +14092,19 @@ update_table_list: /* Update rows in a table */ update: + opt_with_clause UPDATE_SYM opt_optimizer_hint { LEX *lex= Lex; if (Lex->main_select_push()) MYSQL_YYABORT; lex->init_select(); - Lex->first_select_lex()->set_optimizer_hints($2); + Lex->first_select_lex()->set_optimizer_hints($3); lex->sql_command= SQLCOM_UPDATE; lex->duplicates= DUP_ERROR; + Lex->first_select_lex()->master_unit()->set_with_clause($1); + if ($1) + $1->attach_to(Lex->first_select_lex()); } opt_low_priority opt_ignore update_table_list SET update_list @@ -14128,12 +14132,12 @@ update: be too pessimistic. We will decrease lock level if possible later while processing the statement. */ - slex->set_lock_for_tables($4, slex->table_list.elements == 1, false); + slex->set_lock_for_tables($5, slex->table_list.elements == 1, false); } opt_where_clause opt_order_clause delete_limit_clause { - if ($11) - Select->order_list= *($11); + if ($12) + Select->order_list= *($12); } stmt_end {} ; @@ -14181,6 +14185,7 @@ opt_low_priority: /* Delete rows from a table */ delete: + opt_with_clause DELETE_SYM opt_optimizer_hint { LEX *lex= Lex; @@ -14191,7 +14196,10 @@ delete: mysql_init_delete(lex); lex->ignore= 0; lex->first_select_lex()->order_list.empty(); - lex->first_select_lex()->set_optimizer_hints($2); + lex->first_select_lex()->set_optimizer_hints($3); + lex->first_select_lex()->master_unit()->set_with_clause($1); + if ($1) + $1->attach_to(lex->first_select_lex()); } delete_part2 { @@ -15895,6 +15903,11 @@ temporal_literal: } ; +opt_with_clause: + /* empty */ { $$= NULL; } + | with_clause { $$= $1; } + ; + with_clause: WITH opt_recursive { diff --git a/sql/table.cc b/sql/table.cc index cd5b15421dd39..8f9c6398f07af 100644 --- a/sql/table.cc +++ b/sql/table.cc @@ -6190,7 +6190,8 @@ bool TABLE_LIST::create_field_translation(THD *thd) It's needed because some items in the select list, like IN subselects, might be substituted for optimized ones. */ - if (is_view() && get_unit()->prepared && !field_translation_updated) + if (is_merged_derived() && + get_unit()->prepared && !field_translation_updated) { field_translation_updated= TRUE; if (static_cast(field_translation_end - field_translation) <