From 5001300bd48866f885b61fac3857932a4ed9de70 Mon Sep 17 00:00:00 2001 From: Dave Gosselin Date: Tue, 3 Dec 2024 09:43:45 -0500 Subject: [PATCH] MDEV-30469 Support ORDER BY and LIMIT for multi-table DELETE, index hints for single-table DELETE We now allow multitable queries with order by and limit, such as: delete t1.*, t2.* from t1, t2 order by t1.id desc limit 3; To predict what rows will be deleted, run the equivalent select: select t1.*, t2.* from t1, t2 order by t1.id desc limit 3; Additionally, index hints are now supported with single table delete statements: delete from t2 use index(xid) order by (id) limit 2; This approach changes the multi_delete SELECT result interceptor to use a temporary table to collect row ids pertaining to the rows that will be deleted, rather than directly deleting rows from the target table(s). Row ids are collected during send_data, then read during send_eof to delete target rows. In the event that the temporary table created in memory is not big enough for all matching rows, it is converted to an aria table. Other changes: - Deleting from a sequence now affects zero rows instead of emitting an error Limitations: - The federated connector does not create implicit row ids, so we to use a key when conditionally deleting. See the change in federated_maybe_16324629.test --- mysql-test/main/delete.result | 102 ++++++ mysql-test/main/delete.test | 84 +++++ mysql-test/main/delete_innodb.result | 4 +- mysql-test/main/delete_multi_order_by.result | 239 ++++++++++++ mysql-test/main/delete_multi_order_by.test | 131 +++++++ mysql-test/main/delete_single_to_multi.result | 20 +- .../main/delete_use_source_engines.result | 52 +-- .../main/multidelete_engine.combinations | 12 + mysql-test/main/multidelete_engine.test | 20 + .../main/myisam_explain_non_select_all.result | 15 +- .../main/partition_explicit_prune.result | 4 +- .../suite/binlog/r/binlog_row_annotate.result | 16 +- .../binlog_row_annotate.result | 8 +- .../federated/federated_maybe_16324629.result | 2 +- .../federated/federated_maybe_16324629.test | 2 +- .../suite/perfschema/r/multi_table_io.result | 1 + mysql-test/suite/sql_sequence/other.result | 3 - mysql-test/suite/sql_sequence/other.test | 3 - sql/share/errmsg-utf8.txt | 5 +- sql/sql_class.h | 9 +- sql/sql_delete.cc | 342 ++++++++++++------ sql/sql_yacc.yy | 33 +- 22 files changed, 915 insertions(+), 192 deletions(-) create mode 100644 mysql-test/main/delete_multi_order_by.result create mode 100644 mysql-test/main/delete_multi_order_by.test create mode 100644 mysql-test/main/multidelete_engine.combinations create mode 100644 mysql-test/main/multidelete_engine.test 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 {} ;