diff --git a/mysql-test/main/delete.result b/mysql-test/main/delete.result index dda28f165ad..d3a32873551 100644 --- a/mysql-test/main/delete.result +++ b/mysql-test/main/delete.result @@ -680,3 +680,105 @@ id select_type table type possible_keys key key_len ref rows Extra 2 MATERIALIZED t2 ALL NULL NULL NULL NULL 20000 drop table t1, t2; # End of 11.7 tests +# +# MDEV-30469: Add support of ORDER BY and LIMIT to multidelete query. +# +# Check that limits work with hints +create table t2 (id int, index xid(id)); +insert into t2 values (1),(10),(2),(9),(3),(8); +DELETE t2.* FROM t2 use index(xid) ORDER BY (id) LIMIT 2; +select * from t2 ORDER BY (id); +id +3 +8 +9 +10 +DELETE t2.* FROM t2 use index(xid) ORDER BY (id) DESC LIMIT 3; +select * from t2; +id +3 +# Check some useles syntax +DELETE t2.* FROM t2 FORCE INDEX FOR GROUP BY (xid) ORDER BY (id) LIMIT 1; +drop table t2; +# Check that hints work with limit +create table t2 (id int primary key, index xid(id)); +insert into t2 values (1),(10),(2),(9),(3),(8); +# default primary index +explain +DELETE t2.* FROM t2 ORDER BY (id) LIMIT 2; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t2 index NULL PRIMARY 4 NULL 2 +# make it use other undex +explain +DELETE t2.* FROM t2 use index(xid) ORDER BY (id) LIMIT 2; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t2 index NULL xid 4 NULL 2 +explain +DELETE t2.* FROM t2 force index(xid) ORDER BY (id) LIMIT 2; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t2 index NULL xid 4 NULL 2 +# prohibit primary index +explain +DELETE t2.* FROM t2 ignore index(primary) ORDER BY (id) LIMIT 2; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t2 index NULL xid 4 NULL 2 +drop table t2; +# Check that limits work with hints & PS protocol +create table t2 (id int, index xid(id)); +insert into t2 values (1),(10),(2),(9),(3),(8); +prepare stmt from +"DELETE t2.* FROM t2 use index(xid) ORDER BY (id) LIMIT ?"; +set @lim= 2; +execute stmt using @lim; +select * from t2 ORDER BY (id); +id +3 +8 +9 +10 +set @lim= 1; +execute stmt using @lim; +select * from t2 ORDER BY (id); +id +8 +9 +10 +set @lim= 3; +execute stmt using @lim; +select * from t2 ORDER BY (id); +id +drop table t2; +# Check that hints work with limit in normal DELETE syntax +create table t2 (id int primary key, index xid(id)); +insert into t2 values (1),(10),(2),(9),(3),(8); +# default primary index +explain +DELETE FROM t2 ORDER BY (id) LIMIT 2; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t2 index NULL PRIMARY 4 NULL 2 +# make it use other undex +explain +DELETE FROM t2 use index(xid) ORDER BY (id) LIMIT 2; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t2 index NULL xid 4 NULL 2 +explain +DELETE FROM t2 force index(xid) ORDER BY (id) LIMIT 2; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t2 index NULL xid 4 NULL 2 +# prohibit primary index +explain +DELETE FROM t2 ignore index(primary) ORDER BY (id) LIMIT 2; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t2 index NULL xid 4 NULL 2 +# should issue warnings becaouse we can not switch it internally +# to multiupdate due to RETURNING +DELETE FROM t2 ignore index(primary) ORDER BY (id) LIMIT 2 RETURNING id; +id +1 +2 +Warnings: +Warning 4206 Index hints are ignored because they are incompatible with RETURNING clause +drop table t2; +# +# End of 11.4 test +# diff --git a/mysql-test/main/delete.test b/mysql-test/main/delete.test index 66cc472abd8..d747481961e 100644 --- a/mysql-test/main/delete.test +++ b/mysql-test/main/delete.test @@ -744,3 +744,87 @@ explain delete from t1 where b <= 3 and a not in (select b from t2); drop table t1, t2; --echo # End of 11.7 tests + +--echo # +--echo # MDEV-30469: Add support of ORDER BY and LIMIT to multidelete query. +--echo # + +--echo # Check that limits work with hints + +create table t2 (id int, index xid(id)); +insert into t2 values (1),(10),(2),(9),(3),(8); + +DELETE t2.* FROM t2 use index(xid) ORDER BY (id) LIMIT 2; +select * from t2 ORDER BY (id); +DELETE t2.* FROM t2 use index(xid) ORDER BY (id) DESC LIMIT 3; +select * from t2; +--echo # Check some useles syntax +DELETE t2.* FROM t2 FORCE INDEX FOR GROUP BY (xid) ORDER BY (id) LIMIT 1; + +drop table t2; + + +--echo # Check that hints work with limit + +create table t2 (id int primary key, index xid(id)); +insert into t2 values (1),(10),(2),(9),(3),(8); + +--echo # default primary index +explain +DELETE t2.* FROM t2 ORDER BY (id) LIMIT 2; +--echo # make it use other undex +explain +DELETE t2.* FROM t2 use index(xid) ORDER BY (id) LIMIT 2; +explain +DELETE t2.* FROM t2 force index(xid) ORDER BY (id) LIMIT 2; +--echo # prohibit primary index +explain +DELETE t2.* FROM t2 ignore index(primary) ORDER BY (id) LIMIT 2; + +drop table t2; + +--echo # Check that limits work with hints & PS protocol + +create table t2 (id int, index xid(id)); +insert into t2 values (1),(10),(2),(9),(3),(8); + +prepare stmt from +"DELETE t2.* FROM t2 use index(xid) ORDER BY (id) LIMIT ?"; +set @lim= 2; +execute stmt using @lim; +select * from t2 ORDER BY (id); +set @lim= 1; +execute stmt using @lim; +select * from t2 ORDER BY (id); +set @lim= 3; +execute stmt using @lim; +select * from t2 ORDER BY (id); + +drop table t2; + +--echo # Check that hints work with limit in normal DELETE syntax + +create table t2 (id int primary key, index xid(id)); +insert into t2 values (1),(10),(2),(9),(3),(8); + +--echo # default primary index +explain +DELETE FROM t2 ORDER BY (id) LIMIT 2; +--echo # make it use other undex +explain +DELETE FROM t2 use index(xid) ORDER BY (id) LIMIT 2; +explain +DELETE FROM t2 force index(xid) ORDER BY (id) LIMIT 2; +--echo # prohibit primary index +explain +DELETE FROM t2 ignore index(primary) ORDER BY (id) LIMIT 2; + +--echo # should issue warnings becaouse we can not switch it internally +--echo # to multiupdate due to RETURNING +DELETE FROM t2 ignore index(primary) ORDER BY (id) LIMIT 2 RETURNING id; + +drop table t2; + +--echo # +--echo # End of 11.4 test +--echo # diff --git a/mysql-test/main/delete_innodb.result b/mysql-test/main/delete_innodb.result index 7de33f33b9e..4a8ca7cbf03 100644 --- a/mysql-test/main/delete_innodb.result +++ b/mysql-test/main/delete_innodb.result @@ -60,8 +60,8 @@ INSERT INTO t2 values (3); disallows sj optimization analyze DELETE FROM t1 WHERE c1 IN (select c2 from t2) ORDER BY c1 limit 1; id select_type table type possible_keys key key_len ref rows r_rows filtered r_filtered Extra -1 PRIMARY t1 ALL NULL NULL NULL NULL 5 1.00 100.00 100.00 Using where; Using filesort -2 DEPENDENT SUBQUERY t2 ALL NULL NULL NULL NULL 1 1.00 100.00 20.00 Using where +1 PRIMARY t1 ALL NULL NULL NULL NULL 5 2.00 100.00 100.00 Using filesort +1 PRIMARY t2 ALL NULL NULL NULL NULL 1 1.00 100.00 50.00 Using where; FirstMatch(t1) select * from t1; c1 1 diff --git a/mysql-test/main/delete_multi_order_by.result b/mysql-test/main/delete_multi_order_by.result new file mode 100644 index 00000000000..5b5fb98802e --- /dev/null +++ b/mysql-test/main/delete_multi_order_by.result @@ -0,0 +1,239 @@ +create table t1 (id int primary key, v int); +create table t2 (id int primary key, v int); +insert into t1 (id, v) values (4,1),(3,2),(2,3),(1,4); +insert into t2 (id, v) values (5,5),(3,2),(2,3),(6,6); +select * from t1; +id v +4 1 +3 2 +2 3 +1 4 +select * from t2; +id v +5 5 +3 2 +2 3 +6 6 +delete t1.*, t2.* from t1, t2 order by t1.id desc limit 3; +select * from t1; +id v +3 2 +2 3 +1 4 +select * from t2; +id v +6 6 +drop table if exists t1, t2; +create table t1 (id int primary key, v int); +create table t2 (id int primary key, v int); +insert into t1 (id, v) values (4,1),(3,2),(2,3),(1,4); +insert into t2 (id, v) values (5,5),(3,2),(2,3),(6,6); +select * from t1; +id v +4 1 +3 2 +2 3 +1 4 +select * from t2; +id v +5 5 +3 2 +2 3 +6 6 +delete t1.*, t2.* from t1, t2 order by t1.id desc; +select * from t1; +id v +select * from t2; +id v +drop table if exists t1, t2; +create table t1 (id int primary key, v int); +create table t2 (id int primary key, v int); +insert into t1 (id, v) values (4,1),(3,2),(2,3),(1,4); +insert into t2 (id, v) values (5,5),(3,2),(2,3),(6,6); +select * from t1; +id v +4 1 +3 2 +2 3 +1 4 +select * from t2; +id v +5 5 +3 2 +2 3 +6 6 +delete t1.*, t2.* from t1, t2 limit 2; +select * from t1; +id v +3 2 +2 3 +1 4 +select * from t2; +id v +2 3 +6 6 +create table t3 (a int primary key, b text); +insert into t3 (a, b) values (1, 'hello'); +delete from t3 where b = ''; +drop table t3; +drop table if exists t1, t2; +create table t1 (id int primary key, v int); +create table t2 (id int primary key, v int); +insert into t1 (id, v) values (4,1),(3,2),(2,3),(1,4); +insert into t2 (id, v) values (5,5),(3,2),(2,3),(6,6); +select * from t1; +id v +4 1 +3 2 +2 3 +1 4 +select * from t2; +id v +5 5 +3 2 +2 3 +6 6 +delete t1.*, t2.* from t1, t2 where t1.id=t2.id; +select * from t1; +id v +4 1 +1 4 +select * from t2; +id v +5 5 +6 6 +drop table if exists t1; +create table t1(a INT); +insert into t1 values (1),(2),(3); +set session sql_buffer_result=1; +delete t1 from (select sum(a) a from t1) x,t1; +set session sql_buffer_result=default; +select * from t1; +a +drop table t1; +drop table if exists t1, t2; +Warnings: +Note 1051 Unknown table 'test.t1' +create table t1(id1 smallint(5), field char(5)); +create table t2(id2 smallint(5), field char(5)); +insert into t1 values (1, 'a'), (2, 'aa'); +insert into t2 values (1, 'b'), (2, 'bb'); +update t2 inner join t1 on t1.id1=t2.id2 set t2.field=t1.field where 0=1; +update t2, t1 set t2.field=t1.field where t1.id1=t2.id2 and 0=1; +delete t1, t2 from t2 inner join t1 on t1.id1=t2.id2 where 0=1; +drop table t1, t2; +set session sql_buffer_result=1; +create table t1 (id int primary key, v int); +create table t2 (id int primary key, v int); +insert into t1 (id, v) values (4,1),(3,2),(2,3),(1,4); +insert into t2 (id, v) values (5,5),(3,2),(2,3),(6,6); +select * from t1; +id v +4 1 +3 2 +2 3 +1 4 +select * from t2; +id v +5 5 +3 2 +2 3 +6 6 +delete t1.*, t2.* from t1, t2 order by t1.id desc limit 3; +select * from t1; +id v +3 2 +2 3 +1 4 +select * from t2; +id v +6 6 +drop table if exists t1, t2; +create table t1 (id int primary key, v int); +create table t2 (id int primary key, v int); +insert into t1 (id, v) values (4,1),(3,2),(2,3),(1,4); +insert into t2 (id, v) values (5,5),(3,2),(2,3),(6,6); +select * from t1; +id v +4 1 +3 2 +2 3 +1 4 +select * from t2; +id v +5 5 +3 2 +2 3 +6 6 +delete t1.*, t2.* from t1, t2 order by t1.id desc; +select * from t1; +id v +select * from t2; +id v +drop table if exists t1, t2; +create table t1 (id int primary key, v int); +create table t2 (id int primary key, v int); +insert into t1 (id, v) values (4,1),(3,2),(2,3),(1,4); +insert into t2 (id, v) values (5,5),(3,2),(2,3),(6,6); +select * from t1; +id v +4 1 +3 2 +2 3 +1 4 +select * from t2; +id v +5 5 +3 2 +2 3 +6 6 +delete t1.*, t2.* from t1, t2 limit 2; +select * from t1; +id v +3 2 +2 3 +1 4 +select * from t2; +id v +2 3 +6 6 +create table t3 (a int primary key, b text); +insert into t3 (a, b) values (1, 'hello'); +delete from t3 where b = ''; +drop table t3; +drop table if exists t1, t2; +create table t1 (id int primary key, v int); +create table t2 (id int primary key, v int); +insert into t1 (id, v) values (4,1),(3,2),(2,3),(1,4); +insert into t2 (id, v) values (5,5),(3,2),(2,3),(6,6); +select * from t1; +id v +4 1 +3 2 +2 3 +1 4 +select * from t2; +id v +5 5 +3 2 +2 3 +6 6 +delete t1.*, t2.* from t1, t2 where t1.id=t2.id; +select * from t1; +id v +4 1 +1 4 +select * from t2; +id v +5 5 +6 6 +drop table if exists t1, t2; +create table t1(id1 smallint(5), field char(5)); +create table t2(id2 smallint(5), field char(5)); +insert into t1 values (1, 'a'), (2, 'aa'); +insert into t2 values (1, 'b'), (2, 'bb'); +update t2 inner join t1 on t1.id1=t2.id2 set t2.field=t1.field where 0=1; +update t2, t1 set t2.field=t1.field where t1.id1=t2.id2 and 0=1; +delete t1, t2 from t2 inner join t1 on t1.id1=t2.id2 where 0=1; +drop table t1, t2; +set session sql_buffer_result=default; diff --git a/mysql-test/main/delete_multi_order_by.test b/mysql-test/main/delete_multi_order_by.test new file mode 100644 index 00000000000..77c330d76df --- /dev/null +++ b/mysql-test/main/delete_multi_order_by.test @@ -0,0 +1,131 @@ +# +# MDEV-30469 Support ORDER BY and LIMIT for multi-table DELETE, index hints for single-table DELETE. +# +create table t1 (id int primary key, v int); +create table t2 (id int primary key, v int); +insert into t1 (id, v) values (4,1),(3,2),(2,3),(1,4); +insert into t2 (id, v) values (5,5),(3,2),(2,3),(6,6); +select * from t1; +select * from t2; +delete t1.*, t2.* from t1, t2 order by t1.id desc limit 3; +select * from t1; +select * from t2; + +drop table if exists t1, t2; +create table t1 (id int primary key, v int); +create table t2 (id int primary key, v int); +insert into t1 (id, v) values (4,1),(3,2),(2,3),(1,4); +insert into t2 (id, v) values (5,5),(3,2),(2,3),(6,6); +select * from t1; +select * from t2; +delete t1.*, t2.* from t1, t2 order by t1.id desc; +select * from t1; +select * from t2; + +drop table if exists t1, t2; +create table t1 (id int primary key, v int); +create table t2 (id int primary key, v int); +insert into t1 (id, v) values (4,1),(3,2),(2,3),(1,4); +insert into t2 (id, v) values (5,5),(3,2),(2,3),(6,6); +select * from t1; +select * from t2; +delete t1.*, t2.* from t1, t2 limit 2; +select * from t1; +select * from t2; + +create table t3 (a int primary key, b text); +insert into t3 (a, b) values (1, 'hello'); +delete from t3 where b = ''; +drop table t3; + +drop table if exists t1, t2; +create table t1 (id int primary key, v int); +create table t2 (id int primary key, v int); +insert into t1 (id, v) values (4,1),(3,2),(2,3),(1,4); +insert into t2 (id, v) values (5,5),(3,2),(2,3),(6,6); +select * from t1; +select * from t2; +delete t1.*, t2.* from t1, t2 where t1.id=t2.id; +select * from t1; +select * from t2; + +drop table if exists t1; +create table t1(a INT); +insert into t1 values (1),(2),(3); +set session sql_buffer_result=1; +delete t1 from (select sum(a) a from t1) x,t1; +set session sql_buffer_result=default; +select * from t1; +drop table t1; + +drop table if exists t1, t2; +create table t1(id1 smallint(5), field char(5)); +create table t2(id2 smallint(5), field char(5)); +insert into t1 values (1, 'a'), (2, 'aa'); +insert into t2 values (1, 'b'), (2, 'bb'); +update t2 inner join t1 on t1.id1=t2.id2 set t2.field=t1.field where 0=1; +update t2, t1 set t2.field=t1.field where t1.id1=t2.id2 and 0=1; +delete t1, t2 from t2 inner join t1 on t1.id1=t2.id2 where 0=1; +drop table t1, t2; + + +set session sql_buffer_result=1; +create table t1 (id int primary key, v int); +create table t2 (id int primary key, v int); +insert into t1 (id, v) values (4,1),(3,2),(2,3),(1,4); +insert into t2 (id, v) values (5,5),(3,2),(2,3),(6,6); +select * from t1; +select * from t2; +delete t1.*, t2.* from t1, t2 order by t1.id desc limit 3; +select * from t1; +select * from t2; + +drop table if exists t1, t2; +create table t1 (id int primary key, v int); +create table t2 (id int primary key, v int); +insert into t1 (id, v) values (4,1),(3,2),(2,3),(1,4); +insert into t2 (id, v) values (5,5),(3,2),(2,3),(6,6); +select * from t1; +select * from t2; +delete t1.*, t2.* from t1, t2 order by t1.id desc; +select * from t1; +select * from t2; + +drop table if exists t1, t2; +create table t1 (id int primary key, v int); +create table t2 (id int primary key, v int); +insert into t1 (id, v) values (4,1),(3,2),(2,3),(1,4); +insert into t2 (id, v) values (5,5),(3,2),(2,3),(6,6); +select * from t1; +select * from t2; +delete t1.*, t2.* from t1, t2 limit 2; +select * from t1; +select * from t2; + +create table t3 (a int primary key, b text); +insert into t3 (a, b) values (1, 'hello'); +delete from t3 where b = ''; +drop table t3; + +drop table if exists t1, t2; +create table t1 (id int primary key, v int); +create table t2 (id int primary key, v int); +insert into t1 (id, v) values (4,1),(3,2),(2,3),(1,4); +insert into t2 (id, v) values (5,5),(3,2),(2,3),(6,6); +select * from t1; +select * from t2; +delete t1.*, t2.* from t1, t2 where t1.id=t2.id; +select * from t1; +select * from t2; + +drop table if exists t1, t2; +create table t1(id1 smallint(5), field char(5)); +create table t2(id2 smallint(5), field char(5)); +insert into t1 values (1, 'a'), (2, 'aa'); +insert into t2 values (1, 'b'), (2, 'bb'); +update t2 inner join t1 on t1.id1=t2.id2 set t2.field=t1.field where 0=1; +update t2, t1 set t2.field=t1.field where t1.id1=t2.id2 and 0=1; +delete t1, t2 from t2 inner join t1 on t1.id1=t2.id2 where 0=1; + +drop table t1, t2; +set session sql_buffer_result=default; diff --git a/mysql-test/main/delete_single_to_multi.result b/mysql-test/main/delete_single_to_multi.result index 7fac00491be..3b43f553fdc 100644 --- a/mysql-test/main/delete_single_to_multi.result +++ b/mysql-test/main/delete_single_to_multi.result @@ -3342,22 +3342,22 @@ o_custkey in (select c_custkey from customer where c_nationkey in (1,2)) order by o_totalprice limit 500; id select_type table type possible_keys key key_len ref rows Extra -1 PRIMARY orders range i_o_orderdate i_o_orderdate 4 NULL 108 Using where; Using filesort -2 DEPENDENT SUBQUERY customer unique_subquery PRIMARY,i_c_nationkey PRIMARY 4 func 1 Using where +1 PRIMARY customer range PRIMARY,i_c_nationkey i_c_nationkey 5 NULL 13 Using index condition; Using temporary; Using filesort +1 PRIMARY orders ref|filter i_o_orderdate,i_o_custkey i_o_custkey|i_o_orderdate 5|4 dbt3_s001.customer.c_custkey 15 (7%) Using where; Using rowid filter create table t as select * from orders where o_orderDATE between '1992-01-01' and '1992-06-30' and o_custkey in (select c_custkey from customer where c_nationkey in (1,2)); select o_orderkey, o_totalprice from t; o_orderkey o_totalprice -1221 117397.16 324 26868.85 1856 189361.42 -4903 34363.63 -5607 24660.06 +1221 117397.16 1344 43809.37 1925 146382.71 3139 40975.96 +4903 34363.63 +5607 24660.06 delete from orders where o_orderDATE between '1992-01-01' and '1992-06-30' and o_custkey in (select c_custkey from customer where c_nationkey in (1,2)) @@ -3394,14 +3394,14 @@ o_custkey in (select c_custkey from customer where c_nationkey in (1,2)); select o_orderkey, o_totalprice from t; o_orderkey o_totalprice -1221 117397.16 -324 26868.85 1856 189361.42 +324 26868.85 +1221 117397.16 +3139 40975.96 +1925 146382.71 +1344 43809.37 4903 34363.63 5607 24660.06 -1344 43809.37 -1925 146382.71 -3139 40975.96 delete from orders where o_orderDATE between '1992-01-01' and '1992-06-30' and o_custkey in (select c_custkey from customer where c_nationkey in (1,2)); diff --git a/mysql-test/main/delete_use_source_engines.result b/mysql-test/main/delete_use_source_engines.result index b1ad9c009d9..89c4153d650 100644 --- a/mysql-test/main/delete_use_source_engines.result +++ b/mysql-test/main/delete_use_source_engines.result @@ -232,8 +232,8 @@ id select_type table type possible_keys key key_len ref rows Extra explain delete from t1 where c1 in (select a.c2 from t1 a where a.c3 = t1.c3) order by c3 desc limit 1; id select_type table type possible_keys key key_len ref rows Extra -1 PRIMARY t1 ALL NULL NULL NULL NULL 32 Using where; Using filesort -2 DEPENDENT SUBQUERY a ALL NULL NULL NULL NULL 32 Using where +1 PRIMARY t1 ALL NULL NULL NULL NULL 32 Using filesort +1 PRIMARY a ALL NULL NULL NULL NULL 32 Using where; FirstMatch(t1) delete from t1 where c1 in (select a.c2 from t1 a where a.c3 = t1.c3) order by c3 desc limit 1; affected rows: 1 @@ -788,8 +788,8 @@ id select_type table type possible_keys key key_len ref rows Extra explain delete from t1 where c1 in (select a.c2 from t1 a where a.c3 = t1.c3) order by c3 desc limit 1; id select_type table type possible_keys key key_len ref rows Extra -1 PRIMARY t1 ALL NULL NULL NULL NULL 32 Using where; Using filesort -2 DEPENDENT SUBQUERY a index_subquery t1_c2 t1_c2 5 func 5 Using where +1 PRIMARY t1 ALL NULL NULL NULL NULL 32 Using filesort +1 PRIMARY a ALL t1_c2 NULL NULL NULL 32 Using where; FirstMatch(t1) delete from t1 where c1 in (select a.c2 from t1 a where a.c3 = t1.c3) order by c3 desc limit 1; affected rows: 1 @@ -1345,8 +1345,8 @@ id select_type table type possible_keys key key_len ref rows Extra explain delete from t1 where c1 in (select a.c2 from t1 a where a.c3 = t1.c3) order by c3 desc limit 1; id select_type table type possible_keys key key_len ref rows Extra -1 PRIMARY t1 index NULL PRIMARY 4 NULL 1 Using where -2 DEPENDENT SUBQUERY a eq_ref PRIMARY PRIMARY 4 test.t1.c3 1 Using where +1 PRIMARY t1 index PRIMARY PRIMARY 4 NULL 1 +1 PRIMARY a eq_ref PRIMARY PRIMARY 4 test.t1.c3 1 Using where delete from t1 where c1 in (select a.c2 from t1 a where a.c3 = t1.c3) order by c3 desc limit 1; affected rows: 1 @@ -1995,8 +1995,8 @@ id select_type table type possible_keys key key_len ref rows Extra explain delete from t1 where c1 in (select a.c2 from t1 a where a.c3 = t1.c3) order by c3 desc limit 1; id select_type table type possible_keys key key_len ref rows Extra -1 PRIMARY t1 ALL NULL NULL NULL NULL 32 Using where; Using filesort -2 DEPENDENT SUBQUERY a ALL NULL NULL NULL NULL 32 Using where +1 PRIMARY t1 ALL NULL NULL NULL NULL 32 Using filesort +1 PRIMARY a ALL NULL NULL NULL NULL 32 Using where; FirstMatch(t1) delete from t1 where c1 in (select a.c2 from t1 a where a.c3 = t1.c3) order by c3 desc limit 1; affected rows: 1 @@ -2767,7 +2767,7 @@ explain delete from t1 where c1 in (select a.c2 from t1 a where a.c3 = t1.c3) order by c3 desc limit 1; id select_type table type possible_keys key key_len ref rows Extra 1 PRIMARY t1 ALL NULL NULL NULL NULL 32 Using where; Using filesort -2 DEPENDENT SUBQUERY a index_subquery t1_c2 t1_c2 5 func 5 Using where +1 PRIMARY a ref t1_c2 t1_c2 5 test.t1.c1 5 Using where; FirstMatch(t1) delete from t1 where c1 in (select a.c2 from t1 a where a.c3 = t1.c3) order by c3 desc limit 1; affected rows: 1 @@ -3394,7 +3394,7 @@ id select_type table type possible_keys key key_len ref rows Extra analyze delete from t1 where c1 = 1 and exists (select 'X' from t1 a where a.c1 = t1.c2); id select_type table type possible_keys key key_len ref rows r_rows filtered r_filtered Extra 1 PRIMARY t1 ALL NULL NULL NULL NULL 32 32.00 9.38 9.38 Using where -1 PRIMARY a ALL NULL NULL NULL NULL 32 12.00 3.12 5.56 Using where; FirstMatch(t1) +1 PRIMARY a ALL NULL NULL NULL NULL 32 13.33 3.12 5.00 Using where; FirstMatch(t1) select * from t1; c1 c2 c3 1 3 3 @@ -3535,8 +3535,8 @@ id select_type table type possible_keys key key_len ref rows Extra explain delete from t1 where c1 in (select a.c2 from t1 a where a.c3 = t1.c3) order by c3 desc limit 1; id select_type table type possible_keys key key_len ref rows Extra -1 PRIMARY t1 index NULL PRIMARY 4 NULL 1 Using where -2 DEPENDENT SUBQUERY a eq_ref PRIMARY PRIMARY 4 test.t1.c3 1 Using where +1 PRIMARY t1 index PRIMARY PRIMARY 4 NULL 1 +1 PRIMARY a eq_ref PRIMARY PRIMARY 4 test.t1.c3 1 Using where delete from t1 where c1 in (select a.c2 from t1 a where a.c3 = t1.c3) order by c3 desc limit 1; affected rows: 1 @@ -4054,7 +4054,7 @@ and c1 = 2 and exists (select 'X' from v1 a where a.c1 = v1.c1); id select_type table type possible_keys key key_len ref rows r_rows filtered r_filtered Extra 1 PRIMARY t1 ALL NULL NULL NULL NULL # 32.00 3.91 3.12 Using where -3 DEPENDENT SUBQUERY t1 ALL NULL NULL NULL NULL # 6.00 25.00 16.67 Using where +3 DEPENDENT SUBQUERY t1 ALL NULL NULL NULL NULL # 10.00 25.00 10.00 Using where 2 DEPENDENT SUBQUERY a eq_ref PRIMARY PRIMARY 4 test.t1.c3 # 1.00 100.00 100.00 select * from t1; c1 c2 c3 @@ -4400,8 +4400,8 @@ id select_type table type possible_keys key key_len ref rows Extra explain delete from t1 where c1 in (select a.c2 from t1 a where a.c3 = t1.c3) order by c3 desc limit 1; id select_type table type possible_keys key key_len ref rows Extra -1 PRIMARY t1 ALL NULL NULL NULL NULL 32 Using where; Using filesort -2 DEPENDENT SUBQUERY a ALL NULL NULL NULL NULL 32 Using where +1 PRIMARY t1 ALL NULL NULL NULL NULL 32 Using filesort +1 PRIMARY a ALL NULL NULL NULL NULL 32 Using where; FirstMatch(t1) delete from t1 where c1 in (select a.c2 from t1 a where a.c3 = t1.c3) order by c3 desc limit 1; affected rows: 1 @@ -5171,8 +5171,8 @@ id select_type table type possible_keys key key_len ref rows Extra explain delete from t1 where c1 in (select a.c2 from t1 a where a.c3 = t1.c3) order by c3 desc limit 1; id select_type table type possible_keys key key_len ref rows Extra -1 PRIMARY t1 ALL NULL NULL NULL NULL 32 Using where; Using filesort -2 DEPENDENT SUBQUERY a index_subquery t1_c2 t1_c2 5 func 5 Using where +1 PRIMARY t1 ALL NULL NULL NULL NULL 32 Using filesort +1 PRIMARY a ALL t1_c2 NULL NULL NULL 32 Using where; FirstMatch(t1) delete from t1 where c1 in (select a.c2 from t1 a where a.c3 = t1.c3) order by c3 desc limit 1; affected rows: 1 @@ -5799,7 +5799,7 @@ id select_type table type possible_keys key key_len ref rows Extra analyze delete from t1 where c1 = 1 and exists (select 'X' from t1 a where a.c1 = t1.c2); id select_type table type possible_keys key key_len ref rows r_rows filtered r_filtered Extra 1 PRIMARY t1 ALL NULL NULL NULL NULL 32 32.00 9.38 9.38 Using where -1 PRIMARY a ALL NULL NULL NULL NULL 32 13.33 3.12 5.00 Using where; FirstMatch(t1) +1 PRIMARY a ALL NULL NULL NULL NULL 32 14.00 3.12 4.76 Using where; FirstMatch(t1) select * from t1; c1 c2 c3 1 3 3 @@ -5940,8 +5940,8 @@ id select_type table type possible_keys key key_len ref rows Extra explain delete from t1 where c1 in (select a.c2 from t1 a where a.c3 = t1.c3) order by c3 desc limit 1; id select_type table type possible_keys key key_len ref rows Extra -1 PRIMARY t1 index NULL PRIMARY 4 NULL 1 Using where -2 DEPENDENT SUBQUERY a eq_ref PRIMARY PRIMARY 4 test.t1.c3 1 Using where +1 PRIMARY t1 index PRIMARY PRIMARY 4 NULL 1 +1 PRIMARY a eq_ref PRIMARY PRIMARY 4 test.t1.c3 1 Using where delete from t1 where c1 in (select a.c2 from t1 a where a.c3 = t1.c3) order by c3 desc limit 1; affected rows: 1 @@ -6888,8 +6888,8 @@ id select_type table type possible_keys key key_len ref rows Extra explain delete from t1 where c1 in (select a.c2 from t1 a where a.c3 = t1.c3) order by c3 desc limit 1; id select_type table type possible_keys key key_len ref rows Extra -1 PRIMARY t1 ALL NULL NULL NULL NULL 32 Using where; Using filesort -2 DEPENDENT SUBQUERY a ALL NULL NULL NULL NULL 32 Using where +1 PRIMARY t1 ALL NULL NULL NULL NULL 32 Using filesort +1 PRIMARY a ALL NULL NULL NULL NULL 32 Using where; FirstMatch(t1) delete from t1 where c1 in (select a.c2 from t1 a where a.c3 = t1.c3) order by c3 desc limit 1; affected rows: 1 @@ -7643,8 +7643,8 @@ id select_type table type possible_keys key key_len ref rows Extra explain delete from t1 where c1 in (select a.c2 from t1 a where a.c3 = t1.c3) order by c3 desc limit 1; id select_type table type possible_keys key key_len ref rows Extra -1 PRIMARY t1 ALL NULL NULL NULL NULL 32 Using where; Using filesort -2 DEPENDENT SUBQUERY a ALL t1_c2 NULL NULL NULL 32 Using where +1 PRIMARY t1 ALL NULL NULL NULL NULL 32 Using filesort +1 PRIMARY a ALL t1_c2 NULL NULL NULL 32 Using where; FirstMatch(t1) delete from t1 where c1 in (select a.c2 from t1 a where a.c3 = t1.c3) order by c3 desc limit 1; affected rows: 1 @@ -8401,8 +8401,8 @@ id select_type table type possible_keys key key_len ref rows Extra explain delete from t1 where c1 in (select a.c2 from t1 a where a.c3 = t1.c3) order by c3 desc limit 1; id select_type table type possible_keys key key_len ref rows Extra -1 PRIMARY t1 ALL NULL NULL NULL NULL 32 Using where; Using filesort -2 DEPENDENT SUBQUERY a eq_ref PRIMARY PRIMARY 4 test.t1.c3 1 Using where +1 PRIMARY t1 ALL PRIMARY NULL NULL NULL 32 Using filesort +1 PRIMARY a eq_ref PRIMARY PRIMARY 4 test.t1.c3 1 Using where delete from t1 where c1 in (select a.c2 from t1 a where a.c3 = t1.c3) order by c3 desc limit 1; affected rows: 1 diff --git a/mysql-test/main/multidelete_engine.combinations b/mysql-test/main/multidelete_engine.combinations new file mode 100644 index 00000000000..10c52424874 --- /dev/null +++ b/mysql-test/main/multidelete_engine.combinations @@ -0,0 +1,12 @@ +[myisam] +default-storage-engine=myisam + +[aria] +default-storage-engine=aria + +[innodb] +innodb +default-storage-engine=innodb + +[heap] +default-storage-engine=memory diff --git a/mysql-test/main/multidelete_engine.test b/mysql-test/main/multidelete_engine.test new file mode 100644 index 00000000000..7dd1eab5da5 --- /dev/null +++ b/mysql-test/main/multidelete_engine.test @@ -0,0 +1,20 @@ + +--echo # Check that limits work with hints & PS protocol + +create table t2 (id int, index xid(id)); +insert into t2 values (1),(10),(2),(9),(3),(8); + +prepare stmt from +"DELETE t2.* FROM t2 use index(xid) ORDER BY (id) LIMIT ?"; +set @lim= 6; +execute stmt using @lim; +select * from t2 ORDER BY (id); +set @lim= 1; +execute stmt using @lim; +select * from t2 ORDER BY (id); +set @lim= 3; +execute stmt using @lim; +select * from t2 ORDER BY (id); + +drop table t2; + diff --git a/mysql-test/main/myisam_explain_non_select_all.result b/mysql-test/main/myisam_explain_non_select_all.result index 9f3cbde86c3..c3d3247fb29 100644 --- a/mysql-test/main/myisam_explain_non_select_all.result +++ b/mysql-test/main/myisam_explain_non_select_all.result @@ -120,7 +120,8 @@ Handler_read_rnd_next 4 Variable_name Value Handler_delete 1 Handler_read_key 2 -Handler_read_rnd_next 4 +Handler_read_rnd 1 +Handler_read_rnd_next 6 DROP TABLE t1; #4 @@ -927,8 +928,9 @@ Variable_name Value Handler_delete 8 Handler_read_key 19 Handler_read_next 3 -Handler_read_rnd 5 -Handler_read_rnd_next 4 +Handler_read_rnd 8 +Handler_read_rnd_deleted 1 +Handler_read_rnd_next 15 DROP TABLE t1, t2, t3; #20 @@ -1064,7 +1066,8 @@ Handler_read_rnd_next 12 Variable_name Value Handler_delete 3 Handler_read_key 4 -Handler_read_rnd_next 30 +Handler_read_rnd 3 +Handler_read_rnd_next 34 DROP TABLE t1, t2; #22 @@ -2891,7 +2894,7 @@ Variable_name Value Handler_delete 4 Handler_read_key 10 Handler_read_rnd 4 -Handler_read_rnd_next 5 +Handler_read_rnd_next 10 DROP TABLE t1,t2; DROP VIEW v1; @@ -2940,7 +2943,7 @@ Variable_name Value Handler_delete 4 Handler_read_key 10 Handler_read_rnd 4 -Handler_read_rnd_next 5 +Handler_read_rnd_next 10 DROP TABLE t1,t2; DROP VIEW v1; diff --git a/mysql-test/main/partition_explicit_prune.result b/mysql-test/main/partition_explicit_prune.result index 4beafd5b468..11e0dc7c0bb 100644 --- a/mysql-test/main/partition_explicit_prune.result +++ b/mysql-test/main/partition_explicit_prune.result @@ -1236,8 +1236,8 @@ HANDLER_READ_FIRST 1 HANDLER_READ_KEY 2 HANDLER_READ_NEXT 2 HANDLER_READ_RND 4 -HANDLER_READ_RND_NEXT 16 -HANDLER_TMP_WRITE 24 +HANDLER_READ_RND_NEXT 22 +HANDLER_TMP_WRITE 28 # 4 delete (2 in t2 + 2 in t3) # 12 locks (3 in t2, 1 in t3, 2 in t1) x 2 (lock + unlock) # 3 read first (1 in t1 + 1 in t3 + 1 in t3, for second row in t1) diff --git a/mysql-test/suite/binlog/r/binlog_row_annotate.result b/mysql-test/suite/binlog/r/binlog_row_annotate.result index dce9a8de641..5658d5db4b1 100644 --- a/mysql-test/suite/binlog/r/binlog_row_annotate.result +++ b/mysql-test/suite/binlog/r/binlog_row_annotate.result @@ -307,13 +307,13 @@ START TRANSACTION #010909 4:46:40 server id # end_log_pos # Delete_rows: table id # flags: STMT_END_F ### DELETE FROM `test2`.`t2` ### WHERE -### @1=3 /* INT meta=0 nullable=1 is_null=0 */ +### @1=1 /* INT meta=0 nullable=1 is_null=0 */ ### DELETE FROM `test2`.`t2` ### WHERE ### @1=2 /* INT meta=0 nullable=1 is_null=0 */ ### DELETE FROM `test2`.`t2` ### WHERE -### @1=1 /* INT meta=0 nullable=1 is_null=0 */ +### @1=3 /* INT meta=0 nullable=1 is_null=0 */ # Number of rows: 3 # at # #010909 4:46:40 server id # end_log_pos # Query thread_id=# exec_time=# error_code=0 xid= @@ -688,13 +688,13 @@ START TRANSACTION #010909 4:46:40 server id # end_log_pos # Delete_rows: table id # flags: STMT_END_F ### DELETE FROM `test2`.`t2` ### WHERE -### @1=3 /* INT meta=0 nullable=1 is_null=0 */ +### @1=1 /* INT meta=0 nullable=1 is_null=0 */ ### DELETE FROM `test2`.`t2` ### WHERE ### @1=2 /* INT meta=0 nullable=1 is_null=0 */ ### DELETE FROM `test2`.`t2` ### WHERE -### @1=1 /* INT meta=0 nullable=1 is_null=0 */ +### @1=3 /* INT meta=0 nullable=1 is_null=0 */ # Number of rows: 3 # at # #010909 4:46:40 server id # end_log_pos # Query thread_id=# exec_time=# error_code=0 xid= @@ -930,13 +930,13 @@ START TRANSACTION #010909 4:46:40 server id # end_log_pos # Delete_rows: table id # flags: STMT_END_F ### DELETE FROM `test2`.`t2` ### WHERE -### @1=3 /* INT meta=0 nullable=1 is_null=0 */ +### @1=1 /* INT meta=0 nullable=1 is_null=0 */ ### DELETE FROM `test2`.`t2` ### WHERE ### @1=2 /* INT meta=0 nullable=1 is_null=0 */ ### DELETE FROM `test2`.`t2` ### WHERE -### @1=1 /* INT meta=0 nullable=1 is_null=0 */ +### @1=3 /* INT meta=0 nullable=1 is_null=0 */ # Number of rows: 3 # at # #010909 4:46:40 server id # end_log_pos # Query thread_id=# exec_time=# error_code=0 xid= @@ -1305,13 +1305,13 @@ START TRANSACTION #010909 4:46:40 server id # end_log_pos # Delete_rows: table id # flags: STMT_END_F ### DELETE FROM `test2`.`t2` ### WHERE -### @1=3 /* INT meta=0 nullable=1 is_null=0 */ +### @1=1 /* INT meta=0 nullable=1 is_null=0 */ ### DELETE FROM `test2`.`t2` ### WHERE ### @1=2 /* INT meta=0 nullable=1 is_null=0 */ ### DELETE FROM `test2`.`t2` ### WHERE -### @1=1 /* INT meta=0 nullable=1 is_null=0 */ +### @1=3 /* INT meta=0 nullable=1 is_null=0 */ # Number of rows: 3 # at # #010909 4:46:40 server id # end_log_pos # Query thread_id=# exec_time=# error_code=0 xid= diff --git a/mysql-test/suite/binlog_encryption/binlog_row_annotate.result b/mysql-test/suite/binlog_encryption/binlog_row_annotate.result index 26734f5397e..720215bae73 100644 --- a/mysql-test/suite/binlog_encryption/binlog_row_annotate.result +++ b/mysql-test/suite/binlog_encryption/binlog_row_annotate.result @@ -311,13 +311,13 @@ START TRANSACTION #010909 4:46:40 server id # end_log_pos # Delete_rows: table id # flags: STMT_END_F ### DELETE FROM `test2`.`t2` ### WHERE -### @1=3 /* INT meta=0 nullable=1 is_null=0 */ +### @1=1 /* INT meta=0 nullable=1 is_null=0 */ ### DELETE FROM `test2`.`t2` ### WHERE ### @1=2 /* INT meta=0 nullable=1 is_null=0 */ ### DELETE FROM `test2`.`t2` ### WHERE -### @1=1 /* INT meta=0 nullable=1 is_null=0 */ +### @1=3 /* INT meta=0 nullable=1 is_null=0 */ # Number of rows: 3 # at # #010909 4:46:40 server id # end_log_pos # Query thread_id=# exec_time=# error_code=0 xid= @@ -692,13 +692,13 @@ START TRANSACTION #010909 4:46:40 server id # end_log_pos # Delete_rows: table id # flags: STMT_END_F ### DELETE FROM `test2`.`t2` ### WHERE -### @1=3 /* INT meta=0 nullable=1 is_null=0 */ +### @1=1 /* INT meta=0 nullable=1 is_null=0 */ ### DELETE FROM `test2`.`t2` ### WHERE ### @1=2 /* INT meta=0 nullable=1 is_null=0 */ ### DELETE FROM `test2`.`t2` ### WHERE -### @1=1 /* INT meta=0 nullable=1 is_null=0 */ +### @1=3 /* INT meta=0 nullable=1 is_null=0 */ # Number of rows: 3 # at # #010909 4:46:40 server id # end_log_pos # Query thread_id=# exec_time=# error_code=0 xid= diff --git a/mysql-test/suite/federated/federated_maybe_16324629.result b/mysql-test/suite/federated/federated_maybe_16324629.result index e16e4dd101d..29a30ac0b30 100644 --- a/mysql-test/suite/federated/federated_maybe_16324629.result +++ b/mysql-test/suite/federated/federated_maybe_16324629.result @@ -10,7 +10,7 @@ connection master; create table t1 (a int, b int, unique key (a), key (b)) engine=federated CONNECTION='mysql://root@127.0.0.1:SLAVE_PORT/federated/t1'; insert into t1 values (3, 3), (7, 7); -delete t1 from t1 where a = 3; +delete t1 from t1 where b = 3; select * from t1; a b 7 7 diff --git a/mysql-test/suite/federated/federated_maybe_16324629.test b/mysql-test/suite/federated/federated_maybe_16324629.test index 53d79e70c80..7d23c11028c 100644 --- a/mysql-test/suite/federated/federated_maybe_16324629.test +++ b/mysql-test/suite/federated/federated_maybe_16324629.test @@ -13,7 +13,7 @@ eval create table t1 (a int, b int, unique key (a), key (b)) engine=federated CONNECTION='mysql://root@127.0.0.1:$SLAVE_MYPORT/federated/t1'; insert into t1 values (3, 3), (7, 7); -delete t1 from t1 where a = 3; +delete t1 from t1 where b = 3; select * from t1; drop table t1; diff --git a/mysql-test/suite/perfschema/r/multi_table_io.result b/mysql-test/suite/perfschema/r/multi_table_io.result index 27effe010f4..e10cfc87a0a 100644 --- a/mysql-test/suite/perfschema/r/multi_table_io.result +++ b/mysql-test/suite/perfschema/r/multi_table_io.result @@ -69,6 +69,7 @@ wait/io/table/sql/handler TABLE test1 t2 update 1 wait/io/table/sql/handler TABLE test marker insert 1 wait/io/table/sql/handler TABLE test t1 fetch 1 wait/io/table/sql/handler TABLE test1 t2 fetch 1 +wait/io/table/sql/handler TABLE test t1 fetch 1 wait/io/table/sql/handler TABLE test t1 delete 1 wait/io/table/sql/handler TABLE test1 t2 fetch 1 wait/io/table/sql/handler TABLE test1 t2 delete 1 diff --git a/mysql-test/suite/sql_sequence/other.result b/mysql-test/suite/sql_sequence/other.result index e9f8efb4dd8..0c6908b0b41 100644 --- a/mysql-test/suite/sql_sequence/other.result +++ b/mysql-test/suite/sql_sequence/other.result @@ -202,11 +202,8 @@ CREATE SEQUENCE s; CREATE table t1 (a int); insert into t1 values (1),(2); DELETE s FROM s; -ERROR HY000: Storage engine SEQUENCE of the table `test`.`s` doesn't have this option delete t1,s from s,t1; -ERROR HY000: Storage engine SEQUENCE of the table `test`.`s` doesn't have this option delete s,t1 from t1,s; -ERROR HY000: Storage engine SEQUENCE of the table `test`.`s` doesn't have this option DROP SEQUENCE s; DROP TABLE t1; # diff --git a/mysql-test/suite/sql_sequence/other.test b/mysql-test/suite/sql_sequence/other.test index 9761e4268a8..09da2326453 100644 --- a/mysql-test/suite/sql_sequence/other.test +++ b/mysql-test/suite/sql_sequence/other.test @@ -177,11 +177,8 @@ drop sequence s1; CREATE SEQUENCE s; CREATE table t1 (a int); insert into t1 values (1),(2); ---error ER_ILLEGAL_HA DELETE s FROM s; ---error ER_ILLEGAL_HA delete t1,s from s,t1; ---error ER_ILLEGAL_HA delete s,t1 from t1,s; DROP SEQUENCE s; DROP TABLE t1; diff --git a/sql/share/errmsg-utf8.txt b/sql/share/errmsg-utf8.txt index adb7227ed82..3a4445e7e8e 100644 --- a/sql/share/errmsg-utf8.txt +++ b/sql/share/errmsg-utf8.txt @@ -12292,5 +12292,8 @@ ER_VECTOR_FORMAT_INVALID eng "Invalid vector format at offset: %d for '%-.100s'. Must be a valid JSON array of numbers." ER_VEC_DISTANCE_TYPE eng "Cannot determine distance type for VEC_DISTANCE, index is not found" +WARN_INDEX_HINTS_IGNORED + eng "Index hints are ignored because they are incompatible with RETURNING clause" + ukr "Підказки по використанню индексів ігноруются тому що вони несумісні з RETURNING" ER_SIGNAL_SKIP_ROW_FROM_TRIGGER - eng "The row is skipped by a trigger implementation" + eng "The row is skipped by a trigger implementation" \ No newline at end of file diff --git a/sql/sql_class.h b/sql/sql_class.h index 7e63b2e7515..0caaf873007 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -7562,16 +7562,17 @@ class SORT_INFO; class multi_delete :public select_result_interceptor { TABLE_LIST *delete_tables, *table_being_deleted; + TMP_TABLE_PARAM *tmp_table_param; + TABLE **tmp_tables, *main_table; Unique **tempfiles; ha_rows deleted, found; - uint num_of_tables; + uint table_count; int error; bool do_delete; /* True if at least one table we delete from is transactional */ bool transactional_tables; /* True if at least one table we delete from is not transactional */ bool normal_tables; - bool delete_while_scanning; /* error handling (rollback and binlogging) can happen in send_eof() so that afterward abort_result_set() needs to find out that. @@ -7580,15 +7581,17 @@ class multi_delete :public select_result_interceptor public: // Methods used by ColumnStore - uint get_num_of_tables() const { return num_of_tables; } + uint get_num_of_tables() const { return table_count; } TABLE_LIST* get_tables() const { return delete_tables; } public: multi_delete(THD *thd_arg, TABLE_LIST *dt, uint num_of_tables); ~multi_delete(); int prepare(List &list, SELECT_LEX_UNIT *u) override; + int prepare2(JOIN *join) override; int send_data(List &items) override; bool initialize_tables (JOIN *join) override; int do_deletes(); + int rowid_table_deletes(TABLE *table, bool ignore); int do_table_deletes(TABLE *table, SORT_INFO *sort_info, bool ignore); bool send_eof() override; inline ha_rows num_deleted() const { return deleted; } diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc index 7749bf86608..d4438c082d3 100644 --- a/sql/sql_delete.cc +++ b/sql/sql_delete.cc @@ -41,6 +41,7 @@ #include "filesort.h" #include "uniques.h" #include "sql_derived.h" // mysql_handle_derived +#include "key.h" // end_read_record #include "sql_insert.h" // fix_rownum_pointers #include "sql_partition.h" // make_used_partitions_str @@ -1097,12 +1098,23 @@ extern "C" int refpos_order_cmp(void *arg, const void *a, const void *b) } -multi_delete::multi_delete(THD *thd_arg, TABLE_LIST *dt, uint num_of_tables_arg): - select_result_interceptor(thd_arg), delete_tables(dt), deleted(0), found(0), - num_of_tables(num_of_tables_arg), error(0), - do_delete(0), transactional_tables(0), normal_tables(0), error_handled(0) +multi_delete::multi_delete(THD *thd_arg, + TABLE_LIST *dt, + uint num_of_tables_arg) + : select_result_interceptor(thd_arg), + delete_tables(dt), + deleted(0), + found(0), + table_count(num_of_tables_arg), + error(0), + do_delete(0), + transactional_tables(0), + normal_tables(0), + error_handled(0) { - tempfiles= thd_arg->calloc(num_of_tables); + tempfiles= thd_arg->calloc(table_count); + tmp_tables = thd->calloc(table_count); + tmp_table_param = thd->calloc(table_count); } @@ -1116,6 +1128,59 @@ multi_delete::prepare(List &values, SELECT_LEX_UNIT *u) DBUG_RETURN(0); } +static TABLE *item_rowid_table(Item *item) +{ + if (item->type() != Item::FUNC_ITEM) + return NULL; + Item_func *func= (Item_func *)item; + if (func->functype() != Item_func::TEMPTABLE_ROWID) + return NULL; + Item_temptable_rowid *itr= (Item_temptable_rowid *)func; + return itr->table; +} + +/* + multi_delete stores a rowid and new field values for every updated row in a + temporary table (one temporary table per updated table). These rowids are + obtained via Item_temptable_rowid's by calling handler::position(). But if + the join is resolved via a temp table, rowids cannot be obtained from + handler::position() in the multi_update::send_data(). So, they're stored in + the join's temp table (JOIN::add_fields_for_current_rowid()) and here we + replace Item_temptable_rowid's (that would've done handler::position()) with + Item_field's (that will simply take the corresponding field value from the + temp table). +*/ +int multi_delete::prepare2(JOIN *join) +{ + if (!join->need_tmp || !join->tmp_table_keep_current_rowid) + return 0; + + JOIN_TAB *tmptab= join->join_tab + join->exec_join_tab_cnt(); + + for (Item **it= tmptab->tmp_table_param->items_to_copy; *it ; it++) + { + TABLE *tbl= item_rowid_table(*it); + if (!tbl) + continue; + for (uint i= 0; i < table_count; i++) + { + for (Item **it2= tmp_table_param[i].items_to_copy; *it2; it2++) + { + if (item_rowid_table(*it2) != tbl) + continue; + Item_field *fld= new (thd->mem_root) + Item_field(thd, (*it)->get_tmp_table_field()); + if (!fld) + return 1; + fld->result_field= (*it2)->get_tmp_table_field(); + *it2= fld; + } + } + } + return 0; +} + + void multi_delete::prepare_to_read_rows() { /* see multi_update::prepare_to_read_rows() */ @@ -1136,9 +1201,9 @@ multi_delete::initialize_tables(JOIN *join) if (unlikely((thd->variables.option_bits & OPTION_SAFE_UPDATES) && error_if_full_join(join))) DBUG_RETURN(1); + main_table=join->join_tab->table; table_map tables_to_delete_from=0; - delete_while_scanning= true; for (walk= delete_tables; walk; walk= walk->next_local) { TABLE_LIST *tbl= walk->correspondent_table->find_table_for_update(); @@ -1149,21 +1214,10 @@ multi_delete::initialize_tables(JOIN *join) delete is called. */ join->map2table[tbl->table->tablenr]->keep_current_rowid= true; - - if (delete_while_scanning && - unique_table(thd, tbl, join->tables_list, 0)) - { - /* - If the table we are going to delete from appears - in join, we need to defer delete. So the delete - doesn't interfers with the scaning of results. - */ - delete_while_scanning= false; - } } walk= delete_tables; - + uint index= 0; for (JOIN_TAB *tab= first_linear_tab(join, WITHOUT_BUSH_ROOTS, WITH_CONST_TABLES); tab; @@ -1173,6 +1227,7 @@ multi_delete::initialize_tables(JOIN *join) { /* We are going to delete from this table */ TABLE *tbl=walk->table=tab->table; + TABLE_LIST *prior= walk; walk= walk->next_local; /* Don't use KEYREAD optimization on this table */ tbl->no_keyread=1; @@ -1186,25 +1241,41 @@ multi_delete::initialize_tables(JOIN *join) tbl->prepare_triggers_for_delete_stmt_or_event(); tbl->prepare_for_position(); tbl->file->prepare_for_modify(tbl->versioned(VERS_TIMESTAMP), true); - } - else if ((tab->type != JT_SYSTEM && tab->type != JT_CONST) && - walk == delete_tables) - { - /* - We are not deleting from the table we are scanning. In this - case send_data() shouldn't delete any rows a we may touch - the rows in the deleted table many times - */ - delete_while_scanning= false; + + List temp_fields; + tbl->prepare_for_position(); + join->map2table[tbl->tablenr]->keep_current_rowid= true; + Item_temptable_rowid *item= + new (thd->mem_root) Item_temptable_rowid(tbl); + if (!item) + DBUG_RETURN(1); + item->fix_fields(thd, 0); + if (temp_fields.push_back(item, thd->mem_root)) + DBUG_RETURN(1); + /* Make an unique key over the first field to avoid duplicated updates */ + ORDER group; + bzero((char*) &group, sizeof(group)); + group.direction= ORDER::ORDER_ASC; + group.item= (Item**) temp_fields.head_ref(); + TMP_TABLE_PARAM *tmp_param; + prior->shared = index; + tmp_param= &tmp_table_param[prior->shared]; + tmp_param->init(); + tmp_param->tmp_name="update"; + tmp_param->field_count= temp_fields.elements; + tmp_param->func_count= temp_fields.elements; + calc_group_buffer(tmp_param, &group); + tmp_tables[index]=create_tmp_table(thd, tmp_param, temp_fields, + (ORDER*) &group, 0, 0, + TMP_TABLE_ALL_COLUMNS, HA_POS_ERROR, &empty_clex_str); + if (!tmp_tables[index]) + DBUG_RETURN(1); + tmp_tables[index]->file->extra(HA_EXTRA_WRITE_CACHE); + ++index; } } walk= delete_tables; tempfiles_ptr= tempfiles; - if (delete_while_scanning) - { - table_being_deleted= delete_tables; - walk= walk->next_local; - } for (;walk ;walk= walk->next_local) { TABLE *table=walk->table; @@ -1215,6 +1286,7 @@ multi_delete::initialize_tables(JOIN *join) if (init_ftfuncs(thd, thd->lex->current_select, 1)) DBUG_RETURN(true); + join->tmp_table_keep_current_rowid= TRUE; DBUG_RETURN(thd->is_fatal_error); } @@ -1232,82 +1304,69 @@ multi_delete::~multi_delete() table->no_cache= 0; } - for (uint counter= 0; counter < num_of_tables; counter++) + for (uint counter= 0; counter < table_count; counter++) { if (tempfiles[counter]) delete tempfiles[counter]; } + + if (tmp_tables) + { + for (uint cnt = 0; cnt < table_count; cnt++) + { + if (tmp_tables[cnt]) + { + free_tmp_table(thd, tmp_tables[cnt]); + tmp_table_param[cnt].cleanup(); + } + } + } } int multi_delete::send_data(List &values) { - int secure_counter= delete_while_scanning ? -1 : 0; TABLE_LIST *del_table; DBUG_ENTER("multi_delete::send_data"); - bool ignore= thd->lex->ignore; - for (del_table= delete_tables; del_table; - del_table= del_table->next_local, secure_counter++) + del_table= del_table->next_local) { TABLE *table= del_table->table; + // DELETE and TRUNCATE don't affect SEQUENCE, so bail early + if (table->file->ht->db_type == DB_TYPE_SEQUENCE) + continue; /* Check if we are using outer join and we didn't find the row */ if (table->status & (STATUS_NULL_ROW | STATUS_DELETED)) continue; - table->file->position(table->record[0]); found++; - - if (secure_counter < 0) + const uint offset= del_table->shared; + TABLE *tmp_table= tmp_tables[offset]; + if (copy_funcs(tmp_table_param[offset].items_to_copy, thd)) + DBUG_RETURN(1); + /* rowid field is NULL if join tmp table has null row from outer join */ + if (tmp_table->field[0]->is_null()) + continue; + error= tmp_table->file->ha_write_tmp_row(tmp_table->record[0]); + if (error) { - bool trg_skip_row= false; - - /* We are scanning the current table */ - DBUG_ASSERT(del_table == table_being_deleted); - if (table->triggers && - table->triggers->process_triggers(thd, TRG_EVENT_DELETE, - TRG_ACTION_BEFORE, false, - &trg_skip_row)) - DBUG_RETURN(1); - - if (trg_skip_row) - continue; - - table->status|= STATUS_DELETED; - - error= table->delete_row(); - if (likely(!error)) - { - deleted++; - if (!table->file->has_transactions()) - thd->transaction->stmt.modified_non_trans_table= TRUE; - if (table->triggers && - table->triggers->process_triggers(thd, TRG_EVENT_DELETE, - TRG_ACTION_AFTER, false, - nullptr)) - DBUG_RETURN(1); - } - else if (!ignore) - { - /* - If the IGNORE option is used errors caused by ha_delete_row don't - have to stop the iteration. - */ - table->file->print_error(error,MYF(0)); - DBUG_RETURN(1); - } - } - else - { - error=tempfiles[secure_counter]->unique_add((char*) table->file->ref); - if (unlikely(error)) - { - error= 1; // Fatal error - DBUG_RETURN(1); - } + --found; + if (error != HA_ERR_FOUND_DUPP_KEY && + error != HA_ERR_FOUND_DUPP_UNIQUE) + { + if (create_internal_tmp_table_from_heap(thd, tmp_table, + tmp_table_param[offset].start_recinfo, + &tmp_table_param[offset].recinfo, + error, 1, NULL)) + { + do_delete= 0; + DBUG_RETURN(1); // Not a table_is_full error + } + found++; + } } } DBUG_RETURN(0); @@ -1393,19 +1452,21 @@ int multi_delete::do_deletes() if (!found) DBUG_RETURN(0); - table_being_deleted= (delete_while_scanning ? delete_tables->next_local : - delete_tables); + table_being_deleted= delete_tables; for (uint counter= 0; table_being_deleted; table_being_deleted= table_being_deleted->next_local, counter++) { TABLE *table = table_being_deleted->table; + // DELETE and TRUNCATE don't affect SEQUENCE, so bail early + if (table->file->ht->db_type == DB_TYPE_SEQUENCE) + continue; + int local_error; - if (unlikely(tempfiles[counter]->get(table))) + if (tempfiles[counter] && unlikely(tempfiles[counter]->get(table))) DBUG_RETURN(1); - local_error= do_table_deletes(table, &tempfiles[counter]->sort, - thd->lex->ignore); + local_error= rowid_table_deletes(table, thd->lex->ignore); if (unlikely(thd->killed) && likely(!local_error)) DBUG_RETURN(1); @@ -1435,35 +1496,60 @@ int multi_delete::do_deletes() @retval 1 Triggers or handler reported error. @retval -1 End of file from handler. */ -int multi_delete::do_table_deletes(TABLE *table, SORT_INFO *sort_info, - bool ignore) +int multi_delete::rowid_table_deletes(TABLE *table, bool ignore) { int local_error= 0; - READ_RECORD info; ha_rows last_deleted= deleted; - DBUG_ENTER("do_deletes_for_table"); - - if (unlikely(init_read_record(&info, thd, table, NULL, sort_info, 0, 1, - FALSE))) - DBUG_RETURN(1); + DBUG_ENTER("rowid_table_deletes"); + TABLE *err_table= nullptr; bool will_batch= !table->file->start_bulk_delete(); - while (likely(!(local_error= info.read_record())) && likely(!thd->killed)) + TABLE *tmp_table= tmp_tables[table_being_deleted->shared]; + tmp_table->file->extra(HA_EXTRA_CACHE); // Change to read cache + if (unlikely((local_error= table->file->ha_rnd_init(0)))) { - bool trg_skip_row= false; + err_table= table; + goto err; + } + table->file->extra(HA_EXTRA_NO_CACHE); + if (unlikely((local_error= tmp_table->file->ha_rnd_init(1)))) + { + err_table= tmp_table; + goto err; + } + + while (!thd->killed) + { + if (unlikely((local_error= + tmp_table->file->ha_rnd_next(tmp_table->record[0])))) + { + if (local_error == HA_ERR_END_OF_FILE) + { + local_error= 0; + break; + } + err_table= tmp_table; + goto err; + } + + DBUG_ASSERT(!tmp_table->field[0]->is_null()); + String rowid; + tmp_table->field[0]->val_str(&rowid); + if (unlikely((local_error= table->file->ha_rnd_pos(table->record[0], + (uchar*)rowid.ptr())))) + { + // Table aliased to itself had key deleted already + continue; + } if (table->triggers && unlikely(table->triggers->process_triggers(thd, TRG_EVENT_DELETE, - TRG_ACTION_BEFORE, false, - &trg_skip_row))) + TRG_ACTION_BEFORE, FALSE))) { + err_table= table; local_error= 1; break; } - - if (trg_skip_row) - continue; - local_error= table->delete_row(); if (unlikely(local_error) && !ignore) { @@ -1481,9 +1567,9 @@ int multi_delete::do_table_deletes(TABLE *table, SORT_INFO *sort_info, deleted++; if (table->triggers && table->triggers->process_triggers(thd, TRG_EVENT_DELETE, - TRG_ACTION_AFTER, false, - nullptr)) + TRG_ACTION_AFTER, FALSE)) { + err_table= table; local_error= 1; break; } @@ -1500,12 +1586,13 @@ int multi_delete::do_table_deletes(TABLE *table, SORT_INFO *sort_info, } if (last_deleted != deleted && !table->file->has_transactions_and_rollback()) thd->transaction->stmt.modified_non_trans_table= TRUE; - - end_read_record(&info); - +err: + if (err_table) + err_table->file->print_error(local_error,MYF(ME_FATAL)); DBUG_RETURN(local_error); } + /* Send ok to the client @@ -1564,9 +1651,20 @@ bool multi_delete::send_eof() } } if (unlikely(local_error != 0)) + { error_handled= TRUE; // to force early leave from ::abort_result_set() + if (thd->killed == NOT_KILLED && !thd->get_stmt_da()->is_set()) + { + /* + No error message was sent and query was not killed (in which case + mysql_execute_command() will send the error mesage). + */ + ::my_ok(thd, deleted); // Ends the DELETE statement + } + return 1; + } - if (likely(!local_error && !thd->lex->analyze_stmt)) + if (likely(!thd->lex->analyze_stmt)) { ::my_ok(thd, deleted); } @@ -1600,11 +1698,8 @@ void Sql_cmd_delete::remove_order_by_without_limit(THD *thd) bool Sql_cmd_delete::processing_as_multitable_delete_prohibited(THD *thd) { - SELECT_LEX *const select_lex = thd->lex->first_select_lex(); return - ((select_lex->order_list.elements && - select_lex->limit_params.select_limit) || - thd->lex->has_returning()); + (thd->lex->has_returning()); } @@ -1714,6 +1809,19 @@ bool Sql_cmd_delete::prepare_inner(THD *thd) if (!multitable) { + if (select_lex->index_hints || table_list->index_hints) + { + if (!processing_as_multitable_delete_prohibited(thd)) + { + multitable= true; + } + else + { + push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, + WARN_INDEX_HINTS_IGNORED, + ER_THD(thd, WARN_INDEX_HINTS_IGNORED)); + } + } if (table_list->vers_conditions.is_set() && table_list->is_view_or_derived()) { my_error(ER_IT_IS_A_VIEW, MYF(0), table_list->table_name.str); diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index f46f11ec391..59f3a714f6a 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -1764,6 +1764,7 @@ rule: query_expression_tail opt_query_expression_tail order_or_limit + opt_order_or_limit order_limit_lock opt_order_limit_lock @@ -13005,6 +13006,18 @@ opt_procedure_or_into: } ; +opt_order_or_limit: + /* empty */ + { + $$= NULL; + } + | + order_or_limit + { + $1->lock.empty(); + $$= $1; + } + ; order_or_limit: order_clause opt_limit_clause @@ -13997,7 +14010,7 @@ delete_part2: ; delete_single_table: - FROM table_ident opt_table_alias_clause opt_use_partition + FROM table_ident opt_table_alias_clause opt_key_definition opt_use_partition { if (unlikely(!Select-> add_table_to_list(thd, $2, $3, TL_OPTION_UPDATING, @@ -14016,8 +14029,8 @@ delete_single_table: add_table_to_list(thd, $2, $3, TL_OPTION_UPDATING, YYPS->m_lock_type, YYPS->m_mdl_type, - NULL, - $4))) + Select->pop_index_hints(), + $5))) MYSQL_YYABORT; Lex->auxiliary_table_list.first->correspondent_table= Lex->query_tables; @@ -14063,10 +14076,15 @@ single_multi: YYPS->m_lock_type= TL_READ_DEFAULT; YYPS->m_mdl_type= MDL_SHARED_READ; } - FROM join_table_list opt_where_clause + FROM join_table_list opt_where_clause opt_order_or_limit { if (unlikely(multi_delete_set_locks_and_link_aux_tables(Lex))) MYSQL_YYABORT; + if ($6) + { + DBUG_ASSERT(Lex->select_stack_head() == Select); + $6->set_to(Lex->select_stack_head()); + } } stmt_end {} | FROM table_alias_ref_list { @@ -14079,10 +14097,15 @@ single_multi: YYPS->m_lock_type= TL_READ_DEFAULT; YYPS->m_mdl_type= MDL_SHARED_READ; } - USING join_table_list opt_where_clause + USING join_table_list opt_where_clause opt_order_or_limit { if (unlikely(multi_delete_set_locks_and_link_aux_tables(Lex))) MYSQL_YYABORT; + if ($7) + { + DBUG_ASSERT(Lex->select_stack_head() == Select); + $7->set_to(Lex->select_stack_head()); + } } stmt_end {} ;