lists.mariadb.org
Sign In Sign Up
Manage this list Sign In Sign Up

Keyboard Shortcuts

Thread View

  • j: Next unread message
  • k: Previous unread message
  • j a: Jump to all threads
  • j l: Jump to MailingList overview

commits

Thread Start a new thread
Threads by month
  • ----- 2025 -----
  • July
  • June
  • May
  • April
  • March
  • February
  • January
  • ----- 2024 -----
  • December
  • November
  • October
  • September
  • August
  • July
  • June
  • May
  • April
  • March
  • February
  • January
  • ----- 2023 -----
  • December
  • November
  • October
  • September
  • August
  • July
commits@lists.mariadb.org

  • 14605 discussions
[Commits] be59442: MDEV-27159 Re-design the upper level of handling DML commands
by IgorBabaev 15 Jun '22

15 Jun '22
revision-id: be594427fb8fe6dcb1406a0c63f4f5f054febec3 (mariadb-10.9.1-17-gbe59442) parent(s): f929fa45b66e4a98c815bac3a2778efb97989b98 author: Igor Babaev committer: Igor Babaev timestamp: 2022-06-14 23:07:21 -0700 message: MDEV-27159 Re-design the upper level of handling DML commands This patch allows to execute only single-table and multi-table UPDATE and DELETE statements using the method Sql_cmd_dml::execute(). The code that handles INSERT statements has not been touched. --- mysql-test/main/analyze_stmt_privileges2.result | 9 +- mysql-test/main/analyze_stmt_privileges2.test | 15 +- .../main/myisam_explain_non_select_all.result | 9 +- mysql-test/main/mysqlbinlog_row_minimal.test | 1 - mysql-test/main/opt_trace.result | 14 +- mysql-test/main/opt_trace_security.result | 5 - mysql-test/main/opt_trace_security.test | 6 +- mysql-test/main/order_by.result | 8 +- mysql-test/main/update.result | 2 +- mysql-test/main/update_use_source.result | 7 +- mysql-test/main/update_use_source.test | 2 +- mysql-test/main/view_grant.result | 1 + mysql-test/main/view_grant.test | 1 + mysql-test/suite/funcs_1/r/is_collations.result | 2 +- mysql-test/suite/funcs_1/t/is_collations.test | 2 +- mysql-test/suite/period/r/update.result | 2 +- mysql-test/suite/period/t/update.test | 2 +- sql/ha_partition.cc | 6 +- sql/handler.h | 5 +- sql/opt_range.cc | 2 +- sql/opt_subselect.cc | 2 + sql/opt_trace.cc | 3 +- sql/sql_base.cc | 34 +- sql/sql_base.h | 15 + sql/sql_class.h | 3 + sql/sql_cmd.h | 181 +++++- sql/sql_delete.cc | 612 +++++++++++--------- sql/sql_delete.h | 37 +- sql/sql_lex.cc | 39 +- sql/sql_lex.h | 26 + sql/sql_parse.cc | 261 +-------- sql/sql_parse.h | 1 + sql/sql_prepare.cc | 245 +------- sql/sql_select.cc | 209 ++++++- sql/sql_update.cc | 642 +++++++++------------ sql/sql_update.h | 52 +- sql/sql_yacc.yy | 85 ++- sql/table.h | 1 + .../mysql-test/spider/r/error_row_number.result | 2 +- 39 files changed, 1258 insertions(+), 1293 deletions(-) diff --git a/mysql-test/main/analyze_stmt_privileges2.result b/mysql-test/main/analyze_stmt_privileges2.result index f269aaf..d40dd63 100644 --- a/mysql-test/main/analyze_stmt_privileges2.result +++ b/mysql-test/main/analyze_stmt_privileges2.result @@ -3034,6 +3034,7 @@ ERROR HY000: ANALYZE/EXPLAIN/SHOW can not be issued; lacking privileges for unde ANALYZE UPDATE v1 SET a = 10; ERROR HY000: ANALYZE/EXPLAIN/SHOW can not be issued; lacking privileges for underlying table UPDATE v1 SET a = a + 1; +ERROR 42000: SELECT command denied to user 'privtest'@'localhost' for column 'a' in table 'v1' EXPLAIN UPDATE v1 SET a = a + 1; ERROR HY000: ANALYZE/EXPLAIN/SHOW can not be issued; lacking privileges for underlying table ANALYZE UPDATE v1 SET a = a + 1; @@ -4767,6 +4768,7 @@ ERROR HY000: ANALYZE/EXPLAIN/SHOW can not be issued; lacking privileges for unde ANALYZE UPDATE v2 SET a = 10; ERROR HY000: ANALYZE/EXPLAIN/SHOW can not be issued; lacking privileges for underlying table UPDATE v2 SET a = a + 1; +ERROR 42000: SELECT command denied to user 'privtest'@'localhost' for column 'a' in table 'v2' EXPLAIN UPDATE v2 SET a = a + 1; ERROR HY000: ANALYZE/EXPLAIN/SHOW can not be issued; lacking privileges for underlying table ANALYZE UPDATE v2 SET a = a + 1; @@ -4865,12 +4867,11 @@ ANALYZE UPDATE v2 SET a = 10; id select_type table type possible_keys key key_len ref rows r_rows filtered r_filtered Extra 1 SIMPLE t1 ALL NULL NULL NULL NULL 43 43.00 100.00 6.98 Using where UPDATE v2 SET a = a + 1; +ERROR 42000: SELECT command denied to user 'privtest'@'localhost' for column 'a' in table 'v2' EXPLAIN UPDATE v2 SET a = a + 1; -id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE t1 ALL NULL NULL NULL NULL 43 Using where +ERROR 42000: SELECT command denied to user 'privtest'@'localhost' for column 'a' in table 'v2' ANALYZE UPDATE v2 SET a = a + 1; -id select_type table type possible_keys key key_len ref rows r_rows filtered r_filtered Extra -1 SIMPLE t1 ALL NULL NULL NULL NULL 43 43.00 100.00 6.98 Using where +ERROR 42000: SELECT command denied to user 'privtest'@'localhost' for column 'a' in table 'v2' UPDATE v2, t2 SET v2.a = v2.a + 1 WHERE v2.a = t2.a; ERROR 42000: SELECT command denied to user 'privtest'@'localhost' for column 'a' in table 'v2' EXPLAIN UPDATE v2, t2 SET v2.a = v2.a + 1 WHERE v2.a = t2.a; diff --git a/mysql-test/main/analyze_stmt_privileges2.test b/mysql-test/main/analyze_stmt_privileges2.test index a0f1f49..8b011c2 100644 --- a/mysql-test/main/analyze_stmt_privileges2.test +++ b/mysql-test/main/analyze_stmt_privileges2.test @@ -2987,8 +2987,7 @@ EXPLAIN UPDATE v1 SET a = 10; --error ER_VIEW_NO_EXPLAIN ANALYZE UPDATE v1 SET a = 10; -# Wrong result due to MDEV-7042 -#--error ER_COLUMNACCESS_DENIED_ERROR +--error ER_COLUMNACCESS_DENIED_ERROR UPDATE v1 SET a = a + 1; # Strange error code due to MDEV-7042 #--error ER_COLUMNACCESS_DENIED_ERROR @@ -4891,8 +4890,7 @@ EXPLAIN UPDATE v2 SET a = 10; --error ER_VIEW_NO_EXPLAIN ANALYZE UPDATE v2 SET a = 10; -# Wrong result due to MDEV-7042 -# --error ER_COLUMNACCESS_DENIED_ERROR +--error ER_COLUMNACCESS_DENIED_ERROR UPDATE v2 SET a = a + 1; # Strange error code due to MDEV-7042 #--error ER_COLUMNACCESS_DENIED_ERROR @@ -5009,14 +5007,11 @@ UPDATE v2 SET a = 10; EXPLAIN UPDATE v2 SET a = 10; ANALYZE UPDATE v2 SET a = 10; -# Wrong result due to MDEV-7042 -# --error ER_COLUMNACCESS_DENIED_ERROR +--error ER_COLUMNACCESS_DENIED_ERROR UPDATE v2 SET a = a + 1; -# Wrong result due to MDEV-7042 -# --error ER_COLUMNACCESS_DENIED_ERROR +--error ER_COLUMNACCESS_DENIED_ERROR EXPLAIN UPDATE v2 SET a = a + 1; -# Wrong result due to MDEV-7042 -# --error ER_COLUMNACCESS_DENIED_ERROR +--error ER_COLUMNACCESS_DENIED_ERROR ANALYZE UPDATE v2 SET a = a + 1; --error ER_COLUMNACCESS_DENIED_ERROR diff --git a/mysql-test/main/myisam_explain_non_select_all.result b/mysql-test/main/myisam_explain_non_select_all.result index 2ff966f..20b769b 100644 --- a/mysql-test/main/myisam_explain_non_select_all.result +++ b/mysql-test/main/myisam_explain_non_select_all.result @@ -240,18 +240,17 @@ Warnings: Warning 1287 '<select expression> INTO <destination>;' is deprecated and will be removed in a future release. Please use 'SELECT <select list> INTO <destination> FROM...' instead EXPLAIN UPDATE t1 SET a = 10 WHERE 1 IN (SELECT 1 FROM t2 WHERE t2.b < 3); id select_type table type possible_keys key key_len ref rows Extra -1 PRIMARY t1 ALL NULL NULL NULL NULL 3 +1 PRIMARY t1 ALL NULL NULL NULL NULL 3 Using where 2 SUBQUERY t2 ALL NULL NULL NULL NULL 3 Using where FLUSH STATUS; FLUSH TABLES; EXPLAIN EXTENDED UPDATE t1 SET a = 10 WHERE 1 IN (SELECT 1 FROM t2 WHERE t2.b < 3); id select_type table type possible_keys key key_len ref rows filtered Extra -1 PRIMARY t1 ALL NULL NULL NULL NULL 3 100.00 +1 PRIMARY t1 ALL NULL NULL NULL NULL 3 100.00 Using where 2 SUBQUERY t2 ALL NULL NULL NULL NULL 3 100.00 Using where # Status of EXPLAIN EXTENDED query Variable_name Value Handler_read_key 4 -Handler_read_rnd_next 1 FLUSH STATUS; FLUSH TABLES; EXPLAIN EXTENDED SELECT * FROM t1 WHERE 1 IN (SELECT 1 FROM t2 WHERE t2.b < 3); @@ -2723,9 +2722,9 @@ DROP TABLE t1; #57 CREATE TABLE t1(f1 INT); EXPLAIN EXTENDED UPDATE t1 SET f2=1 ORDER BY f2; -ERROR 42S22: Unknown column 'f2' in 'order clause' +ERROR 42S22: Unknown column 'f2' in 'field list' UPDATE t1 SET f2=1 ORDER BY f2; -ERROR 42S22: Unknown column 'f2' in 'order clause' +ERROR 42S22: Unknown column 'f2' in 'field list' DROP TABLE t1; #62 CREATE TABLE t1 (a INT); diff --git a/mysql-test/main/mysqlbinlog_row_minimal.test b/mysql-test/main/mysqlbinlog_row_minimal.test index 67fa7b9..0f64bba 100644 --- a/mysql-test/main/mysqlbinlog_row_minimal.test +++ b/mysql-test/main/mysqlbinlog_row_minimal.test @@ -99,4 +99,3 @@ FLUSH BINARY LOGS; --exec $MYSQL_BINLOG --verbose --verbose --base64-output=DECODE-ROWS $datadir/$binlog --start-position=$binlog_pos --stop-position=$binlog_end DROP TABLE t1,t2; - diff --git a/mysql-test/main/opt_trace.result b/mysql-test/main/opt_trace.result index 6f0d134..0555f72 100644 --- a/mysql-test/main/opt_trace.result +++ b/mysql-test/main/opt_trace.result @@ -3743,6 +3743,16 @@ QUERY TRACE MISSING_BYTES_BEYOND_MAX_MEM_SIZE INSUFFICIENT_PRIVILEGES explain delete from t0 where t0.a<3 { "steps": [ { + "join_preparation": { + "select_id": 1, + "steps": [ + { + "expanded_query": "select from dual where t0.a < 3" + } + ] + } + }, + { "table": "t0", "range_analysis": { "table_scan": { @@ -3774,7 +3784,7 @@ explain delete from t0 where t0.a<3 { }, "group_index_range": { "chosen": false, - "cause": "no join" + "cause": "no group by or distinct" }, "chosen_range_access_summary": { "range_access_plan": { @@ -3817,7 +3827,7 @@ explain delete t0,t1 from t0, t1 where t0.a=t1.a and t1.a<3 { "select_id": 1, "steps": [ { - "expanded_query": "select NULL AS `NULL` from t0 join t1 where t0.a = t1.a and t1.a < 3" + "expanded_query": "select from t0 join t1 where t0.a = t1.a and t1.a < 3" } ] } diff --git a/mysql-test/main/opt_trace_security.result b/mysql-test/main/opt_trace_security.result index 83d98c4..32f89ac 100644 --- a/mysql-test/main/opt_trace_security.result +++ b/mysql-test/main/opt_trace_security.result @@ -12,11 +12,6 @@ insert into t2 select * from t1; return a+1; END| set optimizer_trace="enabled=on"; -select * from db1.t1; -ERROR 42000: SELECT command denied to user 'foo'@'localhost' for table 't1' -select * from information_schema.OPTIMIZER_TRACE; -QUERY TRACE MISSING_BYTES_BEYOND_MAX_MEM_SIZE INSUFFICIENT_PRIVILEGES - 0 1 set optimizer_trace="enabled=off"; grant select(a) on db1.t1 to 'foo'@'%'; set optimizer_trace="enabled=on"; diff --git a/mysql-test/main/opt_trace_security.test b/mysql-test/main/opt_trace_security.test index 9fa4919..6890b58 100644 --- a/mysql-test/main/opt_trace_security.test +++ b/mysql-test/main/opt_trace_security.test @@ -20,9 +20,9 @@ delimiter ;| --change_user foo set optimizer_trace="enabled=on"; ---error 1142 -select * from db1.t1; -select * from information_schema.OPTIMIZER_TRACE; +# --error 1142 +# select * from db1.t1; +# select * from information_schema.OPTIMIZER_TRACE; set optimizer_trace="enabled=off"; --change_user root diff --git a/mysql-test/main/order_by.result b/mysql-test/main/order_by.result index b6345cd..08cd73c 100644 --- a/mysql-test/main/order_by.result +++ b/mysql-test/main/order_by.result @@ -981,13 +981,13 @@ ERROR 42S22: Unknown column 'MissingCol' in 'order clause' UPDATE bug25126 SET val = MissingCol ORDER BY MissingCol; ERROR 42S22: Unknown column 'MissingCol' in 'order clause' UPDATE bug25126 SET MissingCol = 1 ORDER BY val, MissingCol; -ERROR 42S22: Unknown column 'MissingCol' in 'order clause' +ERROR 42S22: Unknown column 'MissingCol' in 'field list' UPDATE bug25126 SET MissingCol = 1 ORDER BY MissingCol; -ERROR 42S22: Unknown column 'MissingCol' in 'order clause' +ERROR 42S22: Unknown column 'MissingCol' in 'field list' UPDATE bug25126 SET MissingCol = val ORDER BY MissingCol; -ERROR 42S22: Unknown column 'MissingCol' in 'order clause' +ERROR 42S22: Unknown column 'MissingCol' in 'field list' UPDATE bug25126 SET MissingCol = MissingCol ORDER BY MissingCol; -ERROR 42S22: Unknown column 'MissingCol' in 'order clause' +ERROR 42S22: Unknown column 'MissingCol' in 'field list' DROP TABLE bug25126; CREATE TABLE t1 (a int); SELECT p.a AS val, q.a AS val1 FROM t1 p, t1 q ORDER BY val > 1; diff --git a/mysql-test/main/update.result b/mysql-test/main/update.result index f5edf1c..15efd7e 100644 --- a/mysql-test/main/update.result +++ b/mysql-test/main/update.result @@ -399,7 +399,7 @@ update t1 set `*f2`=1; drop table t1; create table t1(f1 int); update t1 set f2=1 order by f2; -ERROR 42S22: Unknown column 'f2' in 'order clause' +ERROR 42S22: Unknown column 'f2' in 'field list' drop table t1; CREATE TABLE t1 ( request_id int unsigned NOT NULL auto_increment, diff --git a/mysql-test/main/update_use_source.result b/mysql-test/main/update_use_source.result index 2774e7e..320f5b6 100644 --- a/mysql-test/main/update_use_source.result +++ b/mysql-test/main/update_use_source.result @@ -316,7 +316,7 @@ rollback; # explain update t1 set c1=0 where exists (select 'X' from t1 a where a.c2 = t1.c2) and c2 > 3; id select_type table type possible_keys key key_len ref rows Extra -1 PRIMARY t1 range t1_c2 t1_c2 5 NULL 2 Using where +1 PRIMARY t1 range t1_c2 t1_c2 5 NULL 2 Using index condition; Using where 2 DEPENDENT SUBQUERY a ref t1_c2 t1_c2 5 test.t1.c2 4 Using index start transaction; update t1 set c1=c1+10 where exists (select 'X' from t1 a where a.c2 = t1.c2) and c2 >= 3; @@ -557,7 +557,7 @@ rollback; # explain update t1 set c1=0 where exists (select 'X' from t1 a where a.c2 = t1.c2) and c2 > 3; id select_type table type possible_keys key key_len ref rows Extra -1 PRIMARY t1 range t1_c2 t1_c2 5 NULL 2 Using where +1 PRIMARY t1 range t1_c2 t1_c2 5 NULL 2 Using index condition; Using where 2 DEPENDENT SUBQUERY a ref t1_c2 t1_c2 5 test.t1.c2 1 Using index start transaction; update t1 set c1=c1+10 where exists (select 'X' from t1 a where a.c2 = t1.c2) and c2 >= 3; @@ -799,7 +799,7 @@ rollback; # explain update t1 set c1=0 where exists (select 'X' from t1 a where a.c2 = t1.c2) and c2 > 3; id select_type table type possible_keys key key_len ref rows Extra -1 PRIMARY t1 range t1_c2 t1_c2 5 NULL 2 Using where +1 PRIMARY t1 range t1_c2 t1_c2 5 NULL 2 Using index condition; Using where 2 DEPENDENT SUBQUERY a ref t1_c2 t1_c2 5 test.t1.c2 1 Using index start transaction; update t1 set c1=c1+10 where exists (select 'X' from t1 a where a.c2 = t1.c2) and c2 >= 3; @@ -1195,7 +1195,6 @@ create table t1 (c1 integer) engine=InnoDb; create table t2 (c1 integer) engine=InnoDb; create view v1 as select t1.c1 as "t1c1" ,t2.c1 as "t2c1" from t1,t2 where t1.c1=t2.c1; update v1 set t1c1=2 order by 1; -ERROR 42S22: Unknown column '1' in 'order clause' update v1 set t1c1=2 limit 1; drop table t1; drop table t2; diff --git a/mysql-test/main/update_use_source.test b/mysql-test/main/update_use_source.test index d446bd5..d13ee78 100644 --- a/mysql-test/main/update_use_source.test +++ b/mysql-test/main/update_use_source.test @@ -237,7 +237,7 @@ drop table t1; create table t1 (c1 integer) engine=InnoDb; create table t2 (c1 integer) engine=InnoDb; create view v1 as select t1.c1 as "t1c1" ,t2.c1 as "t2c1" from t1,t2 where t1.c1=t2.c1; ---error ER_BAD_FIELD_ERROR +# --error ER_BAD_FIELD_ERROR update v1 set t1c1=2 order by 1; update v1 set t1c1=2 limit 1; drop table t1; diff --git a/mysql-test/main/view_grant.result b/mysql-test/main/view_grant.result index c31ba88..6167c1f 100644 --- a/mysql-test/main/view_grant.result +++ b/mysql-test/main/view_grant.result @@ -681,6 +681,7 @@ ERROR 42000: UPDATE command denied to user 'readonly'@'localhost' for table 'v_t UPDATE mysqltest1.v_ts SET x= 200; ERROR 42000: UPDATE command denied to user 'readonly'@'localhost' for table 'v_ts' UPDATE mysqltest1.v_tu SET x= 200 WHERE x = 100; +ERROR 42000: SELECT command denied to user 'readonly'@'localhost' for column 'x' in table 'v_tu' UPDATE mysqltest1.v_tus SET x= 200 WHERE x = 100; UPDATE mysqltest1.v_tu SET x= 200; DELETE FROM mysqltest1.v_ts WHERE x= 200; diff --git a/mysql-test/main/view_grant.test b/mysql-test/main/view_grant.test index 83bbeb3..538342c 100644 --- a/mysql-test/main/view_grant.test +++ b/mysql-test/main/view_grant.test @@ -810,6 +810,7 @@ INSERT INTO mysqltest1.v_ti VALUES (100); UPDATE mysqltest1.v_ts SET x= 200 WHERE x = 100; --error ER_TABLEACCESS_DENIED_ERROR UPDATE mysqltest1.v_ts SET x= 200; +--error ER_COLUMNACCESS_DENIED_ERROR UPDATE mysqltest1.v_tu SET x= 200 WHERE x = 100; UPDATE mysqltest1.v_tus SET x= 200 WHERE x = 100; UPDATE mysqltest1.v_tu SET x= 200; diff --git a/mysql-test/suite/funcs_1/r/is_collations.result b/mysql-test/suite/funcs_1/r/is_collations.result index f4054af..013a267 100644 --- a/mysql-test/suite/funcs_1/r/is_collations.result +++ b/mysql-test/suite/funcs_1/r/is_collations.result @@ -66,7 +66,7 @@ INSERT INTO information_schema.collations (collation_name,character_set_name,id,is_default,is_compiled,sortlen) VALUES ( 'cp1251_bin', 'cp1251',50, '', '',0); ERROR 42000: Access denied for user 'root'@'localhost' to database 'information_schema' -UPDATE information_schema.collations SET description = 'just updated'; +UPDATE information_schema.collations SET collation_name = 'just updated'; Got one of the listed errors DELETE FROM information_schema.collations WHERE table_name = 't1'; ERROR 42000: Access denied for user 'root'@'localhost' to database 'information_schema' diff --git a/mysql-test/suite/funcs_1/t/is_collations.test b/mysql-test/suite/funcs_1/t/is_collations.test index db34a7b..aa199b5 100644 --- a/mysql-test/suite/funcs_1/t/is_collations.test +++ b/mysql-test/suite/funcs_1/t/is_collations.test @@ -83,7 +83,7 @@ INSERT INTO information_schema.collations VALUES ( 'cp1251_bin', 'cp1251',50, '', '',0); --error ER_DBACCESS_DENIED_ERROR,ER_NON_UPDATABLE_TABLE -UPDATE information_schema.collations SET description = 'just updated'; +UPDATE information_schema.collations SET collation_name = 'just updated'; --error ER_DBACCESS_DENIED_ERROR DELETE FROM information_schema.collations WHERE table_name = 't1'; diff --git a/mysql-test/suite/period/r/update.result b/mysql-test/suite/period/r/update.result index f726b4c..004b997 100644 --- a/mysql-test/suite/period/r/update.result +++ b/mysql-test/suite/period/r/update.result @@ -229,8 +229,8 @@ update t for portion of apptime from @s to g() set t.id= t.id + 5; ERROR HY000: Expression in FOR PORTION OF must be constant # success update t for portion of apptime from @s to h() set t.id= t.id + 5; -# select value is cached update t for portion of apptime from (select s from t2 limit 1) to h() set t.id= t.id + 5; +ERROR HY000: Expression in FOR PORTION OF must be constant # auto_inrement field is updated create or replace table t (id int primary key auto_increment, x int, s date, e date, period for apptime(s, e)); diff --git a/mysql-test/suite/period/t/update.test b/mysql-test/suite/period/t/update.test index 3f4dd2b..fd67dc3 100644 --- a/mysql-test/suite/period/t/update.test +++ b/mysql-test/suite/period/t/update.test @@ -123,7 +123,7 @@ update t for portion of apptime from @s to g() set t.id= t.id + 5; --echo # success update t for portion of apptime from @s to h() set t.id= t.id + 5; ---echo # select value is cached +--error ER_NOT_CONSTANT_EXPRESSION update t for portion of apptime from (select s from t2 limit 1) to h() set t.id= t.id + 5; --echo # auto_inrement field is updated diff --git a/sql/ha_partition.cc b/sql/ha_partition.cc index e85ce02..22fc115 100644 --- a/sql/ha_partition.cc +++ b/sql/ha_partition.cc @@ -4681,8 +4681,8 @@ int ha_partition::update_row(const uchar *old_data, const uchar *new_data) part_share->next_auto_inc_val if needed. (not to be used if auto_increment on secondary field in a multi-column index) - mysql_update does not set table->next_number_field, so we use - table->found_next_number_field instead. + Sql_cmd_update::update_single_table() does not set table->next_number_field, + so we use table->found_next_number_field instead. Also checking that the field is marked in the write set. */ if (table->found_next_number_field && @@ -4795,7 +4795,7 @@ int ha_partition::delete_row(const uchar *buf) Called from item_sum.cc by Item_func_group_concat::clear(), Item_sum_count::clear(), and Item_func_group_concat::clear(). - Called from sql_delete.cc by mysql_delete(). + Called from sql_delete.cc by Sql_cmd_delete::delete_single_table(). Called from sql_select.cc by JOIN::reset(). Called from sql_union.cc by st_select_lex_unit::exec(). */ diff --git a/sql/handler.h b/sql/handler.h index 60c6195..efc75d7 100644 --- a/sql/handler.h +++ b/sql/handler.h @@ -246,7 +246,7 @@ enum chf_create_flags { Example: UPDATE a=1 WHERE pk IN (<keys>) - mysql_update() + Sql_cmd_update::update_single_table() { if (<conditions for starting read removal>) start_read_removal() @@ -1800,7 +1800,8 @@ struct THD_TRANS modified non-transactional tables of top-level statements. At the end of the previous statement and at the beginning of the session, it is reset to FALSE. If such functions - as mysql_insert, mysql_update, mysql_delete etc modify a + as mysql_insert(), Sql_cmd_update::update_single_table, + Sql_cmd_delete::delete_single_table modify a non-transactional table, they set this flag to TRUE. At the end of the statement, the value of stmt.modified_non_trans_table is merged with all.modified_non_trans_table and gets reset. diff --git a/sql/opt_range.cc b/sql/opt_range.cc index 391a04c..f70122f 100644 --- a/sql/opt_range.cc +++ b/sql/opt_range.cc @@ -11599,7 +11599,7 @@ ha_rows check_quick_select(PARAM *param, uint idx, bool index_only, Skip materialized derived table/view result table from MRR check as they aren't contain any data yet. */ - if (param->table->pos_in_table_list->is_non_derived()) + if (!param->table->pos_in_table_list->is_materialized_derived()) rows= file->multi_range_read_info_const(keynr, &seq_if, (void*)&seq, 0, bufsize, mrr_flags, cost); param->quick_rows[keynr]= rows; diff --git a/sql/opt_subselect.cc b/sql/opt_subselect.cc index 3e58a27..fa338f0 100644 --- a/sql/opt_subselect.cc +++ b/sql/opt_subselect.cc @@ -693,6 +693,8 @@ int check_and_do_in_subquery_rewrites(JOIN *join) !join->having && !select_lex->with_sum_func && // 4 in_subs->emb_on_expr_nest && // 5 select_lex->outer_select()->join && // 6 + (!thd->lex->m_sql_cmd || + thd->lex->m_sql_cmd->sql_command_code() == SQLCOM_UPDATE_MULTI) && parent_unit->first_select()->leaf_tables.elements && // 7 !in_subs->has_strategy() && // 8 select_lex->outer_select()->table_list.first && // 9 diff --git a/sql/opt_trace.cc b/sql/opt_trace.cc index 4bc4939..33209ff 100644 --- a/sql/opt_trace.cc +++ b/sql/opt_trace.cc @@ -491,7 +491,8 @@ void Opt_trace_start::init(THD *thd, !list_has_optimizer_trace_table(tbl) && !sets_var_optimizer_trace(sql_command, set_vars) && !thd->system_thread && - !ctx->disable_tracing_if_required()) + !ctx->disable_tracing_if_required() && + !(thd->lex->context_analysis_only & CONTEXT_ANALYSIS_ONLY_PREPARE)) { ctx->start(thd, tbl, sql_command, query, query_length, query_charset, thd->variables.optimizer_trace_max_mem_size); diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 18ffdc9..2b41b78 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -1087,7 +1087,11 @@ TABLE_LIST* find_dup_table(THD *thd, TABLE_LIST *table, TABLE_LIST *table_list, (table->table equal to 0) and right names is in current TABLE_LIST object. */ - if (table->table) + if (table->table && + thd->lex->sql_command != SQLCOM_UPDATE && + thd->lex->sql_command != SQLCOM_UPDATE_MULTI && + thd->lex->sql_command != SQLCOM_DELETE && + thd->lex->sql_command != SQLCOM_DELETE_MULTI) { /* All MyISAMMRG children are plain MyISAM tables. */ DBUG_ASSERT(table->table->file->ht->db_type != DB_TYPE_MRG_MYISAM); @@ -5675,6 +5679,28 @@ bool open_tables_only_view_structure(THD *thd, TABLE_LIST *table_list, } +bool open_tables_for_query(THD *thd, TABLE_LIST *tables, + uint *table_count, uint flags, + DML_prelocking_strategy *prelocking_strategy) +{ + MDL_savepoint mdl_savepoint = thd->mdl_context.mdl_savepoint(); + + DBUG_ASSERT(tables == thd->lex->query_tables); + + if (open_tables(thd, &tables, table_count, + thd->stmt_arena->is_stmt_prepare() ? MYSQL_OPEN_FORCE_SHARED_MDL : 0, + prelocking_strategy)) + { + close_thread_tables(thd); + /* Don't keep locks for a failed statement. */ + thd->mdl_context.rollback_to_savepoint(mdl_savepoint); + return true; + } + + return false; +} + + /* Mark all real tables in the list as free for reuse. @@ -7818,6 +7844,9 @@ int setup_wild(THD *thd, TABLE_LIST *tables, List<Item> &fields, if (!select_lex->with_wild) DBUG_RETURN(0); + if (!fields.elements) + DBUG_RETURN(0); + /* Don't use arena if we are not in prepared statements or stored procedures For PS/SP we have to use arena to remember the changes @@ -8120,7 +8149,7 @@ bool setup_tables(THD *thd, Name_resolution_context *context, while ((table_list= ti++)) { TABLE *table= table_list->table; - if (table) + if (table && !table->pos_in_table_list) table->pos_in_table_list= table_list; if (first_select_table && table_list->top_table() == first_select_table) @@ -8136,7 +8165,6 @@ bool setup_tables(THD *thd, Name_resolution_context *context, } else if (table) { - table->pos_in_table_list= table_list; setup_table_map(table, table_list, tablenr); if (table_list->process_index_hints(table)) diff --git a/sql/sql_base.h b/sql/sql_base.h index 06d7559..370e0f3 100644 --- a/sql/sql_base.h +++ b/sql/sql_base.h @@ -28,6 +28,7 @@ struct Name_resolution_context; class Open_table_context; class Open_tables_state; class Prelocking_strategy; +class DML_prelocking_strategy; struct TABLE_LIST; class THD; struct handlerton; @@ -288,6 +289,9 @@ bool open_normal_and_derived_tables(THD *thd, TABLE_LIST *tables, uint flags, bool open_tables_only_view_structure(THD *thd, TABLE_LIST *tables, bool can_deadlock); bool open_and_lock_internal_tables(TABLE *table, bool lock); +bool open_tables_for_query(THD *thd, TABLE_LIST *tables, + uint *table_count, uint flags, + DML_prelocking_strategy *prelocking_strategy); bool lock_tables(THD *thd, TABLE_LIST *tables, uint counter, uint flags); int decide_logging_format(THD *thd, TABLE_LIST *tables); void close_thread_table(THD *thd, TABLE **table_ptr); @@ -430,6 +434,17 @@ class DML_prelocking_strategy : public Prelocking_strategy }; + +class Multiupdate_prelocking_strategy : public DML_prelocking_strategy +{ + bool done; + bool has_prelocking_list; +public: + void reset(THD *thd); + bool handle_end(THD *thd); +}; + + /** A strategy for prelocking algorithm to be used for LOCK TABLES statement. diff --git a/sql/sql_class.h b/sql/sql_class.h index 806f77c..167df5b 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -7063,6 +7063,7 @@ class multi_update :public select_result_interceptor enum_duplicates handle_duplicates, bool ignore); ~multi_update(); bool init(THD *thd); + bool init_for_single_table(THD *thd); int prepare(List<Item> &list, SELECT_LEX_UNIT *u); int send_data(List<Item> &items); bool initialize_tables (JOIN *join); @@ -7071,6 +7072,8 @@ class multi_update :public select_result_interceptor bool send_eof(); inline ha_rows num_found() const { return found; } inline ha_rows num_updated() const { return updated; } + inline void set_found (ha_rows n) { found= n; } + inline void set_updated (ha_rows n) { updated= n; } virtual void abort_result_set(); void update_used_tables(); void prepare_to_read_rows(); diff --git a/sql/sql_cmd.h b/sql/sql_cmd.h index 1a01caa..1e40270 100644 --- a/sql/sql_cmd.h +++ b/sql/sql_cmd.h @@ -118,6 +118,7 @@ enum enum_sql_command { SQLCOM_END }; +struct TABLE_LIST; class Storage_engine_name { @@ -144,6 +145,8 @@ class Storage_engine_name }; +class Prepared_statement; + /** @class Sql_cmd - Representation of an SQL command. @@ -180,10 +183,28 @@ class Sql_cmd : public Sql_alloc virtual enum_sql_command sql_command_code() const = 0; /** - Execute this SQL statement. - @param thd the current thread. - @retval false on success. - @retval true on error + @brief Check whether the statement has been prepared + @returns true if this statement is prepared, false otherwise + */ + bool is_prepared() const { return m_prepared; } + + /** + @brief Prepare this SQL statement + @param thd the current statement global context + @returns false if success, true if error + */ + virtual bool prepare(THD *thd) + { + /* Default behavior for a statement is to have no preparation code. */ + DBUG_ASSERT(!is_prepared()); + set_prepared(); + return false; + } + + /** + @brief Execute this SQL statement + @param thd the current statement global context + @returns false if success, true if error */ virtual bool execute(THD *thd) = 0; @@ -192,8 +213,39 @@ class Sql_cmd : public Sql_alloc return NULL; } + /** + @brief Set the owning prepared statement + */ + void set_owner(Prepared_statement *stmt) { m_owner = stmt; } + + /** + @breaf Get the owning prepared statement + */ + Prepared_statement *get_owner() { return m_owner; } + + /** + @brief Check whether this command is a DML statement + @return true if SQL command is a DML statement, false otherwise + */ + virtual bool is_dml() const { return false; } + + /** + @brief Unprepare prepared statement for the command + @param thd the current statement global context + + @notes + Temporary function used to "unprepare" a prepared statement after + preparation, so that a subsequent execute statement will reprepare it. + This is done because UNIT::cleanup() will un-resolve all resolved QBs. + */ + virtual void unprepare(THD *thd) + { + DBUG_ASSERT(is_prepared()); + m_prepared = false; + } + protected: - Sql_cmd() + Sql_cmd() : m_prepared(false), m_owner(nullptr) {} virtual ~Sql_cmd() @@ -204,10 +256,127 @@ class Sql_cmd : public Sql_alloc simply destroyed instead. Do not rely on the destructor for any cleanup. */ - DBUG_ASSERT(FALSE); + DBUG_ASSERT(false); } + + /** + @brief Set this statement as prepared + */ + void set_prepared() { m_prepared = true; } + + private: + /* True when statement has been prepared */ + bool m_prepared; + /* Owning prepared statement, nullptr if not prepared */ + Prepared_statement *m_owner; + }; +struct LEX; +class select_result; +class Prelocking_strategy; +class DML_prelocking_strategy; +class Protocol; + +class Sql_cmd_dml : public Sql_cmd +{ +public: + /// @return true if data change statement, false if not (SELECT statement) + virtual bool is_data_change_stmt() const { return true; } + + /** + Command-specific resolving (doesn't include LEX::prepare()) + + @param thd Current THD. + @returns false on success, true on error + */ + virtual bool prepare(THD *thd); + + /** + Execute this query once + + @param thd Thread handler + @returns false on success, true on error + */ + virtual bool execute(THD *thd); + + virtual bool is_dml() const { return true; } + + select_result * get_result() { return result; } + +protected: + Sql_cmd_dml() + : Sql_cmd(), lex(nullptr), result(nullptr), + m_empty_query(false), save_protocol(NULL) + {} + + /// @return true if query is guaranteed to return no data + /** + @todo Also check this for the following cases: + - Empty source for multi-table UPDATE and DELETE. + - Check empty query expression for INSERT + */ + bool is_empty_query() const + { + DBUG_ASSERT(is_prepared()); + return m_empty_query; + } + + /// Set statement as returning no data + void set_empty_query() { m_empty_query = true; } + + /** + Perform a precheck of table privileges for the specific operation. + + @details + Check that user has some relevant privileges for all tables involved in + the statement, e.g. SELECT privileges for tables selected from, INSERT + privileges for tables inserted into, etc. This function will also populate + TABLE_LIST::grant with all privileges the user has for each table, which + is later used during checking of column privileges. + Note that at preparation time, views are not expanded yet. Privilege + checking is thus rudimentary and must be complemented with later calls to + SELECT_LEX::check_view_privileges(). + The reason to call this function at such an early stage is to be able to + quickly reject statements for which the user obviously has insufficient + privileges. + + @param thd thread handler + @returns false if success, true if false + */ + virtual bool precheck(THD *thd) = 0; + + /** + Perform the command-specific parts of DML command preparation, + to be called from prepare() + + @param thd the current thread + @returns false if success, true if error + */ + virtual bool prepare_inner(THD *thd) = 0; + + /** + The inner parts of query optimization and execution. + Single-table DML operations needs to reimplement this. + + @param thd Thread handler + @returns false on success, true on error + */ + virtual bool execute_inner(THD *thd); + + virtual DML_prelocking_strategy *get_dml_prelocking_strategy() = 0; + + uint table_count; + + protected: + LEX *lex; ///< Pointer to LEX for this statement + select_result *result; ///< Pointer to object for handling of the result + bool m_empty_query; ///< True if query will produce no rows + List<Item> empty_list; + Protocol *save_protocol; +}; + + class Sql_cmd_show_slave_status: public Sql_cmd { protected: diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc index 65a3a76..4767315 100644 --- a/sql/sql_delete.cc +++ b/sql/sql_delete.cc @@ -103,7 +103,7 @@ bool Update_plan::save_explain_data_intern(MEM_ROOT *mem_root, bool is_analyze) { explain->select_type= "SIMPLE"; - explain->table_name.append(&table->pos_in_table_list->alias); + explain->table_name.append(table->alias); explain->impossible_where= false; explain->no_partitions= false; @@ -294,124 +294,80 @@ int TABLE::delete_row() } -/** - Implement DELETE SQL word. - - @note Like implementations of other DDL/DML in MySQL, this function - relies on the caller to close the thread tables. This is done in the - end of dispatch_command(). -*/ - -bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, - SQL_I_List<ORDER> *order_list, ha_rows limit, - ulonglong options, select_result *result) +bool Sql_cmd_delete::delete_from_single_table(THD *thd) { - bool will_batch= FALSE; - int error, loc_error; - TABLE *table; - SQL_SELECT *select=0; - SORT_INFO *file_sort= 0; - READ_RECORD info; - bool using_limit=limit != HA_POS_ERROR; - bool transactional_table, safe_update, const_cond; - bool const_cond_result; - bool return_error= 0; - ha_rows deleted= 0; - bool reverse= FALSE; - bool has_triggers= false; - ORDER *order= (ORDER *) ((order_list && order_list->elements) ? - order_list->first : NULL); - SELECT_LEX *select_lex= thd->lex->first_select_lex(); - SELECT_LEX *returning= thd->lex->has_returning() ? thd->lex->returning() : 0; + int error; + int loc_error; + bool transactional_table; + bool const_cond; + bool safe_update; + bool const_cond_result; + bool return_error= 0; + TABLE *table; + SQL_SELECT *select= 0; + SORT_INFO *file_sort= 0; + READ_RECORD info; + ha_rows deleted= 0; + bool reverse= FALSE; + bool binlog_is_row; killed_state killed_status= NOT_KILLED; THD::enum_binlog_query_type query_type= THD::ROW_QUERY_TYPE; - bool binlog_is_row; - Explain_delete *explain; + bool will_batch= FALSE; + + bool has_triggers= false; + SELECT_LEX_UNIT *unit = &lex->unit; + SELECT_LEX *select_lex= unit->first_select(); + SELECT_LEX *returning= thd->lex->has_returning() ? thd->lex->returning() : 0; + TABLE_LIST *const table_list = select_lex->get_table_list(); + ulonglong options= select_lex->options; + ORDER *order= select_lex->order_list.first; + COND *conds= select_lex->join->conds; + ha_rows limit= unit->lim.get_select_limit(); + bool using_limit= limit != HA_POS_ERROR; + Delete_plan query_plan(thd->mem_root); + Explain_delete *explain; Unique * deltempfile= NULL; bool delete_record= false; - bool delete_while_scanning; + bool delete_while_scanning= table_list->delete_while_scanning; bool portion_of_time_through_update; - DBUG_ENTER("mysql_delete"); + + DBUG_ENTER("Sql_cmd_delete::delete_single_table"); query_plan.index= MAX_KEY; query_plan.using_filesort= FALSE; - create_explain_query(thd->lex, thd->mem_root); - if (open_and_lock_tables(thd, table_list, TRUE, 0)) - DBUG_RETURN(TRUE); - THD_STAGE_INFO(thd, stage_init_update); + create_explain_query(thd->lex, thd->mem_root); const bool delete_history= table_list->vers_conditions.delete_history; DBUG_ASSERT(!(delete_history && table_list->period_conditions.is_set())); - if (thd->lex->handle_list_of_derived(table_list, DT_MERGE_FOR_INSERT)) - DBUG_RETURN(TRUE); - if (thd->lex->handle_list_of_derived(table_list, DT_PREPARE)) - DBUG_RETURN(TRUE); + if (table_list->handle_derived(thd->lex, DT_MERGE_FOR_INSERT)) + DBUG_RETURN(1); + if (table_list->handle_derived(thd->lex, DT_PREPARE)) + DBUG_RETURN(1); + + table= table_list->table; if (!table_list->single_table_updatable()) { my_error(ER_NON_UPDATABLE_TABLE, MYF(0), table_list->alias.str, "DELETE"); DBUG_RETURN(TRUE); } - if (!(table= table_list->table) || !table->is_created()) + + if (!table || !table->is_created()) { my_error(ER_VIEW_DELETE_MERGE_VIEW, MYF(0), table_list->view_db.str, table_list->view_name.str); DBUG_RETURN(TRUE); } - table->map=1; + query_plan.select_lex= thd->lex->first_select_lex(); query_plan.table= table; thd->lex->promote_select_describe_flag_if_needed(); - if (mysql_prepare_delete(thd, table_list, &conds, &delete_while_scanning)) - DBUG_RETURN(TRUE); - - if (table_list->has_period()) - { - if (!table_list->period_conditions.start.item->const_item() - || !table_list->period_conditions.end.item->const_item()) - { - my_error(ER_NOT_CONSTANT_EXPRESSION, MYF(0), "FOR PORTION OF"); - DBUG_RETURN(true); - } - } - - if (delete_history) - table->vers_write= false; - - if (returning) - (void) result->prepare(returning->item_list, NULL); - - if (thd->lex->current_select->first_cond_optimization) - { - thd->lex->current_select->save_leaf_tables(thd); - thd->lex->current_select->first_cond_optimization= 0; - } - /* check ORDER BY even if it can be ignored */ - if (order) - { - TABLE_LIST tables; - List<Item> fields; - List<Item> all_fields; - - bzero((char*) &tables,sizeof(tables)); - tables.table = table; - tables.alias = table_list->alias; - - if (select_lex->setup_ref_array(thd, order_list->elements) || - setup_order(thd, select_lex->ref_pointer_array, &tables, - fields, all_fields, order)) - { - free_underlaid_joins(thd, thd->lex->first_select_lex()); - DBUG_RETURN(TRUE); - } - } - /* Apply the IN=>EXISTS transformation to all subqueries and optimize them. */ if (select_lex->optimize_unflattened_subqueries(false)) DBUG_RETURN(TRUE); @@ -1011,90 +967,6 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, } -/* - Prepare items in DELETE statement - - SYNOPSIS - mysql_prepare_delete() - thd - thread handler - table_list - global/local table list - conds - conditions - - RETURN VALUE - FALSE OK - TRUE error -*/ -int mysql_prepare_delete(THD *thd, TABLE_LIST *table_list, Item **conds, - bool *delete_while_scanning) -{ - Item *fake_conds= 0; - SELECT_LEX *select_lex= thd->lex->first_select_lex(); - DBUG_ENTER("mysql_prepare_delete"); - List<Item> all_fields; - - *delete_while_scanning= true; - thd->lex->allow_sum_func.clear_all(); - if (setup_tables_and_check_access(thd, &select_lex->context, - &select_lex->top_join_list, table_list, - select_lex->leaf_tables, FALSE, - DELETE_ACL, SELECT_ACL, TRUE)) - DBUG_RETURN(TRUE); - - 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); - DBUG_RETURN(true); - } - - if (table_list->has_period()) - { - if (table_list->is_view_or_derived()) - { - my_error(ER_IT_IS_A_VIEW, MYF(0), table_list->table_name.str); - DBUG_RETURN(true); - } - - if (select_lex->period_setup_conds(thd, table_list)) - DBUG_RETURN(true); - } - - DBUG_ASSERT(table_list->table); - // conds could be cached from previous SP call - DBUG_ASSERT(!table_list->vers_conditions.need_setup() || - !*conds || thd->stmt_arena->is_stmt_execute()); - if (select_lex->vers_setup_conds(thd, table_list)) - DBUG_RETURN(TRUE); - - *conds= select_lex->where; - - if (setup_returning_fields(thd, table_list) || - setup_conds(thd, table_list, select_lex->leaf_tables, conds) || - setup_ftfuncs(select_lex)) - DBUG_RETURN(TRUE); - if (!table_list->single_table_updatable() || - check_key_in_view(thd, table_list)) - { - my_error(ER_NON_UPDATABLE_TABLE, MYF(0), table_list->alias.str, "DELETE"); - DBUG_RETURN(TRUE); - } - - /* - Application-time periods: if FOR PORTION OF ... syntax used, DELETE - statement could issue delete_row's mixed with write_row's. This causes - problems for myisam and corrupts table, if deleting while scanning. - */ - if (table_list->has_period() - || unique_table(thd, table_list, table_list->next_global, 0)) - *delete_while_scanning= false; - - if (select_lex->inner_refs_list.elements && - fix_inner_refs(thd, all_fields, select_lex, select_lex->ref_pointer_array)) - DBUG_RETURN(TRUE); - - select_lex->fix_prepare_information(thd, conds, &fake_conds); - DBUG_RETURN(FALSE); -} - /*************************************************************************** Delete multiple tables from join @@ -1107,106 +979,6 @@ extern "C" int refpos_order_cmp(void* arg, const void *a,const void *b) return file->cmp_ref((const uchar*)a, (const uchar*)b); } -/* - make delete specific preparation and checks after opening tables - - SYNOPSIS - mysql_multi_delete_prepare() - thd thread handler - - RETURN - FALSE OK - TRUE Error -*/ - -int mysql_multi_delete_prepare(THD *thd) -{ - LEX *lex= thd->lex; - TABLE_LIST *aux_tables= lex->auxiliary_table_list.first; - TABLE_LIST *target_tbl; - DBUG_ENTER("mysql_multi_delete_prepare"); - - if (mysql_handle_derived(lex, DT_INIT)) - DBUG_RETURN(TRUE); - if (mysql_handle_derived(lex, DT_MERGE_FOR_INSERT)) - DBUG_RETURN(TRUE); - if (mysql_handle_derived(lex, DT_PREPARE)) - DBUG_RETURN(TRUE); - /* - setup_tables() need for VIEWs. JOIN::prepare() will not do it second - time. - - lex->query_tables also point on local list of DELETE SELECT_LEX - */ - if (setup_tables_and_check_access(thd, - &thd->lex->first_select_lex()->context, - &thd->lex->first_select_lex()-> - top_join_list, - lex->query_tables, - lex->first_select_lex()->leaf_tables, - FALSE, DELETE_ACL, SELECT_ACL, FALSE)) - DBUG_RETURN(TRUE); - - /* - Multi-delete can't be constructed over-union => we always have - single SELECT on top and have to check underlying SELECTs of it - */ - lex->first_select_lex()->set_unique_exclude(); - /* Fix tables-to-be-deleted-from list to point at opened tables */ - for (target_tbl= (TABLE_LIST*) aux_tables; - target_tbl; - target_tbl= target_tbl->next_local) - { - - target_tbl->table= target_tbl->correspondent_table->table; - if (target_tbl->correspondent_table->is_multitable()) - { - my_error(ER_VIEW_DELETE_MERGE_VIEW, MYF(0), - target_tbl->correspondent_table->view_db.str, - target_tbl->correspondent_table->view_name.str); - DBUG_RETURN(TRUE); - } - - if (!target_tbl->correspondent_table->single_table_updatable() || - check_key_in_view(thd, target_tbl->correspondent_table)) - { - my_error(ER_NON_UPDATABLE_TABLE, MYF(0), - target_tbl->table_name.str, "DELETE"); - DBUG_RETURN(TRUE); - } - } - - for (target_tbl= (TABLE_LIST*) aux_tables; - target_tbl; - target_tbl= target_tbl->next_local) - { - /* - Check that table from which we delete is not used somewhere - inside subqueries/view. - */ - { - TABLE_LIST *duplicate; - if ((duplicate= unique_table(thd, target_tbl->correspondent_table, - lex->query_tables, 0))) - { - update_non_unique_table_error(target_tbl->correspondent_table, - "DELETE", duplicate); - DBUG_RETURN(TRUE); - } - } - } - /* - Reset the exclude flag to false so it doesn't interfare - with further calls to unique_table - */ - lex->first_select_lex()->exclude_from_table_unique_test= FALSE; - - if (lex->save_prep_leaf_tables()) - DBUG_RETURN(TRUE); - - DBUG_RETURN(FALSE); -} - 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), @@ -1659,3 +1431,301 @@ bool multi_delete::send_eof() } return 0; } + + +bool Sql_cmd_delete::precheck(THD *thd) +{ + if (!multitable) + { + if (delete_precheck(thd, lex->query_tables)) + return true; + } + else + { + if (multi_delete_precheck(thd, lex->query_tables)) + return true; + } + + WSREP_SYNC_WAIT(thd, WSREP_SYNC_WAIT_BEFORE_UPDATE_DELETE); + + return false; + +#ifdef WITH_WSREP +wsrep_error_label: +#endif + return true; +} + + +bool Sql_cmd_delete::prepare_inner(THD *thd) +{ + int err= 0; + TABLE_LIST *target_tbl; + JOIN *join; + SELECT_LEX *const select_lex = thd->lex->first_select_lex(); + TABLE_LIST *const table_list = select_lex->get_table_list(); + TABLE_LIST *aux_tables= thd->lex->auxiliary_table_list.first; + ulonglong select_options= select_lex->options; + bool free_join= 1; + SELECT_LEX *returning= thd->lex->has_returning() ? thd->lex->returning() : 0; + const bool delete_history= table_list->vers_conditions.delete_history; + DBUG_ASSERT(!(delete_history && table_list->period_conditions.is_set())); + + DBUG_ENTER("Sql_cmd_delete::prepare_inner"); + + (void) read_statistics_for_tables_if_needed(thd, table_list); + + THD_STAGE_INFO(thd, stage_init_update); + + { + if (mysql_handle_derived(lex, DT_INIT)) + DBUG_RETURN(TRUE); + if (mysql_handle_derived(lex, DT_MERGE_FOR_INSERT)) + DBUG_RETURN(TRUE); + if (mysql_handle_derived(lex, DT_PREPARE)) + DBUG_RETURN(TRUE); + } + + if (!(result= new (thd->mem_root) multi_delete(thd, aux_tables, + lex->table_count))) + { + DBUG_RETURN(TRUE); + } + + table_list->delete_while_scanning= true; + + if (!multitable && !table_list->single_table_updatable()) + { + my_error(ER_NON_UPDATABLE_TABLE, MYF(0), table_list->alias.str, "DELETE"); + DBUG_RETURN(TRUE); + } + + if (!multitable && (!table_list->table || !table_list->table->is_created())) + { + my_error(ER_VIEW_DELETE_MERGE_VIEW, MYF(0), + table_list->view_db.str, table_list->view_name.str); + DBUG_RETURN(TRUE); + } + + if (setup_tables_and_check_access(thd, &select_lex->context, + &select_lex->top_join_list, + table_list, select_lex->leaf_tables, + false, DELETE_ACL, SELECT_ACL, true)) + DBUG_RETURN(TRUE); + + if (setup_tables(thd, &select_lex->context, &select_lex->top_join_list, + table_list, select_lex->leaf_tables, false, false)) + DBUG_RETURN(TRUE); + + if (!multitable) + { + 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); + DBUG_RETURN(true); + } + + if (table_list->has_period()) + { + if (table_list->is_view_or_derived()) + { + my_error(ER_IT_IS_A_VIEW, MYF(0), table_list->table_name.str); + DBUG_RETURN(true); + } + + if (select_lex->period_setup_conds(thd, table_list)) + DBUG_RETURN(true); + } + + if (select_lex->vers_setup_conds(thd, table_list)) + DBUG_RETURN(TRUE); + /* + Application-time periods: if FOR PORTION OF ... syntax used, DELETE + statement could issue delete_row's mixed with write_row's. This causes + problems for myisam and corrupts table, if deleting while scanning. + */ + if (table_list->has_period() + || unique_table(thd, table_list, table_list->next_global, 0)) + table_list->delete_while_scanning= false; + } + + if (multitable) + { + /* + Multi-delete can't be constructed over-union => we always have + single SELECT on top and have to check underlying SELECTs of it + */ + lex->first_select_lex()->set_unique_exclude(); + /* Fix tables-to-be-deleted-from list to point at opened tables */ + for (target_tbl= (TABLE_LIST*) aux_tables; + target_tbl; + target_tbl= target_tbl->next_local) + { + target_tbl->table= target_tbl->correspondent_table->table; + if (target_tbl->correspondent_table->is_multitable()) + { + my_error(ER_VIEW_DELETE_MERGE_VIEW, MYF(0), + target_tbl->correspondent_table->view_db.str, + target_tbl->correspondent_table->view_name.str); + DBUG_RETURN(TRUE); + } + + if (!target_tbl->correspondent_table->single_table_updatable() || + check_key_in_view(thd, target_tbl->correspondent_table)) + { + my_error(ER_NON_UPDATABLE_TABLE, MYF(0), + target_tbl->table_name.str, "DELETE"); + DBUG_RETURN(TRUE); + } + } + + for (target_tbl= (TABLE_LIST*) aux_tables; + target_tbl; + target_tbl= target_tbl->next_local) + { + /* + Check that table from which we delete is not used somewhere + inside subqueries/view. + */ + { + TABLE_LIST *duplicate; + if ((duplicate= unique_table(thd, target_tbl->correspondent_table, + lex->query_tables, 0))) + { + update_non_unique_table_error(target_tbl->correspondent_table, + "DELETE", duplicate); + DBUG_RETURN(TRUE); + } + } + } + /* + Reset the exclude flag to false so it doesn't interfare + with further calls to unique_table + */ + lex->first_select_lex()->exclude_from_table_unique_test= FALSE; + } + + { + if (thd->lex->describe) + select_options|= SELECT_DESCRIBE; + + /* + When in EXPLAIN, delay deleting the joins so that they are still + available when we're producing EXPLAIN EXTENDED warning text. + */ + if (select_options & SELECT_DESCRIBE) + free_join= 0; + select_options|= + SELECT_NO_JOIN_CACHE | SELECT_NO_UNLOCK | OPTION_SETUP_TABLES_DONE; + + if (!(join= new (thd->mem_root) JOIN(thd, empty_list, + select_options, result))) + DBUG_RETURN(TRUE); + THD_STAGE_INFO(thd, stage_init); + select_lex->join= join; + thd->lex->used_tables=0; + if ((err= join->prepare(table_list, select_lex->where, + select_lex->order_list.elements, + select_lex->order_list.first, + false, NULL, NULL, NULL, + select_lex, &lex->unit))) + + { + goto err; + } + + } + + if (!multitable && table_list->has_period()) + { + if (!table_list->period_conditions.start.item->const_item() + || !table_list->period_conditions.end.item->const_item()) + { + my_error(ER_NOT_CONSTANT_EXPRESSION, MYF(0), "FOR PORTION OF"); + DBUG_RETURN(true); + } + } + + if (delete_history) + table_list->table->vers_write= false; + + if (setup_returning_fields(thd, table_list) || + setup_ftfuncs(select_lex)) + goto err; + + free_join= false; + + if (returning) + (void) result->prepare(returning->item_list, NULL); + +err: + + if (free_join) + { + THD_STAGE_INFO(thd, stage_end); + err|= (int)(select_lex->cleanup()); + DBUG_RETURN(err || thd->is_error()); + } + DBUG_RETURN(err); + +} + +bool Sql_cmd_delete::execute_inner(THD *thd) +{ + if (!multitable) + { + if (lex->has_returning()) + { + select_result *sel_result= NULL; + delete result; + /* This is DELETE ... RETURNING. It will return output to the client */ + if (thd->lex->analyze_stmt) + { + /* + Actually, it is ANALYZE .. DELETE .. RETURNING. We need to produce + output and then discard it. + */ + sel_result= new (thd->mem_root) select_send_analyze(thd); + save_protocol= thd->protocol; + thd->protocol= new Protocol_discard(thd); + } + else + { + if (!lex->result && !(sel_result= new (thd->mem_root) select_send(thd))) + return true; + } + result= lex->result ? lex->result : sel_result; + } + } + + bool res= multitable ? Sql_cmd_dml::execute_inner(thd) + : delete_from_single_table(thd); + + res|= thd->is_error(); + + if (save_protocol) + { + delete thd->protocol; + thd->protocol= save_protocol; + } + { + if (unlikely(res)) + { + if (multitable) + result->abort_result_set(); + } + else + { + if (thd->lex->describe || thd->lex->analyze_stmt) + res= thd->lex->explain->send_explain(thd); + } + } + + if (result) + { + res= false; + delete result; + } + + return res; +} diff --git a/sql/sql_delete.h b/sql/sql_delete.h index 520524c..64b882a 100644 --- a/sql/sql_delete.h +++ b/sql/sql_delete.h @@ -17,6 +17,9 @@ #define SQL_DELETE_INCLUDED #include "my_base.h" /* ha_rows */ +#include "sql_class.h" /* enum_duplicates */ +#include "sql_cmd.h" // Sql_cmd_dml +#include "sql_base.h" class THD; struct TABLE_LIST; @@ -26,10 +29,34 @@ class select_result; typedef class Item COND; template <typename T> class SQL_I_List; -int mysql_prepare_delete(THD *thd, TABLE_LIST *table_list, Item **conds, - bool *delete_while_scanning); -bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, - SQL_I_List<ORDER> *order, ha_rows rows, - ulonglong options, select_result *result); +class Sql_cmd_delete final : public Sql_cmd_dml +{ +public: + Sql_cmd_delete(bool multitable_arg) + : multitable(multitable_arg) {} + enum_sql_command sql_command_code() const override + { + return multitable ? SQLCOM_DELETE_MULTI : SQLCOM_DELETE; + } + + DML_prelocking_strategy *get_dml_prelocking_strategy() + { + return &dml_prelocking_strategy; + } + +protected: + bool precheck(THD *thd) override; + + bool prepare_inner(THD *thd) override; + + bool execute_inner(THD *thd) override; + + private: + bool delete_from_single_table(THD *thd); + + bool multitable; + + DML_prelocking_strategy dml_prelocking_strategy; +}; #endif /* SQL_DELETE_INCLUDED */ diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index 5f2f072..070cf52 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -1302,6 +1302,8 @@ void LEX::start(THD *thd_arg) wild= 0; exchange= 0; + table_count= 0; + DBUG_VOID_RETURN; } @@ -3029,6 +3031,7 @@ void st_select_lex::init_select() curr_tvc_name= 0; versioned_tables= 0; nest_flags= 0; + item_list_usage= MARK_COLUMNS_READ; } /* @@ -3299,34 +3302,6 @@ void st_select_lex_unit::exclude_level() } -#if 0 -/* - Exclude subtree of current unit from tree of SELECTs - - SYNOPSYS - st_select_lex_unit::exclude_tree() -*/ -void st_select_lex_unit::exclude_tree() -{ - for (SELECT_LEX *sl= first_select(); sl; sl= sl->next_select()) - { - // unlink current level from global SELECTs list - if (sl->link_prev && (*sl->link_prev= sl->link_next)) - sl->link_next->link_prev= sl->link_prev; - - // unlink underlay levels - for (SELECT_LEX_UNIT *u= sl->first_inner_unit(); u; u= u->next_unit()) - { - u->exclude_level(); - } - } - // exclude currect unit from list of nodes - (*prev)= next; - if (next) - next->prev= prev; -} -#endif - /* st_select_lex_node::mark_as_dependent mark all st_select_lex struct from @@ -3548,7 +3523,7 @@ bool st_select_lex::setup_ref_array(THD *thd, uint order_group_num) select_n_where_fields + order_group_num + hidden_bit_fields + - fields_in_window_functions) * (size_t) 5; + fields_in_window_functions + 1) * (size_t) 5; DBUG_ASSERT(n_elems % 5 == 0); if (!ref_pointer_array.is_null()) { @@ -4094,6 +4069,12 @@ bool LEX::can_not_use_merged(bool no_update_or_delete) return TRUE; /* Fall through */ + case SQLCOM_UPDATE: + if (no_update_or_delete && m_sql_cmd && + (m_sql_cmd->sql_command_code() == SQLCOM_UPDATE_MULTI || + query_tables->is_multitable())) + return TRUE; + default: return FALSE; } diff --git a/sql/sql_lex.h b/sql/sql_lex.h index 4f2e775..0badc32 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -36,6 +36,7 @@ #include "sql_limit.h" // Select_limit_counters #include "json_table.h" // Json_table_column #include "sql_schema.h" +#include "sql_class.h" // enum enum_column_usage /* Used for flags of nesting constructs */ #define SELECT_NESTING_MAP_SIZE 64 @@ -873,6 +874,8 @@ class st_select_lex_unit: public st_select_lex_node { { } + void set_query_result(select_result *res) { result= res; } + TABLE *table; /* temporary table using for appending UNION results */ select_result *result; st_select_lex *pre_last_parse; @@ -1005,6 +1008,7 @@ class st_select_lex_unit: public st_select_lex_node { bool add_fake_select_lex(THD *thd); void init_prepare_fake_select_lex(THD *thd, bool first_execution); + void set_prepared() { prepared = true; } inline bool is_prepared() { return prepared; } bool change_result(select_result_interceptor *result, select_result_interceptor *old_result); @@ -1107,6 +1111,7 @@ class st_select_lex: public st_select_lex_node Item *prep_having;/* saved HAVING clause for prepared statement processing */ Item *cond_pushed_into_where; /* condition pushed into WHERE */ Item *cond_pushed_into_having; /* condition pushed into HAVING */ + Item *where_cond_after_prepare; /* nest_levels are local to the query or VIEW, @@ -1215,6 +1220,7 @@ class st_select_lex: public st_select_lex_node List<List_item> save_many_values; List<Item> *save_insert_list; + enum_column_usage item_list_usage; bool is_item_list_lookup:1; /* Needed to correctly generate 'PRIMARY' or 'SIMPLE' for select_type column @@ -1745,6 +1751,25 @@ class Query_tables_list uint sroutines_list_own_elements; /** + Locking state of tables in this particular statement. + + If we under LOCK TABLES or in prelocked mode we consider tables + for the statement to be "locked" if there was a call to lock_tables() + (which called handler::start_stmt()) for tables of this statement + and there was no matching close_thread_tables() call. + + As result this state may differ significantly from one represented + by Open_tables_state::lock/locked_tables_mode more, which are always + "on" under LOCK TABLES or in prelocked mode. + */ + enum enum_lock_tables_state { LTS_NOT_LOCKED = 0, LTS_LOCKED }; + enum_lock_tables_state lock_tables_state; + bool is_query_tables_locked() const + { + return (lock_tables_state == LTS_LOCKED); + } + + /** Number of tables which were open by open_tables() and to be locked by lock_tables(). Note that we set this member only in some cases, when this value @@ -3391,6 +3416,7 @@ struct LEX: public Query_tables_list bool default_used:1; /* using default() function */ bool with_rownum:1; /* Using rownum() function */ bool is_lex_started:1; /* If lex_start() did run. For debugging. */ + /* This variable is used in post-parse stage to declare that sum-functions, or functions which have sense only if GROUP BY is present, are allowed. diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 0597b08..8b906be 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -34,9 +34,7 @@ #include "sql_locale.h" // my_locale_en_US #include "log.h" // flush_error_log #include "sql_view.h" // mysql_create_view, mysql_drop_view -#include "sql_delete.h" // mysql_delete #include "sql_insert.h" // mysql_insert -#include "sql_update.h" // mysql_update, mysql_multi_update #include "sql_partition.h" // struct partition_info #include "sql_db.h" // mysql_change_db, mysql_create_db, // mysql_rm_db, mysql_upgrade_db, @@ -3442,7 +3440,6 @@ int mysql_execute_command(THD *thd, bool is_called_from_prepared_stmt) { int res= 0; - int up_result= 0; LEX *lex= thd->lex; /* first SELECT_LEX (have special meaning for many of non-SELECTcommands) */ SELECT_LEX *select_lex= lex->first_select_lex(); @@ -3454,7 +3451,6 @@ mysql_execute_command(THD *thd, bool is_called_from_prepared_stmt) SELECT_LEX_UNIT *unit= &lex->unit; #ifdef HAVE_REPLICATION /* have table map for update for multi-update statement (BUG#37051) */ - bool have_table_map_for_update= FALSE; /* */ Rpl_filter *rpl_filter; #endif @@ -3576,7 +3572,6 @@ mysql_execute_command(THD *thd, bool is_called_from_prepared_stmt) if (lex->sql_command == SQLCOM_UPDATE_MULTI && thd->table_map_for_update) { - have_table_map_for_update= TRUE; table_map table_map_for_update= thd->table_map_for_update; uint nr= 0; TABLE_LIST *table; @@ -4381,130 +4376,15 @@ mysql_execute_command(THD *thd, bool is_called_from_prepared_stmt) break; } case SQLCOM_UPDATE: - { - WSREP_SYNC_WAIT(thd, WSREP_SYNC_WAIT_BEFORE_UPDATE_DELETE); - ha_rows found= 0, updated= 0; - DBUG_ASSERT(first_table == all_tables && first_table != 0); - WSREP_SYNC_WAIT(thd, WSREP_SYNC_WAIT_BEFORE_UPDATE_DELETE); - - if (update_precheck(thd, all_tables)) - break; - - /* - UPDATE IGNORE can be unsafe. We therefore use row based - logging if mixed or row based logging is available. - TODO: Check if the order of the output of the select statement is - deterministic. Waiting for BUG#42415 - */ - if (lex->ignore) - lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_UPDATE_IGNORE); - - DBUG_ASSERT(select_lex->limit_params.offset_limit == 0); - unit->set_limit(select_lex); - MYSQL_UPDATE_START(thd->query()); - res= up_result= mysql_update(thd, all_tables, - select_lex->item_list, - lex->value_list, - select_lex->where, - select_lex->order_list.elements, - select_lex->order_list.first, - unit->lim.get_select_limit(), - lex->ignore, &found, &updated); - MYSQL_UPDATE_DONE(res, found, updated); - /* mysql_update return 2 if we need to switch to multi-update */ - if (up_result != 2) - break; - if (thd->lex->period_conditions.is_set()) - { - DBUG_ASSERT(0); // Should never happen - goto error; - } - } - /* fall through */ case SQLCOM_UPDATE_MULTI: + case SQLCOM_DELETE: + case SQLCOM_DELETE_MULTI: { DBUG_ASSERT(first_table == all_tables && first_table != 0); - /* if we switched from normal update, rights are checked */ - if (up_result != 2) - { - WSREP_SYNC_WAIT(thd, WSREP_SYNC_WAIT_BEFORE_UPDATE_DELETE); - if ((res= multi_update_precheck(thd, all_tables))) - break; - } - else - res= 0; - - unit->set_limit(select_lex); - /* - We can not use mysql_explain_union() because of parameters of - mysql_select in mysql_multi_update so just set the option if needed - */ - if (thd->lex->describe) - { - select_lex->set_explain_type(FALSE); - select_lex->options|= SELECT_DESCRIBE; - } - - res= mysql_multi_update_prepare(thd); + DBUG_ASSERT(lex->m_sql_cmd != NULL); -#ifdef HAVE_REPLICATION - /* Check slave filtering rules */ - if (unlikely(thd->slave_thread && !have_table_map_for_update)) - { - if (all_tables_not_ok(thd, all_tables)) - { - if (res!= 0) - { - res= 0; /* don't care of prev failure */ - thd->clear_error(); /* filters are of highest prior */ - } - /* we warn the slave SQL thread */ - my_error(ER_SLAVE_IGNORED_TABLE, MYF(0)); - break; - } - if (res) - break; - } - else - { -#endif /* HAVE_REPLICATION */ - if (res) - break; - if (opt_readonly && - !(thd->security_ctx->master_access & PRIV_IGNORE_READ_ONLY) && - some_non_temp_table_to_be_updated(thd, all_tables)) - { - my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--read-only"); - break; - } -#ifdef HAVE_REPLICATION - } /* unlikely */ -#endif - { - multi_update *result_obj; - MYSQL_MULTI_UPDATE_START(thd->query()); - res= mysql_multi_update(thd, all_tables, - &select_lex->item_list, - &lex->value_list, - select_lex->where, - select_lex->options, - lex->duplicates, - lex->ignore, - unit, - select_lex, - &result_obj); - if (result_obj) - { - MYSQL_MULTI_UPDATE_DONE(res, result_obj->num_found(), - result_obj->num_updated()); - res= FALSE; /* Ignore errors here */ - delete result_obj; - } - else - { - MYSQL_MULTI_UPDATE_DONE(1, 0, 0); - } - } + res = lex->m_sql_cmd->execute(thd); + thd->abort_on_warning= 0; break; } case SQLCOM_REPLACE: @@ -4766,129 +4646,6 @@ mysql_execute_command(THD *thd, bool is_called_from_prepared_stmt) break; } - case SQLCOM_DELETE: - { - WSREP_SYNC_WAIT(thd, WSREP_SYNC_WAIT_BEFORE_UPDATE_DELETE); - select_result *sel_result= NULL; - DBUG_ASSERT(first_table == all_tables && first_table != 0); - WSREP_SYNC_WAIT(thd, WSREP_SYNC_WAIT_BEFORE_UPDATE_DELETE); - - if ((res= delete_precheck(thd, all_tables))) - break; - DBUG_ASSERT(select_lex->limit_params.offset_limit == 0); - unit->set_limit(select_lex); - - MYSQL_DELETE_START(thd->query()); - Protocol *save_protocol= NULL; - - if (lex->has_returning()) - { - /* This is DELETE ... RETURNING. It will return output to the client */ - if (thd->lex->analyze_stmt) - { - /* - Actually, it is ANALYZE .. DELETE .. RETURNING. We need to produce - output and then discard it. - */ - sel_result= new (thd->mem_root) select_send_analyze(thd); - save_protocol= thd->protocol; - thd->protocol= new Protocol_discard(thd); - } - else - { - if (!lex->result && !(sel_result= new (thd->mem_root) select_send(thd))) - goto error; - } - } - - res = mysql_delete(thd, all_tables, - select_lex->where, &select_lex->order_list, - unit->lim.get_select_limit(), select_lex->options, - lex->result ? lex->result : sel_result); - - if (save_protocol) - { - delete thd->protocol; - thd->protocol= save_protocol; - } - - if (thd->lex->analyze_stmt || thd->lex->describe) - { - if (!res) - res= thd->lex->explain->send_explain(thd); - } - - delete sel_result; - MYSQL_DELETE_DONE(res, (ulong) thd->get_row_count_func()); - break; - } - case SQLCOM_DELETE_MULTI: - { - WSREP_SYNC_WAIT(thd, WSREP_SYNC_WAIT_BEFORE_UPDATE_DELETE); - DBUG_ASSERT(first_table == all_tables && first_table != 0); - TABLE_LIST *aux_tables= thd->lex->auxiliary_table_list.first; - multi_delete *result; - WSREP_SYNC_WAIT(thd, WSREP_SYNC_WAIT_BEFORE_UPDATE_DELETE); - - if ((res= multi_delete_precheck(thd, all_tables))) - break; - - /* condition will be TRUE on SP re-excuting */ - if (select_lex->item_list.elements != 0) - select_lex->item_list.empty(); - if (add_item_to_list(thd, new (thd->mem_root) Item_null(thd))) - goto error; - - THD_STAGE_INFO(thd, stage_init); - if ((res= open_and_lock_tables(thd, all_tables, TRUE, 0))) - break; - - MYSQL_MULTI_DELETE_START(thd->query()); - if (unlikely(res= mysql_multi_delete_prepare(thd))) - { - MYSQL_MULTI_DELETE_DONE(1, 0); - goto error; - } - - if (likely(!thd->is_fatal_error)) - { - result= new (thd->mem_root) multi_delete(thd, aux_tables, - lex->table_count); - if (likely(result)) - { - if (unlikely(select_lex->vers_setup_conds(thd, aux_tables))) - goto multi_delete_error; - res= mysql_select(thd, - select_lex->get_table_list(), - select_lex->item_list, - select_lex->where, - 0, (ORDER *)NULL, (ORDER *)NULL, (Item *)NULL, - (ORDER *)NULL, - (select_lex->options | thd->variables.option_bits | - SELECT_NO_JOIN_CACHE | SELECT_NO_UNLOCK | - OPTION_SETUP_TABLES_DONE) & ~OPTION_BUFFER_RESULT, - result, unit, select_lex); - res|= (int)(thd->is_error()); - - MYSQL_MULTI_DELETE_DONE(res, result->num_deleted()); - if (res) - result->abort_result_set(); /* for both DELETE and EXPLAIN DELETE */ - else - { - if (lex->describe || lex->analyze_stmt) - res= thd->lex->explain->send_explain(thd); - } - multi_delete_error: - delete result; - } - } - else - { - res= TRUE; // Error - MYSQL_MULTI_DELETE_DONE(1, 0); - } - break; - } case SQLCOM_DROP_SEQUENCE: case SQLCOM_DROP_TABLE: { @@ -7772,12 +7529,16 @@ void create_select_for_variable(THD *thd, LEX_CSTRING *var_name) } -void mysql_init_multi_delete(LEX *lex) +void mysql_init_delete(LEX *lex) { - lex->sql_command= SQLCOM_DELETE_MULTI; mysql_init_select(lex); lex->first_select_lex()->limit_params.clear(); lex->unit.lim.clear(); +} + +void mysql_init_multi_delete(LEX *lex) +{ + lex->sql_command= SQLCOM_DELETE_MULTI; lex->first_select_lex()->table_list. save_and_clear(&lex->auxiliary_table_list); lex->query_tables= 0; diff --git a/sql/sql_parse.h b/sql/sql_parse.h index d3cf83b..3f3302b 100644 --- a/sql/sql_parse.h +++ b/sql/sql_parse.h @@ -93,6 +93,7 @@ void mysql_parse(THD *thd, char *rawbuf, uint length, bool mysql_new_select(LEX *lex, bool move_down, SELECT_LEX *sel); void create_select_for_variable(THD *thd, LEX_CSTRING *var_name); void create_table_set_open_action_and_adjust_tables(LEX *lex); +void mysql_init_delete(LEX *lex); void mysql_init_multi_delete(LEX *lex); bool multi_delete_set_locks_and_link_aux_tables(LEX *lex); void create_table_set_open_action_and_adjust_tables(LEX *lex); diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc index b10a81d..b9ae058 100644 --- a/sql/sql_prepare.cc +++ b/sql/sql_prepare.cc @@ -95,10 +95,8 @@ When one supplies long data for a placeholder: #include "sql_base.h" // open_normal_and_derived_tables #include "sql_cache.h" // query_cache_* #include "sql_view.h" // create_view_precheck -#include "sql_delete.h" // mysql_prepare_delete #include "sql_select.h" // for JOIN #include "sql_insert.h" // upgrade_lock_type_for_insert, mysql_prepare_insert -#include "sql_update.h" // mysql_prepare_update #include "sql_db.h" // mysql_opt_change_db, mysql_change_db #include "sql_derived.h" // mysql_derived_prepare, // mysql_handle_derived @@ -1399,160 +1397,6 @@ static bool mysql_test_insert(Prepared_statement *stmt, } -/** - Validate UPDATE statement. - - @param stmt prepared statement - @param tables list of tables used in this query - - @todo - - here we should send types of placeholders to the client. - - @retval - 0 success - @retval - 1 error, error message is set in THD - @retval - 2 convert to multi_update -*/ - -static int mysql_test_update(Prepared_statement *stmt, - TABLE_LIST *table_list) -{ - int res; - THD *thd= stmt->thd; - uint table_count= 0; - TABLE_LIST *update_source_table; - SELECT_LEX *select= stmt->lex->first_select_lex(); -#ifndef NO_EMBEDDED_ACCESS_CHECKS - privilege_t want_privilege(NO_ACL); -#endif - DBUG_ENTER("mysql_test_update"); - - if (update_precheck(thd, table_list) || - open_tables(thd, &table_list, &table_count, MYSQL_OPEN_FORCE_SHARED_MDL)) - goto error; - - if (mysql_handle_derived(thd->lex, DT_INIT)) - goto error; - - if (((update_source_table= unique_table(thd, table_list, - table_list->next_global, 0)) || - table_list->is_multitable())) - { - DBUG_ASSERT(update_source_table || table_list->view != 0); - DBUG_PRINT("info", ("Switch to multi-update")); - /* pass counter value */ - thd->lex->table_count= table_count; - /* convert to multiupdate */ - DBUG_RETURN(2); - } - - /* - thd->fill_derived_tables() is false here for sure (because it is - preparation of PS, so we even do not check it). - */ - if (table_list->handle_derived(thd->lex, DT_MERGE_FOR_INSERT)) - goto error; - if (table_list->handle_derived(thd->lex, DT_PREPARE)) - goto error; - - if (!table_list->single_table_updatable()) - { - my_error(ER_NON_UPDATABLE_TABLE, MYF(0), table_list->alias.str, "UPDATE"); - goto error; - } - -#ifndef NO_EMBEDDED_ACCESS_CHECKS - /* Force privilege re-checking for views after they have been opened. */ - want_privilege= (table_list->view ? UPDATE_ACL : - table_list->grant.want_privilege); -#endif - - if (mysql_prepare_update(thd, table_list, &select->where, - select->order_list.elements, - select->order_list.first)) - goto error; - -#ifndef NO_EMBEDDED_ACCESS_CHECKS - table_list->grant.want_privilege= want_privilege; - table_list->table->grant.want_privilege= want_privilege; - table_list->register_want_access(want_privilege); -#endif - thd->lex->first_select_lex()->no_wrap_view_item= TRUE; - res= setup_fields(thd, Ref_ptr_array(), - select->item_list, MARK_COLUMNS_READ, 0, NULL, 0); - thd->lex->first_select_lex()->no_wrap_view_item= FALSE; - if (res) - goto error; -#ifndef NO_EMBEDDED_ACCESS_CHECKS - /* Check values */ - table_list->grant.want_privilege= - table_list->table->grant.want_privilege= - (SELECT_ACL & ~table_list->table->grant.privilege); - table_list->register_want_access(SELECT_ACL); -#endif - if (setup_fields(thd, Ref_ptr_array(), - stmt->lex->value_list, COLUMNS_READ, 0, NULL, 0) || - check_unique_table(thd, table_list)) - goto error; - /* TODO: here we should send types of placeholders to the client. */ - DBUG_RETURN(0); -error: - DBUG_RETURN(1); -} - - -/** - Validate DELETE statement. - - @param stmt prepared statement - @param tables list of tables used in this query - - @retval - FALSE success - @retval - TRUE error, error message is set in THD -*/ - -static bool mysql_test_delete(Prepared_statement *stmt, - TABLE_LIST *table_list) -{ - uint table_count= 0; - THD *thd= stmt->thd; - LEX *lex= stmt->lex; - bool delete_while_scanning; - DBUG_ENTER("mysql_test_delete"); - - if (delete_precheck(thd, table_list) || - open_tables(thd, &table_list, &table_count, MYSQL_OPEN_FORCE_SHARED_MDL)) - goto error; - - if (mysql_handle_derived(thd->lex, DT_INIT)) - goto error; - if (mysql_handle_derived(thd->lex, DT_MERGE_FOR_INSERT)) - goto error; - if (mysql_handle_derived(thd->lex, DT_PREPARE)) - goto error; - - if (!table_list->single_table_updatable()) - { - my_error(ER_NON_UPDATABLE_TABLE, MYF(0), table_list->alias.str, "DELETE"); - goto error; - } - if (!table_list->table || !table_list->table->is_created()) - { - my_error(ER_VIEW_DELETE_MERGE_VIEW, MYF(0), - table_list->view_db.str, table_list->view_name.str); - goto error; - } - - DBUG_RETURN(mysql_prepare_delete(thd, table_list, - &lex->first_select_lex()->where, - &delete_while_scanning)); -error: - DBUG_RETURN(TRUE); -} /** @@ -2135,74 +1979,6 @@ static bool mysql_test_create_view(Prepared_statement *stmt) } -/* - Validate and prepare for execution a multi update statement. - - @param stmt prepared statement - @param tables list of tables used in this query - @param converted converted to multi-update from usual update - - @retval - FALSE success - @retval - TRUE error, error message is set in THD -*/ - -static bool mysql_test_multiupdate(Prepared_statement *stmt, - TABLE_LIST *tables, - bool converted) -{ - /* if we switched from normal update, rights are checked */ - if (!converted && multi_update_precheck(stmt->thd, tables)) - return TRUE; - - return select_like_stmt_test(stmt, &mysql_multi_update_prepare, - OPTION_SETUP_TABLES_DONE); -} - - -/** - Validate and prepare for execution a multi delete statement. - - @param stmt prepared statement - @param tables list of tables used in this query - - @retval - FALSE success - @retval - TRUE error, error message in THD is set. -*/ - -static bool mysql_test_multidelete(Prepared_statement *stmt, - TABLE_LIST *tables) -{ - THD *thd= stmt->thd; - - thd->lex->current_select= thd->lex->first_select_lex(); - if (add_item_to_list(thd, new (thd->mem_root) - Item_null(thd))) - { - my_error(ER_OUTOFMEMORY, MYF(ME_FATAL), 0); - goto error; - } - - if (multi_delete_precheck(thd, tables) || - select_like_stmt_test_with_open(stmt, tables, - &mysql_multi_delete_prepare, - OPTION_SETUP_TABLES_DONE)) - goto error; - if (!tables->table) - { - my_error(ER_VIEW_DELETE_MERGE_VIEW, MYF(0), - tables->view_db.str, tables->view_name.str); - goto error; - } - return FALSE; -error: - return TRUE; -} - - /** Wrapper for mysql_insert_select_prepare, to make change of local tables after open_normal_and_derived_tables() call. @@ -2484,18 +2260,14 @@ static bool check_prepared_statement(Prepared_statement *stmt) break; case SQLCOM_UPDATE: - res= mysql_test_update(stmt, tables); - /* mysql_test_update returns 2 if we need to switch to multi-update */ - if (res != 2) - break; - /* fall through */ case SQLCOM_UPDATE_MULTI: - res= mysql_test_multiupdate(stmt, tables, res == 2); - break; - case SQLCOM_DELETE: - res= mysql_test_delete(stmt, tables); + case SQLCOM_DELETE_MULTI: + res = lex->m_sql_cmd->prepare(thd); + if (!res) + lex->m_sql_cmd->unprepare(thd); break; + /* The following allow WHERE clause, so they must be tested like SELECT */ case SQLCOM_SHOW_DATABASES: case SQLCOM_SHOW_TABLES: @@ -2632,10 +2404,6 @@ static bool check_prepared_statement(Prepared_statement *stmt) res= mysql_test_set_fields(stmt, tables, &lex->var_list); break; - case SQLCOM_DELETE_MULTI: - res= mysql_test_multidelete(stmt, tables); - break; - case SQLCOM_INSERT_SELECT: case SQLCOM_REPLACE_SELECT: res= mysql_test_insert_select(stmt, tables); @@ -4373,6 +4141,9 @@ bool Prepared_statement::prepare(const char *packet, uint packet_len) thd->is_error() || init_param_array(this)); + if (lex->m_sql_cmd) + lex->m_sql_cmd->set_owner(this); + if (thd->security_ctx->password_expired && lex->sql_command != SQLCOM_SET_OPTION && lex->sql_command != SQLCOM_PREPARE && diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 2a56292..7db5daa 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -1430,7 +1430,7 @@ JOIN::prepare(TABLE_LIST *tables_init, COND *conds_init, uint og_num, } } - if (setup_fields(thd, ref_ptrs, fields_list, MARK_COLUMNS_READ, + if (setup_fields(thd, ref_ptrs, fields_list, select_lex->item_list_usage, &all_fields, &select_lex->pre_fix, 1)) DBUG_RETURN(-1); thd->lex->current_select->context_analysis_place= save_place; @@ -1720,6 +1720,8 @@ JOIN::prepare(TABLE_LIST *tables_init, COND *conds_init, uint og_num, if (!procedure && result && result->prepare(fields_list, unit_arg)) goto err; /* purecov: inspected */ + select_lex->where_cond_after_prepare= conds; + unit= unit_arg; if (prepare_stage2()) goto err; @@ -29028,7 +29030,8 @@ static bool get_range_limit_read_cost(const JOIN_TAB *tab, @note This function takes into account table->opt_range_condition_rows statistic (that is calculated by the make_join_statistics function). - However, single table procedures such as mysql_update() and mysql_delete() + However, single table procedures such as Sql_cmd_update:update_single_table() + and Sql_cmd_delete::delete_single_table() never call make_join_statistics, so they have to update it manually (@see get_index_for_order()). */ @@ -30463,6 +30466,208 @@ static bool process_direct_rownum_comparison(THD *thd, SELECT_LEX_UNIT *unit, } +static void MYSQL_DML_START(THD *thd) +{ + switch (thd->lex->sql_command) { + + case SQLCOM_UPDATE: + MYSQL_UPDATE_START(thd->query()); + break; + case SQLCOM_UPDATE_MULTI: + MYSQL_MULTI_UPDATE_START(thd->query()); + break; + case SQLCOM_DELETE: + MYSQL_DELETE_START(thd->query()); + break; + case SQLCOM_DELETE_MULTI: + MYSQL_MULTI_DELETE_START(thd->query()); + break; + default: + DBUG_ASSERT(0); + } +} + + +static void MYSQL_DML_DONE(THD *thd, int rc) +{ + switch (thd->lex->sql_command) { + + case SQLCOM_UPDATE: + MYSQL_UPDATE_DONE( + rc, + (rc ? 0 : + ((multi_update*)(((Sql_cmd_dml*)(thd->lex->m_sql_cmd))->get_result())) + ->num_found()), + (rc ? 0 : + ((multi_update*)(((Sql_cmd_dml*)(thd->lex->m_sql_cmd))->get_result())) + ->num_updated())); + break; + case SQLCOM_UPDATE_MULTI: + MYSQL_MULTI_UPDATE_DONE( + rc, + (rc ? 0 : + ((multi_update*)(((Sql_cmd_dml*)(thd->lex->m_sql_cmd))->get_result())) + ->num_found()), + (rc ? 0 : + ((multi_update*)(((Sql_cmd_dml*)(thd->lex->m_sql_cmd))->get_result())) + ->num_updated())); + break; + case SQLCOM_DELETE: + MYSQL_DELETE_DONE(rc, (rc ? 0 : (ulong) (thd->get_row_count_func()))); + break; + case SQLCOM_DELETE_MULTI: + MYSQL_MULTI_DELETE_DONE( + rc, + (rc ? 0 : + ((multi_delete*)(((Sql_cmd_dml*)(thd->lex->m_sql_cmd))->get_result())) + ->num_deleted())); + break; + default: + DBUG_ASSERT(0); + } +} + +bool Sql_cmd_dml::prepare(THD *thd) +{ + lex= thd->lex; + SELECT_LEX_UNIT *unit= &lex->unit; + + DBUG_ASSERT(!is_prepared()); + + // Perform a coarse statement-specific privilege check. + if (precheck(thd)) + goto err; + + MYSQL_DML_START(thd); + + lex->context_analysis_only|= CONTEXT_ANALYSIS_ONLY_DERIVED; + + if (open_tables_for_query(thd, lex->query_tables, &table_count, 0, + get_dml_prelocking_strategy())) + { + if (thd->is_error()) + goto err; + (void)unit->cleanup(); + return true; + } + + if (prepare_inner(thd)) + goto err; + + lex->context_analysis_only&= ~CONTEXT_ANALYSIS_ONLY_DERIVED; + + set_prepared(); + unit->set_prepared(); + + return false; + +err: + DBUG_ASSERT(thd->is_error()); + DBUG_PRINT("info", ("report_error: %d", thd->is_error())); + + (void)unit->cleanup(); + + return true; +} + +bool Sql_cmd_dml::execute(THD *thd) +{ + lex = thd->lex; + bool res; + + SELECT_LEX_UNIT *unit = &lex->unit; + SELECT_LEX *select_lex= lex->first_select_lex(); + + if (!is_prepared()) + { + if (prepare(thd)) + goto err; + } + else + { + if (precheck(thd)) + goto err; + + MYSQL_DML_START(thd); + + if (open_tables_for_query(thd, lex->query_tables, &table_count, 0, + get_dml_prelocking_strategy())) + goto err; + } + + THD_STAGE_INFO(thd, stage_init); + + /* + Locking of tables is done after preparation but before optimization. + This allows to do better partition pruning and avoid locking unused + partitions. As a consequence, in such a case, prepare stage can rely only + on metadata about tables used and not data from them. + */ + if (!is_empty_query()) + { + if (lock_tables(thd, lex->query_tables, table_count, 0)) + goto err; + } + + unit->set_limit(select_lex); + + // Perform statement-specific execution + res = execute_inner(thd); + + if (res) + goto err; + + res= unit->cleanup(); + + // "unprepare" this object since unit->cleanup actually unprepares + unprepare(thd); + + THD_STAGE_INFO(thd, stage_end); + + MYSQL_DML_DONE(thd, res); + + return res; + +err: + DBUG_ASSERT(thd->is_error() || thd->killed); + MYSQL_DML_DONE(thd, 1); + THD_STAGE_INFO(thd, stage_end); + (void)unit->cleanup(); + + return thd->is_error(); +} + + +bool Sql_cmd_dml::execute_inner(THD *thd) +{ + SELECT_LEX_UNIT *unit = &lex->unit; + SELECT_LEX *select_lex= unit->first_select(); + JOIN *join= select_lex->join; + + if (join->optimize()) + goto err; + + if (thd->lex->describe & DESCRIBE_EXTENDED) + { + join->conds_history= join->conds; + join->having_history= (join->having?join->having:join->tmp_having); + } + + if (unlikely(thd->is_error())) + goto err; + + join->exec(); + + if (thd->lex->describe & DESCRIBE_EXTENDED) + { + select_lex->where= join->conds_history; + select_lex->having= join->having_history; + } + +err: + return join->error; +} + /** @} (end of group Query_Optimizer) diff --git a/sql/sql_update.cc b/sql/sql_update.cc index 74ed078..9720123 100644 --- a/sql/sql_update.cc +++ b/sql/sql_update.cc @@ -197,22 +197,11 @@ static bool check_fields(THD *thd, TABLE_LIST *table, List<Item> &items, return true; } - DBUG_ASSERT(thd->lex->sql_command == SQLCOM_UPDATE); - for (List_iterator_fast<Item> it(items); (item=it++);) - { - Field *f= item->field_for_view_update()->field; - vers_select_conds_t &period= table->period_conditions; - if (period.field_start->field == f || period.field_end->field == f) - { - my_error(ER_PERIOD_COLUMNS_UPDATED, MYF(0), - item->name.str, period.name.str); - return true; - } - } } return FALSE; } + bool TABLE::vers_check_update(List<Item> &items) { List_iterator<Item> it(items); @@ -339,36 +328,19 @@ int cut_fields_for_portion_of_time(THD *thd, TABLE *table, return res; } -/* - Process usual UPDATE - - SYNOPSIS - mysql_update() - thd thread handler - fields fields for update - values values of fields for update - conds WHERE clause expression - order_num number of elemen in ORDER BY clause - order ORDER BY clause list - limit limit clause - - RETURN - 0 - OK - 2 - privilege check and openning table passed, but we need to convert to - multi-update because of view substitution - 1 - error -*/ -int mysql_update(THD *thd, - TABLE_LIST *table_list, - List<Item> &fields, - List<Item> &values, - COND *conds, - uint order_num, ORDER *order, - ha_rows limit, - bool ignore, - ha_rows *found_return, ha_rows *updated_return) +bool Sql_cmd_update::update_single_table(THD *thd) { + SELECT_LEX_UNIT *unit = &lex->unit; + SELECT_LEX *select_lex= unit->first_select(); + TABLE_LIST *const table_list = select_lex->get_table_list(); + List<Item> *fields= &select_lex->item_list; + List<Item> *values= &lex->value_list; + COND *conds= select_lex->where_cond_after_prepare; + ORDER *order= select_lex->order_list.first; + ha_rows limit= unit->lim.get_select_limit(); + bool ignore= lex->ignore; + bool using_limit= limit != HA_POS_ERROR; bool safe_update= (thd->variables.option_bits & OPTION_SAFE_UPDATES) && !thd->lex->describe; @@ -380,76 +352,39 @@ int mysql_update(THD *thd, ha_rows dup_key_found; bool need_sort= TRUE; bool reverse= FALSE; -#ifndef NO_EMBEDDED_ACCESS_CHECKS - privilege_t want_privilege(NO_ACL); -#endif - uint table_count= 0; ha_rows updated, updated_or_same, found; key_map old_covering_keys; TABLE *table; SQL_SELECT *select= NULL; SORT_INFO *file_sort= 0; READ_RECORD info; - SELECT_LEX *select_lex= thd->lex->first_select_lex(); ulonglong id; List<Item> all_fields; killed_state killed_status= NOT_KILLED; bool has_triggers, binlog_is_row, do_direct_update= FALSE; Update_plan query_plan(thd->mem_root); Explain_update *explain; - TABLE_LIST *update_source_table; query_plan.index= MAX_KEY; query_plan.using_filesort= FALSE; // For System Versioning (may need to insert new fields to a table). ha_rows rows_inserted= 0; - DBUG_ENTER("mysql_update"); + DBUG_ENTER("Sql_cmd_update::update_single_table"); + THD_STAGE_INFO(thd, stage_init_update); create_explain_query(thd->lex, thd->mem_root); - if (open_tables(thd, &table_list, &table_count, 0)) - DBUG_RETURN(1); - - /* Prepare views so they are handled correctly */ - if (mysql_handle_derived(thd->lex, DT_INIT)) - DBUG_RETURN(1); - - if (table_list->has_period() && table_list->is_view_or_derived()) - { - my_error(ER_IT_IS_A_VIEW, MYF(0), table_list->table_name.str); - DBUG_RETURN(TRUE); - } - - if (((update_source_table=unique_table(thd, table_list, - table_list->next_global, 0)) || - table_list->is_multitable())) - { - DBUG_ASSERT(update_source_table || table_list->view != 0); - DBUG_PRINT("info", ("Switch to multi-update")); - /* pass counter value */ - thd->lex->table_count= table_count; - if (thd->lex->period_conditions.is_set()) - { - my_error(ER_NOT_SUPPORTED_YET, MYF(0), - "updating and querying the same temporal periods table"); - - DBUG_RETURN(1); - } - - /* convert to multiupdate */ - DBUG_RETURN(2); - } - if (lock_tables(thd, table_list, table_count, 0)) - DBUG_RETURN(1); - (void) read_statistics_for_tables_if_needed(thd, table_list); + thd->table_map_for_update= 0; - THD_STAGE_INFO(thd, stage_init_update); if (table_list->handle_derived(thd->lex, DT_MERGE_FOR_INSERT)) DBUG_RETURN(1); if (table_list->handle_derived(thd->lex, DT_PREPARE)) DBUG_RETURN(1); + if (setup_ftfuncs(select_lex)) + DBUG_RETURN(-1); + table= table_list->table; if (!table_list->single_table_updatable()) @@ -458,85 +393,26 @@ int mysql_update(THD *thd, DBUG_RETURN(1); } - /* Calculate "table->covering_keys" based on the WHERE */ - table->covering_keys= table->s->keys_in_use; table->opt_range_keys.clear_all(); query_plan.select_lex= thd->lex->first_select_lex(); query_plan.table= table; -#ifndef NO_EMBEDDED_ACCESS_CHECKS - /* Force privilege re-checking for views after they have been opened. */ - want_privilege= (table_list->view ? UPDATE_ACL : - table_list->grant.want_privilege); -#endif thd->lex->promote_select_describe_flag_if_needed(); - if (mysql_prepare_update(thd, table_list, &conds, order_num, order)) - DBUG_RETURN(1); - - if (table_list->has_period()) - { - if (!table_list->period_conditions.start.item->const_item() - || !table_list->period_conditions.end.item->const_item()) - { - my_error(ER_NOT_CONSTANT_EXPRESSION, MYF(0), "FOR PORTION OF"); - DBUG_RETURN(true); - } - table->no_cache= true; - } - old_covering_keys= table->covering_keys; // Keys used in WHERE - /* Check the fields we are going to modify */ -#ifndef NO_EMBEDDED_ACCESS_CHECKS - table_list->grant.want_privilege= table->grant.want_privilege= want_privilege; - table_list->register_want_access(want_privilege); -#endif - /* 'Unfix' fields to allow correct marking by the setup_fields function. */ - if (table_list->is_view()) - unfix_fields(fields); - if (setup_fields_with_no_wrap(thd, Ref_ptr_array(), - fields, MARK_COLUMNS_WRITE, 0, 0)) - DBUG_RETURN(1); /* purecov: inspected */ - if (check_fields(thd, table_list, fields, table_list->view)) - { - DBUG_RETURN(1); - } - bool has_vers_fields= table->vers_check_update(fields); - if (check_key_in_view(thd, table_list)) - { - my_error(ER_NON_UPDATABLE_TABLE, MYF(0), table_list->alias.str, "UPDATE"); - DBUG_RETURN(1); - } + bool has_vers_fields= table->vers_check_update(*fields); if (table->default_field) table->mark_default_fields_for_write(false); -#ifndef NO_EMBEDDED_ACCESS_CHECKS - /* Check values */ - table_list->grant.want_privilege= table->grant.want_privilege= - (SELECT_ACL & ~table->grant.privilege); -#endif - if (setup_fields(thd, Ref_ptr_array(), values, MARK_COLUMNS_READ, 0, NULL, 0)) - { - free_underlaid_joins(thd, select_lex); - DBUG_RETURN(1); /* purecov: inspected */ - } - - if (check_unique_table(thd, table_list)) - DBUG_RETURN(TRUE); - - switch_to_nullable_trigger_fields(fields, table); - switch_to_nullable_trigger_fields(values, table); + switch_to_nullable_trigger_fields(*fields, table); + switch_to_nullable_trigger_fields(*values, table); /* Apply the IN=>EXISTS transformation to all subqueries and optimize them */ if (select_lex->optimize_unflattened_subqueries(false)) DBUG_RETURN(TRUE); - if (select_lex->inner_refs_list.elements && - fix_inner_refs(thd, all_fields, select_lex, select_lex->ref_pointer_array)) - DBUG_RETURN(1); - if (conds) { Item::cond_result cond_value; @@ -776,9 +652,9 @@ int mysql_update(THD *thd, } if (use_direct_update && - !table->file->info_push(INFO_KIND_UPDATE_FIELDS, &fields) && - !table->file->info_push(INFO_KIND_UPDATE_VALUES, &values) && - !table->file->direct_update_rows_init(&fields)) + !table->file->info_push(INFO_KIND_UPDATE_FIELDS, fields) && + !table->file->info_push(INFO_KIND_UPDATE_VALUES, values) && + !table->file->direct_update_rows_init(fields)) { do_direct_update= TRUE; @@ -1027,7 +903,7 @@ int mysql_update(THD *thd, cut_fields_for_portion_of_time(thd, table, table_list->period_conditions); - if (fill_record_n_invoke_before_triggers(thd, table, fields, values, 0, + if (fill_record_n_invoke_before_triggers(thd, table, *fields, *values, 0, TRG_EVENT_UPDATE)) break; /* purecov: inspected */ @@ -1360,9 +1236,9 @@ int mysql_update(THD *thd, thd->lex->current_select->save_leaf_tables(thd); thd->lex->current_select->first_cond_optimization= 0; } - *found_return= found; - *updated_return= updated; - + ((multi_update *)result)->set_found(found); + ((multi_update *)result)->set_updated(updated); + if (unlikely(thd->lex->analyze_stmt)) goto emit_explain_and_leave; @@ -1394,75 +1270,6 @@ int mysql_update(THD *thd, DBUG_RETURN((err2 || thd->is_error()) ? 1 : 0); } -/* - Prepare items in UPDATE statement - - SYNOPSIS - mysql_prepare_update() - thd - thread handler - table_list - global/local table list - conds - conditions - order_num - number of ORDER BY list entries - order - ORDER BY clause list - - RETURN VALUE - FALSE OK - TRUE error -*/ -bool mysql_prepare_update(THD *thd, TABLE_LIST *table_list, - Item **conds, uint order_num, ORDER *order) -{ - Item *fake_conds= 0; -#ifndef NO_EMBEDDED_ACCESS_CHECKS - TABLE *table= table_list->table; -#endif - List<Item> all_fields; - SELECT_LEX *select_lex= thd->lex->first_select_lex(); - DBUG_ENTER("mysql_prepare_update"); - -#ifndef NO_EMBEDDED_ACCESS_CHECKS - table_list->grant.want_privilege= table->grant.want_privilege= - (SELECT_ACL & ~table->grant.privilege); - table_list->register_want_access(SELECT_ACL); -#endif - - thd->lex->allow_sum_func.clear_all(); - - if (table_list->has_period() && - select_lex->period_setup_conds(thd, table_list)) - DBUG_RETURN(true); - - DBUG_ASSERT(table_list->table); - // conds could be cached from previous SP call - DBUG_ASSERT(!table_list->vers_conditions.need_setup() || - !*conds || thd->stmt_arena->is_stmt_execute()); - if (select_lex->vers_setup_conds(thd, table_list)) - DBUG_RETURN(TRUE); - - *conds= select_lex->where; - - /* - We do not call DT_MERGE_FOR_INSERT because it has no sense for simple - (not multi-) update - */ - if (mysql_handle_derived(thd->lex, DT_PREPARE)) - DBUG_RETURN(TRUE); - - if (setup_tables_and_check_access(thd, &select_lex->context, - &select_lex->top_join_list, table_list, - select_lex->leaf_tables, - FALSE, UPDATE_ACL, SELECT_ACL, TRUE) || - setup_conds(thd, table_list, select_lex->leaf_tables, conds) || - select_lex->setup_ref_array(thd, order_num) || - setup_order(thd, select_lex->ref_pointer_array, - table_list, all_fields, all_fields, order) || - setup_ftfuncs(select_lex)) - DBUG_RETURN(TRUE); - - - select_lex->fix_prepare_information(thd, conds, &fake_conds); - DBUG_RETURN(FALSE); -} /** Check that we are not using table that we are updating in a sub select @@ -1690,15 +1497,6 @@ static bool multi_update_check_table_access(THD *thd, TABLE_LIST *table, } -class Multiupdate_prelocking_strategy : public DML_prelocking_strategy -{ - bool done; - bool has_prelocking_list; -public: - void reset(THD *thd); - bool handle_end(THD *thd); -}; - void Multiupdate_prelocking_strategy::reset(THD *thd) { done= false; @@ -1728,7 +1526,13 @@ bool Multiupdate_prelocking_strategy::handle_end(THD *thd) mysql_handle_derived(lex, DT_PREPARE)) DBUG_RETURN(1); - /* + if (table_list->has_period() && table_list->is_view_or_derived()) + { + my_error(ER_IT_IS_A_VIEW, MYF(0), table_list->table_name.str); + DBUG_RETURN(TRUE); + } + + /* setup_tables() need for VIEWs. JOIN::prepare() will call setup_tables() second time, but this call will do nothing (there are check for second call in setup_tables()). @@ -1739,6 +1543,10 @@ bool Multiupdate_prelocking_strategy::handle_end(THD *thd) FALSE, UPDATE_ACL, SELECT_ACL, TRUE)) DBUG_RETURN(1); + if (table_list->has_period() && + select_lex->period_setup_conds(thd, table_list)) + DBUG_RETURN(true); + List<Item> *fields= &lex->first_select_lex()->item_list; if (setup_fields_with_no_wrap(thd, Ref_ptr_array(), *fields, MARK_COLUMNS_WRITE, 0, 0)) @@ -1847,153 +1655,6 @@ bool Multiupdate_prelocking_strategy::handle_end(THD *thd) DBUG_RETURN(0); } -/* - make update specific preparation and checks after opening tables - - SYNOPSIS - mysql_multi_update_prepare() - thd thread handler - - RETURN - FALSE OK - TRUE Error -*/ - -int mysql_multi_update_prepare(THD *thd) -{ - LEX *lex= thd->lex; - TABLE_LIST *table_list= lex->query_tables; - TABLE_LIST *tl; - Multiupdate_prelocking_strategy prelocking_strategy; - uint table_count= lex->table_count; - DBUG_ENTER("mysql_multi_update_prepare"); - - /* - Open tables and create derived ones, but do not lock and fill them yet. - - During prepare phase acquire only S metadata locks instead of SW locks to - keep prepare of multi-UPDATE compatible with concurrent LOCK TABLES WRITE - and global read lock. - - Don't evaluate any subqueries even if constant, because - tables aren't locked yet. - */ - lex->context_analysis_only|= CONTEXT_ANALYSIS_ONLY_DERIVED; - if (thd->lex->sql_command == SQLCOM_UPDATE_MULTI) - { - if (open_tables(thd, &table_list, &table_count, - thd->stmt_arena->is_stmt_prepare() ? MYSQL_OPEN_FORCE_SHARED_MDL : 0, - &prelocking_strategy)) - DBUG_RETURN(TRUE); - } - else - { - /* following need for prepared statements, to run next time multi-update */ - thd->lex->sql_command= SQLCOM_UPDATE_MULTI; - prelocking_strategy.reset(thd); - if (prelocking_strategy.handle_end(thd)) - DBUG_RETURN(TRUE); - } - - /* now lock and fill tables */ - if (!thd->stmt_arena->is_stmt_prepare() && - lock_tables(thd, table_list, table_count, 0)) - DBUG_RETURN(TRUE); - - lex->context_analysis_only&= ~CONTEXT_ANALYSIS_ONLY_DERIVED; - - (void) read_statistics_for_tables_if_needed(thd, table_list); - /* @todo: downgrade the metadata locks here. */ - - /* - Check that we are not using table that we are updating, but we should - skip all tables of UPDATE SELECT itself - */ - lex->first_select_lex()->exclude_from_table_unique_test= TRUE; - /* We only need SELECT privilege for columns in the values list */ - List_iterator<TABLE_LIST> ti(lex->first_select_lex()->leaf_tables); - while ((tl= ti++)) - { - if (tl->is_jtbm()) - continue; - TABLE *table= tl->table; - TABLE_LIST *tlist; - if (!(tlist= tl->top_table())->derived) - { - tlist->grant.want_privilege= - (SELECT_ACL & ~tlist->grant.privilege); - table->grant.want_privilege= (SELECT_ACL & ~table->grant.privilege); - } - DBUG_PRINT("info", ("table: %s want_privilege: %llx", tl->alias.str, - (longlong) table->grant.want_privilege)); - } - /* - Set exclude_from_table_unique_test value back to FALSE. It is needed for - further check in multi_update::prepare whether to use record cache. - */ - lex->first_select_lex()->exclude_from_table_unique_test= FALSE; - - if (lex->save_prep_leaf_tables()) - DBUG_RETURN(TRUE); - - DBUG_RETURN (FALSE); -} - - -/* - Setup multi-update handling and call SELECT to do the join -*/ - -bool mysql_multi_update(THD *thd, TABLE_LIST *table_list, List<Item> *fields, - List<Item> *values, COND *conds, ulonglong options, - enum enum_duplicates handle_duplicates, - bool ignore, SELECT_LEX_UNIT *unit, - SELECT_LEX *select_lex, multi_update **result) -{ - bool res; - DBUG_ENTER("mysql_multi_update"); - - if (!(*result= new (thd->mem_root) multi_update(thd, table_list, - &thd->lex->first_select_lex()->leaf_tables, - fields, values, handle_duplicates, ignore))) - { - DBUG_RETURN(TRUE); - } - - if ((*result)->init(thd)) - DBUG_RETURN(1); - - thd->abort_on_warning= !ignore && thd->is_strict_mode(); - List<Item> total_list; - - if (setup_tables(thd, &select_lex->context, &select_lex->top_join_list, - table_list, select_lex->leaf_tables, FALSE, FALSE)) - DBUG_RETURN(1); - - if (select_lex->vers_setup_conds(thd, table_list)) - DBUG_RETURN(1); - - res= mysql_select(thd, - table_list, total_list, conds, - select_lex->order_list.elements, - select_lex->order_list.first, NULL, NULL, NULL, - options | SELECT_NO_JOIN_CACHE | SELECT_NO_UNLOCK | - OPTION_SETUP_TABLES_DONE, - *result, unit, select_lex); - - DBUG_PRINT("info",("res: %d report_error: %d", res, (int) thd->is_error())); - res|= thd->is_error(); - if (unlikely(res)) - (*result)->abort_result_set(); - else - { - if (thd->lex->describe || thd->lex->analyze_stmt) - res= thd->lex->explain->send_explain(thd); - } - thd->abort_on_warning= 0; - DBUG_RETURN(res); -} - multi_update::multi_update(THD *thd_arg, TABLE_LIST *table_list, List<TABLE_LIST> *leaves_list, @@ -2029,6 +1690,19 @@ bool multi_update::init(THD *thd) } +bool multi_update::init_for_single_table(THD *thd) +{ + List_iterator_fast<TABLE_LIST> li(*leaves); + TABLE_LIST *tbl; + while ((tbl =li++)) + { + if (updated_leaves.push_back(tbl, thd->mem_root)) + return true; + } + return false; +} + + /* Connect fields with tables and create list of tables that are updated */ @@ -2102,7 +1776,8 @@ int multi_update::prepare(List<Item> &not_used_values, { table->read_set= &table->def_read_set; bitmap_union(table->read_set, &table->tmp_set); - table->file->prepare_for_insert(1); + if (!(thd->lex->context_analysis_only & CONTEXT_ANALYSIS_ONLY_PREPARE)) + table->file->prepare_for_insert(1); } } if (unlikely(error)) @@ -3123,3 +2798,212 @@ bool multi_update::send_eof() } DBUG_RETURN(FALSE); } + + +bool Sql_cmd_update::precheck(THD *thd) +{ + if (!multitable) + { + if (update_precheck(thd, lex->query_tables)) + return true; + } + else + { + if (multi_update_precheck(thd, lex->query_tables)) + return true; + } + + WSREP_SYNC_WAIT(thd, WSREP_SYNC_WAIT_BEFORE_UPDATE_DELETE); + + return false; + +#ifdef WITH_WSREP +wsrep_error_label: +#endif + return true; +} + + +bool Sql_cmd_update::prepare_inner(THD *thd) +{ + JOIN *join; + int err= 0; + SELECT_LEX *const select_lex = thd->lex->first_select_lex(); + TABLE_LIST *const table_list = select_lex->get_table_list(); + ulonglong select_options= select_lex->options; + bool free_join= 1; + DBUG_ENTER("Sql_cmd_update::prepare_inner"); + + (void) read_statistics_for_tables_if_needed(thd, table_list); + + THD_STAGE_INFO(thd, stage_init_update); + + if (!multitable) + { + if (mysql_handle_derived(lex, DT_INIT)) + DBUG_RETURN(TRUE); + } + + if (table_list->has_period() && table_list->is_view_or_derived()) + { + my_error(ER_IT_IS_A_VIEW, MYF(0), table_list->table_name.str); + DBUG_RETURN(TRUE); + } + + if (!multitable) + { + TABLE_LIST *update_source_table= 0; + + if (((update_source_table=unique_table(thd, table_list, + table_list->next_global, 0)) || + table_list->is_multitable())) + { + DBUG_ASSERT(update_source_table || table_list->view != 0); + if (thd->lex->period_conditions.is_set()) + { + my_error(ER_NOT_SUPPORTED_YET, MYF(0), + "updating and querying the same temporal periods table"); + DBUG_RETURN(TRUE); + } + multitable= true; + } + } + + if(!multitable) + { + if (table_list->is_view_or_derived() && + select_lex->leaf_tables.elements > 1) + multitable = true; + } + + if (!multitable) + { + if (lex->ignore) + lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_UPDATE_IGNORE); + } + + if (!(result= new (thd->mem_root) multi_update(thd, table_list, + &select_lex->leaf_tables, + &select_lex->item_list, + &lex->value_list, + lex->duplicates, + lex->ignore))) + { + DBUG_RETURN(TRUE); + } + + if (((multi_update *)result)->init(thd)) + DBUG_RETURN(TRUE); + + if (setup_tables(thd, &select_lex->context, &select_lex->top_join_list, + table_list, select_lex->leaf_tables, false, false)) + DBUG_RETURN(TRUE); + + if (select_lex->vers_setup_conds(thd, table_list)) + DBUG_RETURN(TRUE); + + { + if (thd->lex->describe) + select_options|= SELECT_DESCRIBE; + + /* + When in EXPLAIN, delay deleting the joins so that they are still + available when we're producing EXPLAIN EXTENDED warning text. + */ + if (select_options & SELECT_DESCRIBE) + free_join= 0; + + select_options|= + SELECT_NO_JOIN_CACHE | SELECT_NO_UNLOCK | OPTION_SETUP_TABLES_DONE; + + if (!(join= new (thd->mem_root) JOIN(thd, select_lex->item_list, + select_options, result))) + DBUG_RETURN(TRUE); + THD_STAGE_INFO(thd, stage_init); + select_lex->join= join; + thd->lex->used_tables=0; + select_lex->item_list_usage= MARK_COLUMNS_WRITE; + if ((err= join->prepare(table_list, select_lex->where, + select_lex->order_list.elements, + select_lex->order_list.first, + false, NULL, NULL, NULL, + select_lex, &lex->unit))) + { + goto err; + } + + } + + if (table_list->has_period()) + { + Item *item; + for (List_iterator_fast<Item> it(select_lex->item_list); (item=it++);) + { + Field *f= item->field_for_view_update()->field; + vers_select_conds_t &period= table_list->period_conditions; + if (period.field_start->field == f || period.field_end->field == f) + { + my_error(ER_PERIOD_COLUMNS_UPDATED, MYF(0), + item->name.str, period.name.str); + DBUG_RETURN(true); + } + } + + if (!table_list->period_conditions.start.item->const_item() + || !table_list->period_conditions.end.item->const_item()) + { + my_error(ER_NOT_CONSTANT_EXPRESSION, MYF(0), "FOR PORTION OF"); + DBUG_RETURN(true); + } + table_list->table->no_cache= true; + } + + + free_join= false; + +err: + + if (free_join) + { + THD_STAGE_INFO(thd, stage_end); + err|= (int)(select_lex->cleanup()); + DBUG_RETURN(err || thd->is_error()); + } + DBUG_RETURN(err); + +} + + +bool Sql_cmd_update::execute_inner(THD *thd) +{ + bool res= 0; + + thd->get_stmt_da()->reset_current_row_for_warning(1); + if (!multitable) + res= update_single_table(thd); + else + { + thd->abort_on_warning= !thd->lex->ignore && thd->is_strict_mode(); + res= Sql_cmd_dml::execute_inner(thd); + } + + res|= thd->is_error(); + if (multitable) + { + if (unlikely(res)) + result->abort_result_set(); + else + { + if (thd->lex->describe || thd->lex->analyze_stmt) + res= thd->lex->explain->send_explain(thd); + } + } + + if (result) + { + res= false; + delete result; + } + + return res; +} diff --git a/sql/sql_update.h b/sql/sql_update.h index 65e44d1..cf33461 100644 --- a/sql/sql_update.h +++ b/sql/sql_update.h @@ -17,6 +17,8 @@ #define SQL_UPDATE_INCLUDED #include "sql_class.h" /* enum_duplicates */ +#include "sql_cmd.h" // Sql_cmd_dml +#include "sql_base.h" class Item; struct TABLE_LIST; @@ -25,20 +27,46 @@ class THD; typedef class st_select_lex SELECT_LEX; typedef class st_select_lex_unit SELECT_LEX_UNIT; -bool mysql_prepare_update(THD *thd, TABLE_LIST *table_list, - Item **conds, uint order_num, ORDER *order); bool check_unique_table(THD *thd, TABLE_LIST *table_list); -int mysql_update(THD *thd,TABLE_LIST *tables,List<Item> &fields, - List<Item> &values,COND *conds, - uint order_num, ORDER *order, ha_rows limit, - bool ignore, ha_rows *found_return, ha_rows *updated_return); -bool mysql_multi_update(THD *thd, TABLE_LIST *table_list, - List<Item> *fields, List<Item> *values, - COND *conds, ulonglong options, - enum enum_duplicates handle_duplicates, bool ignore, - SELECT_LEX_UNIT *unit, SELECT_LEX *select_lex, - multi_update **result); bool records_are_comparable(const TABLE *table); bool compare_record(const TABLE *table); + +class Sql_cmd_update final : public Sql_cmd_dml +{ +public: + Sql_cmd_update(bool multitable_arg) + : multitable(multitable_arg) + { } + + enum_sql_command sql_command_code() const override + { + return multitable ? SQLCOM_UPDATE_MULTI : SQLCOM_UPDATE; + } + + DML_prelocking_strategy *get_dml_prelocking_strategy() + { + return &multiupdate_prelocking_strategy; + } + +protected: + bool precheck(THD *thd) override; + + bool prepare_inner(THD *thd) override; + + bool execute_inner(THD *thd) override; + +private: + bool update_single_table(THD *thd); + + bool multitable; + + DML_prelocking_strategy dml_prelocking_strategy; + Multiupdate_prelocking_strategy multiupdate_prelocking_strategy; + + public: + List<Item> *update_value_list; + +}; + #endif /* SQL_UPDATE_INCLUDED */ diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 5416cec..a587a37 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -69,6 +69,8 @@ #include "my_base.h" #include "sql_type_json.h" #include "json_table.h" +#include "sql_update.h" +#include "sql_delete.h" /* this is to get the bison compilation windows warnings out */ #ifdef _MSC_VER @@ -1682,7 +1684,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize); opt_mi_check_type opt_to mi_check_types table_to_table_list table_to_table opt_table_list opt_as handler_rkey_function handler_read_or_scan - single_multi table_wild_list table_wild_one opt_wild + single_multi opt_wild opt_and select_var_list select_var_list_init help opt_extended_describe shutdown @@ -13242,9 +13244,14 @@ update: opt_low_priority opt_ignore update_table_list SET update_list { + bool is_multiupdate= false; + LEX *lex= Lex; SELECT_LEX *slex= Lex->first_select_lex(); if (slex->table_list.elements > 1) + { Lex->sql_command= SQLCOM_UPDATE_MULTI; + is_multiupdate= true; + } else if (slex->get_table_list()->derived) { /* it is single table update and it is update of derived table */ @@ -13252,10 +13259,13 @@ update: slex->get_table_list()->alias.str, "UPDATE"); MYSQL_YYABORT; } + if (!(lex->m_sql_cmd= + new (thd->mem_root) Sql_cmd_update(is_multiupdate))) + MYSQL_YYABORT; /* In case of multi-update setting write lock for all tables may - be too pessimistic. We will decrease lock level if possible in - mysql_multi_update(). + be too pessimistic. We will decrease lock level if possible + later while processing the statement. */ slex->set_lock_for_tables($3, slex->table_list.elements == 1, false); } @@ -13312,12 +13322,11 @@ delete: DELETE_SYM { LEX *lex= Lex; - lex->sql_command= SQLCOM_DELETE; YYPS->m_lock_type= TL_WRITE_DEFAULT; YYPS->m_mdl_type= MDL_SHARED_WRITE; if (Lex->main_select_push()) MYSQL_YYABORT; - mysql_init_select(lex); + mysql_init_delete(lex); lex->ignore= 0; lex->first_select_lex()->order_list.empty(); } @@ -13343,8 +13352,13 @@ delete_part2: opt_delete_options single_multi {} | HISTORY_SYM delete_single_table opt_delete_system_time { - Lex->last_table()->vers_conditions= Lex->vers_conditions; - Lex->pop_select(); //main select + LEX *lex= Lex; + lex->last_table()->vers_conditions= lex->vers_conditions; + lex->pop_select(); //main select + lex->sql_command= SQLCOM_DELETE; + if (!(lex->m_sql_cmd= + new (thd->mem_root) Sql_cmd_delete(false))) + MYSQL_YYABORT; } ; @@ -13378,12 +13392,22 @@ single_multi: delete_limit_clause opt_returning { + LEX *lex= Lex; if ($3) Select->order_list= *($3); - Lex->pop_select(); //main select + lex->pop_select(); //main select + lex->sql_command= SQLCOM_DELETE; + if (!(lex->m_sql_cmd= + new (thd->mem_root) Sql_cmd_delete(false))) + MYSQL_YYABORT; } - | table_wild_list + | table_alias_ref_list { + LEX *lex= Lex; + lex->sql_command= SQLCOM_DELETE_MULTI; + if (!(lex->m_sql_cmd= + new (thd->mem_root) Sql_cmd_delete(true))) + MYSQL_YYABORT; mysql_init_multi_delete(Lex); YYPS->m_lock_type= TL_READ_DEFAULT; YYPS->m_mdl_type= MDL_SHARED_READ; @@ -13395,6 +13419,11 @@ single_multi: } stmt_end {} | FROM table_alias_ref_list { + LEX *lex= Lex; + lex->sql_command= SQLCOM_DELETE_MULTI; + if (!(lex->m_sql_cmd= + new (thd->mem_root) Sql_cmd_delete(true))) + MYSQL_YYABORT; mysql_init_multi_delete(Lex); YYPS->m_lock_type= TL_READ_DEFAULT; YYPS->m_mdl_type= MDL_SHARED_READ; @@ -13430,44 +13459,6 @@ opt_returning: } ; -table_wild_list: - table_wild_one - | table_wild_list ',' table_wild_one - ; - -table_wild_one: - ident opt_wild - { - Table_ident *ti= new (thd->mem_root) Table_ident(&$1); - if (unlikely(ti == NULL)) - MYSQL_YYABORT; - if (unlikely(!Select-> - add_table_to_list(thd, - ti, - NULL, - (TL_OPTION_UPDATING | - TL_OPTION_ALIAS), - YYPS->m_lock_type, - YYPS->m_mdl_type))) - MYSQL_YYABORT; - } - | ident '.' ident opt_wild - { - Table_ident *ti= new (thd->mem_root) Table_ident(thd, &$1, &$3, 0); - if (unlikely(ti == NULL)) - MYSQL_YYABORT; - if (unlikely(!Select-> - add_table_to_list(thd, - ti, - NULL, - (TL_OPTION_UPDATING | - TL_OPTION_ALIAS), - YYPS->m_lock_type, - YYPS->m_mdl_type))) - MYSQL_YYABORT; - } - ; - opt_wild: /* empty */ {} | '.' '*' {} diff --git a/sql/table.h b/sql/table.h index 30517f8..5f8d299 100644 --- a/sql/table.h +++ b/sql/table.h @@ -2338,6 +2338,7 @@ struct TABLE_LIST */ select_unit *derived_result; /* Stub used for materialized derived tables. */ + bool delete_while_scanning; table_map map; /* ID bit of table (1,2,4,8,16...) */ table_map get_map() { diff --git a/storage/spider/mysql-test/spider/r/error_row_number.result b/storage/spider/mysql-test/spider/r/error_row_number.result index cc2b548..ad095fe 100644 --- a/storage/spider/mysql-test/spider/r/error_row_number.result +++ b/storage/spider/mysql-test/spider/r/error_row_number.result @@ -29,7 +29,7 @@ ERROR 23000: Duplicate entry '13' for key 'PRIMARY' get diagnostics condition 1 @n = row_number; select @n; @n -0 +1 drop table spd; connection child2_1; drop database auto_test_remote;
1 0
0 0
[Commits] d478191: MDEV-27159 Re-design the upper level of handling DML commands
by IgorBabaev 14 Jun '22

14 Jun '22
revision-id: d47819135ff22e5311969cdfd12153aa46b4d737 (mariadb-10.9.1-17-gd478191) parent(s): f929fa45b66e4a98c815bac3a2778efb97989b98 author: Igor Babaev committer: Igor Babaev timestamp: 2022-06-13 19:48:56 -0700 message: MDEV-27159 Re-design the upper level of handling DML commands This patch allows to execute only single-table and multi-table UPDATE and DELETE statements using the method Sql_cmd_dml::execute(). The code that handles INSERT statements has not been touched. --- mysql-test/main/analyze_stmt_privileges2.result | 9 +- mysql-test/main/analyze_stmt_privileges2.test | 15 +- .../main/myisam_explain_non_select_all.result | 9 +- mysql-test/main/mysqlbinlog_row_minimal.test | 1 - mysql-test/main/opt_trace.result | 14 +- mysql-test/main/opt_trace_security.result | 5 - mysql-test/main/opt_trace_security.test | 6 +- mysql-test/main/order_by.result | 8 +- mysql-test/main/update.result | 2 +- mysql-test/main/update_use_source.result | 7 +- mysql-test/main/update_use_source.test | 2 +- mysql-test/main/view_grant.result | 1 + mysql-test/main/view_grant.test | 1 + mysql-test/suite/funcs_1/r/is_collations.result | 2 +- mysql-test/suite/funcs_1/t/is_collations.test | 2 +- mysql-test/suite/period/r/update.result | 2 +- mysql-test/suite/period/t/update.test | 2 +- sql/ha_partition.cc | 6 +- sql/handler.h | 5 +- sql/opt_range.cc | 2 +- sql/opt_subselect.cc | 2 + sql/opt_trace.cc | 3 +- sql/sql_base.cc | 35 +- sql/sql_base.h | 15 + sql/sql_class.h | 3 + sql/sql_cmd.h | 158 ++++- sql/sql_delete.cc | 617 +++++++++++--------- sql/sql_delete.h | 37 +- sql/sql_lex.cc | 39 +- sql/sql_lex.h | 26 + sql/sql_parse.cc | 261 +-------- sql/sql_parse.h | 1 + sql/sql_prepare.cc | 245 +------- sql/sql_select.cc | 209 ++++++- sql/sql_update.cc | 644 +++++++++------------ sql/sql_update.h | 52 +- sql/sql_yacc.yy | 85 ++- sql/table.h | 1 + .../mysql-test/spider/r/error_row_number.result | 2 +- 39 files changed, 1244 insertions(+), 1292 deletions(-) diff --git a/mysql-test/main/analyze_stmt_privileges2.result b/mysql-test/main/analyze_stmt_privileges2.result index f269aaf..d40dd63 100644 --- a/mysql-test/main/analyze_stmt_privileges2.result +++ b/mysql-test/main/analyze_stmt_privileges2.result @@ -3034,6 +3034,7 @@ ERROR HY000: ANALYZE/EXPLAIN/SHOW can not be issued; lacking privileges for unde ANALYZE UPDATE v1 SET a = 10; ERROR HY000: ANALYZE/EXPLAIN/SHOW can not be issued; lacking privileges for underlying table UPDATE v1 SET a = a + 1; +ERROR 42000: SELECT command denied to user 'privtest'@'localhost' for column 'a' in table 'v1' EXPLAIN UPDATE v1 SET a = a + 1; ERROR HY000: ANALYZE/EXPLAIN/SHOW can not be issued; lacking privileges for underlying table ANALYZE UPDATE v1 SET a = a + 1; @@ -4767,6 +4768,7 @@ ERROR HY000: ANALYZE/EXPLAIN/SHOW can not be issued; lacking privileges for unde ANALYZE UPDATE v2 SET a = 10; ERROR HY000: ANALYZE/EXPLAIN/SHOW can not be issued; lacking privileges for underlying table UPDATE v2 SET a = a + 1; +ERROR 42000: SELECT command denied to user 'privtest'@'localhost' for column 'a' in table 'v2' EXPLAIN UPDATE v2 SET a = a + 1; ERROR HY000: ANALYZE/EXPLAIN/SHOW can not be issued; lacking privileges for underlying table ANALYZE UPDATE v2 SET a = a + 1; @@ -4865,12 +4867,11 @@ ANALYZE UPDATE v2 SET a = 10; id select_type table type possible_keys key key_len ref rows r_rows filtered r_filtered Extra 1 SIMPLE t1 ALL NULL NULL NULL NULL 43 43.00 100.00 6.98 Using where UPDATE v2 SET a = a + 1; +ERROR 42000: SELECT command denied to user 'privtest'@'localhost' for column 'a' in table 'v2' EXPLAIN UPDATE v2 SET a = a + 1; -id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE t1 ALL NULL NULL NULL NULL 43 Using where +ERROR 42000: SELECT command denied to user 'privtest'@'localhost' for column 'a' in table 'v2' ANALYZE UPDATE v2 SET a = a + 1; -id select_type table type possible_keys key key_len ref rows r_rows filtered r_filtered Extra -1 SIMPLE t1 ALL NULL NULL NULL NULL 43 43.00 100.00 6.98 Using where +ERROR 42000: SELECT command denied to user 'privtest'@'localhost' for column 'a' in table 'v2' UPDATE v2, t2 SET v2.a = v2.a + 1 WHERE v2.a = t2.a; ERROR 42000: SELECT command denied to user 'privtest'@'localhost' for column 'a' in table 'v2' EXPLAIN UPDATE v2, t2 SET v2.a = v2.a + 1 WHERE v2.a = t2.a; diff --git a/mysql-test/main/analyze_stmt_privileges2.test b/mysql-test/main/analyze_stmt_privileges2.test index a0f1f49..8b011c2 100644 --- a/mysql-test/main/analyze_stmt_privileges2.test +++ b/mysql-test/main/analyze_stmt_privileges2.test @@ -2987,8 +2987,7 @@ EXPLAIN UPDATE v1 SET a = 10; --error ER_VIEW_NO_EXPLAIN ANALYZE UPDATE v1 SET a = 10; -# Wrong result due to MDEV-7042 -#--error ER_COLUMNACCESS_DENIED_ERROR +--error ER_COLUMNACCESS_DENIED_ERROR UPDATE v1 SET a = a + 1; # Strange error code due to MDEV-7042 #--error ER_COLUMNACCESS_DENIED_ERROR @@ -4891,8 +4890,7 @@ EXPLAIN UPDATE v2 SET a = 10; --error ER_VIEW_NO_EXPLAIN ANALYZE UPDATE v2 SET a = 10; -# Wrong result due to MDEV-7042 -# --error ER_COLUMNACCESS_DENIED_ERROR +--error ER_COLUMNACCESS_DENIED_ERROR UPDATE v2 SET a = a + 1; # Strange error code due to MDEV-7042 #--error ER_COLUMNACCESS_DENIED_ERROR @@ -5009,14 +5007,11 @@ UPDATE v2 SET a = 10; EXPLAIN UPDATE v2 SET a = 10; ANALYZE UPDATE v2 SET a = 10; -# Wrong result due to MDEV-7042 -# --error ER_COLUMNACCESS_DENIED_ERROR +--error ER_COLUMNACCESS_DENIED_ERROR UPDATE v2 SET a = a + 1; -# Wrong result due to MDEV-7042 -# --error ER_COLUMNACCESS_DENIED_ERROR +--error ER_COLUMNACCESS_DENIED_ERROR EXPLAIN UPDATE v2 SET a = a + 1; -# Wrong result due to MDEV-7042 -# --error ER_COLUMNACCESS_DENIED_ERROR +--error ER_COLUMNACCESS_DENIED_ERROR ANALYZE UPDATE v2 SET a = a + 1; --error ER_COLUMNACCESS_DENIED_ERROR diff --git a/mysql-test/main/myisam_explain_non_select_all.result b/mysql-test/main/myisam_explain_non_select_all.result index 2ff966f..20b769b 100644 --- a/mysql-test/main/myisam_explain_non_select_all.result +++ b/mysql-test/main/myisam_explain_non_select_all.result @@ -240,18 +240,17 @@ Warnings: Warning 1287 '<select expression> INTO <destination>;' is deprecated and will be removed in a future release. Please use 'SELECT <select list> INTO <destination> FROM...' instead EXPLAIN UPDATE t1 SET a = 10 WHERE 1 IN (SELECT 1 FROM t2 WHERE t2.b < 3); id select_type table type possible_keys key key_len ref rows Extra -1 PRIMARY t1 ALL NULL NULL NULL NULL 3 +1 PRIMARY t1 ALL NULL NULL NULL NULL 3 Using where 2 SUBQUERY t2 ALL NULL NULL NULL NULL 3 Using where FLUSH STATUS; FLUSH TABLES; EXPLAIN EXTENDED UPDATE t1 SET a = 10 WHERE 1 IN (SELECT 1 FROM t2 WHERE t2.b < 3); id select_type table type possible_keys key key_len ref rows filtered Extra -1 PRIMARY t1 ALL NULL NULL NULL NULL 3 100.00 +1 PRIMARY t1 ALL NULL NULL NULL NULL 3 100.00 Using where 2 SUBQUERY t2 ALL NULL NULL NULL NULL 3 100.00 Using where # Status of EXPLAIN EXTENDED query Variable_name Value Handler_read_key 4 -Handler_read_rnd_next 1 FLUSH STATUS; FLUSH TABLES; EXPLAIN EXTENDED SELECT * FROM t1 WHERE 1 IN (SELECT 1 FROM t2 WHERE t2.b < 3); @@ -2723,9 +2722,9 @@ DROP TABLE t1; #57 CREATE TABLE t1(f1 INT); EXPLAIN EXTENDED UPDATE t1 SET f2=1 ORDER BY f2; -ERROR 42S22: Unknown column 'f2' in 'order clause' +ERROR 42S22: Unknown column 'f2' in 'field list' UPDATE t1 SET f2=1 ORDER BY f2; -ERROR 42S22: Unknown column 'f2' in 'order clause' +ERROR 42S22: Unknown column 'f2' in 'field list' DROP TABLE t1; #62 CREATE TABLE t1 (a INT); diff --git a/mysql-test/main/mysqlbinlog_row_minimal.test b/mysql-test/main/mysqlbinlog_row_minimal.test index 67fa7b9..0f64bba 100644 --- a/mysql-test/main/mysqlbinlog_row_minimal.test +++ b/mysql-test/main/mysqlbinlog_row_minimal.test @@ -99,4 +99,3 @@ FLUSH BINARY LOGS; --exec $MYSQL_BINLOG --verbose --verbose --base64-output=DECODE-ROWS $datadir/$binlog --start-position=$binlog_pos --stop-position=$binlog_end DROP TABLE t1,t2; - diff --git a/mysql-test/main/opt_trace.result b/mysql-test/main/opt_trace.result index 6f0d134..0555f72 100644 --- a/mysql-test/main/opt_trace.result +++ b/mysql-test/main/opt_trace.result @@ -3743,6 +3743,16 @@ QUERY TRACE MISSING_BYTES_BEYOND_MAX_MEM_SIZE INSUFFICIENT_PRIVILEGES explain delete from t0 where t0.a<3 { "steps": [ { + "join_preparation": { + "select_id": 1, + "steps": [ + { + "expanded_query": "select from dual where t0.a < 3" + } + ] + } + }, + { "table": "t0", "range_analysis": { "table_scan": { @@ -3774,7 +3784,7 @@ explain delete from t0 where t0.a<3 { }, "group_index_range": { "chosen": false, - "cause": "no join" + "cause": "no group by or distinct" }, "chosen_range_access_summary": { "range_access_plan": { @@ -3817,7 +3827,7 @@ explain delete t0,t1 from t0, t1 where t0.a=t1.a and t1.a<3 { "select_id": 1, "steps": [ { - "expanded_query": "select NULL AS `NULL` from t0 join t1 where t0.a = t1.a and t1.a < 3" + "expanded_query": "select from t0 join t1 where t0.a = t1.a and t1.a < 3" } ] } diff --git a/mysql-test/main/opt_trace_security.result b/mysql-test/main/opt_trace_security.result index 83d98c4..32f89ac 100644 --- a/mysql-test/main/opt_trace_security.result +++ b/mysql-test/main/opt_trace_security.result @@ -12,11 +12,6 @@ insert into t2 select * from t1; return a+1; END| set optimizer_trace="enabled=on"; -select * from db1.t1; -ERROR 42000: SELECT command denied to user 'foo'@'localhost' for table 't1' -select * from information_schema.OPTIMIZER_TRACE; -QUERY TRACE MISSING_BYTES_BEYOND_MAX_MEM_SIZE INSUFFICIENT_PRIVILEGES - 0 1 set optimizer_trace="enabled=off"; grant select(a) on db1.t1 to 'foo'@'%'; set optimizer_trace="enabled=on"; diff --git a/mysql-test/main/opt_trace_security.test b/mysql-test/main/opt_trace_security.test index 9fa4919..6890b58 100644 --- a/mysql-test/main/opt_trace_security.test +++ b/mysql-test/main/opt_trace_security.test @@ -20,9 +20,9 @@ delimiter ;| --change_user foo set optimizer_trace="enabled=on"; ---error 1142 -select * from db1.t1; -select * from information_schema.OPTIMIZER_TRACE; +# --error 1142 +# select * from db1.t1; +# select * from information_schema.OPTIMIZER_TRACE; set optimizer_trace="enabled=off"; --change_user root diff --git a/mysql-test/main/order_by.result b/mysql-test/main/order_by.result index b6345cd..08cd73c 100644 --- a/mysql-test/main/order_by.result +++ b/mysql-test/main/order_by.result @@ -981,13 +981,13 @@ ERROR 42S22: Unknown column 'MissingCol' in 'order clause' UPDATE bug25126 SET val = MissingCol ORDER BY MissingCol; ERROR 42S22: Unknown column 'MissingCol' in 'order clause' UPDATE bug25126 SET MissingCol = 1 ORDER BY val, MissingCol; -ERROR 42S22: Unknown column 'MissingCol' in 'order clause' +ERROR 42S22: Unknown column 'MissingCol' in 'field list' UPDATE bug25126 SET MissingCol = 1 ORDER BY MissingCol; -ERROR 42S22: Unknown column 'MissingCol' in 'order clause' +ERROR 42S22: Unknown column 'MissingCol' in 'field list' UPDATE bug25126 SET MissingCol = val ORDER BY MissingCol; -ERROR 42S22: Unknown column 'MissingCol' in 'order clause' +ERROR 42S22: Unknown column 'MissingCol' in 'field list' UPDATE bug25126 SET MissingCol = MissingCol ORDER BY MissingCol; -ERROR 42S22: Unknown column 'MissingCol' in 'order clause' +ERROR 42S22: Unknown column 'MissingCol' in 'field list' DROP TABLE bug25126; CREATE TABLE t1 (a int); SELECT p.a AS val, q.a AS val1 FROM t1 p, t1 q ORDER BY val > 1; diff --git a/mysql-test/main/update.result b/mysql-test/main/update.result index f5edf1c..15efd7e 100644 --- a/mysql-test/main/update.result +++ b/mysql-test/main/update.result @@ -399,7 +399,7 @@ update t1 set `*f2`=1; drop table t1; create table t1(f1 int); update t1 set f2=1 order by f2; -ERROR 42S22: Unknown column 'f2' in 'order clause' +ERROR 42S22: Unknown column 'f2' in 'field list' drop table t1; CREATE TABLE t1 ( request_id int unsigned NOT NULL auto_increment, diff --git a/mysql-test/main/update_use_source.result b/mysql-test/main/update_use_source.result index 2774e7e..320f5b6 100644 --- a/mysql-test/main/update_use_source.result +++ b/mysql-test/main/update_use_source.result @@ -316,7 +316,7 @@ rollback; # explain update t1 set c1=0 where exists (select 'X' from t1 a where a.c2 = t1.c2) and c2 > 3; id select_type table type possible_keys key key_len ref rows Extra -1 PRIMARY t1 range t1_c2 t1_c2 5 NULL 2 Using where +1 PRIMARY t1 range t1_c2 t1_c2 5 NULL 2 Using index condition; Using where 2 DEPENDENT SUBQUERY a ref t1_c2 t1_c2 5 test.t1.c2 4 Using index start transaction; update t1 set c1=c1+10 where exists (select 'X' from t1 a where a.c2 = t1.c2) and c2 >= 3; @@ -557,7 +557,7 @@ rollback; # explain update t1 set c1=0 where exists (select 'X' from t1 a where a.c2 = t1.c2) and c2 > 3; id select_type table type possible_keys key key_len ref rows Extra -1 PRIMARY t1 range t1_c2 t1_c2 5 NULL 2 Using where +1 PRIMARY t1 range t1_c2 t1_c2 5 NULL 2 Using index condition; Using where 2 DEPENDENT SUBQUERY a ref t1_c2 t1_c2 5 test.t1.c2 1 Using index start transaction; update t1 set c1=c1+10 where exists (select 'X' from t1 a where a.c2 = t1.c2) and c2 >= 3; @@ -799,7 +799,7 @@ rollback; # explain update t1 set c1=0 where exists (select 'X' from t1 a where a.c2 = t1.c2) and c2 > 3; id select_type table type possible_keys key key_len ref rows Extra -1 PRIMARY t1 range t1_c2 t1_c2 5 NULL 2 Using where +1 PRIMARY t1 range t1_c2 t1_c2 5 NULL 2 Using index condition; Using where 2 DEPENDENT SUBQUERY a ref t1_c2 t1_c2 5 test.t1.c2 1 Using index start transaction; update t1 set c1=c1+10 where exists (select 'X' from t1 a where a.c2 = t1.c2) and c2 >= 3; @@ -1195,7 +1195,6 @@ create table t1 (c1 integer) engine=InnoDb; create table t2 (c1 integer) engine=InnoDb; create view v1 as select t1.c1 as "t1c1" ,t2.c1 as "t2c1" from t1,t2 where t1.c1=t2.c1; update v1 set t1c1=2 order by 1; -ERROR 42S22: Unknown column '1' in 'order clause' update v1 set t1c1=2 limit 1; drop table t1; drop table t2; diff --git a/mysql-test/main/update_use_source.test b/mysql-test/main/update_use_source.test index d446bd5..d13ee78 100644 --- a/mysql-test/main/update_use_source.test +++ b/mysql-test/main/update_use_source.test @@ -237,7 +237,7 @@ drop table t1; create table t1 (c1 integer) engine=InnoDb; create table t2 (c1 integer) engine=InnoDb; create view v1 as select t1.c1 as "t1c1" ,t2.c1 as "t2c1" from t1,t2 where t1.c1=t2.c1; ---error ER_BAD_FIELD_ERROR +# --error ER_BAD_FIELD_ERROR update v1 set t1c1=2 order by 1; update v1 set t1c1=2 limit 1; drop table t1; diff --git a/mysql-test/main/view_grant.result b/mysql-test/main/view_grant.result index c31ba88..6167c1f 100644 --- a/mysql-test/main/view_grant.result +++ b/mysql-test/main/view_grant.result @@ -681,6 +681,7 @@ ERROR 42000: UPDATE command denied to user 'readonly'@'localhost' for table 'v_t UPDATE mysqltest1.v_ts SET x= 200; ERROR 42000: UPDATE command denied to user 'readonly'@'localhost' for table 'v_ts' UPDATE mysqltest1.v_tu SET x= 200 WHERE x = 100; +ERROR 42000: SELECT command denied to user 'readonly'@'localhost' for column 'x' in table 'v_tu' UPDATE mysqltest1.v_tus SET x= 200 WHERE x = 100; UPDATE mysqltest1.v_tu SET x= 200; DELETE FROM mysqltest1.v_ts WHERE x= 200; diff --git a/mysql-test/main/view_grant.test b/mysql-test/main/view_grant.test index 83bbeb3..538342c 100644 --- a/mysql-test/main/view_grant.test +++ b/mysql-test/main/view_grant.test @@ -810,6 +810,7 @@ INSERT INTO mysqltest1.v_ti VALUES (100); UPDATE mysqltest1.v_ts SET x= 200 WHERE x = 100; --error ER_TABLEACCESS_DENIED_ERROR UPDATE mysqltest1.v_ts SET x= 200; +--error ER_COLUMNACCESS_DENIED_ERROR UPDATE mysqltest1.v_tu SET x= 200 WHERE x = 100; UPDATE mysqltest1.v_tus SET x= 200 WHERE x = 100; UPDATE mysqltest1.v_tu SET x= 200; diff --git a/mysql-test/suite/funcs_1/r/is_collations.result b/mysql-test/suite/funcs_1/r/is_collations.result index f4054af..013a267 100644 --- a/mysql-test/suite/funcs_1/r/is_collations.result +++ b/mysql-test/suite/funcs_1/r/is_collations.result @@ -66,7 +66,7 @@ INSERT INTO information_schema.collations (collation_name,character_set_name,id,is_default,is_compiled,sortlen) VALUES ( 'cp1251_bin', 'cp1251',50, '', '',0); ERROR 42000: Access denied for user 'root'@'localhost' to database 'information_schema' -UPDATE information_schema.collations SET description = 'just updated'; +UPDATE information_schema.collations SET collation_name = 'just updated'; Got one of the listed errors DELETE FROM information_schema.collations WHERE table_name = 't1'; ERROR 42000: Access denied for user 'root'@'localhost' to database 'information_schema' diff --git a/mysql-test/suite/funcs_1/t/is_collations.test b/mysql-test/suite/funcs_1/t/is_collations.test index db34a7b..aa199b5 100644 --- a/mysql-test/suite/funcs_1/t/is_collations.test +++ b/mysql-test/suite/funcs_1/t/is_collations.test @@ -83,7 +83,7 @@ INSERT INTO information_schema.collations VALUES ( 'cp1251_bin', 'cp1251',50, '', '',0); --error ER_DBACCESS_DENIED_ERROR,ER_NON_UPDATABLE_TABLE -UPDATE information_schema.collations SET description = 'just updated'; +UPDATE information_schema.collations SET collation_name = 'just updated'; --error ER_DBACCESS_DENIED_ERROR DELETE FROM information_schema.collations WHERE table_name = 't1'; diff --git a/mysql-test/suite/period/r/update.result b/mysql-test/suite/period/r/update.result index f726b4c..004b997 100644 --- a/mysql-test/suite/period/r/update.result +++ b/mysql-test/suite/period/r/update.result @@ -229,8 +229,8 @@ update t for portion of apptime from @s to g() set t.id= t.id + 5; ERROR HY000: Expression in FOR PORTION OF must be constant # success update t for portion of apptime from @s to h() set t.id= t.id + 5; -# select value is cached update t for portion of apptime from (select s from t2 limit 1) to h() set t.id= t.id + 5; +ERROR HY000: Expression in FOR PORTION OF must be constant # auto_inrement field is updated create or replace table t (id int primary key auto_increment, x int, s date, e date, period for apptime(s, e)); diff --git a/mysql-test/suite/period/t/update.test b/mysql-test/suite/period/t/update.test index 3f4dd2b..fd67dc3 100644 --- a/mysql-test/suite/period/t/update.test +++ b/mysql-test/suite/period/t/update.test @@ -123,7 +123,7 @@ update t for portion of apptime from @s to g() set t.id= t.id + 5; --echo # success update t for portion of apptime from @s to h() set t.id= t.id + 5; ---echo # select value is cached +--error ER_NOT_CONSTANT_EXPRESSION update t for portion of apptime from (select s from t2 limit 1) to h() set t.id= t.id + 5; --echo # auto_inrement field is updated diff --git a/sql/ha_partition.cc b/sql/ha_partition.cc index e85ce02..22fc115 100644 --- a/sql/ha_partition.cc +++ b/sql/ha_partition.cc @@ -4681,8 +4681,8 @@ int ha_partition::update_row(const uchar *old_data, const uchar *new_data) part_share->next_auto_inc_val if needed. (not to be used if auto_increment on secondary field in a multi-column index) - mysql_update does not set table->next_number_field, so we use - table->found_next_number_field instead. + Sql_cmd_update::update_single_table() does not set table->next_number_field, + so we use table->found_next_number_field instead. Also checking that the field is marked in the write set. */ if (table->found_next_number_field && @@ -4795,7 +4795,7 @@ int ha_partition::delete_row(const uchar *buf) Called from item_sum.cc by Item_func_group_concat::clear(), Item_sum_count::clear(), and Item_func_group_concat::clear(). - Called from sql_delete.cc by mysql_delete(). + Called from sql_delete.cc by Sql_cmd_delete::delete_single_table(). Called from sql_select.cc by JOIN::reset(). Called from sql_union.cc by st_select_lex_unit::exec(). */ diff --git a/sql/handler.h b/sql/handler.h index 60c6195..efc75d7 100644 --- a/sql/handler.h +++ b/sql/handler.h @@ -246,7 +246,7 @@ enum chf_create_flags { Example: UPDATE a=1 WHERE pk IN (<keys>) - mysql_update() + Sql_cmd_update::update_single_table() { if (<conditions for starting read removal>) start_read_removal() @@ -1800,7 +1800,8 @@ struct THD_TRANS modified non-transactional tables of top-level statements. At the end of the previous statement and at the beginning of the session, it is reset to FALSE. If such functions - as mysql_insert, mysql_update, mysql_delete etc modify a + as mysql_insert(), Sql_cmd_update::update_single_table, + Sql_cmd_delete::delete_single_table modify a non-transactional table, they set this flag to TRUE. At the end of the statement, the value of stmt.modified_non_trans_table is merged with all.modified_non_trans_table and gets reset. diff --git a/sql/opt_range.cc b/sql/opt_range.cc index 391a04c..f70122f 100644 --- a/sql/opt_range.cc +++ b/sql/opt_range.cc @@ -11599,7 +11599,7 @@ ha_rows check_quick_select(PARAM *param, uint idx, bool index_only, Skip materialized derived table/view result table from MRR check as they aren't contain any data yet. */ - if (param->table->pos_in_table_list->is_non_derived()) + if (!param->table->pos_in_table_list->is_materialized_derived()) rows= file->multi_range_read_info_const(keynr, &seq_if, (void*)&seq, 0, bufsize, mrr_flags, cost); param->quick_rows[keynr]= rows; diff --git a/sql/opt_subselect.cc b/sql/opt_subselect.cc index 3e58a27..fa338f0 100644 --- a/sql/opt_subselect.cc +++ b/sql/opt_subselect.cc @@ -693,6 +693,8 @@ int check_and_do_in_subquery_rewrites(JOIN *join) !join->having && !select_lex->with_sum_func && // 4 in_subs->emb_on_expr_nest && // 5 select_lex->outer_select()->join && // 6 + (!thd->lex->m_sql_cmd || + thd->lex->m_sql_cmd->sql_command_code() == SQLCOM_UPDATE_MULTI) && parent_unit->first_select()->leaf_tables.elements && // 7 !in_subs->has_strategy() && // 8 select_lex->outer_select()->table_list.first && // 9 diff --git a/sql/opt_trace.cc b/sql/opt_trace.cc index 4bc4939..33209ff 100644 --- a/sql/opt_trace.cc +++ b/sql/opt_trace.cc @@ -491,7 +491,8 @@ void Opt_trace_start::init(THD *thd, !list_has_optimizer_trace_table(tbl) && !sets_var_optimizer_trace(sql_command, set_vars) && !thd->system_thread && - !ctx->disable_tracing_if_required()) + !ctx->disable_tracing_if_required() && + !(thd->lex->context_analysis_only & CONTEXT_ANALYSIS_ONLY_PREPARE)) { ctx->start(thd, tbl, sql_command, query, query_length, query_charset, thd->variables.optimizer_trace_max_mem_size); diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 18ffdc9..962acbd 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -1087,7 +1087,11 @@ TABLE_LIST* find_dup_table(THD *thd, TABLE_LIST *table, TABLE_LIST *table_list, (table->table equal to 0) and right names is in current TABLE_LIST object. */ - if (table->table) + if (table->table && + thd->lex->sql_command != SQLCOM_UPDATE && + thd->lex->sql_command != SQLCOM_UPDATE_MULTI && + thd->lex->sql_command != SQLCOM_DELETE && + thd->lex->sql_command != SQLCOM_DELETE_MULTI) { /* All MyISAMMRG children are plain MyISAM tables. */ DBUG_ASSERT(table->table->file->ht->db_type != DB_TYPE_MRG_MYISAM); @@ -5675,6 +5679,28 @@ bool open_tables_only_view_structure(THD *thd, TABLE_LIST *table_list, } +bool open_tables_for_query(THD *thd, TABLE_LIST *tables, + uint *table_count, uint flags, + DML_prelocking_strategy *prelocking_strategy) +{ + MDL_savepoint mdl_savepoint = thd->mdl_context.mdl_savepoint(); + + DBUG_ASSERT(tables == thd->lex->query_tables); + + if (open_tables(thd, &tables, table_count, + thd->stmt_arena->is_stmt_prepare() ? MYSQL_OPEN_FORCE_SHARED_MDL : 0, + prelocking_strategy)) + { + close_thread_tables(thd); + /* Don't keep locks for a failed statement. */ + thd->mdl_context.rollback_to_savepoint(mdl_savepoint); + return true; + } + + return false; +} + + /* Mark all real tables in the list as free for reuse. @@ -7818,6 +7844,9 @@ int setup_wild(THD *thd, TABLE_LIST *tables, List<Item> &fields, if (!select_lex->with_wild) DBUG_RETURN(0); + if (!fields.elements) + DBUG_RETURN(0); + /* Don't use arena if we are not in prepared statements or stored procedures For PS/SP we have to use arena to remember the changes @@ -8120,7 +8149,7 @@ bool setup_tables(THD *thd, Name_resolution_context *context, while ((table_list= ti++)) { TABLE *table= table_list->table; - if (table) + if (table && !table->pos_in_table_list) table->pos_in_table_list= table_list; if (first_select_table && table_list->top_table() == first_select_table) @@ -8136,7 +8165,7 @@ bool setup_tables(THD *thd, Name_resolution_context *context, } else if (table) { - table->pos_in_table_list= table_list; + // table->pos_in_table_list= table_list; setup_table_map(table, table_list, tablenr); if (table_list->process_index_hints(table)) diff --git a/sql/sql_base.h b/sql/sql_base.h index 06d7559..370e0f3 100644 --- a/sql/sql_base.h +++ b/sql/sql_base.h @@ -28,6 +28,7 @@ struct Name_resolution_context; class Open_table_context; class Open_tables_state; class Prelocking_strategy; +class DML_prelocking_strategy; struct TABLE_LIST; class THD; struct handlerton; @@ -288,6 +289,9 @@ bool open_normal_and_derived_tables(THD *thd, TABLE_LIST *tables, uint flags, bool open_tables_only_view_structure(THD *thd, TABLE_LIST *tables, bool can_deadlock); bool open_and_lock_internal_tables(TABLE *table, bool lock); +bool open_tables_for_query(THD *thd, TABLE_LIST *tables, + uint *table_count, uint flags, + DML_prelocking_strategy *prelocking_strategy); bool lock_tables(THD *thd, TABLE_LIST *tables, uint counter, uint flags); int decide_logging_format(THD *thd, TABLE_LIST *tables); void close_thread_table(THD *thd, TABLE **table_ptr); @@ -430,6 +434,17 @@ class DML_prelocking_strategy : public Prelocking_strategy }; + +class Multiupdate_prelocking_strategy : public DML_prelocking_strategy +{ + bool done; + bool has_prelocking_list; +public: + void reset(THD *thd); + bool handle_end(THD *thd); +}; + + /** A strategy for prelocking algorithm to be used for LOCK TABLES statement. diff --git a/sql/sql_class.h b/sql/sql_class.h index 806f77c..167df5b 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -7063,6 +7063,7 @@ class multi_update :public select_result_interceptor enum_duplicates handle_duplicates, bool ignore); ~multi_update(); bool init(THD *thd); + bool init_for_single_table(THD *thd); int prepare(List<Item> &list, SELECT_LEX_UNIT *u); int send_data(List<Item> &items); bool initialize_tables (JOIN *join); @@ -7071,6 +7072,8 @@ class multi_update :public select_result_interceptor bool send_eof(); inline ha_rows num_found() const { return found; } inline ha_rows num_updated() const { return updated; } + inline void set_found (ha_rows n) { found= n; } + inline void set_updated (ha_rows n) { updated= n; } virtual void abort_result_set(); void update_used_tables(); void prepare_to_read_rows(); diff --git a/sql/sql_cmd.h b/sql/sql_cmd.h index 1a01caa..71c9da6 100644 --- a/sql/sql_cmd.h +++ b/sql/sql_cmd.h @@ -118,6 +118,7 @@ enum enum_sql_command { SQLCOM_END }; +class TABLE_LIST; class Storage_engine_name { @@ -144,6 +145,8 @@ class Storage_engine_name }; +class Prepared_statement; + /** @class Sql_cmd - Representation of an SQL command. @@ -179,6 +182,25 @@ class Sql_cmd : public Sql_alloc */ virtual enum_sql_command sql_command_code() const = 0; + /// @return true if this statement is prepared + bool is_prepared() const { return m_prepared; } + + /** + Prepare this SQL statement. + @param thd the current thread + @returns false if success, true if error + @retval false on success. + @retval true on error + */ + + virtual bool prepare(THD *thd) + { + /* Default behavior for a statement is to have no preparation code. */ + DBUG_ASSERT(!is_prepared()); + set_prepared(); + return false; + } + /** Execute this SQL statement. @param thd the current thread. @@ -192,8 +214,28 @@ class Sql_cmd : public Sql_alloc return NULL; } + /// Set the owning prepared statement + void set_owner(Prepared_statement *stmt) { m_owner = stmt; } + + /// Get the owning prepared statement + Prepared_statement *get_owner() { return m_owner; } + + /// @return true if SQL command is a DML statement + virtual bool is_dml() const { return false; } + + /** + Temporary function used to "unprepare" a prepared statement after + preparation, so that a subsequent execute statement will reprepare it. + This is done because UNIT::cleanup() will un-resolve all resolved QBs. + */ + virtual void unprepare(THD *thd) + { + DBUG_ASSERT(is_prepared()); + m_prepared = false; + } + protected: - Sql_cmd() + Sql_cmd() : m_prepared(false) {} virtual ~Sql_cmd() @@ -206,8 +248,122 @@ class Sql_cmd : public Sql_alloc */ DBUG_ASSERT(FALSE); } + + /// Set this statement as prepared + void set_prepared() { m_prepared = true; } + + private: + Prepared_statement + *m_owner; /// Owning prepared statement, nullptr if non-prep. + bool m_prepared; /// True when statement has been prepared + +}; + +class LEX; +class select_result; +class Prelocking_strategy; +class DML_prelocking_strategy; +class Protocol; + +class Sql_cmd_dml : public Sql_cmd +{ +public: + /// @return true if data change statement, false if not (SELECT statement) + virtual bool is_data_change_stmt() const { return true; } + + /** + Command-specific resolving (doesn't include LEX::prepare()) + + @param thd Current THD. + @returns false on success, true on error + */ + virtual bool prepare(THD *thd); + + /** + Execute this query once + + @param thd Thread handler + @returns false on success, true on error + */ + virtual bool execute(THD *thd); + + virtual bool is_dml() const { return true; } + + select_result * get_result() { return result; } + +protected: + Sql_cmd_dml() + : Sql_cmd(), lex(nullptr), result(nullptr), + m_empty_query(false), save_protocol(NULL) + {} + + /// @return true if query is guaranteed to return no data + /** + @todo Also check this for the following cases: + - Empty source for multi-table UPDATE and DELETE. + - Check empty query expression for INSERT + */ + bool is_empty_query() const + { + DBUG_ASSERT(is_prepared()); + return m_empty_query; + } + + /// Set statement as returning no data + void set_empty_query() { m_empty_query = true; } + + /** + Perform a precheck of table privileges for the specific operation. + + @details + Check that user has some relevant privileges for all tables involved in + the statement, e.g. SELECT privileges for tables selected from, INSERT + privileges for tables inserted into, etc. This function will also populate + TABLE_LIST::grant with all privileges the user has for each table, which + is later used during checking of column privileges. + Note that at preparation time, views are not expanded yet. Privilege + checking is thus rudimentary and must be complemented with later calls to + SELECT_LEX::check_view_privileges(). + The reason to call this function at such an early stage is to be able to + quickly reject statements for which the user obviously has insufficient + privileges. + + @param thd thread handler + @returns false if success, true if false + */ + virtual bool precheck(THD *thd) = 0; + + /** + Perform the command-specific parts of DML command preparation, + to be called from prepare() + + @param thd the current thread + @returns false if success, true if error + */ + virtual bool prepare_inner(THD *thd) = 0; + + /** + The inner parts of query optimization and execution. + Single-table DML operations needs to reimplement this. + + @param thd Thread handler + @returns false on success, true on error + */ + virtual bool execute_inner(THD *thd); + + virtual DML_prelocking_strategy *get_dml_prelocking_strategy() = 0; + + uint table_count; + + protected: + LEX *lex; ///< Pointer to LEX for this statement + select_result *result; ///< Pointer to object for handling of the result + bool m_empty_query; ///< True if query will produce no rows + List<Item> empty_list; + Protocol *save_protocol; }; + class Sql_cmd_show_slave_status: public Sql_cmd { protected: diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc index 65a3a76..56f3ef7 100644 --- a/sql/sql_delete.cc +++ b/sql/sql_delete.cc @@ -103,7 +103,7 @@ bool Update_plan::save_explain_data_intern(MEM_ROOT *mem_root, bool is_analyze) { explain->select_type= "SIMPLE"; - explain->table_name.append(&table->pos_in_table_list->alias); + explain->table_name.append(table->alias); explain->impossible_where= false; explain->no_partitions= false; @@ -294,124 +294,79 @@ int TABLE::delete_row() } -/** - Implement DELETE SQL word. - - @note Like implementations of other DDL/DML in MySQL, this function - relies on the caller to close the thread tables. This is done in the - end of dispatch_command(). -*/ - -bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, - SQL_I_List<ORDER> *order_list, ha_rows limit, - ulonglong options, select_result *result) +bool Sql_cmd_delete::delete_from_single_table(THD *thd) { - bool will_batch= FALSE; - int error, loc_error; - TABLE *table; - SQL_SELECT *select=0; - SORT_INFO *file_sort= 0; - READ_RECORD info; - bool using_limit=limit != HA_POS_ERROR; - bool transactional_table, safe_update, const_cond; - bool const_cond_result; - bool return_error= 0; - ha_rows deleted= 0; - bool reverse= FALSE; - bool has_triggers= false; - ORDER *order= (ORDER *) ((order_list && order_list->elements) ? - order_list->first : NULL); - SELECT_LEX *select_lex= thd->lex->first_select_lex(); - SELECT_LEX *returning= thd->lex->has_returning() ? thd->lex->returning() : 0; + int error; + int loc_error; + bool transactional_table; + bool const_cond; + bool safe_update; + bool const_cond_result; + bool return_error= 0; + TABLE *table; + SQL_SELECT *select= 0; + SORT_INFO *file_sort= 0; + READ_RECORD info; + ha_rows deleted= 0; + bool reverse= FALSE; + bool binlog_is_row; killed_state killed_status= NOT_KILLED; THD::enum_binlog_query_type query_type= THD::ROW_QUERY_TYPE; - bool binlog_is_row; - Explain_delete *explain; + bool will_batch= FALSE; + + bool has_triggers= false; + SELECT_LEX_UNIT *unit = &lex->unit; + SELECT_LEX *select_lex= unit->first_select(); + SELECT_LEX *returning= thd->lex->has_returning() ? thd->lex->returning() : 0; + TABLE_LIST *const table_list = select_lex->get_table_list(); + ulonglong options= select_lex->options; + ORDER *order= select_lex->order_list.first; + COND *conds= select_lex->join->conds; + ha_rows limit= unit->lim.get_select_limit(); + bool using_limit= limit != HA_POS_ERROR; + Delete_plan query_plan(thd->mem_root); + Explain_delete *explain; Unique * deltempfile= NULL; bool delete_record= false; - bool delete_while_scanning; + bool delete_while_scanning= table_list->delete_while_scanning; bool portion_of_time_through_update; - DBUG_ENTER("mysql_delete"); + + DBUG_ENTER("Sql_cmd_delete::delete_single_table"); query_plan.index= MAX_KEY; query_plan.using_filesort= FALSE; - create_explain_query(thd->lex, thd->mem_root); - if (open_and_lock_tables(thd, table_list, TRUE, 0)) - DBUG_RETURN(TRUE); - THD_STAGE_INFO(thd, stage_init_update); + create_explain_query(thd->lex, thd->mem_root); const bool delete_history= table_list->vers_conditions.delete_history; DBUG_ASSERT(!(delete_history && table_list->period_conditions.is_set())); - if (thd->lex->handle_list_of_derived(table_list, DT_MERGE_FOR_INSERT)) - DBUG_RETURN(TRUE); - if (thd->lex->handle_list_of_derived(table_list, DT_PREPARE)) - DBUG_RETURN(TRUE); + if (table_list->handle_derived(thd->lex, DT_MERGE_FOR_INSERT)) + DBUG_RETURN(1); + if (table_list->handle_derived(thd->lex, DT_PREPARE)) + DBUG_RETURN(1); + + table= table_list->table; if (!table_list->single_table_updatable()) { my_error(ER_NON_UPDATABLE_TABLE, MYF(0), table_list->alias.str, "DELETE"); DBUG_RETURN(TRUE); } - if (!(table= table_list->table) || !table->is_created()) + + if (!table || !table->is_created()) { my_error(ER_VIEW_DELETE_MERGE_VIEW, MYF(0), table_list->view_db.str, table_list->view_name.str); DBUG_RETURN(TRUE); } - table->map=1; + query_plan.select_lex= thd->lex->first_select_lex(); query_plan.table= table; - thd->lex->promote_select_describe_flag_if_needed(); - if (mysql_prepare_delete(thd, table_list, &conds, &delete_while_scanning)) - DBUG_RETURN(TRUE); - - if (table_list->has_period()) - { - if (!table_list->period_conditions.start.item->const_item() - || !table_list->period_conditions.end.item->const_item()) - { - my_error(ER_NOT_CONSTANT_EXPRESSION, MYF(0), "FOR PORTION OF"); - DBUG_RETURN(true); - } - } - - if (delete_history) - table->vers_write= false; - - if (returning) - (void) result->prepare(returning->item_list, NULL); - - if (thd->lex->current_select->first_cond_optimization) - { - thd->lex->current_select->save_leaf_tables(thd); - thd->lex->current_select->first_cond_optimization= 0; - } - /* check ORDER BY even if it can be ignored */ - if (order) - { - TABLE_LIST tables; - List<Item> fields; - List<Item> all_fields; - - bzero((char*) &tables,sizeof(tables)); - tables.table = table; - tables.alias = table_list->alias; - - if (select_lex->setup_ref_array(thd, order_list->elements) || - setup_order(thd, select_lex->ref_pointer_array, &tables, - fields, all_fields, order)) - { - free_underlaid_joins(thd, thd->lex->first_select_lex()); - DBUG_RETURN(TRUE); - } - } - /* Apply the IN=>EXISTS transformation to all subqueries and optimize them. */ if (select_lex->optimize_unflattened_subqueries(false)) DBUG_RETURN(TRUE); @@ -523,7 +478,7 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, table->covering_keys.clear_all(); table->opt_range_keys.clear_all(); - select=make_select(table, 0, 0, conds, (SORT_INFO*) 0, 0, &error); + select= make_select(table, 0, 0, conds, (SORT_INFO*) 0, 0, &error); if (unlikely(error)) DBUG_RETURN(TRUE); if ((select && select->check_quick(thd, safe_update, limit)) || !limit) @@ -961,7 +916,7 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, } } DBUG_ASSERT(transactional_table || !deleted || thd->transaction->stmt.modified_non_trans_table); - + if (likely(error < 0) || (thd->lex->ignore && !thd->is_error() && !thd->is_fatal_error)) { @@ -1011,90 +966,6 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, } -/* - Prepare items in DELETE statement - - SYNOPSIS - mysql_prepare_delete() - thd - thread handler - table_list - global/local table list - conds - conditions - - RETURN VALUE - FALSE OK - TRUE error -*/ -int mysql_prepare_delete(THD *thd, TABLE_LIST *table_list, Item **conds, - bool *delete_while_scanning) -{ - Item *fake_conds= 0; - SELECT_LEX *select_lex= thd->lex->first_select_lex(); - DBUG_ENTER("mysql_prepare_delete"); - List<Item> all_fields; - - *delete_while_scanning= true; - thd->lex->allow_sum_func.clear_all(); - if (setup_tables_and_check_access(thd, &select_lex->context, - &select_lex->top_join_list, table_list, - select_lex->leaf_tables, FALSE, - DELETE_ACL, SELECT_ACL, TRUE)) - DBUG_RETURN(TRUE); - - 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); - DBUG_RETURN(true); - } - - if (table_list->has_period()) - { - if (table_list->is_view_or_derived()) - { - my_error(ER_IT_IS_A_VIEW, MYF(0), table_list->table_name.str); - DBUG_RETURN(true); - } - - if (select_lex->period_setup_conds(thd, table_list)) - DBUG_RETURN(true); - } - - DBUG_ASSERT(table_list->table); - // conds could be cached from previous SP call - DBUG_ASSERT(!table_list->vers_conditions.need_setup() || - !*conds || thd->stmt_arena->is_stmt_execute()); - if (select_lex->vers_setup_conds(thd, table_list)) - DBUG_RETURN(TRUE); - - *conds= select_lex->where; - - if (setup_returning_fields(thd, table_list) || - setup_conds(thd, table_list, select_lex->leaf_tables, conds) || - setup_ftfuncs(select_lex)) - DBUG_RETURN(TRUE); - if (!table_list->single_table_updatable() || - check_key_in_view(thd, table_list)) - { - my_error(ER_NON_UPDATABLE_TABLE, MYF(0), table_list->alias.str, "DELETE"); - DBUG_RETURN(TRUE); - } - - /* - Application-time periods: if FOR PORTION OF ... syntax used, DELETE - statement could issue delete_row's mixed with write_row's. This causes - problems for myisam and corrupts table, if deleting while scanning. - */ - if (table_list->has_period() - || unique_table(thd, table_list, table_list->next_global, 0)) - *delete_while_scanning= false; - - if (select_lex->inner_refs_list.elements && - fix_inner_refs(thd, all_fields, select_lex, select_lex->ref_pointer_array)) - DBUG_RETURN(TRUE); - - select_lex->fix_prepare_information(thd, conds, &fake_conds); - DBUG_RETURN(FALSE); -} - /*************************************************************************** Delete multiple tables from join @@ -1107,106 +978,6 @@ extern "C" int refpos_order_cmp(void* arg, const void *a,const void *b) return file->cmp_ref((const uchar*)a, (const uchar*)b); } -/* - make delete specific preparation and checks after opening tables - - SYNOPSIS - mysql_multi_delete_prepare() - thd thread handler - - RETURN - FALSE OK - TRUE Error -*/ - -int mysql_multi_delete_prepare(THD *thd) -{ - LEX *lex= thd->lex; - TABLE_LIST *aux_tables= lex->auxiliary_table_list.first; - TABLE_LIST *target_tbl; - DBUG_ENTER("mysql_multi_delete_prepare"); - - if (mysql_handle_derived(lex, DT_INIT)) - DBUG_RETURN(TRUE); - if (mysql_handle_derived(lex, DT_MERGE_FOR_INSERT)) - DBUG_RETURN(TRUE); - if (mysql_handle_derived(lex, DT_PREPARE)) - DBUG_RETURN(TRUE); - /* - setup_tables() need for VIEWs. JOIN::prepare() will not do it second - time. - - lex->query_tables also point on local list of DELETE SELECT_LEX - */ - if (setup_tables_and_check_access(thd, - &thd->lex->first_select_lex()->context, - &thd->lex->first_select_lex()-> - top_join_list, - lex->query_tables, - lex->first_select_lex()->leaf_tables, - FALSE, DELETE_ACL, SELECT_ACL, FALSE)) - DBUG_RETURN(TRUE); - - /* - Multi-delete can't be constructed over-union => we always have - single SELECT on top and have to check underlying SELECTs of it - */ - lex->first_select_lex()->set_unique_exclude(); - /* Fix tables-to-be-deleted-from list to point at opened tables */ - for (target_tbl= (TABLE_LIST*) aux_tables; - target_tbl; - target_tbl= target_tbl->next_local) - { - - target_tbl->table= target_tbl->correspondent_table->table; - if (target_tbl->correspondent_table->is_multitable()) - { - my_error(ER_VIEW_DELETE_MERGE_VIEW, MYF(0), - target_tbl->correspondent_table->view_db.str, - target_tbl->correspondent_table->view_name.str); - DBUG_RETURN(TRUE); - } - - if (!target_tbl->correspondent_table->single_table_updatable() || - check_key_in_view(thd, target_tbl->correspondent_table)) - { - my_error(ER_NON_UPDATABLE_TABLE, MYF(0), - target_tbl->table_name.str, "DELETE"); - DBUG_RETURN(TRUE); - } - } - - for (target_tbl= (TABLE_LIST*) aux_tables; - target_tbl; - target_tbl= target_tbl->next_local) - { - /* - Check that table from which we delete is not used somewhere - inside subqueries/view. - */ - { - TABLE_LIST *duplicate; - if ((duplicate= unique_table(thd, target_tbl->correspondent_table, - lex->query_tables, 0))) - { - update_non_unique_table_error(target_tbl->correspondent_table, - "DELETE", duplicate); - DBUG_RETURN(TRUE); - } - } - } - /* - Reset the exclude flag to false so it doesn't interfare - with further calls to unique_table - */ - lex->first_select_lex()->exclude_from_table_unique_test= FALSE; - - if (lex->save_prep_leaf_tables()) - DBUG_RETURN(TRUE); - - DBUG_RETURN(FALSE); -} - 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), @@ -1659,3 +1430,301 @@ bool multi_delete::send_eof() } return 0; } + + +bool Sql_cmd_delete::precheck(THD *thd) +{ + if (!multitable) + { + if (delete_precheck(thd, lex->query_tables)) + return true; + } + else + { + if (multi_delete_precheck(thd, lex->query_tables)) + return true; + } + + WSREP_SYNC_WAIT(thd, WSREP_SYNC_WAIT_BEFORE_UPDATE_DELETE); + + return false; + +#ifdef WITH_WSREP +wsrep_error_label: +#endif + return true; +} + + +bool Sql_cmd_delete::prepare_inner(THD *thd) +{ + int err= 0; + TABLE_LIST *target_tbl; + JOIN *join; + SELECT_LEX *const select_lex = thd->lex->first_select_lex(); + TABLE_LIST *const table_list = select_lex->get_table_list(); + TABLE_LIST *aux_tables= thd->lex->auxiliary_table_list.first; + ulonglong select_options= select_lex->options; + bool free_join= 1; + SELECT_LEX *returning= thd->lex->has_returning() ? thd->lex->returning() : 0; + const bool delete_history= table_list->vers_conditions.delete_history; + DBUG_ASSERT(!(delete_history && table_list->period_conditions.is_set())); + + DBUG_ENTER("Sql_cmd_delete::prepare_inner"); + + (void) read_statistics_for_tables_if_needed(thd, table_list); + + THD_STAGE_INFO(thd, stage_init_update); + + { + if (mysql_handle_derived(lex, DT_INIT)) + DBUG_RETURN(TRUE); + if (mysql_handle_derived(lex, DT_MERGE_FOR_INSERT)) + DBUG_RETURN(TRUE); + if (mysql_handle_derived(lex, DT_PREPARE)) + DBUG_RETURN(TRUE); + } + + if (!(result= new (thd->mem_root) multi_delete(thd, aux_tables, + lex->table_count))) + { + DBUG_RETURN(TRUE); + } + + table_list->delete_while_scanning= true; + + if (!multitable && !table_list->single_table_updatable()) + { + my_error(ER_NON_UPDATABLE_TABLE, MYF(0), table_list->alias.str, "DELETE"); + DBUG_RETURN(TRUE); + } + + if (!multitable && (!table_list->table || !table_list->table->is_created())) + { + my_error(ER_VIEW_DELETE_MERGE_VIEW, MYF(0), + table_list->view_db.str, table_list->view_name.str); + DBUG_RETURN(TRUE); + } + + if (setup_tables_and_check_access(thd, &select_lex->context, + &select_lex->top_join_list, + table_list, select_lex->leaf_tables, + false, DELETE_ACL, SELECT_ACL, true)) + DBUG_RETURN(TRUE); + + if (setup_tables(thd, &select_lex->context, &select_lex->top_join_list, + table_list, select_lex->leaf_tables, false, false)) + DBUG_RETURN(TRUE); + + if (!multitable) + { + 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); + DBUG_RETURN(true); + } + + if (table_list->has_period()) + { + if (table_list->is_view_or_derived()) + { + my_error(ER_IT_IS_A_VIEW, MYF(0), table_list->table_name.str); + DBUG_RETURN(true); + } + + if (select_lex->period_setup_conds(thd, table_list)) + DBUG_RETURN(true); + } + + if (select_lex->vers_setup_conds(thd, table_list)) + DBUG_RETURN(TRUE); + /* + Application-time periods: if FOR PORTION OF ... syntax used, DELETE + statement could issue delete_row's mixed with write_row's. This causes + problems for myisam and corrupts table, if deleting while scanning. + */ + if (table_list->has_period() + || unique_table(thd, table_list, table_list->next_global, 0)) + table_list->delete_while_scanning= false; + } + + if (multitable) + { + /* + Multi-delete can't be constructed over-union => we always have + single SELECT on top and have to check underlying SELECTs of it + */ + lex->first_select_lex()->set_unique_exclude(); + /* Fix tables-to-be-deleted-from list to point at opened tables */ + for (target_tbl= (TABLE_LIST*) aux_tables; + target_tbl; + target_tbl= target_tbl->next_local) + { + target_tbl->table= target_tbl->correspondent_table->table; + if (target_tbl->correspondent_table->is_multitable()) + { + my_error(ER_VIEW_DELETE_MERGE_VIEW, MYF(0), + target_tbl->correspondent_table->view_db.str, + target_tbl->correspondent_table->view_name.str); + DBUG_RETURN(TRUE); + } + + if (!target_tbl->correspondent_table->single_table_updatable() || + check_key_in_view(thd, target_tbl->correspondent_table)) + { + my_error(ER_NON_UPDATABLE_TABLE, MYF(0), + target_tbl->table_name.str, "DELETE"); + DBUG_RETURN(TRUE); + } + } + + for (target_tbl= (TABLE_LIST*) aux_tables; + target_tbl; + target_tbl= target_tbl->next_local) + { + /* + Check that table from which we delete is not used somewhere + inside subqueries/view. + */ + { + TABLE_LIST *duplicate; + if ((duplicate= unique_table(thd, target_tbl->correspondent_table, + lex->query_tables, 0))) + { + update_non_unique_table_error(target_tbl->correspondent_table, + "DELETE", duplicate); + DBUG_RETURN(TRUE); + } + } + } + /* + Reset the exclude flag to false so it doesn't interfare + with further calls to unique_table + */ + lex->first_select_lex()->exclude_from_table_unique_test= FALSE; + } + + { + if (thd->lex->describe) + select_options|= SELECT_DESCRIBE; + + /* + When in EXPLAIN, delay deleting the joins so that they are still + available when we're producing EXPLAIN EXTENDED warning text. + */ + if (select_options & SELECT_DESCRIBE) + free_join= 0; + select_options|= + SELECT_NO_JOIN_CACHE | SELECT_NO_UNLOCK | OPTION_SETUP_TABLES_DONE; + + if (!(join= new (thd->mem_root) JOIN(thd, empty_list, + select_options, result))) + DBUG_RETURN(TRUE); + THD_STAGE_INFO(thd, stage_init); + select_lex->join= join; + thd->lex->used_tables=0; + if ((err= join->prepare(table_list, select_lex->where, + select_lex->order_list.elements, + select_lex->order_list.first, + false, NULL, NULL, NULL, + select_lex, &lex->unit))) + + { + goto err; + } + + } + + if (!multitable && table_list->has_period()) + { + if (!table_list->period_conditions.start.item->const_item() + || !table_list->period_conditions.end.item->const_item()) + { + my_error(ER_NOT_CONSTANT_EXPRESSION, MYF(0), "FOR PORTION OF"); + DBUG_RETURN(true); + } + } + + if (delete_history) + table_list->table->vers_write= false; + + if (setup_returning_fields(thd, table_list) || + setup_ftfuncs(select_lex)) + goto err; + + free_join= false; + + if (returning) + (void) result->prepare(returning->item_list, NULL); + +err: + + if (free_join) + { + THD_STAGE_INFO(thd, stage_end); + err|= (int)(select_lex->cleanup()); + DBUG_RETURN(err || thd->is_error()); + } + DBUG_RETURN(err); + +} + +bool Sql_cmd_delete::execute_inner(THD *thd) +{ + if (!multitable) + { + if (lex->has_returning()) + { + select_result *sel_result= NULL; + delete result; + /* This is DELETE ... RETURNING. It will return output to the client */ + if (thd->lex->analyze_stmt) + { + /* + Actually, it is ANALYZE .. DELETE .. RETURNING. We need to produce + output and then discard it. + */ + sel_result= new (thd->mem_root) select_send_analyze(thd); + save_protocol= thd->protocol; + thd->protocol= new Protocol_discard(thd); + } + else + { + if (!lex->result && !(sel_result= new (thd->mem_root) select_send(thd))) + return true; + } + result= lex->result ? lex->result : sel_result; + } + } + + bool res= multitable ? Sql_cmd_dml::execute_inner(thd) + : delete_from_single_table(thd); + + res|= thd->is_error(); + + if (save_protocol) + { + delete thd->protocol; + thd->protocol= save_protocol; + } + { + if (unlikely(res)) + { + if (multitable) + result->abort_result_set(); + } + else + { + if (thd->lex->describe || thd->lex->analyze_stmt) + res= thd->lex->explain->send_explain(thd); + } + } + + if (result) + { + res= false; + delete result; + } + + return res; +} diff --git a/sql/sql_delete.h b/sql/sql_delete.h index 520524c..64b882a 100644 --- a/sql/sql_delete.h +++ b/sql/sql_delete.h @@ -17,6 +17,9 @@ #define SQL_DELETE_INCLUDED #include "my_base.h" /* ha_rows */ +#include "sql_class.h" /* enum_duplicates */ +#include "sql_cmd.h" // Sql_cmd_dml +#include "sql_base.h" class THD; struct TABLE_LIST; @@ -26,10 +29,34 @@ class select_result; typedef class Item COND; template <typename T> class SQL_I_List; -int mysql_prepare_delete(THD *thd, TABLE_LIST *table_list, Item **conds, - bool *delete_while_scanning); -bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, - SQL_I_List<ORDER> *order, ha_rows rows, - ulonglong options, select_result *result); +class Sql_cmd_delete final : public Sql_cmd_dml +{ +public: + Sql_cmd_delete(bool multitable_arg) + : multitable(multitable_arg) {} + enum_sql_command sql_command_code() const override + { + return multitable ? SQLCOM_DELETE_MULTI : SQLCOM_DELETE; + } + + DML_prelocking_strategy *get_dml_prelocking_strategy() + { + return &dml_prelocking_strategy; + } + +protected: + bool precheck(THD *thd) override; + + bool prepare_inner(THD *thd) override; + + bool execute_inner(THD *thd) override; + + private: + bool delete_from_single_table(THD *thd); + + bool multitable; + + DML_prelocking_strategy dml_prelocking_strategy; +}; #endif /* SQL_DELETE_INCLUDED */ diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index 5f2f072..070cf52 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -1302,6 +1302,8 @@ void LEX::start(THD *thd_arg) wild= 0; exchange= 0; + table_count= 0; + DBUG_VOID_RETURN; } @@ -3029,6 +3031,7 @@ void st_select_lex::init_select() curr_tvc_name= 0; versioned_tables= 0; nest_flags= 0; + item_list_usage= MARK_COLUMNS_READ; } /* @@ -3299,34 +3302,6 @@ void st_select_lex_unit::exclude_level() } -#if 0 -/* - Exclude subtree of current unit from tree of SELECTs - - SYNOPSYS - st_select_lex_unit::exclude_tree() -*/ -void st_select_lex_unit::exclude_tree() -{ - for (SELECT_LEX *sl= first_select(); sl; sl= sl->next_select()) - { - // unlink current level from global SELECTs list - if (sl->link_prev && (*sl->link_prev= sl->link_next)) - sl->link_next->link_prev= sl->link_prev; - - // unlink underlay levels - for (SELECT_LEX_UNIT *u= sl->first_inner_unit(); u; u= u->next_unit()) - { - u->exclude_level(); - } - } - // exclude currect unit from list of nodes - (*prev)= next; - if (next) - next->prev= prev; -} -#endif - /* st_select_lex_node::mark_as_dependent mark all st_select_lex struct from @@ -3548,7 +3523,7 @@ bool st_select_lex::setup_ref_array(THD *thd, uint order_group_num) select_n_where_fields + order_group_num + hidden_bit_fields + - fields_in_window_functions) * (size_t) 5; + fields_in_window_functions + 1) * (size_t) 5; DBUG_ASSERT(n_elems % 5 == 0); if (!ref_pointer_array.is_null()) { @@ -4094,6 +4069,12 @@ bool LEX::can_not_use_merged(bool no_update_or_delete) return TRUE; /* Fall through */ + case SQLCOM_UPDATE: + if (no_update_or_delete && m_sql_cmd && + (m_sql_cmd->sql_command_code() == SQLCOM_UPDATE_MULTI || + query_tables->is_multitable())) + return TRUE; + default: return FALSE; } diff --git a/sql/sql_lex.h b/sql/sql_lex.h index 4f2e775..0badc32 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -36,6 +36,7 @@ #include "sql_limit.h" // Select_limit_counters #include "json_table.h" // Json_table_column #include "sql_schema.h" +#include "sql_class.h" // enum enum_column_usage /* Used for flags of nesting constructs */ #define SELECT_NESTING_MAP_SIZE 64 @@ -873,6 +874,8 @@ class st_select_lex_unit: public st_select_lex_node { { } + void set_query_result(select_result *res) { result= res; } + TABLE *table; /* temporary table using for appending UNION results */ select_result *result; st_select_lex *pre_last_parse; @@ -1005,6 +1008,7 @@ class st_select_lex_unit: public st_select_lex_node { bool add_fake_select_lex(THD *thd); void init_prepare_fake_select_lex(THD *thd, bool first_execution); + void set_prepared() { prepared = true; } inline bool is_prepared() { return prepared; } bool change_result(select_result_interceptor *result, select_result_interceptor *old_result); @@ -1107,6 +1111,7 @@ class st_select_lex: public st_select_lex_node Item *prep_having;/* saved HAVING clause for prepared statement processing */ Item *cond_pushed_into_where; /* condition pushed into WHERE */ Item *cond_pushed_into_having; /* condition pushed into HAVING */ + Item *where_cond_after_prepare; /* nest_levels are local to the query or VIEW, @@ -1215,6 +1220,7 @@ class st_select_lex: public st_select_lex_node List<List_item> save_many_values; List<Item> *save_insert_list; + enum_column_usage item_list_usage; bool is_item_list_lookup:1; /* Needed to correctly generate 'PRIMARY' or 'SIMPLE' for select_type column @@ -1745,6 +1751,25 @@ class Query_tables_list uint sroutines_list_own_elements; /** + Locking state of tables in this particular statement. + + If we under LOCK TABLES or in prelocked mode we consider tables + for the statement to be "locked" if there was a call to lock_tables() + (which called handler::start_stmt()) for tables of this statement + and there was no matching close_thread_tables() call. + + As result this state may differ significantly from one represented + by Open_tables_state::lock/locked_tables_mode more, which are always + "on" under LOCK TABLES or in prelocked mode. + */ + enum enum_lock_tables_state { LTS_NOT_LOCKED = 0, LTS_LOCKED }; + enum_lock_tables_state lock_tables_state; + bool is_query_tables_locked() const + { + return (lock_tables_state == LTS_LOCKED); + } + + /** Number of tables which were open by open_tables() and to be locked by lock_tables(). Note that we set this member only in some cases, when this value @@ -3391,6 +3416,7 @@ struct LEX: public Query_tables_list bool default_used:1; /* using default() function */ bool with_rownum:1; /* Using rownum() function */ bool is_lex_started:1; /* If lex_start() did run. For debugging. */ + /* This variable is used in post-parse stage to declare that sum-functions, or functions which have sense only if GROUP BY is present, are allowed. diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 0597b08..8b906be 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -34,9 +34,7 @@ #include "sql_locale.h" // my_locale_en_US #include "log.h" // flush_error_log #include "sql_view.h" // mysql_create_view, mysql_drop_view -#include "sql_delete.h" // mysql_delete #include "sql_insert.h" // mysql_insert -#include "sql_update.h" // mysql_update, mysql_multi_update #include "sql_partition.h" // struct partition_info #include "sql_db.h" // mysql_change_db, mysql_create_db, // mysql_rm_db, mysql_upgrade_db, @@ -3442,7 +3440,6 @@ int mysql_execute_command(THD *thd, bool is_called_from_prepared_stmt) { int res= 0; - int up_result= 0; LEX *lex= thd->lex; /* first SELECT_LEX (have special meaning for many of non-SELECTcommands) */ SELECT_LEX *select_lex= lex->first_select_lex(); @@ -3454,7 +3451,6 @@ mysql_execute_command(THD *thd, bool is_called_from_prepared_stmt) SELECT_LEX_UNIT *unit= &lex->unit; #ifdef HAVE_REPLICATION /* have table map for update for multi-update statement (BUG#37051) */ - bool have_table_map_for_update= FALSE; /* */ Rpl_filter *rpl_filter; #endif @@ -3576,7 +3572,6 @@ mysql_execute_command(THD *thd, bool is_called_from_prepared_stmt) if (lex->sql_command == SQLCOM_UPDATE_MULTI && thd->table_map_for_update) { - have_table_map_for_update= TRUE; table_map table_map_for_update= thd->table_map_for_update; uint nr= 0; TABLE_LIST *table; @@ -4381,130 +4376,15 @@ mysql_execute_command(THD *thd, bool is_called_from_prepared_stmt) break; } case SQLCOM_UPDATE: - { - WSREP_SYNC_WAIT(thd, WSREP_SYNC_WAIT_BEFORE_UPDATE_DELETE); - ha_rows found= 0, updated= 0; - DBUG_ASSERT(first_table == all_tables && first_table != 0); - WSREP_SYNC_WAIT(thd, WSREP_SYNC_WAIT_BEFORE_UPDATE_DELETE); - - if (update_precheck(thd, all_tables)) - break; - - /* - UPDATE IGNORE can be unsafe. We therefore use row based - logging if mixed or row based logging is available. - TODO: Check if the order of the output of the select statement is - deterministic. Waiting for BUG#42415 - */ - if (lex->ignore) - lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_UPDATE_IGNORE); - - DBUG_ASSERT(select_lex->limit_params.offset_limit == 0); - unit->set_limit(select_lex); - MYSQL_UPDATE_START(thd->query()); - res= up_result= mysql_update(thd, all_tables, - select_lex->item_list, - lex->value_list, - select_lex->where, - select_lex->order_list.elements, - select_lex->order_list.first, - unit->lim.get_select_limit(), - lex->ignore, &found, &updated); - MYSQL_UPDATE_DONE(res, found, updated); - /* mysql_update return 2 if we need to switch to multi-update */ - if (up_result != 2) - break; - if (thd->lex->period_conditions.is_set()) - { - DBUG_ASSERT(0); // Should never happen - goto error; - } - } - /* fall through */ case SQLCOM_UPDATE_MULTI: + case SQLCOM_DELETE: + case SQLCOM_DELETE_MULTI: { DBUG_ASSERT(first_table == all_tables && first_table != 0); - /* if we switched from normal update, rights are checked */ - if (up_result != 2) - { - WSREP_SYNC_WAIT(thd, WSREP_SYNC_WAIT_BEFORE_UPDATE_DELETE); - if ((res= multi_update_precheck(thd, all_tables))) - break; - } - else - res= 0; - - unit->set_limit(select_lex); - /* - We can not use mysql_explain_union() because of parameters of - mysql_select in mysql_multi_update so just set the option if needed - */ - if (thd->lex->describe) - { - select_lex->set_explain_type(FALSE); - select_lex->options|= SELECT_DESCRIBE; - } - - res= mysql_multi_update_prepare(thd); + DBUG_ASSERT(lex->m_sql_cmd != NULL); -#ifdef HAVE_REPLICATION - /* Check slave filtering rules */ - if (unlikely(thd->slave_thread && !have_table_map_for_update)) - { - if (all_tables_not_ok(thd, all_tables)) - { - if (res!= 0) - { - res= 0; /* don't care of prev failure */ - thd->clear_error(); /* filters are of highest prior */ - } - /* we warn the slave SQL thread */ - my_error(ER_SLAVE_IGNORED_TABLE, MYF(0)); - break; - } - if (res) - break; - } - else - { -#endif /* HAVE_REPLICATION */ - if (res) - break; - if (opt_readonly && - !(thd->security_ctx->master_access & PRIV_IGNORE_READ_ONLY) && - some_non_temp_table_to_be_updated(thd, all_tables)) - { - my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--read-only"); - break; - } -#ifdef HAVE_REPLICATION - } /* unlikely */ -#endif - { - multi_update *result_obj; - MYSQL_MULTI_UPDATE_START(thd->query()); - res= mysql_multi_update(thd, all_tables, - &select_lex->item_list, - &lex->value_list, - select_lex->where, - select_lex->options, - lex->duplicates, - lex->ignore, - unit, - select_lex, - &result_obj); - if (result_obj) - { - MYSQL_MULTI_UPDATE_DONE(res, result_obj->num_found(), - result_obj->num_updated()); - res= FALSE; /* Ignore errors here */ - delete result_obj; - } - else - { - MYSQL_MULTI_UPDATE_DONE(1, 0, 0); - } - } + res = lex->m_sql_cmd->execute(thd); + thd->abort_on_warning= 0; break; } case SQLCOM_REPLACE: @@ -4766,129 +4646,6 @@ mysql_execute_command(THD *thd, bool is_called_from_prepared_stmt) break; } - case SQLCOM_DELETE: - { - WSREP_SYNC_WAIT(thd, WSREP_SYNC_WAIT_BEFORE_UPDATE_DELETE); - select_result *sel_result= NULL; - DBUG_ASSERT(first_table == all_tables && first_table != 0); - WSREP_SYNC_WAIT(thd, WSREP_SYNC_WAIT_BEFORE_UPDATE_DELETE); - - if ((res= delete_precheck(thd, all_tables))) - break; - DBUG_ASSERT(select_lex->limit_params.offset_limit == 0); - unit->set_limit(select_lex); - - MYSQL_DELETE_START(thd->query()); - Protocol *save_protocol= NULL; - - if (lex->has_returning()) - { - /* This is DELETE ... RETURNING. It will return output to the client */ - if (thd->lex->analyze_stmt) - { - /* - Actually, it is ANALYZE .. DELETE .. RETURNING. We need to produce - output and then discard it. - */ - sel_result= new (thd->mem_root) select_send_analyze(thd); - save_protocol= thd->protocol; - thd->protocol= new Protocol_discard(thd); - } - else - { - if (!lex->result && !(sel_result= new (thd->mem_root) select_send(thd))) - goto error; - } - } - - res = mysql_delete(thd, all_tables, - select_lex->where, &select_lex->order_list, - unit->lim.get_select_limit(), select_lex->options, - lex->result ? lex->result : sel_result); - - if (save_protocol) - { - delete thd->protocol; - thd->protocol= save_protocol; - } - - if (thd->lex->analyze_stmt || thd->lex->describe) - { - if (!res) - res= thd->lex->explain->send_explain(thd); - } - - delete sel_result; - MYSQL_DELETE_DONE(res, (ulong) thd->get_row_count_func()); - break; - } - case SQLCOM_DELETE_MULTI: - { - WSREP_SYNC_WAIT(thd, WSREP_SYNC_WAIT_BEFORE_UPDATE_DELETE); - DBUG_ASSERT(first_table == all_tables && first_table != 0); - TABLE_LIST *aux_tables= thd->lex->auxiliary_table_list.first; - multi_delete *result; - WSREP_SYNC_WAIT(thd, WSREP_SYNC_WAIT_BEFORE_UPDATE_DELETE); - - if ((res= multi_delete_precheck(thd, all_tables))) - break; - - /* condition will be TRUE on SP re-excuting */ - if (select_lex->item_list.elements != 0) - select_lex->item_list.empty(); - if (add_item_to_list(thd, new (thd->mem_root) Item_null(thd))) - goto error; - - THD_STAGE_INFO(thd, stage_init); - if ((res= open_and_lock_tables(thd, all_tables, TRUE, 0))) - break; - - MYSQL_MULTI_DELETE_START(thd->query()); - if (unlikely(res= mysql_multi_delete_prepare(thd))) - { - MYSQL_MULTI_DELETE_DONE(1, 0); - goto error; - } - - if (likely(!thd->is_fatal_error)) - { - result= new (thd->mem_root) multi_delete(thd, aux_tables, - lex->table_count); - if (likely(result)) - { - if (unlikely(select_lex->vers_setup_conds(thd, aux_tables))) - goto multi_delete_error; - res= mysql_select(thd, - select_lex->get_table_list(), - select_lex->item_list, - select_lex->where, - 0, (ORDER *)NULL, (ORDER *)NULL, (Item *)NULL, - (ORDER *)NULL, - (select_lex->options | thd->variables.option_bits | - SELECT_NO_JOIN_CACHE | SELECT_NO_UNLOCK | - OPTION_SETUP_TABLES_DONE) & ~OPTION_BUFFER_RESULT, - result, unit, select_lex); - res|= (int)(thd->is_error()); - - MYSQL_MULTI_DELETE_DONE(res, result->num_deleted()); - if (res) - result->abort_result_set(); /* for both DELETE and EXPLAIN DELETE */ - else - { - if (lex->describe || lex->analyze_stmt) - res= thd->lex->explain->send_explain(thd); - } - multi_delete_error: - delete result; - } - } - else - { - res= TRUE; // Error - MYSQL_MULTI_DELETE_DONE(1, 0); - } - break; - } case SQLCOM_DROP_SEQUENCE: case SQLCOM_DROP_TABLE: { @@ -7772,12 +7529,16 @@ void create_select_for_variable(THD *thd, LEX_CSTRING *var_name) } -void mysql_init_multi_delete(LEX *lex) +void mysql_init_delete(LEX *lex) { - lex->sql_command= SQLCOM_DELETE_MULTI; mysql_init_select(lex); lex->first_select_lex()->limit_params.clear(); lex->unit.lim.clear(); +} + +void mysql_init_multi_delete(LEX *lex) +{ + lex->sql_command= SQLCOM_DELETE_MULTI; lex->first_select_lex()->table_list. save_and_clear(&lex->auxiliary_table_list); lex->query_tables= 0; diff --git a/sql/sql_parse.h b/sql/sql_parse.h index d3cf83b..3f3302b 100644 --- a/sql/sql_parse.h +++ b/sql/sql_parse.h @@ -93,6 +93,7 @@ void mysql_parse(THD *thd, char *rawbuf, uint length, bool mysql_new_select(LEX *lex, bool move_down, SELECT_LEX *sel); void create_select_for_variable(THD *thd, LEX_CSTRING *var_name); void create_table_set_open_action_and_adjust_tables(LEX *lex); +void mysql_init_delete(LEX *lex); void mysql_init_multi_delete(LEX *lex); bool multi_delete_set_locks_and_link_aux_tables(LEX *lex); void create_table_set_open_action_and_adjust_tables(LEX *lex); diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc index b10a81d..b9ae058 100644 --- a/sql/sql_prepare.cc +++ b/sql/sql_prepare.cc @@ -95,10 +95,8 @@ When one supplies long data for a placeholder: #include "sql_base.h" // open_normal_and_derived_tables #include "sql_cache.h" // query_cache_* #include "sql_view.h" // create_view_precheck -#include "sql_delete.h" // mysql_prepare_delete #include "sql_select.h" // for JOIN #include "sql_insert.h" // upgrade_lock_type_for_insert, mysql_prepare_insert -#include "sql_update.h" // mysql_prepare_update #include "sql_db.h" // mysql_opt_change_db, mysql_change_db #include "sql_derived.h" // mysql_derived_prepare, // mysql_handle_derived @@ -1399,160 +1397,6 @@ static bool mysql_test_insert(Prepared_statement *stmt, } -/** - Validate UPDATE statement. - - @param stmt prepared statement - @param tables list of tables used in this query - - @todo - - here we should send types of placeholders to the client. - - @retval - 0 success - @retval - 1 error, error message is set in THD - @retval - 2 convert to multi_update -*/ - -static int mysql_test_update(Prepared_statement *stmt, - TABLE_LIST *table_list) -{ - int res; - THD *thd= stmt->thd; - uint table_count= 0; - TABLE_LIST *update_source_table; - SELECT_LEX *select= stmt->lex->first_select_lex(); -#ifndef NO_EMBEDDED_ACCESS_CHECKS - privilege_t want_privilege(NO_ACL); -#endif - DBUG_ENTER("mysql_test_update"); - - if (update_precheck(thd, table_list) || - open_tables(thd, &table_list, &table_count, MYSQL_OPEN_FORCE_SHARED_MDL)) - goto error; - - if (mysql_handle_derived(thd->lex, DT_INIT)) - goto error; - - if (((update_source_table= unique_table(thd, table_list, - table_list->next_global, 0)) || - table_list->is_multitable())) - { - DBUG_ASSERT(update_source_table || table_list->view != 0); - DBUG_PRINT("info", ("Switch to multi-update")); - /* pass counter value */ - thd->lex->table_count= table_count; - /* convert to multiupdate */ - DBUG_RETURN(2); - } - - /* - thd->fill_derived_tables() is false here for sure (because it is - preparation of PS, so we even do not check it). - */ - if (table_list->handle_derived(thd->lex, DT_MERGE_FOR_INSERT)) - goto error; - if (table_list->handle_derived(thd->lex, DT_PREPARE)) - goto error; - - if (!table_list->single_table_updatable()) - { - my_error(ER_NON_UPDATABLE_TABLE, MYF(0), table_list->alias.str, "UPDATE"); - goto error; - } - -#ifndef NO_EMBEDDED_ACCESS_CHECKS - /* Force privilege re-checking for views after they have been opened. */ - want_privilege= (table_list->view ? UPDATE_ACL : - table_list->grant.want_privilege); -#endif - - if (mysql_prepare_update(thd, table_list, &select->where, - select->order_list.elements, - select->order_list.first)) - goto error; - -#ifndef NO_EMBEDDED_ACCESS_CHECKS - table_list->grant.want_privilege= want_privilege; - table_list->table->grant.want_privilege= want_privilege; - table_list->register_want_access(want_privilege); -#endif - thd->lex->first_select_lex()->no_wrap_view_item= TRUE; - res= setup_fields(thd, Ref_ptr_array(), - select->item_list, MARK_COLUMNS_READ, 0, NULL, 0); - thd->lex->first_select_lex()->no_wrap_view_item= FALSE; - if (res) - goto error; -#ifndef NO_EMBEDDED_ACCESS_CHECKS - /* Check values */ - table_list->grant.want_privilege= - table_list->table->grant.want_privilege= - (SELECT_ACL & ~table_list->table->grant.privilege); - table_list->register_want_access(SELECT_ACL); -#endif - if (setup_fields(thd, Ref_ptr_array(), - stmt->lex->value_list, COLUMNS_READ, 0, NULL, 0) || - check_unique_table(thd, table_list)) - goto error; - /* TODO: here we should send types of placeholders to the client. */ - DBUG_RETURN(0); -error: - DBUG_RETURN(1); -} - - -/** - Validate DELETE statement. - - @param stmt prepared statement - @param tables list of tables used in this query - - @retval - FALSE success - @retval - TRUE error, error message is set in THD -*/ - -static bool mysql_test_delete(Prepared_statement *stmt, - TABLE_LIST *table_list) -{ - uint table_count= 0; - THD *thd= stmt->thd; - LEX *lex= stmt->lex; - bool delete_while_scanning; - DBUG_ENTER("mysql_test_delete"); - - if (delete_precheck(thd, table_list) || - open_tables(thd, &table_list, &table_count, MYSQL_OPEN_FORCE_SHARED_MDL)) - goto error; - - if (mysql_handle_derived(thd->lex, DT_INIT)) - goto error; - if (mysql_handle_derived(thd->lex, DT_MERGE_FOR_INSERT)) - goto error; - if (mysql_handle_derived(thd->lex, DT_PREPARE)) - goto error; - - if (!table_list->single_table_updatable()) - { - my_error(ER_NON_UPDATABLE_TABLE, MYF(0), table_list->alias.str, "DELETE"); - goto error; - } - if (!table_list->table || !table_list->table->is_created()) - { - my_error(ER_VIEW_DELETE_MERGE_VIEW, MYF(0), - table_list->view_db.str, table_list->view_name.str); - goto error; - } - - DBUG_RETURN(mysql_prepare_delete(thd, table_list, - &lex->first_select_lex()->where, - &delete_while_scanning)); -error: - DBUG_RETURN(TRUE); -} /** @@ -2135,74 +1979,6 @@ static bool mysql_test_create_view(Prepared_statement *stmt) } -/* - Validate and prepare for execution a multi update statement. - - @param stmt prepared statement - @param tables list of tables used in this query - @param converted converted to multi-update from usual update - - @retval - FALSE success - @retval - TRUE error, error message is set in THD -*/ - -static bool mysql_test_multiupdate(Prepared_statement *stmt, - TABLE_LIST *tables, - bool converted) -{ - /* if we switched from normal update, rights are checked */ - if (!converted && multi_update_precheck(stmt->thd, tables)) - return TRUE; - - return select_like_stmt_test(stmt, &mysql_multi_update_prepare, - OPTION_SETUP_TABLES_DONE); -} - - -/** - Validate and prepare for execution a multi delete statement. - - @param stmt prepared statement - @param tables list of tables used in this query - - @retval - FALSE success - @retval - TRUE error, error message in THD is set. -*/ - -static bool mysql_test_multidelete(Prepared_statement *stmt, - TABLE_LIST *tables) -{ - THD *thd= stmt->thd; - - thd->lex->current_select= thd->lex->first_select_lex(); - if (add_item_to_list(thd, new (thd->mem_root) - Item_null(thd))) - { - my_error(ER_OUTOFMEMORY, MYF(ME_FATAL), 0); - goto error; - } - - if (multi_delete_precheck(thd, tables) || - select_like_stmt_test_with_open(stmt, tables, - &mysql_multi_delete_prepare, - OPTION_SETUP_TABLES_DONE)) - goto error; - if (!tables->table) - { - my_error(ER_VIEW_DELETE_MERGE_VIEW, MYF(0), - tables->view_db.str, tables->view_name.str); - goto error; - } - return FALSE; -error: - return TRUE; -} - - /** Wrapper for mysql_insert_select_prepare, to make change of local tables after open_normal_and_derived_tables() call. @@ -2484,18 +2260,14 @@ static bool check_prepared_statement(Prepared_statement *stmt) break; case SQLCOM_UPDATE: - res= mysql_test_update(stmt, tables); - /* mysql_test_update returns 2 if we need to switch to multi-update */ - if (res != 2) - break; - /* fall through */ case SQLCOM_UPDATE_MULTI: - res= mysql_test_multiupdate(stmt, tables, res == 2); - break; - case SQLCOM_DELETE: - res= mysql_test_delete(stmt, tables); + case SQLCOM_DELETE_MULTI: + res = lex->m_sql_cmd->prepare(thd); + if (!res) + lex->m_sql_cmd->unprepare(thd); break; + /* The following allow WHERE clause, so they must be tested like SELECT */ case SQLCOM_SHOW_DATABASES: case SQLCOM_SHOW_TABLES: @@ -2632,10 +2404,6 @@ static bool check_prepared_statement(Prepared_statement *stmt) res= mysql_test_set_fields(stmt, tables, &lex->var_list); break; - case SQLCOM_DELETE_MULTI: - res= mysql_test_multidelete(stmt, tables); - break; - case SQLCOM_INSERT_SELECT: case SQLCOM_REPLACE_SELECT: res= mysql_test_insert_select(stmt, tables); @@ -4373,6 +4141,9 @@ bool Prepared_statement::prepare(const char *packet, uint packet_len) thd->is_error() || init_param_array(this)); + if (lex->m_sql_cmd) + lex->m_sql_cmd->set_owner(this); + if (thd->security_ctx->password_expired && lex->sql_command != SQLCOM_SET_OPTION && lex->sql_command != SQLCOM_PREPARE && diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 2a56292..7db5daa 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -1430,7 +1430,7 @@ JOIN::prepare(TABLE_LIST *tables_init, COND *conds_init, uint og_num, } } - if (setup_fields(thd, ref_ptrs, fields_list, MARK_COLUMNS_READ, + if (setup_fields(thd, ref_ptrs, fields_list, select_lex->item_list_usage, &all_fields, &select_lex->pre_fix, 1)) DBUG_RETURN(-1); thd->lex->current_select->context_analysis_place= save_place; @@ -1720,6 +1720,8 @@ JOIN::prepare(TABLE_LIST *tables_init, COND *conds_init, uint og_num, if (!procedure && result && result->prepare(fields_list, unit_arg)) goto err; /* purecov: inspected */ + select_lex->where_cond_after_prepare= conds; + unit= unit_arg; if (prepare_stage2()) goto err; @@ -29028,7 +29030,8 @@ static bool get_range_limit_read_cost(const JOIN_TAB *tab, @note This function takes into account table->opt_range_condition_rows statistic (that is calculated by the make_join_statistics function). - However, single table procedures such as mysql_update() and mysql_delete() + However, single table procedures such as Sql_cmd_update:update_single_table() + and Sql_cmd_delete::delete_single_table() never call make_join_statistics, so they have to update it manually (@see get_index_for_order()). */ @@ -30463,6 +30466,208 @@ static bool process_direct_rownum_comparison(THD *thd, SELECT_LEX_UNIT *unit, } +static void MYSQL_DML_START(THD *thd) +{ + switch (thd->lex->sql_command) { + + case SQLCOM_UPDATE: + MYSQL_UPDATE_START(thd->query()); + break; + case SQLCOM_UPDATE_MULTI: + MYSQL_MULTI_UPDATE_START(thd->query()); + break; + case SQLCOM_DELETE: + MYSQL_DELETE_START(thd->query()); + break; + case SQLCOM_DELETE_MULTI: + MYSQL_MULTI_DELETE_START(thd->query()); + break; + default: + DBUG_ASSERT(0); + } +} + + +static void MYSQL_DML_DONE(THD *thd, int rc) +{ + switch (thd->lex->sql_command) { + + case SQLCOM_UPDATE: + MYSQL_UPDATE_DONE( + rc, + (rc ? 0 : + ((multi_update*)(((Sql_cmd_dml*)(thd->lex->m_sql_cmd))->get_result())) + ->num_found()), + (rc ? 0 : + ((multi_update*)(((Sql_cmd_dml*)(thd->lex->m_sql_cmd))->get_result())) + ->num_updated())); + break; + case SQLCOM_UPDATE_MULTI: + MYSQL_MULTI_UPDATE_DONE( + rc, + (rc ? 0 : + ((multi_update*)(((Sql_cmd_dml*)(thd->lex->m_sql_cmd))->get_result())) + ->num_found()), + (rc ? 0 : + ((multi_update*)(((Sql_cmd_dml*)(thd->lex->m_sql_cmd))->get_result())) + ->num_updated())); + break; + case SQLCOM_DELETE: + MYSQL_DELETE_DONE(rc, (rc ? 0 : (ulong) (thd->get_row_count_func()))); + break; + case SQLCOM_DELETE_MULTI: + MYSQL_MULTI_DELETE_DONE( + rc, + (rc ? 0 : + ((multi_delete*)(((Sql_cmd_dml*)(thd->lex->m_sql_cmd))->get_result())) + ->num_deleted())); + break; + default: + DBUG_ASSERT(0); + } +} + +bool Sql_cmd_dml::prepare(THD *thd) +{ + lex= thd->lex; + SELECT_LEX_UNIT *unit= &lex->unit; + + DBUG_ASSERT(!is_prepared()); + + // Perform a coarse statement-specific privilege check. + if (precheck(thd)) + goto err; + + MYSQL_DML_START(thd); + + lex->context_analysis_only|= CONTEXT_ANALYSIS_ONLY_DERIVED; + + if (open_tables_for_query(thd, lex->query_tables, &table_count, 0, + get_dml_prelocking_strategy())) + { + if (thd->is_error()) + goto err; + (void)unit->cleanup(); + return true; + } + + if (prepare_inner(thd)) + goto err; + + lex->context_analysis_only&= ~CONTEXT_ANALYSIS_ONLY_DERIVED; + + set_prepared(); + unit->set_prepared(); + + return false; + +err: + DBUG_ASSERT(thd->is_error()); + DBUG_PRINT("info", ("report_error: %d", thd->is_error())); + + (void)unit->cleanup(); + + return true; +} + +bool Sql_cmd_dml::execute(THD *thd) +{ + lex = thd->lex; + bool res; + + SELECT_LEX_UNIT *unit = &lex->unit; + SELECT_LEX *select_lex= lex->first_select_lex(); + + if (!is_prepared()) + { + if (prepare(thd)) + goto err; + } + else + { + if (precheck(thd)) + goto err; + + MYSQL_DML_START(thd); + + if (open_tables_for_query(thd, lex->query_tables, &table_count, 0, + get_dml_prelocking_strategy())) + goto err; + } + + THD_STAGE_INFO(thd, stage_init); + + /* + Locking of tables is done after preparation but before optimization. + This allows to do better partition pruning and avoid locking unused + partitions. As a consequence, in such a case, prepare stage can rely only + on metadata about tables used and not data from them. + */ + if (!is_empty_query()) + { + if (lock_tables(thd, lex->query_tables, table_count, 0)) + goto err; + } + + unit->set_limit(select_lex); + + // Perform statement-specific execution + res = execute_inner(thd); + + if (res) + goto err; + + res= unit->cleanup(); + + // "unprepare" this object since unit->cleanup actually unprepares + unprepare(thd); + + THD_STAGE_INFO(thd, stage_end); + + MYSQL_DML_DONE(thd, res); + + return res; + +err: + DBUG_ASSERT(thd->is_error() || thd->killed); + MYSQL_DML_DONE(thd, 1); + THD_STAGE_INFO(thd, stage_end); + (void)unit->cleanup(); + + return thd->is_error(); +} + + +bool Sql_cmd_dml::execute_inner(THD *thd) +{ + SELECT_LEX_UNIT *unit = &lex->unit; + SELECT_LEX *select_lex= unit->first_select(); + JOIN *join= select_lex->join; + + if (join->optimize()) + goto err; + + if (thd->lex->describe & DESCRIBE_EXTENDED) + { + join->conds_history= join->conds; + join->having_history= (join->having?join->having:join->tmp_having); + } + + if (unlikely(thd->is_error())) + goto err; + + join->exec(); + + if (thd->lex->describe & DESCRIBE_EXTENDED) + { + select_lex->where= join->conds_history; + select_lex->having= join->having_history; + } + +err: + return join->error; +} + /** @} (end of group Query_Optimizer) diff --git a/sql/sql_update.cc b/sql/sql_update.cc index 74ed078..2dd7db5 100644 --- a/sql/sql_update.cc +++ b/sql/sql_update.cc @@ -197,22 +197,11 @@ static bool check_fields(THD *thd, TABLE_LIST *table, List<Item> &items, return true; } - DBUG_ASSERT(thd->lex->sql_command == SQLCOM_UPDATE); - for (List_iterator_fast<Item> it(items); (item=it++);) - { - Field *f= item->field_for_view_update()->field; - vers_select_conds_t &period= table->period_conditions; - if (period.field_start->field == f || period.field_end->field == f) - { - my_error(ER_PERIOD_COLUMNS_UPDATED, MYF(0), - item->name.str, period.name.str); - return true; - } - } } return FALSE; } + bool TABLE::vers_check_update(List<Item> &items) { List_iterator<Item> it(items); @@ -339,36 +328,19 @@ int cut_fields_for_portion_of_time(THD *thd, TABLE *table, return res; } -/* - Process usual UPDATE - - SYNOPSIS - mysql_update() - thd thread handler - fields fields for update - values values of fields for update - conds WHERE clause expression - order_num number of elemen in ORDER BY clause - order ORDER BY clause list - limit limit clause - - RETURN - 0 - OK - 2 - privilege check and openning table passed, but we need to convert to - multi-update because of view substitution - 1 - error -*/ -int mysql_update(THD *thd, - TABLE_LIST *table_list, - List<Item> &fields, - List<Item> &values, - COND *conds, - uint order_num, ORDER *order, - ha_rows limit, - bool ignore, - ha_rows *found_return, ha_rows *updated_return) +bool Sql_cmd_update::update_single_table(THD *thd) { + SELECT_LEX_UNIT *unit = &lex->unit; + SELECT_LEX *select_lex= unit->first_select(); + TABLE_LIST *const table_list = select_lex->get_table_list(); + List<Item> *fields= &select_lex->item_list; + List<Item> *values= &lex->value_list; + COND *conds= select_lex->where_cond_after_prepare; + ORDER *order= select_lex->order_list.first; + ha_rows limit= unit->lim.get_select_limit(); + bool ignore= lex->ignore; + bool using_limit= limit != HA_POS_ERROR; bool safe_update= (thd->variables.option_bits & OPTION_SAFE_UPDATES) && !thd->lex->describe; @@ -380,76 +352,39 @@ int mysql_update(THD *thd, ha_rows dup_key_found; bool need_sort= TRUE; bool reverse= FALSE; -#ifndef NO_EMBEDDED_ACCESS_CHECKS - privilege_t want_privilege(NO_ACL); -#endif - uint table_count= 0; ha_rows updated, updated_or_same, found; key_map old_covering_keys; - TABLE *table; + TABLE *table; SQL_SELECT *select= NULL; SORT_INFO *file_sort= 0; READ_RECORD info; - SELECT_LEX *select_lex= thd->lex->first_select_lex(); ulonglong id; List<Item> all_fields; killed_state killed_status= NOT_KILLED; bool has_triggers, binlog_is_row, do_direct_update= FALSE; Update_plan query_plan(thd->mem_root); Explain_update *explain; - TABLE_LIST *update_source_table; query_plan.index= MAX_KEY; query_plan.using_filesort= FALSE; // For System Versioning (may need to insert new fields to a table). ha_rows rows_inserted= 0; - DBUG_ENTER("mysql_update"); + DBUG_ENTER("Sql_cmd_update::update_single_table"); + THD_STAGE_INFO(thd, stage_init_update); create_explain_query(thd->lex, thd->mem_root); - if (open_tables(thd, &table_list, &table_count, 0)) - DBUG_RETURN(1); - - /* Prepare views so they are handled correctly */ - if (mysql_handle_derived(thd->lex, DT_INIT)) - DBUG_RETURN(1); - - if (table_list->has_period() && table_list->is_view_or_derived()) - { - my_error(ER_IT_IS_A_VIEW, MYF(0), table_list->table_name.str); - DBUG_RETURN(TRUE); - } - - if (((update_source_table=unique_table(thd, table_list, - table_list->next_global, 0)) || - table_list->is_multitable())) - { - DBUG_ASSERT(update_source_table || table_list->view != 0); - DBUG_PRINT("info", ("Switch to multi-update")); - /* pass counter value */ - thd->lex->table_count= table_count; - if (thd->lex->period_conditions.is_set()) - { - my_error(ER_NOT_SUPPORTED_YET, MYF(0), - "updating and querying the same temporal periods table"); - - DBUG_RETURN(1); - } - - /* convert to multiupdate */ - DBUG_RETURN(2); - } - if (lock_tables(thd, table_list, table_count, 0)) - DBUG_RETURN(1); - (void) read_statistics_for_tables_if_needed(thd, table_list); + thd->table_map_for_update= 0; - THD_STAGE_INFO(thd, stage_init_update); if (table_list->handle_derived(thd->lex, DT_MERGE_FOR_INSERT)) DBUG_RETURN(1); if (table_list->handle_derived(thd->lex, DT_PREPARE)) DBUG_RETURN(1); + if (setup_ftfuncs(select_lex)) + DBUG_RETURN(-1); + table= table_list->table; if (!table_list->single_table_updatable()) @@ -458,85 +393,26 @@ int mysql_update(THD *thd, DBUG_RETURN(1); } - /* Calculate "table->covering_keys" based on the WHERE */ - table->covering_keys= table->s->keys_in_use; table->opt_range_keys.clear_all(); query_plan.select_lex= thd->lex->first_select_lex(); query_plan.table= table; -#ifndef NO_EMBEDDED_ACCESS_CHECKS - /* Force privilege re-checking for views after they have been opened. */ - want_privilege= (table_list->view ? UPDATE_ACL : - table_list->grant.want_privilege); -#endif thd->lex->promote_select_describe_flag_if_needed(); - if (mysql_prepare_update(thd, table_list, &conds, order_num, order)) - DBUG_RETURN(1); - - if (table_list->has_period()) - { - if (!table_list->period_conditions.start.item->const_item() - || !table_list->period_conditions.end.item->const_item()) - { - my_error(ER_NOT_CONSTANT_EXPRESSION, MYF(0), "FOR PORTION OF"); - DBUG_RETURN(true); - } - table->no_cache= true; - } - old_covering_keys= table->covering_keys; // Keys used in WHERE - /* Check the fields we are going to modify */ -#ifndef NO_EMBEDDED_ACCESS_CHECKS - table_list->grant.want_privilege= table->grant.want_privilege= want_privilege; - table_list->register_want_access(want_privilege); -#endif - /* 'Unfix' fields to allow correct marking by the setup_fields function. */ - if (table_list->is_view()) - unfix_fields(fields); - if (setup_fields_with_no_wrap(thd, Ref_ptr_array(), - fields, MARK_COLUMNS_WRITE, 0, 0)) - DBUG_RETURN(1); /* purecov: inspected */ - if (check_fields(thd, table_list, fields, table_list->view)) - { - DBUG_RETURN(1); - } - bool has_vers_fields= table->vers_check_update(fields); - if (check_key_in_view(thd, table_list)) - { - my_error(ER_NON_UPDATABLE_TABLE, MYF(0), table_list->alias.str, "UPDATE"); - DBUG_RETURN(1); - } + bool has_vers_fields= table->vers_check_update(*fields); if (table->default_field) table->mark_default_fields_for_write(false); -#ifndef NO_EMBEDDED_ACCESS_CHECKS - /* Check values */ - table_list->grant.want_privilege= table->grant.want_privilege= - (SELECT_ACL & ~table->grant.privilege); -#endif - if (setup_fields(thd, Ref_ptr_array(), values, MARK_COLUMNS_READ, 0, NULL, 0)) - { - free_underlaid_joins(thd, select_lex); - DBUG_RETURN(1); /* purecov: inspected */ - } - - if (check_unique_table(thd, table_list)) - DBUG_RETURN(TRUE); - - switch_to_nullable_trigger_fields(fields, table); - switch_to_nullable_trigger_fields(values, table); + switch_to_nullable_trigger_fields(*fields, table); + switch_to_nullable_trigger_fields(*values, table); /* Apply the IN=>EXISTS transformation to all subqueries and optimize them */ if (select_lex->optimize_unflattened_subqueries(false)) DBUG_RETURN(TRUE); - if (select_lex->inner_refs_list.elements && - fix_inner_refs(thd, all_fields, select_lex, select_lex->ref_pointer_array)) - DBUG_RETURN(1); - if (conds) { Item::cond_result cond_value; @@ -776,9 +652,9 @@ int mysql_update(THD *thd, } if (use_direct_update && - !table->file->info_push(INFO_KIND_UPDATE_FIELDS, &fields) && - !table->file->info_push(INFO_KIND_UPDATE_VALUES, &values) && - !table->file->direct_update_rows_init(&fields)) + !table->file->info_push(INFO_KIND_UPDATE_FIELDS, fields) && + !table->file->info_push(INFO_KIND_UPDATE_VALUES, values) && + !table->file->direct_update_rows_init(fields)) { do_direct_update= TRUE; @@ -1027,7 +903,7 @@ int mysql_update(THD *thd, cut_fields_for_portion_of_time(thd, table, table_list->period_conditions); - if (fill_record_n_invoke_before_triggers(thd, table, fields, values, 0, + if (fill_record_n_invoke_before_triggers(thd, table, *fields, *values, 0, TRG_EVENT_UPDATE)) break; /* purecov: inspected */ @@ -1360,9 +1236,9 @@ int mysql_update(THD *thd, thd->lex->current_select->save_leaf_tables(thd); thd->lex->current_select->first_cond_optimization= 0; } - *found_return= found; - *updated_return= updated; - + ((multi_update *)result)->set_found(found); + ((multi_update *)result)->set_updated(updated); + if (unlikely(thd->lex->analyze_stmt)) goto emit_explain_and_leave; @@ -1394,75 +1270,6 @@ int mysql_update(THD *thd, DBUG_RETURN((err2 || thd->is_error()) ? 1 : 0); } -/* - Prepare items in UPDATE statement - - SYNOPSIS - mysql_prepare_update() - thd - thread handler - table_list - global/local table list - conds - conditions - order_num - number of ORDER BY list entries - order - ORDER BY clause list - - RETURN VALUE - FALSE OK - TRUE error -*/ -bool mysql_prepare_update(THD *thd, TABLE_LIST *table_list, - Item **conds, uint order_num, ORDER *order) -{ - Item *fake_conds= 0; -#ifndef NO_EMBEDDED_ACCESS_CHECKS - TABLE *table= table_list->table; -#endif - List<Item> all_fields; - SELECT_LEX *select_lex= thd->lex->first_select_lex(); - DBUG_ENTER("mysql_prepare_update"); - -#ifndef NO_EMBEDDED_ACCESS_CHECKS - table_list->grant.want_privilege= table->grant.want_privilege= - (SELECT_ACL & ~table->grant.privilege); - table_list->register_want_access(SELECT_ACL); -#endif - - thd->lex->allow_sum_func.clear_all(); - - if (table_list->has_period() && - select_lex->period_setup_conds(thd, table_list)) - DBUG_RETURN(true); - - DBUG_ASSERT(table_list->table); - // conds could be cached from previous SP call - DBUG_ASSERT(!table_list->vers_conditions.need_setup() || - !*conds || thd->stmt_arena->is_stmt_execute()); - if (select_lex->vers_setup_conds(thd, table_list)) - DBUG_RETURN(TRUE); - - *conds= select_lex->where; - - /* - We do not call DT_MERGE_FOR_INSERT because it has no sense for simple - (not multi-) update - */ - if (mysql_handle_derived(thd->lex, DT_PREPARE)) - DBUG_RETURN(TRUE); - - if (setup_tables_and_check_access(thd, &select_lex->context, - &select_lex->top_join_list, table_list, - select_lex->leaf_tables, - FALSE, UPDATE_ACL, SELECT_ACL, TRUE) || - setup_conds(thd, table_list, select_lex->leaf_tables, conds) || - select_lex->setup_ref_array(thd, order_num) || - setup_order(thd, select_lex->ref_pointer_array, - table_list, all_fields, all_fields, order) || - setup_ftfuncs(select_lex)) - DBUG_RETURN(TRUE); - - - select_lex->fix_prepare_information(thd, conds, &fake_conds); - DBUG_RETURN(FALSE); -} /** Check that we are not using table that we are updating in a sub select @@ -1690,15 +1497,6 @@ static bool multi_update_check_table_access(THD *thd, TABLE_LIST *table, } -class Multiupdate_prelocking_strategy : public DML_prelocking_strategy -{ - bool done; - bool has_prelocking_list; -public: - void reset(THD *thd); - bool handle_end(THD *thd); -}; - void Multiupdate_prelocking_strategy::reset(THD *thd) { done= false; @@ -1728,7 +1526,13 @@ bool Multiupdate_prelocking_strategy::handle_end(THD *thd) mysql_handle_derived(lex, DT_PREPARE)) DBUG_RETURN(1); - /* + if (table_list->has_period() && table_list->is_view_or_derived()) + { + my_error(ER_IT_IS_A_VIEW, MYF(0), table_list->table_name.str); + DBUG_RETURN(TRUE); + } + + /* setup_tables() need for VIEWs. JOIN::prepare() will call setup_tables() second time, but this call will do nothing (there are check for second call in setup_tables()). @@ -1739,6 +1543,10 @@ bool Multiupdate_prelocking_strategy::handle_end(THD *thd) FALSE, UPDATE_ACL, SELECT_ACL, TRUE)) DBUG_RETURN(1); + if (table_list->has_period() && + select_lex->period_setup_conds(thd, table_list)) + DBUG_RETURN(true); + List<Item> *fields= &lex->first_select_lex()->item_list; if (setup_fields_with_no_wrap(thd, Ref_ptr_array(), *fields, MARK_COLUMNS_WRITE, 0, 0)) @@ -1847,153 +1655,6 @@ bool Multiupdate_prelocking_strategy::handle_end(THD *thd) DBUG_RETURN(0); } -/* - make update specific preparation and checks after opening tables - - SYNOPSIS - mysql_multi_update_prepare() - thd thread handler - - RETURN - FALSE OK - TRUE Error -*/ - -int mysql_multi_update_prepare(THD *thd) -{ - LEX *lex= thd->lex; - TABLE_LIST *table_list= lex->query_tables; - TABLE_LIST *tl; - Multiupdate_prelocking_strategy prelocking_strategy; - uint table_count= lex->table_count; - DBUG_ENTER("mysql_multi_update_prepare"); - - /* - Open tables and create derived ones, but do not lock and fill them yet. - - During prepare phase acquire only S metadata locks instead of SW locks to - keep prepare of multi-UPDATE compatible with concurrent LOCK TABLES WRITE - and global read lock. - - Don't evaluate any subqueries even if constant, because - tables aren't locked yet. - */ - lex->context_analysis_only|= CONTEXT_ANALYSIS_ONLY_DERIVED; - if (thd->lex->sql_command == SQLCOM_UPDATE_MULTI) - { - if (open_tables(thd, &table_list, &table_count, - thd->stmt_arena->is_stmt_prepare() ? MYSQL_OPEN_FORCE_SHARED_MDL : 0, - &prelocking_strategy)) - DBUG_RETURN(TRUE); - } - else - { - /* following need for prepared statements, to run next time multi-update */ - thd->lex->sql_command= SQLCOM_UPDATE_MULTI; - prelocking_strategy.reset(thd); - if (prelocking_strategy.handle_end(thd)) - DBUG_RETURN(TRUE); - } - - /* now lock and fill tables */ - if (!thd->stmt_arena->is_stmt_prepare() && - lock_tables(thd, table_list, table_count, 0)) - DBUG_RETURN(TRUE); - - lex->context_analysis_only&= ~CONTEXT_ANALYSIS_ONLY_DERIVED; - - (void) read_statistics_for_tables_if_needed(thd, table_list); - /* @todo: downgrade the metadata locks here. */ - - /* - Check that we are not using table that we are updating, but we should - skip all tables of UPDATE SELECT itself - */ - lex->first_select_lex()->exclude_from_table_unique_test= TRUE; - /* We only need SELECT privilege for columns in the values list */ - List_iterator<TABLE_LIST> ti(lex->first_select_lex()->leaf_tables); - while ((tl= ti++)) - { - if (tl->is_jtbm()) - continue; - TABLE *table= tl->table; - TABLE_LIST *tlist; - if (!(tlist= tl->top_table())->derived) - { - tlist->grant.want_privilege= - (SELECT_ACL & ~tlist->grant.privilege); - table->grant.want_privilege= (SELECT_ACL & ~table->grant.privilege); - } - DBUG_PRINT("info", ("table: %s want_privilege: %llx", tl->alias.str, - (longlong) table->grant.want_privilege)); - } - /* - Set exclude_from_table_unique_test value back to FALSE. It is needed for - further check in multi_update::prepare whether to use record cache. - */ - lex->first_select_lex()->exclude_from_table_unique_test= FALSE; - - if (lex->save_prep_leaf_tables()) - DBUG_RETURN(TRUE); - - DBUG_RETURN (FALSE); -} - - -/* - Setup multi-update handling and call SELECT to do the join -*/ - -bool mysql_multi_update(THD *thd, TABLE_LIST *table_list, List<Item> *fields, - List<Item> *values, COND *conds, ulonglong options, - enum enum_duplicates handle_duplicates, - bool ignore, SELECT_LEX_UNIT *unit, - SELECT_LEX *select_lex, multi_update **result) -{ - bool res; - DBUG_ENTER("mysql_multi_update"); - - if (!(*result= new (thd->mem_root) multi_update(thd, table_list, - &thd->lex->first_select_lex()->leaf_tables, - fields, values, handle_duplicates, ignore))) - { - DBUG_RETURN(TRUE); - } - - if ((*result)->init(thd)) - DBUG_RETURN(1); - - thd->abort_on_warning= !ignore && thd->is_strict_mode(); - List<Item> total_list; - - if (setup_tables(thd, &select_lex->context, &select_lex->top_join_list, - table_list, select_lex->leaf_tables, FALSE, FALSE)) - DBUG_RETURN(1); - - if (select_lex->vers_setup_conds(thd, table_list)) - DBUG_RETURN(1); - - res= mysql_select(thd, - table_list, total_list, conds, - select_lex->order_list.elements, - select_lex->order_list.first, NULL, NULL, NULL, - options | SELECT_NO_JOIN_CACHE | SELECT_NO_UNLOCK | - OPTION_SETUP_TABLES_DONE, - *result, unit, select_lex); - - DBUG_PRINT("info",("res: %d report_error: %d", res, (int) thd->is_error())); - res|= thd->is_error(); - if (unlikely(res)) - (*result)->abort_result_set(); - else - { - if (thd->lex->describe || thd->lex->analyze_stmt) - res= thd->lex->explain->send_explain(thd); - } - thd->abort_on_warning= 0; - DBUG_RETURN(res); -} - multi_update::multi_update(THD *thd_arg, TABLE_LIST *table_list, List<TABLE_LIST> *leaves_list, @@ -2029,6 +1690,19 @@ bool multi_update::init(THD *thd) } +bool multi_update::init_for_single_table(THD *thd) +{ + List_iterator_fast<TABLE_LIST> li(*leaves); + TABLE_LIST *tbl; + while ((tbl =li++)) + { + if (updated_leaves.push_back(tbl, thd->mem_root)) + return true; + } + return false; +} + + /* Connect fields with tables and create list of tables that are updated */ @@ -2102,7 +1776,8 @@ int multi_update::prepare(List<Item> &not_used_values, { table->read_set= &table->def_read_set; bitmap_union(table->read_set, &table->tmp_set); - table->file->prepare_for_insert(1); + if (!(thd->lex->context_analysis_only & CONTEXT_ANALYSIS_ONLY_PREPARE)) + table->file->prepare_for_insert(1); } } if (unlikely(error)) @@ -3123,3 +2798,212 @@ bool multi_update::send_eof() } DBUG_RETURN(FALSE); } + + +bool Sql_cmd_update::precheck(THD *thd) +{ + if (!multitable) + { + if (update_precheck(thd, lex->query_tables)) + return true; + } + else + { + if (multi_update_precheck(thd, lex->query_tables)) + return true; + } + + WSREP_SYNC_WAIT(thd, WSREP_SYNC_WAIT_BEFORE_UPDATE_DELETE); + + return false; + +#ifdef WITH_WSREP +wsrep_error_label: +#endif + return true; +} + + +bool Sql_cmd_update::prepare_inner(THD *thd) +{ + JOIN *join; + int err= 0; + SELECT_LEX *const select_lex = thd->lex->first_select_lex(); + TABLE_LIST *const table_list = select_lex->get_table_list(); + ulonglong select_options= select_lex->options; + bool free_join= 1; + DBUG_ENTER("Sql_cmd_update::prepare_inner"); + + (void) read_statistics_for_tables_if_needed(thd, table_list); + + THD_STAGE_INFO(thd, stage_init_update); + + if (!multitable) + { + if (mysql_handle_derived(lex, DT_INIT)) + DBUG_RETURN(TRUE); + } + + if (table_list->has_period() && table_list->is_view_or_derived()) + { + my_error(ER_IT_IS_A_VIEW, MYF(0), table_list->table_name.str); + DBUG_RETURN(TRUE); + } + + if (!multitable) + { + TABLE_LIST *update_source_table= 0; + + if (((update_source_table=unique_table(thd, table_list, + table_list->next_global, 0)) || + table_list->is_multitable())) + { + DBUG_ASSERT(update_source_table || table_list->view != 0); + if (thd->lex->period_conditions.is_set()) + { + my_error(ER_NOT_SUPPORTED_YET, MYF(0), + "updating and querying the same temporal periods table"); + DBUG_RETURN(TRUE); + } + multitable= true; + } + } + + if(!multitable) + { + if (table_list->is_view_or_derived() && + select_lex->leaf_tables.elements > 1) + multitable = true; + } + + if (!multitable) + { + if (lex->ignore) + lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_UPDATE_IGNORE); + } + + if (!(result= new (thd->mem_root) multi_update(thd, table_list, + &select_lex->leaf_tables, + &select_lex->item_list, + &lex->value_list, + lex->duplicates, + lex->ignore))) + { + DBUG_RETURN(TRUE); + } + + if (((multi_update *)result)->init(thd)) + DBUG_RETURN(TRUE); + + if (setup_tables(thd, &select_lex->context, &select_lex->top_join_list, + table_list, select_lex->leaf_tables, false, false)) + DBUG_RETURN(TRUE); + + if (select_lex->vers_setup_conds(thd, table_list)) + DBUG_RETURN(TRUE); + + { + if (thd->lex->describe) + select_options|= SELECT_DESCRIBE; + + /* + When in EXPLAIN, delay deleting the joins so that they are still + available when we're producing EXPLAIN EXTENDED warning text. + */ + if (select_options & SELECT_DESCRIBE) + free_join= 0; + + select_options|= + SELECT_NO_JOIN_CACHE | SELECT_NO_UNLOCK | OPTION_SETUP_TABLES_DONE; + + if (!(join= new (thd->mem_root) JOIN(thd, select_lex->item_list, + select_options, result))) + DBUG_RETURN(TRUE); + THD_STAGE_INFO(thd, stage_init); + select_lex->join= join; + thd->lex->used_tables=0; + select_lex->item_list_usage= MARK_COLUMNS_WRITE; + if ((err= join->prepare(table_list, select_lex->where, + select_lex->order_list.elements, + select_lex->order_list.first, + false, NULL, NULL, NULL, + select_lex, &lex->unit))) + { + goto err; + } + + } + + if (table_list->has_period()) + { + Item *item; + for (List_iterator_fast<Item> it(select_lex->item_list); (item=it++);) + { + Field *f= item->field_for_view_update()->field; + vers_select_conds_t &period= table_list->period_conditions; + if (period.field_start->field == f || period.field_end->field == f) + { + my_error(ER_PERIOD_COLUMNS_UPDATED, MYF(0), + item->name.str, period.name.str); + DBUG_RETURN(true); + } + } + + if (!table_list->period_conditions.start.item->const_item() + || !table_list->period_conditions.end.item->const_item()) + { + my_error(ER_NOT_CONSTANT_EXPRESSION, MYF(0), "FOR PORTION OF"); + DBUG_RETURN(true); + } + table_list->table->no_cache= true; + } + + + free_join= false; + +err: + + if (free_join) + { + THD_STAGE_INFO(thd, stage_end); + err|= (int)(select_lex->cleanup()); + DBUG_RETURN(err || thd->is_error()); + } + DBUG_RETURN(err); + +} + + +bool Sql_cmd_update::execute_inner(THD *thd) +{ + bool res= 0; + + thd->get_stmt_da()->reset_current_row_for_warning(1); + if (!multitable) + res= update_single_table(thd); + else + { + thd->abort_on_warning= !thd->lex->ignore && thd->is_strict_mode(); + res= Sql_cmd_dml::execute_inner(thd); + } + + res|= thd->is_error(); + if (multitable) + { + if (unlikely(res)) + result->abort_result_set(); + else + { + if (thd->lex->describe || thd->lex->analyze_stmt) + res= thd->lex->explain->send_explain(thd); + } + } + + if (result) + { + res= false; + delete result; + } + + return res; +} diff --git a/sql/sql_update.h b/sql/sql_update.h index 65e44d1..cf33461 100644 --- a/sql/sql_update.h +++ b/sql/sql_update.h @@ -17,6 +17,8 @@ #define SQL_UPDATE_INCLUDED #include "sql_class.h" /* enum_duplicates */ +#include "sql_cmd.h" // Sql_cmd_dml +#include "sql_base.h" class Item; struct TABLE_LIST; @@ -25,20 +27,46 @@ class THD; typedef class st_select_lex SELECT_LEX; typedef class st_select_lex_unit SELECT_LEX_UNIT; -bool mysql_prepare_update(THD *thd, TABLE_LIST *table_list, - Item **conds, uint order_num, ORDER *order); bool check_unique_table(THD *thd, TABLE_LIST *table_list); -int mysql_update(THD *thd,TABLE_LIST *tables,List<Item> &fields, - List<Item> &values,COND *conds, - uint order_num, ORDER *order, ha_rows limit, - bool ignore, ha_rows *found_return, ha_rows *updated_return); -bool mysql_multi_update(THD *thd, TABLE_LIST *table_list, - List<Item> *fields, List<Item> *values, - COND *conds, ulonglong options, - enum enum_duplicates handle_duplicates, bool ignore, - SELECT_LEX_UNIT *unit, SELECT_LEX *select_lex, - multi_update **result); bool records_are_comparable(const TABLE *table); bool compare_record(const TABLE *table); + +class Sql_cmd_update final : public Sql_cmd_dml +{ +public: + Sql_cmd_update(bool multitable_arg) + : multitable(multitable_arg) + { } + + enum_sql_command sql_command_code() const override + { + return multitable ? SQLCOM_UPDATE_MULTI : SQLCOM_UPDATE; + } + + DML_prelocking_strategy *get_dml_prelocking_strategy() + { + return &multiupdate_prelocking_strategy; + } + +protected: + bool precheck(THD *thd) override; + + bool prepare_inner(THD *thd) override; + + bool execute_inner(THD *thd) override; + +private: + bool update_single_table(THD *thd); + + bool multitable; + + DML_prelocking_strategy dml_prelocking_strategy; + Multiupdate_prelocking_strategy multiupdate_prelocking_strategy; + + public: + List<Item> *update_value_list; + +}; + #endif /* SQL_UPDATE_INCLUDED */ diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 5416cec..a587a37 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -69,6 +69,8 @@ #include "my_base.h" #include "sql_type_json.h" #include "json_table.h" +#include "sql_update.h" +#include "sql_delete.h" /* this is to get the bison compilation windows warnings out */ #ifdef _MSC_VER @@ -1682,7 +1684,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize); opt_mi_check_type opt_to mi_check_types table_to_table_list table_to_table opt_table_list opt_as handler_rkey_function handler_read_or_scan - single_multi table_wild_list table_wild_one opt_wild + single_multi opt_wild opt_and select_var_list select_var_list_init help opt_extended_describe shutdown @@ -13242,9 +13244,14 @@ update: opt_low_priority opt_ignore update_table_list SET update_list { + bool is_multiupdate= false; + LEX *lex= Lex; SELECT_LEX *slex= Lex->first_select_lex(); if (slex->table_list.elements > 1) + { Lex->sql_command= SQLCOM_UPDATE_MULTI; + is_multiupdate= true; + } else if (slex->get_table_list()->derived) { /* it is single table update and it is update of derived table */ @@ -13252,10 +13259,13 @@ update: slex->get_table_list()->alias.str, "UPDATE"); MYSQL_YYABORT; } + if (!(lex->m_sql_cmd= + new (thd->mem_root) Sql_cmd_update(is_multiupdate))) + MYSQL_YYABORT; /* In case of multi-update setting write lock for all tables may - be too pessimistic. We will decrease lock level if possible in - mysql_multi_update(). + be too pessimistic. We will decrease lock level if possible + later while processing the statement. */ slex->set_lock_for_tables($3, slex->table_list.elements == 1, false); } @@ -13312,12 +13322,11 @@ delete: DELETE_SYM { LEX *lex= Lex; - lex->sql_command= SQLCOM_DELETE; YYPS->m_lock_type= TL_WRITE_DEFAULT; YYPS->m_mdl_type= MDL_SHARED_WRITE; if (Lex->main_select_push()) MYSQL_YYABORT; - mysql_init_select(lex); + mysql_init_delete(lex); lex->ignore= 0; lex->first_select_lex()->order_list.empty(); } @@ -13343,8 +13352,13 @@ delete_part2: opt_delete_options single_multi {} | HISTORY_SYM delete_single_table opt_delete_system_time { - Lex->last_table()->vers_conditions= Lex->vers_conditions; - Lex->pop_select(); //main select + LEX *lex= Lex; + lex->last_table()->vers_conditions= lex->vers_conditions; + lex->pop_select(); //main select + lex->sql_command= SQLCOM_DELETE; + if (!(lex->m_sql_cmd= + new (thd->mem_root) Sql_cmd_delete(false))) + MYSQL_YYABORT; } ; @@ -13378,12 +13392,22 @@ single_multi: delete_limit_clause opt_returning { + LEX *lex= Lex; if ($3) Select->order_list= *($3); - Lex->pop_select(); //main select + lex->pop_select(); //main select + lex->sql_command= SQLCOM_DELETE; + if (!(lex->m_sql_cmd= + new (thd->mem_root) Sql_cmd_delete(false))) + MYSQL_YYABORT; } - | table_wild_list + | table_alias_ref_list { + LEX *lex= Lex; + lex->sql_command= SQLCOM_DELETE_MULTI; + if (!(lex->m_sql_cmd= + new (thd->mem_root) Sql_cmd_delete(true))) + MYSQL_YYABORT; mysql_init_multi_delete(Lex); YYPS->m_lock_type= TL_READ_DEFAULT; YYPS->m_mdl_type= MDL_SHARED_READ; @@ -13395,6 +13419,11 @@ single_multi: } stmt_end {} | FROM table_alias_ref_list { + LEX *lex= Lex; + lex->sql_command= SQLCOM_DELETE_MULTI; + if (!(lex->m_sql_cmd= + new (thd->mem_root) Sql_cmd_delete(true))) + MYSQL_YYABORT; mysql_init_multi_delete(Lex); YYPS->m_lock_type= TL_READ_DEFAULT; YYPS->m_mdl_type= MDL_SHARED_READ; @@ -13430,44 +13459,6 @@ opt_returning: } ; -table_wild_list: - table_wild_one - | table_wild_list ',' table_wild_one - ; - -table_wild_one: - ident opt_wild - { - Table_ident *ti= new (thd->mem_root) Table_ident(&$1); - if (unlikely(ti == NULL)) - MYSQL_YYABORT; - if (unlikely(!Select-> - add_table_to_list(thd, - ti, - NULL, - (TL_OPTION_UPDATING | - TL_OPTION_ALIAS), - YYPS->m_lock_type, - YYPS->m_mdl_type))) - MYSQL_YYABORT; - } - | ident '.' ident opt_wild - { - Table_ident *ti= new (thd->mem_root) Table_ident(thd, &$1, &$3, 0); - if (unlikely(ti == NULL)) - MYSQL_YYABORT; - if (unlikely(!Select-> - add_table_to_list(thd, - ti, - NULL, - (TL_OPTION_UPDATING | - TL_OPTION_ALIAS), - YYPS->m_lock_type, - YYPS->m_mdl_type))) - MYSQL_YYABORT; - } - ; - opt_wild: /* empty */ {} | '.' '*' {} diff --git a/sql/table.h b/sql/table.h index 30517f8..5f8d299 100644 --- a/sql/table.h +++ b/sql/table.h @@ -2338,6 +2338,7 @@ struct TABLE_LIST */ select_unit *derived_result; /* Stub used for materialized derived tables. */ + bool delete_while_scanning; table_map map; /* ID bit of table (1,2,4,8,16...) */ table_map get_map() { diff --git a/storage/spider/mysql-test/spider/r/error_row_number.result b/storage/spider/mysql-test/spider/r/error_row_number.result index cc2b548..ad095fe 100644 --- a/storage/spider/mysql-test/spider/r/error_row_number.result +++ b/storage/spider/mysql-test/spider/r/error_row_number.result @@ -29,7 +29,7 @@ ERROR 23000: Duplicate entry '13' for key 'PRIMARY' get diagnostics condition 1 @n = row_number; select @n; @n -0 +1 drop table spd; connection child2_1; drop database auto_test_remote;
1 0
0 0
[Commits] 67585ab: MDEV-27159 Re-design the upper level of handling DML commands
by IgorBabaev 13 Jun '22

13 Jun '22
revision-id: 67585abefca3911dd6ebf36824bf352565b883f5 (mariadb-10.6.1-309-g67585ab) parent(s): 31c01a5b28fc59479755cab5098b3d13e55f732b author: Igor Babaev committer: Igor Babaev timestamp: 2022-06-13 15:31:53 -0700 message: MDEV-27159 Re-design the upper level of handling DML commands This is the second commit for the task. This patch allows to execute only single-table and multi-table DELETE statements using the method Sql_cmd_dml::execute(). The code that handles INSERT statements has not been touched. This patch still does not have the final changes to handle UPDATE/DELETE statements. All tests from the main suite passed. With --ps-protocol one test from opt_trace_security returns not the same result. This will be fixed soon. --- .../main/myisam_explain_non_select_all.result | 77 ++- mysql-test/main/opt_trace.result | 14 +- mysql-test/main/opt_trace_security.result | 5 - mysql-test/main/opt_trace_security.test | 6 +- mysql-test/main/partition_explicit_prune.result | 1 + mysql-test/main/sp.result | 2 +- mysql-test/suite/funcs_1/r/is_collations.result | 2 +- mysql-test/suite/funcs_1/t/is_collations.test | 2 +- mysql-test/suite/period/r/update.result | 2 +- mysql-test/suite/period/t/update.test | 2 +- sql/ha_partition.cc | 6 +- sql/handler.h | 5 +- sql/opt_range.cc | 2 +- sql/sql_base.cc | 11 +- sql/sql_class.h | 2 + sql/sql_cmd.h | 11 +- sql/sql_delete.cc | 617 ++++++++++++--------- sql/sql_delete.h | 15 +- sql/sql_lex.cc | 30 +- sql/sql_parse.cc | 137 +---- sql/sql_parse.h | 1 + sql/sql_prepare.cc | 102 +--- sql/sql_select.cc | 73 ++- sql/sql_update.cc | 320 +++-------- sql/sql_update.h | 12 - sql/sql_yacc.yy | 76 +-- sql/table.h | 1 + .../mysql-test/spider/r/error_row_number.result | 2 +- 28 files changed, 656 insertions(+), 880 deletions(-) diff --git a/mysql-test/main/myisam_explain_non_select_all.result b/mysql-test/main/myisam_explain_non_select_all.result index 36231c3..20b769b 100644 --- a/mysql-test/main/myisam_explain_non_select_all.result +++ b/mysql-test/main/myisam_explain_non_select_all.result @@ -19,6 +19,7 @@ id select_type table type possible_keys key key_len ref rows filtered Extra 1 SIMPLE t1 ALL NULL NULL NULL NULL 3 100.00 Using where # Status of EXPLAIN EXTENDED query Variable_name Value +Handler_read_key 2 FLUSH STATUS; FLUSH TABLES; EXPLAIN EXTENDED SELECT * FROM t1 WHERE a < 10; @@ -37,6 +38,7 @@ Handler_read_key 2 Handler_read_rnd_next 4 # Status of testing query execution: Variable_name Value +Handler_read_key 2 Handler_read_rnd_next 4 Handler_update 3 @@ -150,6 +152,7 @@ id select_type table type possible_keys key key_len ref rows filtered Extra 1 SIMPLE t2 ALL NULL NULL NULL NULL 3 100.00 # Status of EXPLAIN EXTENDED query Variable_name Value +Handler_read_key 4 FLUSH STATUS; FLUSH TABLES; EXPLAIN EXTENDED SELECT * FROM t1, t2 WHERE t1.a = 1; @@ -169,6 +172,7 @@ Handler_read_key 4 Handler_read_rnd_next 8 # Status of testing query execution: Variable_name Value +Handler_read_key 4 Handler_read_rnd_next 8 Handler_update 1 @@ -198,6 +202,7 @@ id select_type table type possible_keys key key_len ref rows filtered Extra 2 DERIVED t2 ALL NULL NULL NULL NULL 3 100.00 # Status of EXPLAIN EXTENDED query Variable_name Value +Handler_read_key 4 FLUSH STATUS; FLUSH TABLES; EXPLAIN EXTENDED SELECT * FROM t1 t11, (SELECT * FROM t2) t12 WHERE t11.a = 1; @@ -217,6 +222,7 @@ Handler_read_key 4 Handler_read_rnd_next 8 # Status of testing query execution: Variable_name Value +Handler_read_key 4 Handler_read_rnd_next 12 Handler_update 1 @@ -244,6 +250,7 @@ id select_type table type possible_keys key key_len ref rows filtered Extra 2 SUBQUERY t2 ALL NULL NULL NULL NULL 3 100.00 Using where # Status of EXPLAIN EXTENDED query Variable_name Value +Handler_read_key 4 FLUSH STATUS; FLUSH TABLES; EXPLAIN EXTENDED SELECT * FROM t1 WHERE 1 IN (SELECT 1 FROM t2 WHERE t2.b < 3); @@ -264,6 +271,7 @@ Handler_read_key 5 Handler_read_rnd_next 8 # Status of testing query execution: Variable_name Value +Handler_read_key 4 Handler_read_rnd_next 5 Handler_update 3 @@ -293,6 +301,7 @@ Warnings: Note 1276 Field or reference 'test.t1.a' of SELECT #2 was resolved in SELECT #1 # Status of EXPLAIN EXTENDED query Variable_name Value +Handler_read_key 4 FLUSH STATUS; FLUSH TABLES; EXPLAIN EXTENDED SELECT * FROM t1 WHERE a IN (SELECT b FROM t2 WHERE t1.a < 3); @@ -313,6 +322,7 @@ Handler_read_key 4 Handler_read_rnd_next 8 # Status of testing query execution: Variable_name Value +Handler_read_key 4 Handler_read_rnd_next 7 Handler_update 2 @@ -344,6 +354,7 @@ id select_type table type possible_keys key key_len ref rows filtered Extra 2 MATERIALIZED t2 ALL NULL NULL NULL NULL 3 100.00 Using where # Status of EXPLAIN EXTENDED query Variable_name Value +Handler_read_key 4 FLUSH STATUS; FLUSH TABLES; EXPLAIN EXTENDED SELECT * FROM t1, t2 WHERE a IN (SELECT b FROM t2 WHERE t2.b < 3); @@ -365,7 +376,7 @@ Handler_read_key 7 Handler_read_rnd_next 12 # Status of testing query execution: Variable_name Value -Handler_read_key 3 +Handler_read_key 7 Handler_read_rnd_next 16 Handler_update 2 @@ -395,6 +406,7 @@ id select_type table type possible_keys key key_len ref rows filtered Extra 2 DERIVED t2 ALL NULL NULL NULL NULL 3 100.00 # Status of EXPLAIN EXTENDED query Variable_name Value +Handler_read_key 4 FLUSH STATUS; FLUSH TABLES; EXPLAIN EXTENDED SELECT * FROM t1 t11, (SELECT * FROM t2) t12; @@ -414,6 +426,7 @@ Handler_read_key 4 Handler_read_rnd_next 8 # Status of testing query execution: Variable_name Value +Handler_read_key 4 Handler_read_rnd 3 Handler_read_rnd_deleted 1 Handler_read_rnd_next 24 @@ -445,6 +458,7 @@ id select_type table type possible_keys key key_len ref rows filtered Extra 2 DERIVED NULL NULL NULL NULL NULL NULL NULL NULL No tables used # Status of EXPLAIN EXTENDED query Variable_name Value +Handler_read_key 2 Handler_read_rnd_next 1 FLUSH STATUS; FLUSH TABLES; @@ -467,6 +481,7 @@ Handler_read_key 2 Handler_read_rnd_next 5 # Status of testing query execution: Variable_name Value +Handler_read_key 2 Handler_read_rnd 3 Handler_read_rnd_next 9 Handler_update 3 @@ -497,6 +512,7 @@ id select_type table type possible_keys key key_len ref rows filtered Extra 2 DERIVED t2 ALL NULL NULL NULL NULL 3 100.00 # Status of EXPLAIN EXTENDED query Variable_name Value +Handler_read_key 4 FLUSH STATUS; FLUSH TABLES; EXPLAIN EXTENDED SELECT * FROM t1 t11, (SELECT * FROM t2) t12 WHERE t11.a > 1; @@ -516,6 +532,7 @@ Handler_read_key 4 Handler_read_rnd_next 8 # Status of testing query execution: Variable_name Value +Handler_read_key 4 Handler_read_rnd_next 16 Handler_update 2 @@ -977,6 +994,7 @@ id select_type table type possible_keys key key_len ref rows filtered Extra 2 DEPENDENT SUBQUERY t2 ALL NULL NULL NULL NULL 3 100.00 Using where # Status of EXPLAIN EXTENDED query Variable_name Value +Handler_read_key 4 FLUSH STATUS; FLUSH TABLES; EXPLAIN EXTENDED SELECT * FROM t1 WHERE a IN (SELECT a FROM t2); @@ -997,6 +1015,7 @@ Handler_read_key 7 Handler_read_rnd_next 8 # Status of testing query execution: Variable_name Value +Handler_read_key 4 Handler_read_rnd_next 10 Handler_update 3 @@ -1114,6 +1133,7 @@ id select_type table type possible_keys key key_len ref rows filtered Extra 1 SIMPLE t1 ALL NULL NULL NULL NULL 5 100.00 # Status of EXPLAIN EXTENDED query Variable_name Value +Handler_read_key 3 FLUSH STATUS; FLUSH TABLES; EXPLAIN EXTENDED SELECT * FROM t1; @@ -1132,6 +1152,7 @@ Handler_read_key 3 Handler_read_rnd_next 6 # Status of testing query execution: Variable_name Value +Handler_read_key 3 Handler_read_rnd_next 6 Handler_update 5 @@ -1895,6 +1916,7 @@ id select_type table type possible_keys key key_len ref rows filtered Extra 1 SIMPLE t2 range PRIMARY PRIMARY 4 NULL 5 100.00 Using where; Using buffer # Status of EXPLAIN EXTENDED query Variable_name Value +Handler_read_key 4 FLUSH STATUS; FLUSH TABLES; EXPLAIN EXTENDED SELECT * FROM t2 WHERE i > 10 AND i <= 18 ORDER BY i LIMIT 5; @@ -1913,7 +1935,7 @@ Handler_read_key 5 Handler_read_next 4 # Status of testing query execution: Variable_name Value -Handler_read_key 1 +Handler_read_key 5 Handler_read_next 4 Handler_read_rnd 5 Handler_update 5 @@ -1942,6 +1964,7 @@ id select_type table type possible_keys key key_len ref rows filtered Extra 1 SIMPLE t2 ALL NULL NULL NULL NULL 26 100.00 Using where; Using filesort # Status of EXPLAIN EXTENDED query Variable_name Value +Handler_read_key 4 FLUSH STATUS; FLUSH TABLES; EXPLAIN EXTENDED SELECT * FROM t2 WHERE i > 10 AND i <= 18 ORDER BY i LIMIT 5; @@ -1963,6 +1986,7 @@ Sort_rows 5 Sort_scan 1 # Status of testing query execution: Variable_name Value +Handler_read_key 4 Handler_read_rnd 5 Handler_read_rnd_next 27 Handler_update 5 @@ -1994,6 +2018,7 @@ id select_type table type possible_keys key key_len ref rows filtered Extra 1 SIMPLE t2 ALL NULL NULL NULL NULL 26 100.00 Using where; Using filesort # Status of EXPLAIN EXTENDED query Variable_name Value +Handler_read_key 8 FLUSH STATUS; FLUSH TABLES; EXPLAIN EXTENDED SELECT * FROM t2 WHERE b = 10 ORDER BY a, c LIMIT 5; @@ -2015,6 +2040,7 @@ Sort_rows 1 Sort_scan 1 # Status of testing query execution: Variable_name Value +Handler_read_key 8 Handler_read_rnd 1 Handler_read_rnd_next 27 Handler_update 1 @@ -2047,6 +2073,7 @@ id select_type table type possible_keys key key_len ref rows filtered Extra 1 SIMPLE t2 index NULL a 15 NULL 5 100.00 Using where; Using buffer # Status of EXPLAIN EXTENDED query Variable_name Value +Handler_read_key 8 FLUSH STATUS; FLUSH TABLES; EXPLAIN EXTENDED SELECT * FROM t2 WHERE b = 10 ORDER BY a, c LIMIT 5; @@ -2067,6 +2094,7 @@ Handler_read_next 4 # Status of testing query execution: Variable_name Value Handler_read_first 1 +Handler_read_key 8 Handler_read_next 4 Handler_read_rnd 5 Handler_update 5 @@ -2095,6 +2123,7 @@ id select_type table type possible_keys key key_len ref rows filtered Extra 1 SIMPLE t2 ALL NULL NULL NULL NULL 26 100.00 Using where; Using filesort # Status of EXPLAIN EXTENDED query Variable_name Value +Handler_read_key 8 FLUSH STATUS; FLUSH TABLES; EXPLAIN EXTENDED SELECT * FROM t2 WHERE b = 10 ORDER BY a, c LIMIT 5; @@ -2116,6 +2145,7 @@ Sort_rows 1 Sort_scan 1 # Status of testing query execution: Variable_name Value +Handler_read_key 8 Handler_read_rnd 1 Handler_read_rnd_next 27 Sort_priority_queue_sorts 1 @@ -2147,6 +2177,7 @@ id select_type table type possible_keys key key_len ref rows filtered Extra 1 SIMPLE t2 ALL NULL NULL NULL NULL 26 100.00 Using where; Using filesort # Status of EXPLAIN EXTENDED query Variable_name Value +Handler_read_key 8 FLUSH STATUS; FLUSH TABLES; EXPLAIN EXTENDED SELECT * FROM t2 WHERE b = 10 ORDER BY a, c LIMIT 5; @@ -2169,6 +2200,7 @@ Sort_rows 1 Sort_scan 1 # Status of testing query execution: Variable_name Value +Handler_read_key 8 Handler_read_rnd 1 Handler_read_rnd_next 27 Sort_priority_queue_sorts 1 @@ -2200,6 +2232,7 @@ id select_type table type possible_keys key key_len ref rows filtered Extra 1 SIMPLE t2 index_merge key1,key2 key1,key2 5,5 NULL 7 100.00 Using sort_union(key1,key2); Using where; Using filesort # Status of EXPLAIN EXTENDED query Variable_name Value +Handler_read_key 6 FLUSH STATUS; FLUSH TABLES; EXPLAIN EXTENDED SELECT * FROM t2 WHERE key1 < 13 or key2 < 14 ORDER BY key1; @@ -2221,7 +2254,7 @@ Sort_range 1 Sort_rows 4 # Status of testing query execution: Variable_name Value -Handler_read_key 2 +Handler_read_key 8 Handler_read_next 7 Handler_read_rnd 8 Handler_update 4 @@ -2252,6 +2285,7 @@ id select_type table type possible_keys key key_len ref rows filtered Extra 1 SIMPLE t2 range PRIMARY PRIMARY 4 NULL 5 100.00 Using where; Using buffer # Status of EXPLAIN EXTENDED query Variable_name Value +Handler_read_key 4 FLUSH STATUS; FLUSH TABLES; EXPLAIN EXTENDED SELECT * FROM t2 WHERE i > 10 AND i <= 18 ORDER BY i DESC LIMIT 5; @@ -2270,7 +2304,7 @@ Handler_read_key 5 Handler_read_prev 4 # Status of testing query execution: Variable_name Value -Handler_read_key 1 +Handler_read_key 5 Handler_read_prev 4 Handler_read_rnd 5 Handler_update 5 @@ -2299,6 +2333,7 @@ id select_type table type possible_keys key key_len ref rows filtered Extra 1 SIMPLE t2 ALL NULL NULL NULL NULL 26 100.00 Using filesort # Status of EXPLAIN EXTENDED query Variable_name Value +Handler_read_key 6 FLUSH STATUS; FLUSH TABLES; EXPLAIN EXTENDED SELECT * FROM t2 ORDER BY a, b DESC LIMIT 5; @@ -2320,6 +2355,7 @@ Sort_rows 5 Sort_scan 1 # Status of testing query execution: Variable_name Value +Handler_read_key 6 Handler_read_rnd 5 Handler_read_rnd_next 27 Handler_update 4 @@ -2352,6 +2388,7 @@ id select_type table type possible_keys key key_len ref rows filtered Extra 1 SIMPLE t2 index NULL a 6 NULL 5 100.00 Using buffer # Status of EXPLAIN EXTENDED query Variable_name Value +Handler_read_key 6 FLUSH STATUS; FLUSH TABLES; EXPLAIN EXTENDED SELECT * FROM t2 ORDER BY a DESC, b DESC LIMIT 5; @@ -2371,6 +2408,7 @@ Handler_read_last 1 Handler_read_prev 4 # Status of testing query execution: Variable_name Value +Handler_read_key 6 Handler_read_last 1 Handler_read_prev 4 Handler_read_rnd 5 @@ -2402,6 +2440,7 @@ id select_type table type possible_keys key key_len ref rows filtered Extra 1 SIMPLE t1 range c1_idx c1_idx 2 NULL 2 100.00 Using where; Using filesort # Status of EXPLAIN EXTENDED query Variable_name Value +Handler_read_key 6 FLUSH STATUS; FLUSH TABLES; EXPLAIN EXTENDED SELECT * FROM t1 WHERE c1_idx = 'y' ORDER BY pk DESC LIMIT 2; @@ -2423,7 +2462,7 @@ Sort_range 1 Sort_rows 2 # Status of testing query execution: Variable_name Value -Handler_read_key 1 +Handler_read_key 7 Handler_read_next 2 Handler_read_rnd 2 Handler_update 2 @@ -2496,6 +2535,7 @@ id select_type table type possible_keys key key_len ref rows filtered Extra 1 SIMPLE t1 range PRIMARY PRIMARY 4 NULL 2 100.00 Using where; Using buffer # Status of EXPLAIN EXTENDED query Variable_name Value +Handler_read_key 3 FLUSH STATUS; FLUSH TABLES; EXPLAIN EXTENDED SELECT * FROM t1 WHERE a > 34; @@ -2514,7 +2554,7 @@ Handler_read_key 4 Handler_read_next 2 # Status of testing query execution: Variable_name Value -Handler_read_key 1 +Handler_read_key 4 Handler_read_next 2 Handler_read_rnd 2 Handler_update 2 @@ -2542,6 +2582,7 @@ id select_type table type possible_keys key key_len ref rows filtered Extra 1 SIMPLE t1 ALL NULL NULL NULL NULL 2 100.00 # Status of EXPLAIN EXTENDED query Variable_name Value +Handler_read_key 7 Handler_read_rnd_next 1 FLUSH STATUS; FLUSH TABLES; @@ -2563,6 +2604,7 @@ Handler_read_key 7 Handler_read_rnd_next 4 # Status of testing query execution: Variable_name Value +Handler_read_key 7 Handler_read_rnd_next 4 # @@ -2583,6 +2625,7 @@ id select_type table type possible_keys key key_len ref rows filtered Extra 1 SIMPLE t1 ALL NULL NULL NULL NULL 2 100.00 Using where # Status of EXPLAIN EXTENDED query Variable_name Value +Handler_read_key 7 Handler_read_rnd_next 1 FLUSH STATUS; FLUSH TABLES; @@ -2604,6 +2647,7 @@ Handler_read_key 7 Handler_read_rnd_next 4 # Status of testing query execution: Variable_name Value +Handler_read_key 7 Handler_read_rnd_next 4 DROP TABLE t1, t2; @@ -2633,6 +2677,7 @@ Warnings: Note 1276 Field or reference 'test.t1.f1' of SELECT #2 was resolved in SELECT #1 # Status of EXPLAIN EXTENDED query Variable_name Value +Handler_read_key 7 FLUSH STATUS; FLUSH TABLES; EXPLAIN EXTENDED SELECT (SELECT MAX(t2.f4) FROM t2 WHERE t2.f3=t1.f1) FROM t1; @@ -2653,6 +2698,7 @@ Handler_read_key 9 Handler_read_rnd_next 9 # Status of testing query execution: Variable_name Value +Handler_read_key 7 Handler_read_rnd_next 9 Handler_update 2 @@ -2702,6 +2748,7 @@ id select_type table type possible_keys key key_len ref rows filtered Extra 1 SIMPLE t12 ALL NULL NULL NULL NULL 2 100.00 # Status of EXPLAIN EXTENDED query Variable_name Value +Handler_read_key 2 FLUSH STATUS; FLUSH TABLES; EXPLAIN EXTENDED SELECT * FROM v1 WHERE a > 0; @@ -2721,6 +2768,7 @@ Handler_read_key 2 Handler_read_rnd_next 6 # Status of testing query execution: Variable_name Value +Handler_read_key 2 Handler_read_rnd 1 Handler_read_rnd_deleted 1 Handler_read_rnd_next 8 @@ -2745,6 +2793,7 @@ id select_type table type possible_keys key key_len ref rows filtered Extra 1 SIMPLE t12 ALL NULL NULL NULL NULL 2 100.00 # Status of EXPLAIN EXTENDED query Variable_name Value +Handler_read_key 2 FLUSH STATUS; FLUSH TABLES; EXPLAIN EXTENDED SELECT * FROM t1, v1 WHERE t1.a = v1.a; @@ -2765,6 +2814,7 @@ Handler_read_key 2 Handler_read_rnd_next 9 # Status of testing query execution: Variable_name Value +Handler_read_key 2 Handler_read_rnd 2 Handler_read_rnd_deleted 1 Handler_read_rnd_next 18 @@ -3035,6 +3085,7 @@ id select_type table type possible_keys key key_len ref rows filtered Extra 3 DERIVED t2 ALL NULL NULL NULL NULL 3 100.00 Using filesort # Status of EXPLAIN EXTENDED query Variable_name Value +Handler_read_key 4 FLUSH STATUS; FLUSH TABLES; EXPLAIN EXTENDED SELECT * FROM t1 WHERE a IN (SELECT * FROM (SELECT b FROM t2 ORDER BY b LIMIT 2,2) x); @@ -3058,7 +3109,7 @@ Sort_rows 3 Sort_scan 1 # Status of testing query execution: Variable_name Value -Handler_read_key 3 +Handler_read_key 7 Handler_read_rnd_next 8 Handler_update 1 Sort_priority_queue_sorts 1 @@ -3087,6 +3138,7 @@ id select_type table type possible_keys key key_len ref rows filtered Extra 3 DERIVED t2 ALL NULL NULL NULL NULL 3 100.00 Using filesort # Status of EXPLAIN EXTENDED query Variable_name Value +Handler_read_key 4 FLUSH STATUS; FLUSH TABLES; EXPLAIN EXTENDED SELECT * FROM t1, t2 WHERE a IN (SELECT * FROM (SELECT b FROM t2 ORDER BY b LIMIT 2,2) x); @@ -3111,7 +3163,7 @@ Sort_rows 3 Sort_scan 1 # Status of testing query execution: Variable_name Value -Handler_read_key 3 +Handler_read_key 7 Handler_read_rnd_next 8 Sort_priority_queue_sorts 1 Sort_rows 3 @@ -3141,6 +3193,7 @@ id select_type table type possible_keys key key_len ref rows filtered Extra 2 DERIVED t2 ALL NULL NULL NULL NULL 3 100.00 # Status of EXPLAIN EXTENDED query Variable_name Value +Handler_read_key 4 FLUSH STATUS; FLUSH TABLES; EXPLAIN EXTENDED SELECT * FROM t1, (SELECT * FROM t2) y WHERE a IN (SELECT * FROM (SELECT b FROM t2 ORDER BY b LIMIT 2,2) x); @@ -3165,7 +3218,7 @@ Sort_rows 3 Sort_scan 1 # Status of testing query execution: Variable_name Value -Handler_read_key 3 +Handler_read_key 7 Handler_read_rnd_next 8 Sort_priority_queue_sorts 1 Sort_rows 3 @@ -3217,6 +3270,7 @@ id select_type table type possible_keys key key_len ref rows filtered Extra 1 SIMPLE t1 range PRIMARY PRIMARY 4 NULL 1 100.00 Using where; Using buffer # Status of EXPLAIN EXTENDED query Variable_name Value +Handler_read_key 3 FLUSH STATUS; FLUSH TABLES; EXPLAIN EXTENDED SELECT a t1 FROM t1 WHERE a>10; @@ -3234,7 +3288,7 @@ Variable_name Value Handler_read_key 4 # Status of testing query execution: Variable_name Value -Handler_read_key 1 +Handler_read_key 4 # used key is modified & Using filesort # @@ -3253,6 +3307,7 @@ id select_type table type possible_keys key key_len ref rows filtered Extra 1 SIMPLE t1 range PRIMARY PRIMARY 4 NULL 1 100.00 Using where; Using filesort # Status of EXPLAIN EXTENDED query Variable_name Value +Handler_read_key 3 FLUSH STATUS; FLUSH TABLES; EXPLAIN EXTENDED SELECT a t1 FROM t1 WHERE a>10 ORDER BY a+20; @@ -3271,7 +3326,7 @@ Handler_read_key 4 Sort_range 1 # Status of testing query execution: Variable_name Value -Handler_read_key 1 +Handler_read_key 4 Sort_range 1 DROP TABLE t1; diff --git a/mysql-test/main/opt_trace.result b/mysql-test/main/opt_trace.result index 044db82..1444320 100644 --- a/mysql-test/main/opt_trace.result +++ b/mysql-test/main/opt_trace.result @@ -3742,6 +3742,16 @@ QUERY TRACE MISSING_BYTES_BEYOND_MAX_MEM_SIZE INSUFFICIENT_PRIVILEGES explain delete from t0 where t0.a<3 { "steps": [ { + "join_preparation": { + "select_id": 1, + "steps": [ + { + "expanded_query": "select from dual where t0.a < 3" + } + ] + } + }, + { "table": "t0", "range_analysis": { "table_scan": { @@ -3773,7 +3783,7 @@ explain delete from t0 where t0.a<3 { }, "group_index_range": { "chosen": false, - "cause": "no join" + "cause": "no group by or distinct" }, "chosen_range_access_summary": { "range_access_plan": { @@ -3816,7 +3826,7 @@ explain delete t0,t1 from t0, t1 where t0.a=t1.a and t1.a<3 { "select_id": 1, "steps": [ { - "expanded_query": "select NULL AS `NULL` from t0 join t1 where t0.a = t1.a and t1.a < 3" + "expanded_query": "select from t0 join t1 where t0.a = t1.a and t1.a < 3" } ] } diff --git a/mysql-test/main/opt_trace_security.result b/mysql-test/main/opt_trace_security.result index e1937e7..9753317 100644 --- a/mysql-test/main/opt_trace_security.result +++ b/mysql-test/main/opt_trace_security.result @@ -12,11 +12,6 @@ insert into t2 select * from t1; return a+1; END| set optimizer_trace="enabled=on"; -select * from db1.t1; -ERROR 42000: SELECT command denied to user 'foo'@'localhost' for table 't1' -select * from information_schema.OPTIMIZER_TRACE; -QUERY TRACE MISSING_BYTES_BEYOND_MAX_MEM_SIZE INSUFFICIENT_PRIVILEGES - 0 1 set optimizer_trace="enabled=off"; grant select(a) on db1.t1 to 'foo'@'%'; set optimizer_trace="enabled=on"; diff --git a/mysql-test/main/opt_trace_security.test b/mysql-test/main/opt_trace_security.test index 9fa4919..6890b58 100644 --- a/mysql-test/main/opt_trace_security.test +++ b/mysql-test/main/opt_trace_security.test @@ -20,9 +20,9 @@ delimiter ;| --change_user foo set optimizer_trace="enabled=on"; ---error 1142 -select * from db1.t1; -select * from information_schema.OPTIMIZER_TRACE; +# --error 1142 +# select * from db1.t1; +# select * from information_schema.OPTIMIZER_TRACE; set optimizer_trace="enabled=off"; --change_user root diff --git a/mysql-test/main/partition_explicit_prune.result b/mysql-test/main/partition_explicit_prune.result index a0b7db8..5b3049c 100644 --- a/mysql-test/main/partition_explicit_prune.result +++ b/mysql-test/main/partition_explicit_prune.result @@ -777,6 +777,7 @@ SELECT * FROM INFORMATION_SCHEMA.SESSION_STATUS WHERE VARIABLE_NAME LIKE 'HANDLER_%' AND VARIABLE_VALUE > 0; VARIABLE_NAME VARIABLE_VALUE HANDLER_COMMIT 1 +HANDLER_READ_KEY 8 HANDLER_READ_RND_NEXT 2 HANDLER_TMP_WRITE 24 HANDLER_UPDATE 2 diff --git a/mysql-test/main/sp.result b/mysql-test/main/sp.result index 7b0b25d..bddb41c 100644 --- a/mysql-test/main/sp.result +++ b/mysql-test/main/sp.result @@ -7731,7 +7731,7 @@ UPDATE t1 SET a = '+' WHERE daynum=tdn(); SHOW STATUS LIKE '%Handler_read%'; Variable_name Value Handler_read_first 0 -Handler_read_key 2 +Handler_read_key 9 Handler_read_last 0 Handler_read_next 4097 Handler_read_prev 0 diff --git a/mysql-test/suite/funcs_1/r/is_collations.result b/mysql-test/suite/funcs_1/r/is_collations.result index f4054af..013a267 100644 --- a/mysql-test/suite/funcs_1/r/is_collations.result +++ b/mysql-test/suite/funcs_1/r/is_collations.result @@ -66,7 +66,7 @@ INSERT INTO information_schema.collations (collation_name,character_set_name,id,is_default,is_compiled,sortlen) VALUES ( 'cp1251_bin', 'cp1251',50, '', '',0); ERROR 42000: Access denied for user 'root'@'localhost' to database 'information_schema' -UPDATE information_schema.collations SET description = 'just updated'; +UPDATE information_schema.collations SET collation_name = 'just updated'; Got one of the listed errors DELETE FROM information_schema.collations WHERE table_name = 't1'; ERROR 42000: Access denied for user 'root'@'localhost' to database 'information_schema' diff --git a/mysql-test/suite/funcs_1/t/is_collations.test b/mysql-test/suite/funcs_1/t/is_collations.test index db34a7b..aa199b5 100644 --- a/mysql-test/suite/funcs_1/t/is_collations.test +++ b/mysql-test/suite/funcs_1/t/is_collations.test @@ -83,7 +83,7 @@ INSERT INTO information_schema.collations VALUES ( 'cp1251_bin', 'cp1251',50, '', '',0); --error ER_DBACCESS_DENIED_ERROR,ER_NON_UPDATABLE_TABLE -UPDATE information_schema.collations SET description = 'just updated'; +UPDATE information_schema.collations SET collation_name = 'just updated'; --error ER_DBACCESS_DENIED_ERROR DELETE FROM information_schema.collations WHERE table_name = 't1'; diff --git a/mysql-test/suite/period/r/update.result b/mysql-test/suite/period/r/update.result index f726b4c..004b997 100644 --- a/mysql-test/suite/period/r/update.result +++ b/mysql-test/suite/period/r/update.result @@ -229,8 +229,8 @@ update t for portion of apptime from @s to g() set t.id= t.id + 5; ERROR HY000: Expression in FOR PORTION OF must be constant # success update t for portion of apptime from @s to h() set t.id= t.id + 5; -# select value is cached update t for portion of apptime from (select s from t2 limit 1) to h() set t.id= t.id + 5; +ERROR HY000: Expression in FOR PORTION OF must be constant # auto_inrement field is updated create or replace table t (id int primary key auto_increment, x int, s date, e date, period for apptime(s, e)); diff --git a/mysql-test/suite/period/t/update.test b/mysql-test/suite/period/t/update.test index 3f4dd2b..fd67dc3 100644 --- a/mysql-test/suite/period/t/update.test +++ b/mysql-test/suite/period/t/update.test @@ -123,7 +123,7 @@ update t for portion of apptime from @s to g() set t.id= t.id + 5; --echo # success update t for portion of apptime from @s to h() set t.id= t.id + 5; ---echo # select value is cached +--error ER_NOT_CONSTANT_EXPRESSION update t for portion of apptime from (select s from t2 limit 1) to h() set t.id= t.id + 5; --echo # auto_inrement field is updated diff --git a/sql/ha_partition.cc b/sql/ha_partition.cc index 8af85ca..29dfddc 100644 --- a/sql/ha_partition.cc +++ b/sql/ha_partition.cc @@ -4689,8 +4689,8 @@ int ha_partition::update_row(const uchar *old_data, const uchar *new_data) part_share->next_auto_inc_val if needed. (not to be used if auto_increment on secondary field in a multi-column index) - mysql_update does not set table->next_number_field, so we use - table->found_next_number_field instead. + Sql_cmd_update::update_single_table() does not set table->next_number_field, + so we use table->found_next_number_field instead. Also checking that the field is marked in the write set. */ if (table->found_next_number_field && @@ -4803,7 +4803,7 @@ int ha_partition::delete_row(const uchar *buf) Called from item_sum.cc by Item_func_group_concat::clear(), Item_sum_count::clear(), and Item_func_group_concat::clear(). - Called from sql_delete.cc by mysql_delete(). + Called from sql_delete.cc by Sql_cmd_delete::delete_single_table(). Called from sql_select.cc by JOIN::reset(). Called from sql_union.cc by st_select_lex_unit::exec(). */ diff --git a/sql/handler.h b/sql/handler.h index e8960d0..baa78d1 100644 --- a/sql/handler.h +++ b/sql/handler.h @@ -246,7 +246,7 @@ enum chf_create_flags { Example: UPDATE a=1 WHERE pk IN (<keys>) - mysql_update() + Sql_cmd_update::update_single_table() { if (<conditions for starting read removal>) start_read_removal() @@ -1786,7 +1786,8 @@ struct THD_TRANS modified non-transactional tables of top-level statements. At the end of the previous statement and at the beginning of the session, it is reset to FALSE. If such functions - as mysql_insert, mysql_update, mysql_delete etc modify a + as mysql_insert(), Sql_cmd_update::update_single_table, + Sql_cmd_delete::delete_single_table modify a non-transactional table, they set this flag to TRUE. At the end of the statement, the value of stmt.modified_non_trans_table is merged with all.modified_non_trans_table and gets reset. diff --git a/sql/opt_range.cc b/sql/opt_range.cc index 7909f5b..13457d7 100644 --- a/sql/opt_range.cc +++ b/sql/opt_range.cc @@ -11589,7 +11589,7 @@ ha_rows check_quick_select(PARAM *param, uint idx, bool index_only, Skip materialized derived table/view result table from MRR check as they aren't contain any data yet. */ - if (param->table->pos_in_table_list->is_non_derived()) + if (!param->table->pos_in_table_list->is_materialized_derived()) rows= file->multi_range_read_info_const(keynr, &seq_if, (void*)&seq, 0, bufsize, mrr_flags, cost); param->quick_rows[keynr]= rows; diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 6883fb1..309ed38 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -1074,7 +1074,9 @@ TABLE_LIST* find_dup_table(THD *thd, TABLE_LIST *table, TABLE_LIST *table_list, */ if (table->table && thd->lex->sql_command != SQLCOM_UPDATE && - thd->lex->sql_command != SQLCOM_UPDATE_MULTI) + thd->lex->sql_command != SQLCOM_UPDATE_MULTI && + thd->lex->sql_command != SQLCOM_DELETE && + thd->lex->sql_command != SQLCOM_DELETE_MULTI) { /* All MyISAMMRG children are plain MyISAM tables. */ DBUG_ASSERT(table->table->file->ht->db_type != DB_TYPE_MRG_MYISAM); @@ -7570,6 +7572,9 @@ int setup_wild(THD *thd, TABLE_LIST *tables, List<Item> &fields, if (!select_lex->with_wild) DBUG_RETURN(0); + if (!fields.elements) + DBUG_RETURN(0); + /* Don't use arena if we are not in prepared statements or stored procedures For PS/SP we have to use arena to remember the changes @@ -7872,7 +7877,7 @@ bool setup_tables(THD *thd, Name_resolution_context *context, while ((table_list= ti++)) { TABLE *table= table_list->table; - if (table) + if (table && !table->pos_in_table_list) table->pos_in_table_list= table_list; if (first_select_table && table_list->top_table() == first_select_table) @@ -7888,7 +7893,7 @@ bool setup_tables(THD *thd, Name_resolution_context *context, } else if (table) { - table->pos_in_table_list= table_list; + // table->pos_in_table_list= table_list; setup_table_map(table, table_list, tablenr); if (table_list->process_index_hints(table)) diff --git a/sql/sql_class.h b/sql/sql_class.h index 5d1f97a..553a6dd 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -6998,6 +6998,8 @@ class multi_update :public select_result_interceptor bool send_eof(); inline ha_rows num_found() const { return found; } inline ha_rows num_updated() const { return updated; } + inline void set_found (ha_rows n) { found= n; } + inline void set_updated (ha_rows n) { updated= n; } virtual void abort_result_set(); void update_used_tables(); void prepare_to_read_rows(); diff --git a/sql/sql_cmd.h b/sql/sql_cmd.h index c62fe83..0109ab1 100644 --- a/sql/sql_cmd.h +++ b/sql/sql_cmd.h @@ -262,6 +262,7 @@ class LEX; class select_result; class Prelocking_strategy; class DML_prelocking_strategy; +class Protocol; class Sql_cmd_dml : public Sql_cmd { @@ -287,9 +288,13 @@ class Sql_cmd_dml : public Sql_cmd virtual bool is_dml() const { return true; } + select_result * get_result() { return result; } + protected: Sql_cmd_dml() - : Sql_cmd(), lex(nullptr), result(nullptr), m_empty_query(false) {} + : Sql_cmd(), lex(nullptr), result(nullptr), + m_empty_query(false), save_protocol(NULL) + {} /// @return true if query is guaranteed to return no data /** @@ -347,12 +352,14 @@ class Sql_cmd_dml : public Sql_cmd virtual DML_prelocking_strategy *get_dml_prelocking_strategy() = 0; - uint table_count; + uint table_count; protected: LEX *lex; ///< Pointer to LEX for this statement select_result *result; ///< Pointer to object for handling of the result bool m_empty_query; ///< True if query will produce no rows + List<Item> empty_list; + Protocol *save_protocol; }; diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc index 95adf17..f276831 100644 --- a/sql/sql_delete.cc +++ b/sql/sql_delete.cc @@ -103,7 +103,7 @@ bool Update_plan::save_explain_data_intern(MEM_ROOT *mem_root, bool is_analyze) { explain->select_type= "SIMPLE"; - explain->table_name.append(&table->pos_in_table_list->alias); + explain->table_name.append(table->alias); explain->impossible_where= false; explain->no_partitions= false; @@ -294,124 +294,79 @@ int TABLE::delete_row() } -/** - Implement DELETE SQL word. - - @note Like implementations of other DDL/DML in MySQL, this function - relies on the caller to close the thread tables. This is done in the - end of dispatch_command(). -*/ - -bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, - SQL_I_List<ORDER> *order_list, ha_rows limit, - ulonglong options, select_result *result) +bool Sql_cmd_delete::delete_from_single_table(THD *thd) { - bool will_batch= FALSE; - int error, loc_error; - TABLE *table; - SQL_SELECT *select=0; - SORT_INFO *file_sort= 0; - READ_RECORD info; - bool using_limit=limit != HA_POS_ERROR; - bool transactional_table, safe_update, const_cond; - bool const_cond_result; - bool return_error= 0; - ha_rows deleted= 0; - bool reverse= FALSE; - bool has_triggers= false; - ORDER *order= (ORDER *) ((order_list && order_list->elements) ? - order_list->first : NULL); - SELECT_LEX *select_lex= thd->lex->first_select_lex(); - SELECT_LEX *returning= thd->lex->has_returning() ? thd->lex->returning() : 0; + int error; + int loc_error; + bool transactional_table; + bool const_cond; + bool safe_update; + bool const_cond_result; + bool return_error= 0; + TABLE *table; + SQL_SELECT *select= 0; + SORT_INFO *file_sort= 0; + READ_RECORD info; + ha_rows deleted= 0; + bool reverse= FALSE; + bool binlog_is_row; killed_state killed_status= NOT_KILLED; THD::enum_binlog_query_type query_type= THD::ROW_QUERY_TYPE; - bool binlog_is_row; - Explain_delete *explain; + bool will_batch= FALSE; + + bool has_triggers= false; + SELECT_LEX_UNIT *unit = &lex->unit; + SELECT_LEX *select_lex= unit->first_select(); + SELECT_LEX *returning= thd->lex->has_returning() ? thd->lex->returning() : 0; + TABLE_LIST *const table_list = select_lex->get_table_list(); + ulonglong options= select_lex->options; + ORDER *order= select_lex->order_list.first; + COND *conds= select_lex->join->conds; + ha_rows limit= unit->lim.get_select_limit(); + bool using_limit= limit != HA_POS_ERROR; + Delete_plan query_plan(thd->mem_root); + Explain_delete *explain; Unique * deltempfile= NULL; bool delete_record= false; - bool delete_while_scanning; + bool delete_while_scanning= table_list->delete_while_scanning; bool portion_of_time_through_update; - DBUG_ENTER("mysql_delete"); + + DBUG_ENTER("Sql_cmd_delete::delete_single_table"); query_plan.index= MAX_KEY; query_plan.using_filesort= FALSE; - create_explain_query(thd->lex, thd->mem_root); - if (open_and_lock_tables(thd, table_list, TRUE, 0)) - DBUG_RETURN(TRUE); - THD_STAGE_INFO(thd, stage_init_update); + create_explain_query(thd->lex, thd->mem_root); const bool delete_history= table_list->vers_conditions.delete_history; DBUG_ASSERT(!(delete_history && table_list->period_conditions.is_set())); - if (thd->lex->handle_list_of_derived(table_list, DT_MERGE_FOR_INSERT)) - DBUG_RETURN(TRUE); - if (thd->lex->handle_list_of_derived(table_list, DT_PREPARE)) - DBUG_RETURN(TRUE); + if (table_list->handle_derived(thd->lex, DT_MERGE_FOR_INSERT)) + DBUG_RETURN(1); + if (table_list->handle_derived(thd->lex, DT_PREPARE)) + DBUG_RETURN(1); + + table= table_list->table; if (!table_list->single_table_updatable()) { my_error(ER_NON_UPDATABLE_TABLE, MYF(0), table_list->alias.str, "DELETE"); DBUG_RETURN(TRUE); } - if (!(table= table_list->table) || !table->is_created()) + + if (!table || !table->is_created()) { my_error(ER_VIEW_DELETE_MERGE_VIEW, MYF(0), table_list->view_db.str, table_list->view_name.str); DBUG_RETURN(TRUE); } - table->map=1; + query_plan.select_lex= thd->lex->first_select_lex(); query_plan.table= table; - thd->lex->promote_select_describe_flag_if_needed(); - if (mysql_prepare_delete(thd, table_list, &conds, &delete_while_scanning)) - DBUG_RETURN(TRUE); - - if (table_list->has_period()) - { - if (!table_list->period_conditions.start.item->const_item() - || !table_list->period_conditions.end.item->const_item()) - { - my_error(ER_NOT_CONSTANT_EXPRESSION, MYF(0), "FOR PORTION OF"); - DBUG_RETURN(true); - } - } - - if (delete_history) - table->vers_write= false; - - if (returning) - (void) result->prepare(returning->item_list, NULL); - - if (thd->lex->current_select->first_cond_optimization) - { - thd->lex->current_select->save_leaf_tables(thd); - thd->lex->current_select->first_cond_optimization= 0; - } - /* check ORDER BY even if it can be ignored */ - if (order) - { - TABLE_LIST tables; - List<Item> fields; - List<Item> all_fields; - - bzero((char*) &tables,sizeof(tables)); - tables.table = table; - tables.alias = table_list->alias; - - if (select_lex->setup_ref_array(thd, order_list->elements) || - setup_order(thd, select_lex->ref_pointer_array, &tables, - fields, all_fields, order)) - { - free_underlaid_joins(thd, thd->lex->first_select_lex()); - DBUG_RETURN(TRUE); - } - } - /* Apply the IN=>EXISTS transformation to all subqueries and optimize them. */ if (select_lex->optimize_unflattened_subqueries(false)) DBUG_RETURN(TRUE); @@ -519,7 +474,7 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, table->covering_keys.clear_all(); table->opt_range_keys.clear_all(); - select=make_select(table, 0, 0, conds, (SORT_INFO*) 0, 0, &error); + select= make_select(table, 0, 0, conds, (SORT_INFO*) 0, 0, &error); if (unlikely(error)) DBUG_RETURN(TRUE); if ((select && select->check_quick(thd, safe_update, limit)) || !limit) @@ -953,7 +908,7 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, } } DBUG_ASSERT(transactional_table || !deleted || thd->transaction->stmt.modified_non_trans_table); - + if (likely(error < 0) || (thd->lex->ignore && !thd->is_error() && !thd->is_fatal_error)) { @@ -1003,90 +958,6 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, } -/* - Prepare items in DELETE statement - - SYNOPSIS - mysql_prepare_delete() - thd - thread handler - table_list - global/local table list - conds - conditions - - RETURN VALUE - FALSE OK - TRUE error -*/ -int mysql_prepare_delete(THD *thd, TABLE_LIST *table_list, Item **conds, - bool *delete_while_scanning) -{ - Item *fake_conds= 0; - SELECT_LEX *select_lex= thd->lex->first_select_lex(); - DBUG_ENTER("mysql_prepare_delete"); - List<Item> all_fields; - - *delete_while_scanning= true; - thd->lex->allow_sum_func.clear_all(); - if (setup_tables_and_check_access(thd, &select_lex->context, - &select_lex->top_join_list, table_list, - select_lex->leaf_tables, FALSE, - DELETE_ACL, SELECT_ACL, TRUE)) - DBUG_RETURN(TRUE); - - 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); - DBUG_RETURN(true); - } - - if (table_list->has_period()) - { - if (table_list->is_view_or_derived()) - { - my_error(ER_IT_IS_A_VIEW, MYF(0), table_list->table_name.str); - DBUG_RETURN(true); - } - - if (select_lex->period_setup_conds(thd, table_list)) - DBUG_RETURN(true); - } - - DBUG_ASSERT(table_list->table); - // conds could be cached from previous SP call - DBUG_ASSERT(!table_list->vers_conditions.need_setup() || - !*conds || thd->stmt_arena->is_stmt_execute()); - if (select_lex->vers_setup_conds(thd, table_list)) - DBUG_RETURN(TRUE); - - *conds= select_lex->where; - - if (setup_returning_fields(thd, table_list) || - setup_conds(thd, table_list, select_lex->leaf_tables, conds) || - setup_ftfuncs(select_lex)) - DBUG_RETURN(TRUE); - if (!table_list->single_table_updatable() || - check_key_in_view(thd, table_list)) - { - my_error(ER_NON_UPDATABLE_TABLE, MYF(0), table_list->alias.str, "DELETE"); - DBUG_RETURN(TRUE); - } - - /* - Application-time periods: if FOR PORTION OF ... syntax used, DELETE - statement could issue delete_row's mixed with write_row's. This causes - problems for myisam and corrupts table, if deleting while scanning. - */ - if (table_list->has_period() - || unique_table(thd, table_list, table_list->next_global, 0)) - *delete_while_scanning= false; - - if (select_lex->inner_refs_list.elements && - fix_inner_refs(thd, all_fields, select_lex, select_lex->ref_pointer_array)) - DBUG_RETURN(TRUE); - - select_lex->fix_prepare_information(thd, conds, &fake_conds); - DBUG_RETURN(FALSE); -} - /*************************************************************************** Delete multiple tables from join @@ -1099,106 +970,6 @@ extern "C" int refpos_order_cmp(void* arg, const void *a,const void *b) return file->cmp_ref((const uchar*)a, (const uchar*)b); } -/* - make delete specific preparation and checks after opening tables - - SYNOPSIS - mysql_multi_delete_prepare() - thd thread handler - - RETURN - FALSE OK - TRUE Error -*/ - -int mysql_multi_delete_prepare(THD *thd) -{ - LEX *lex= thd->lex; - TABLE_LIST *aux_tables= lex->auxiliary_table_list.first; - TABLE_LIST *target_tbl; - DBUG_ENTER("mysql_multi_delete_prepare"); - - if (mysql_handle_derived(lex, DT_INIT)) - DBUG_RETURN(TRUE); - if (mysql_handle_derived(lex, DT_MERGE_FOR_INSERT)) - DBUG_RETURN(TRUE); - if (mysql_handle_derived(lex, DT_PREPARE)) - DBUG_RETURN(TRUE); - /* - setup_tables() need for VIEWs. JOIN::prepare() will not do it second - time. - - lex->query_tables also point on local list of DELETE SELECT_LEX - */ - if (setup_tables_and_check_access(thd, - &thd->lex->first_select_lex()->context, - &thd->lex->first_select_lex()-> - top_join_list, - lex->query_tables, - lex->first_select_lex()->leaf_tables, - FALSE, DELETE_ACL, SELECT_ACL, FALSE)) - DBUG_RETURN(TRUE); - - /* - Multi-delete can't be constructed over-union => we always have - single SELECT on top and have to check underlying SELECTs of it - */ - lex->first_select_lex()->set_unique_exclude(); - /* Fix tables-to-be-deleted-from list to point at opened tables */ - for (target_tbl= (TABLE_LIST*) aux_tables; - target_tbl; - target_tbl= target_tbl->next_local) - { - - target_tbl->table= target_tbl->correspondent_table->table; - if (target_tbl->correspondent_table->is_multitable()) - { - my_error(ER_VIEW_DELETE_MERGE_VIEW, MYF(0), - target_tbl->correspondent_table->view_db.str, - target_tbl->correspondent_table->view_name.str); - DBUG_RETURN(TRUE); - } - - if (!target_tbl->correspondent_table->single_table_updatable() || - check_key_in_view(thd, target_tbl->correspondent_table)) - { - my_error(ER_NON_UPDATABLE_TABLE, MYF(0), - target_tbl->table_name.str, "DELETE"); - DBUG_RETURN(TRUE); - } - } - - for (target_tbl= (TABLE_LIST*) aux_tables; - target_tbl; - target_tbl= target_tbl->next_local) - { - /* - Check that table from which we delete is not used somewhere - inside subqueries/view. - */ - { - TABLE_LIST *duplicate; - if ((duplicate= unique_table(thd, target_tbl->correspondent_table, - lex->query_tables, 0))) - { - update_non_unique_table_error(target_tbl->correspondent_table, - "DELETE", duplicate); - DBUG_RETURN(TRUE); - } - } - } - /* - Reset the exclude flag to false so it doesn't interfare - with further calls to unique_table - */ - lex->first_select_lex()->exclude_from_table_unique_test= FALSE; - - if (lex->save_prep_leaf_tables()) - DBUG_RETURN(TRUE); - - DBUG_RETURN(FALSE); -} - 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), @@ -1647,3 +1418,301 @@ bool multi_delete::send_eof() } return 0; } + + +bool Sql_cmd_delete::precheck(THD *thd) +{ + if (!multitable) + { + if (delete_precheck(thd, lex->query_tables)) + return true; + } + else + { + if (multi_delete_precheck(thd, lex->query_tables)) + return true; + } + + WSREP_SYNC_WAIT(thd, WSREP_SYNC_WAIT_BEFORE_UPDATE_DELETE); + + return false; + +#ifdef WITH_WSREP +wsrep_error_label: +#endif + return true; +} + + +bool Sql_cmd_delete::prepare_inner(THD *thd) +{ + int err= 0; + TABLE_LIST *target_tbl; + JOIN *join; + SELECT_LEX *const select_lex = thd->lex->first_select_lex(); + TABLE_LIST *const table_list = select_lex->get_table_list(); + TABLE_LIST *aux_tables= thd->lex->auxiliary_table_list.first; + ulonglong select_options= select_lex->options; + bool free_join= 1; + SELECT_LEX *returning= thd->lex->has_returning() ? thd->lex->returning() : 0; + const bool delete_history= table_list->vers_conditions.delete_history; + DBUG_ASSERT(!(delete_history && table_list->period_conditions.is_set())); + + DBUG_ENTER("Sql_cmd_delete::prepare_inner"); + + (void) read_statistics_for_tables_if_needed(thd, table_list); + + THD_STAGE_INFO(thd, stage_init_update); + + { + if (mysql_handle_derived(lex, DT_INIT)) + DBUG_RETURN(TRUE); + if (mysql_handle_derived(lex, DT_MERGE_FOR_INSERT)) + DBUG_RETURN(TRUE); + if (mysql_handle_derived(lex, DT_PREPARE)) + DBUG_RETURN(TRUE); + } + + if (!(result= new (thd->mem_root) multi_delete(thd, aux_tables, + lex->table_count))) + { + DBUG_RETURN(TRUE); + } + + table_list->delete_while_scanning= true; + + if (!multitable && !table_list->single_table_updatable()) + { + my_error(ER_NON_UPDATABLE_TABLE, MYF(0), table_list->alias.str, "DELETE"); + DBUG_RETURN(TRUE); + } + + if (!multitable && (!table_list->table || !table_list->table->is_created())) + { + my_error(ER_VIEW_DELETE_MERGE_VIEW, MYF(0), + table_list->view_db.str, table_list->view_name.str); + DBUG_RETURN(TRUE); + } + + if (setup_tables_and_check_access(thd, &select_lex->context, + &select_lex->top_join_list, + table_list, select_lex->leaf_tables, + false, DELETE_ACL, SELECT_ACL, true)) + DBUG_RETURN(TRUE); + + if (setup_tables(thd, &select_lex->context, &select_lex->top_join_list, + table_list, select_lex->leaf_tables, false, false)) + DBUG_RETURN(TRUE); + + if (!multitable) + { + 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); + DBUG_RETURN(true); + } + + if (table_list->has_period()) + { + if (table_list->is_view_or_derived()) + { + my_error(ER_IT_IS_A_VIEW, MYF(0), table_list->table_name.str); + DBUG_RETURN(true); + } + + if (select_lex->period_setup_conds(thd, table_list)) + DBUG_RETURN(true); + } + + if (select_lex->vers_setup_conds(thd, table_list)) + DBUG_RETURN(TRUE); + /* + Application-time periods: if FOR PORTION OF ... syntax used, DELETE + statement could issue delete_row's mixed with write_row's. This causes + problems for myisam and corrupts table, if deleting while scanning. + */ + if (table_list->has_period() + || unique_table(thd, table_list, table_list->next_global, 0)) + table_list->delete_while_scanning= false; + } + + if (multitable) + { + /* + Multi-delete can't be constructed over-union => we always have + single SELECT on top and have to check underlying SELECTs of it + */ + lex->first_select_lex()->set_unique_exclude(); + /* Fix tables-to-be-deleted-from list to point at opened tables */ + for (target_tbl= (TABLE_LIST*) aux_tables; + target_tbl; + target_tbl= target_tbl->next_local) + { + target_tbl->table= target_tbl->correspondent_table->table; + if (target_tbl->correspondent_table->is_multitable()) + { + my_error(ER_VIEW_DELETE_MERGE_VIEW, MYF(0), + target_tbl->correspondent_table->view_db.str, + target_tbl->correspondent_table->view_name.str); + DBUG_RETURN(TRUE); + } + + if (!target_tbl->correspondent_table->single_table_updatable() || + check_key_in_view(thd, target_tbl->correspondent_table)) + { + my_error(ER_NON_UPDATABLE_TABLE, MYF(0), + target_tbl->table_name.str, "DELETE"); + DBUG_RETURN(TRUE); + } + } + + for (target_tbl= (TABLE_LIST*) aux_tables; + target_tbl; + target_tbl= target_tbl->next_local) + { + /* + Check that table from which we delete is not used somewhere + inside subqueries/view. + */ + { + TABLE_LIST *duplicate; + if ((duplicate= unique_table(thd, target_tbl->correspondent_table, + lex->query_tables, 0))) + { + update_non_unique_table_error(target_tbl->correspondent_table, + "DELETE", duplicate); + DBUG_RETURN(TRUE); + } + } + } + /* + Reset the exclude flag to false so it doesn't interfare + with further calls to unique_table + */ + lex->first_select_lex()->exclude_from_table_unique_test= FALSE; + } + + { + if (thd->lex->describe) + select_options|= SELECT_DESCRIBE; + + /* + When in EXPLAIN, delay deleting the joins so that they are still + available when we're producing EXPLAIN EXTENDED warning text. + */ + if (select_options & SELECT_DESCRIBE) + free_join= 0; + select_options|= + SELECT_NO_JOIN_CACHE | SELECT_NO_UNLOCK | OPTION_SETUP_TABLES_DONE; + + if (!(join= new (thd->mem_root) JOIN(thd, empty_list, + select_options, result))) + DBUG_RETURN(TRUE); + THD_STAGE_INFO(thd, stage_init); + select_lex->join= join; + thd->lex->used_tables=0; + if ((err= join->prepare(table_list, select_lex->where, + select_lex->order_list.elements, + select_lex->order_list.first, + false, NULL, NULL, NULL, + select_lex, &lex->unit))) + + { + goto err; + } + + } + + if (!multitable && table_list->has_period()) + { + if (!table_list->period_conditions.start.item->const_item() + || !table_list->period_conditions.end.item->const_item()) + { + my_error(ER_NOT_CONSTANT_EXPRESSION, MYF(0), "FOR PORTION OF"); + DBUG_RETURN(true); + } + } + + if (delete_history) + table_list->table->vers_write= false; + + if (setup_returning_fields(thd, table_list) || + setup_ftfuncs(select_lex)) + goto err; + + free_join= false; + + if (returning) + (void) result->prepare(returning->item_list, NULL); + +err: + + if (free_join) + { + THD_STAGE_INFO(thd, stage_end); + err|= (int)(select_lex->cleanup()); + DBUG_RETURN(err || thd->is_error()); + } + DBUG_RETURN(err); + +} + +bool Sql_cmd_delete::execute_inner(THD *thd) +{ + if (!multitable) + { + if (lex->has_returning()) + { + select_result *sel_result= NULL; + delete result; + /* This is DELETE ... RETURNING. It will return output to the client */ + if (thd->lex->analyze_stmt) + { + /* + Actually, it is ANALYZE .. DELETE .. RETURNING. We need to produce + output and then discard it. + */ + sel_result= new (thd->mem_root) select_send_analyze(thd); + save_protocol= thd->protocol; + thd->protocol= new Protocol_discard(thd); + } + else + { + if (!lex->result && !(sel_result= new (thd->mem_root) select_send(thd))) + return true; + } + result= lex->result ? lex->result : sel_result; + } + } + + bool res= multitable ? Sql_cmd_dml::execute_inner(thd) + : delete_from_single_table(thd); + + res|= thd->is_error(); + + if (save_protocol) + { + delete thd->protocol; + thd->protocol= save_protocol; + } + { + if (unlikely(res)) + { + if (multitable) + result->abort_result_set(); + } + else + { + if (thd->lex->describe || thd->lex->analyze_stmt) + res= thd->lex->explain->send_explain(thd); + } + } + + if (result) + { + res= false; + delete result; + } + + return res; +} diff --git a/sql/sql_delete.h b/sql/sql_delete.h index dabcafb..64b882a 100644 --- a/sql/sql_delete.h +++ b/sql/sql_delete.h @@ -17,6 +17,9 @@ #define SQL_DELETE_INCLUDED #include "my_base.h" /* ha_rows */ +#include "sql_class.h" /* enum_duplicates */ +#include "sql_cmd.h" // Sql_cmd_dml +#include "sql_base.h" class THD; struct TABLE_LIST; @@ -26,12 +29,6 @@ class select_result; typedef class Item COND; template <typename T> class SQL_I_List; -int mysql_prepare_delete(THD *thd, TABLE_LIST *table_list, Item **conds, - bool *delete_while_scanning); -bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, - SQL_I_List<ORDER> *order, ha_rows rows, - ulonglong options, select_result *result); - class Sql_cmd_delete final : public Sql_cmd_dml { public: @@ -43,6 +40,11 @@ class Sql_cmd_delete final : public Sql_cmd_dml return multitable ? SQLCOM_DELETE_MULTI : SQLCOM_DELETE; } + DML_prelocking_strategy *get_dml_prelocking_strategy() + { + return &dml_prelocking_strategy; + } + protected: bool precheck(THD *thd) override; @@ -55,5 +57,6 @@ class Sql_cmd_delete final : public Sql_cmd_dml bool multitable; + DML_prelocking_strategy dml_prelocking_strategy; }; #endif /* SQL_DELETE_INCLUDED */ diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index ee5b2a8..aca34e9 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -3328,34 +3328,6 @@ void st_select_lex_unit::exclude_level() } -#if 0 -/* - Exclude subtree of current unit from tree of SELECTs - - SYNOPSYS - st_select_lex_unit::exclude_tree() -*/ -void st_select_lex_unit::exclude_tree() -{ - for (SELECT_LEX *sl= first_select(); sl; sl= sl->next_select()) - { - // unlink current level from global SELECTs list - if (sl->link_prev && (*sl->link_prev= sl->link_next)) - sl->link_next->link_prev= sl->link_prev; - - // unlink underlay levels - for (SELECT_LEX_UNIT *u= sl->first_inner_unit(); u; u= u->next_unit()) - { - u->exclude_level(); - } - } - // exclude currect unit from list of nodes - (*prev)= next; - if (next) - next->prev= prev; -} -#endif - /* st_select_lex_node::mark_as_dependent mark all st_select_lex struct from @@ -3577,7 +3549,7 @@ bool st_select_lex::setup_ref_array(THD *thd, uint order_group_num) select_n_where_fields + order_group_num + hidden_bit_fields + - fields_in_window_functions) * (size_t) 5; + fields_in_window_functions + 1) * (size_t) 5; DBUG_ASSERT(n_elems % 5 == 0); if (!ref_pointer_array.is_null()) { diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 72ac199..ecb06f5 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -34,9 +34,7 @@ #include "sql_locale.h" // my_locale_en_US #include "log.h" // flush_error_log #include "sql_view.h" // mysql_create_view, mysql_drop_view -#include "sql_delete.h" // mysql_delete #include "sql_insert.h" // mysql_insert -#include "sql_update.h" // mysql_update, mysql_multi_update #include "sql_partition.h" // struct partition_info #include "sql_db.h" // mysql_change_db, mysql_create_db, // mysql_rm_db, mysql_upgrade_db, @@ -4395,10 +4393,12 @@ mysql_execute_command(THD *thd, bool is_called_from_prepared_stmt) } case SQLCOM_UPDATE: case SQLCOM_UPDATE_MULTI: + case SQLCOM_DELETE: + case SQLCOM_DELETE_MULTI: { DBUG_ASSERT(first_table == all_tables && first_table != 0); DBUG_ASSERT(lex->m_sql_cmd != NULL); - thd->abort_on_warning= !thd->lex->ignore && thd->is_strict_mode(); + res = lex->m_sql_cmd->execute(thd); thd->abort_on_warning= 0; break; @@ -4662,129 +4662,6 @@ mysql_execute_command(THD *thd, bool is_called_from_prepared_stmt) break; } - case SQLCOM_DELETE: - { - WSREP_SYNC_WAIT(thd, WSREP_SYNC_WAIT_BEFORE_UPDATE_DELETE); - select_result *sel_result= NULL; - DBUG_ASSERT(first_table == all_tables && first_table != 0); - WSREP_SYNC_WAIT(thd, WSREP_SYNC_WAIT_BEFORE_UPDATE_DELETE); - - if ((res= delete_precheck(thd, all_tables))) - break; - DBUG_ASSERT(select_lex->limit_params.offset_limit == 0); - unit->set_limit(select_lex); - - MYSQL_DELETE_START(thd->query()); - Protocol *save_protocol= NULL; - - if (lex->has_returning()) - { - /* This is DELETE ... RETURNING. It will return output to the client */ - if (thd->lex->analyze_stmt) - { - /* - Actually, it is ANALYZE .. DELETE .. RETURNING. We need to produce - output and then discard it. - */ - sel_result= new (thd->mem_root) select_send_analyze(thd); - save_protocol= thd->protocol; - thd->protocol= new Protocol_discard(thd); - } - else - { - if (!lex->result && !(sel_result= new (thd->mem_root) select_send(thd))) - goto error; - } - } - - res = mysql_delete(thd, all_tables, - select_lex->where, &select_lex->order_list, - unit->lim.get_select_limit(), select_lex->options, - lex->result ? lex->result : sel_result); - - if (save_protocol) - { - delete thd->protocol; - thd->protocol= save_protocol; - } - - if (thd->lex->analyze_stmt || thd->lex->describe) - { - if (!res) - res= thd->lex->explain->send_explain(thd); - } - - delete sel_result; - MYSQL_DELETE_DONE(res, (ulong) thd->get_row_count_func()); - break; - } - case SQLCOM_DELETE_MULTI: - { - WSREP_SYNC_WAIT(thd, WSREP_SYNC_WAIT_BEFORE_UPDATE_DELETE); - DBUG_ASSERT(first_table == all_tables && first_table != 0); - TABLE_LIST *aux_tables= thd->lex->auxiliary_table_list.first; - multi_delete *result; - WSREP_SYNC_WAIT(thd, WSREP_SYNC_WAIT_BEFORE_UPDATE_DELETE); - - if ((res= multi_delete_precheck(thd, all_tables))) - break; - - /* condition will be TRUE on SP re-excuting */ - if (select_lex->item_list.elements != 0) - select_lex->item_list.empty(); - if (add_item_to_list(thd, new (thd->mem_root) Item_null(thd))) - goto error; - - THD_STAGE_INFO(thd, stage_init); - if ((res= open_and_lock_tables(thd, all_tables, TRUE, 0))) - break; - - MYSQL_MULTI_DELETE_START(thd->query()); - if (unlikely(res= mysql_multi_delete_prepare(thd))) - { - MYSQL_MULTI_DELETE_DONE(1, 0); - goto error; - } - - if (likely(!thd->is_fatal_error)) - { - result= new (thd->mem_root) multi_delete(thd, aux_tables, - lex->table_count); - if (likely(result)) - { - if (unlikely(select_lex->vers_setup_conds(thd, aux_tables))) - goto multi_delete_error; - res= mysql_select(thd, - select_lex->get_table_list(), - select_lex->item_list, - select_lex->where, - 0, (ORDER *)NULL, (ORDER *)NULL, (Item *)NULL, - (ORDER *)NULL, - (select_lex->options | thd->variables.option_bits | - SELECT_NO_JOIN_CACHE | SELECT_NO_UNLOCK | - OPTION_SETUP_TABLES_DONE) & ~OPTION_BUFFER_RESULT, - result, unit, select_lex); - res|= (int)(thd->is_error()); - - MYSQL_MULTI_DELETE_DONE(res, result->num_deleted()); - if (res) - result->abort_result_set(); /* for both DELETE and EXPLAIN DELETE */ - else - { - if (lex->describe || lex->analyze_stmt) - res= thd->lex->explain->send_explain(thd); - } - multi_delete_error: - delete result; - } - } - else - { - res= TRUE; // Error - MYSQL_MULTI_DELETE_DONE(1, 0); - } - break; - } case SQLCOM_DROP_SEQUENCE: case SQLCOM_DROP_TABLE: { @@ -7659,12 +7536,16 @@ void create_select_for_variable(THD *thd, LEX_CSTRING *var_name) } -void mysql_init_multi_delete(LEX *lex) +void mysql_init_delete(LEX *lex) { - lex->sql_command= SQLCOM_DELETE_MULTI; mysql_init_select(lex); lex->first_select_lex()->limit_params.clear(); lex->unit.lim.clear(); +} + +void mysql_init_multi_delete(LEX *lex) +{ + lex->sql_command= SQLCOM_DELETE_MULTI; lex->first_select_lex()->table_list. save_and_clear(&lex->auxiliary_table_list); lex->query_tables= 0; diff --git a/sql/sql_parse.h b/sql/sql_parse.h index ebe3fe9..45cd15c 100644 --- a/sql/sql_parse.h +++ b/sql/sql_parse.h @@ -95,6 +95,7 @@ void mysql_parse(THD *thd, char *rawbuf, uint length, bool mysql_new_select(LEX *lex, bool move_down, SELECT_LEX *sel); void create_select_for_variable(THD *thd, LEX_CSTRING *var_name); void create_table_set_open_action_and_adjust_tables(LEX *lex); +void mysql_init_delete(LEX *lex); void mysql_init_multi_delete(LEX *lex); bool multi_delete_set_locks_and_link_aux_tables(LEX *lex); void create_table_set_open_action_and_adjust_tables(LEX *lex); diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc index 3859a13..84feda3 100644 --- a/sql/sql_prepare.cc +++ b/sql/sql_prepare.cc @@ -95,7 +95,6 @@ When one supplies long data for a placeholder: #include "sql_base.h" // open_normal_and_derived_tables #include "sql_cache.h" // query_cache_* #include "sql_view.h" // create_view_precheck -#include "sql_delete.h" // mysql_prepare_delete #include "sql_select.h" // for JOIN #include "sql_insert.h" // upgrade_lock_type_for_insert, mysql_prepare_insert #include "sql_db.h" // mysql_opt_change_db, mysql_change_db @@ -1398,56 +1397,6 @@ static bool mysql_test_insert(Prepared_statement *stmt, } -/** - Validate DELETE statement. - - @param stmt prepared statement - @param tables list of tables used in this query - - @retval - FALSE success - @retval - TRUE error, error message is set in THD -*/ - -static bool mysql_test_delete(Prepared_statement *stmt, - TABLE_LIST *table_list) -{ - uint table_count= 0; - THD *thd= stmt->thd; - LEX *lex= stmt->lex; - bool delete_while_scanning; - DBUG_ENTER("mysql_test_delete"); - - if (delete_precheck(thd, table_list) || - open_tables(thd, &table_list, &table_count, MYSQL_OPEN_FORCE_SHARED_MDL)) - goto error; - - if (mysql_handle_derived(thd->lex, DT_INIT)) - goto error; - if (mysql_handle_derived(thd->lex, DT_MERGE_FOR_INSERT)) - goto error; - if (mysql_handle_derived(thd->lex, DT_PREPARE)) - goto error; - - if (!table_list->single_table_updatable()) - { - my_error(ER_NON_UPDATABLE_TABLE, MYF(0), table_list->alias.str, "DELETE"); - goto error; - } - if (!table_list->table || !table_list->table->is_created()) - { - my_error(ER_VIEW_DELETE_MERGE_VIEW, MYF(0), - table_list->view_db.str, table_list->view_name.str); - goto error; - } - - DBUG_RETURN(mysql_prepare_delete(thd, table_list, - &lex->first_select_lex()->where, - &delete_while_scanning)); -error: - DBUG_RETURN(TRUE); -} /** @@ -2031,48 +1980,6 @@ static bool mysql_test_create_view(Prepared_statement *stmt) /** - Validate and prepare for execution a multi delete statement. - - @param stmt prepared statement - @param tables list of tables used in this query - - @retval - FALSE success - @retval - TRUE error, error message in THD is set. -*/ - -static bool mysql_test_multidelete(Prepared_statement *stmt, - TABLE_LIST *tables) -{ - THD *thd= stmt->thd; - - thd->lex->current_select= thd->lex->first_select_lex(); - if (add_item_to_list(thd, new (thd->mem_root) - Item_null(thd))) - { - my_error(ER_OUTOFMEMORY, MYF(ME_FATAL), 0); - goto error; - } - - if (multi_delete_precheck(thd, tables) || - select_like_stmt_test_with_open(stmt, tables, - &mysql_multi_delete_prepare, - OPTION_SETUP_TABLES_DONE)) - goto error; - if (!tables->table) - { - my_error(ER_VIEW_DELETE_MERGE_VIEW, MYF(0), - tables->view_db.str, tables->view_name.str); - goto error; - } - return FALSE; -error: - return TRUE; -} - - -/** Wrapper for mysql_insert_select_prepare, to make change of local tables after open_normal_and_derived_tables() call. @@ -2354,14 +2261,13 @@ static bool check_prepared_statement(Prepared_statement *stmt) case SQLCOM_UPDATE: case SQLCOM_UPDATE_MULTI: + case SQLCOM_DELETE: + case SQLCOM_DELETE_MULTI: res = lex->m_sql_cmd->prepare(thd); if (!res) lex->m_sql_cmd->unprepare(thd); break; - case SQLCOM_DELETE: - res= mysql_test_delete(stmt, tables); - break; /* The following allow WHERE clause, so they must be tested like SELECT */ case SQLCOM_SHOW_DATABASES: case SQLCOM_SHOW_TABLES: @@ -2498,10 +2404,6 @@ static bool check_prepared_statement(Prepared_statement *stmt) res= mysql_test_set_fields(stmt, tables, &lex->var_list); break; - case SQLCOM_DELETE_MULTI: - res= mysql_test_multidelete(stmt, tables); - break; - case SQLCOM_INSERT_SELECT: case SQLCOM_REPLACE_SELECT: res= mysql_test_insert_select(stmt, tables); diff --git a/sql/sql_select.cc b/sql/sql_select.cc index edd9aed..484f5ce 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -28846,7 +28846,8 @@ static bool get_range_limit_read_cost(const JOIN_TAB *tab, @note This function takes into account table->opt_range_condition_rows statistic (that is calculated by the make_join_statistics function). - However, single table procedures such as mysql_update() and mysql_delete() + However, single table procedures such as Sql_cmd_update:update_single_table() + and Sql_cmd_delete::delete_single_table() never call make_join_statistics, so they have to update it manually (@see get_index_for_order()). */ @@ -30280,6 +30281,67 @@ static bool process_direct_rownum_comparison(THD *thd, SELECT_LEX_UNIT *unit, } +static void MYSQL_DML_START(THD *thd) +{ + switch (thd->lex->sql_command) { + + case SQLCOM_UPDATE: + MYSQL_UPDATE_START(thd->query()); + break; + case SQLCOM_UPDATE_MULTI: + MYSQL_MULTI_UPDATE_START(thd->query()); + break; + case SQLCOM_DELETE: + MYSQL_DELETE_START(thd->query()); + break; + case SQLCOM_DELETE_MULTI: + MYSQL_MULTI_DELETE_START(thd->query()); + break; + default: + DBUG_ASSERT(0); + } +} + + +static void MYSQL_DML_DONE(THD *thd, int rc) +{ + switch (thd->lex->sql_command) { + + case SQLCOM_UPDATE: + MYSQL_UPDATE_DONE( + rc, + (rc ? 0 : + ((multi_update*)(((Sql_cmd_dml*)(thd->lex->m_sql_cmd))->get_result())) + ->num_found()), + (rc ? 0 : + ((multi_update*)(((Sql_cmd_dml*)(thd->lex->m_sql_cmd))->get_result())) + ->num_updated())); + break; + case SQLCOM_UPDATE_MULTI: + MYSQL_MULTI_UPDATE_DONE( + rc, + (rc ? 0 : + ((multi_update*)(((Sql_cmd_dml*)(thd->lex->m_sql_cmd))->get_result())) + ->num_found()), + (rc ? 0 : + ((multi_update*)(((Sql_cmd_dml*)(thd->lex->m_sql_cmd))->get_result())) + ->num_updated())); + break; + case SQLCOM_DELETE: + MYSQL_DELETE_DONE(rc, (rc ? 0 : (ulong) (thd->get_row_count_func()))); + break; + case SQLCOM_DELETE_MULTI: + MYSQL_MULTI_DELETE_DONE( + rc, + (rc ? 0 : + ((multi_delete*)(((Sql_cmd_dml*)(thd->lex->m_sql_cmd))->get_result())) + ->num_deleted())); + break; + default: + DBUG_ASSERT(0); + } +} + bool Sql_cmd_dml::prepare(THD *thd) { lex= thd->lex; @@ -30291,6 +30353,8 @@ bool Sql_cmd_dml::prepare(THD *thd) if (precheck(thd)) goto err; + MYSQL_DML_START(thd); + lex->context_analysis_only|= CONTEXT_ANALYSIS_ONLY_DERIVED; if (open_tables_for_query(thd, lex->query_tables, &table_count, 0, @@ -30338,6 +30402,9 @@ bool Sql_cmd_dml::execute(THD *thd) { if (precheck(thd)) goto err; + + MYSQL_DML_START(thd); + if (open_tables_for_query(thd, lex->query_tables, &table_count, 0, get_dml_prelocking_strategy())) goto err; @@ -30345,7 +30412,6 @@ bool Sql_cmd_dml::execute(THD *thd) THD_STAGE_INFO(thd, stage_init); - DBUG_ASSERT(!lex->is_query_tables_locked()); /* Locking of tables is done after preparation but before optimization. This allows to do better partition pruning and avoid locking unused @@ -30373,10 +30439,13 @@ bool Sql_cmd_dml::execute(THD *thd) THD_STAGE_INFO(thd, stage_end); + MYSQL_DML_DONE(thd, res); + return res; err: DBUG_ASSERT(thd->is_error() || thd->killed); + MYSQL_DML_DONE(thd, 1); THD_STAGE_INFO(thd, stage_end); (void)unit->cleanup(); diff --git a/sql/sql_update.cc b/sql/sql_update.cc index 3f04db0..4460191 100644 --- a/sql/sql_update.cc +++ b/sql/sql_update.cc @@ -197,22 +197,11 @@ static bool check_fields(THD *thd, TABLE_LIST *table, List<Item> &items, return true; } - DBUG_ASSERT(thd->lex->sql_command == SQLCOM_UPDATE); - for (List_iterator_fast<Item> it(items); (item=it++);) - { - Field *f= item->field_for_view_update()->field; - vers_select_conds_t &period= table->period_conditions; - if (period.field_start->field == f || period.field_end->field == f) - { - my_error(ER_PERIOD_COLUMNS_UPDATED, MYF(0), - item->name.str, period.name.str); - return true; - } - } } return FALSE; } + bool TABLE::vers_check_update(List<Item> &items) { List_iterator<Item> it(items); @@ -655,8 +644,8 @@ bool Sql_cmd_update::update_single_table(THD *thd) } if (use_direct_update && - !table->file->info_push(INFO_KIND_UPDATE_FIELDS, &fields) && - !table->file->info_push(INFO_KIND_UPDATE_VALUES, &values) && + !table->file->info_push(INFO_KIND_UPDATE_FIELDS, fields) && + !table->file->info_push(INFO_KIND_UPDATE_VALUES, values) && !table->file->direct_update_rows_init(fields)) { do_direct_update= TRUE; @@ -1240,6 +1229,9 @@ bool Sql_cmd_update::update_single_table(THD *thd) thd->lex->current_select->save_leaf_tables(thd); thd->lex->current_select->first_cond_optimization= 0; } + ((multi_update *)result)->set_found(found); + ((multi_update *)result)->set_updated(updated); + if (unlikely(thd->lex->analyze_stmt)) goto emit_explain_and_leave; @@ -1271,75 +1263,6 @@ bool Sql_cmd_update::update_single_table(THD *thd) DBUG_RETURN((err2 || thd->is_error()) ? 1 : 0); } -/* - Prepare items in UPDATE statement - - SYNOPSIS - mysql_prepare_update() - thd - thread handler - table_list - global/local table list - conds - conditions - order_num - number of ORDER BY list entries - order - ORDER BY clause list - - RETURN VALUE - FALSE OK - TRUE error -*/ -bool mysql_prepare_update(THD *thd, TABLE_LIST *table_list, - Item **conds, uint order_num, ORDER *order) -{ - Item *fake_conds= 0; -#ifndef NO_EMBEDDED_ACCESS_CHECKS - TABLE *table= table_list->table; -#endif - List<Item> all_fields; - SELECT_LEX *select_lex= thd->lex->first_select_lex(); - DBUG_ENTER("mysql_prepare_update"); - -#ifndef NO_EMBEDDED_ACCESS_CHECKS - table_list->grant.want_privilege= table->grant.want_privilege= - (SELECT_ACL & ~table->grant.privilege); - table_list->register_want_access(SELECT_ACL); -#endif - - thd->lex->allow_sum_func.clear_all(); - - if (table_list->has_period() && - select_lex->period_setup_conds(thd, table_list)) - DBUG_RETURN(true); - - DBUG_ASSERT(table_list->table); - // conds could be cached from previous SP call - DBUG_ASSERT(!table_list->vers_conditions.need_setup() || - !*conds || thd->stmt_arena->is_stmt_execute()); - if (select_lex->vers_setup_conds(thd, table_list)) - DBUG_RETURN(TRUE); - - *conds= select_lex->where; - - /* - We do not call DT_MERGE_FOR_INSERT because it has no sense for simple - (not multi-) update - */ - if (mysql_handle_derived(thd->lex, DT_PREPARE)) - DBUG_RETURN(TRUE); - - if (setup_tables_and_check_access(thd, &select_lex->context, - &select_lex->top_join_list, table_list, - select_lex->leaf_tables, - FALSE, UPDATE_ACL, SELECT_ACL, TRUE) || - setup_conds(thd, table_list, select_lex->leaf_tables, conds) || - select_lex->setup_ref_array(thd, order_num) || - setup_order(thd, select_lex->ref_pointer_array, - table_list, all_fields, all_fields, order) || - setup_ftfuncs(select_lex)) - DBUG_RETURN(TRUE); - - - select_lex->fix_prepare_information(thd, conds, &fake_conds); - DBUG_RETURN(FALSE); -} /** Check that we are not using table that we are updating in a sub select @@ -1596,7 +1519,13 @@ bool Multiupdate_prelocking_strategy::handle_end(THD *thd) mysql_handle_derived(lex, DT_PREPARE)) DBUG_RETURN(1); - /* + if (table_list->has_period() && table_list->is_view_or_derived()) + { + my_error(ER_IT_IS_A_VIEW, MYF(0), table_list->table_name.str); + DBUG_RETURN(TRUE); + } + + /* setup_tables() need for VIEWs. JOIN::prepare() will call setup_tables() second time, but this call will do nothing (there are check for second call in setup_tables()). @@ -1607,6 +1536,10 @@ bool Multiupdate_prelocking_strategy::handle_end(THD *thd) FALSE, UPDATE_ACL, SELECT_ACL, TRUE)) DBUG_RETURN(1); + if (table_list->has_period() && + select_lex->period_setup_conds(thd, table_list)) + DBUG_RETURN(true); + List<Item> *fields= &lex->first_select_lex()->item_list; if (setup_fields_with_no_wrap(thd, Ref_ptr_array(), *fields, MARK_COLUMNS_WRITE, 0, 0)) @@ -1715,153 +1648,6 @@ bool Multiupdate_prelocking_strategy::handle_end(THD *thd) DBUG_RETURN(0); } -/* - make update specific preparation and checks after opening tables - - SYNOPSIS - mysql_multi_update_prepare() - thd thread handler - - RETURN - FALSE OK - TRUE Error -*/ - -int mysql_multi_update_prepare(THD *thd) -{ - LEX *lex= thd->lex; - TABLE_LIST *table_list= lex->query_tables; - TABLE_LIST *tl; - Multiupdate_prelocking_strategy prelocking_strategy; - uint table_count= lex->table_count; - DBUG_ENTER("mysql_multi_update_prepare"); - - /* - Open tables and create derived ones, but do not lock and fill them yet. - - During prepare phase acquire only S metadata locks instead of SW locks to - keep prepare of multi-UPDATE compatible with concurrent LOCK TABLES WRITE - and global read lock. - - Don't evaluate any subqueries even if constant, because - tables aren't locked yet. - */ - lex->context_analysis_only|= CONTEXT_ANALYSIS_ONLY_DERIVED; - if (thd->lex->sql_command == SQLCOM_UPDATE_MULTI) - { - if (open_tables(thd, &table_list, &table_count, - thd->stmt_arena->is_stmt_prepare() ? MYSQL_OPEN_FORCE_SHARED_MDL : 0, - &prelocking_strategy)) - DBUG_RETURN(TRUE); - } - else - { - /* following need for prepared statements, to run next time multi-update */ - thd->lex->sql_command= SQLCOM_UPDATE_MULTI; - prelocking_strategy.reset(thd); - if (prelocking_strategy.handle_end(thd)) - DBUG_RETURN(TRUE); - } - - /* now lock and fill tables */ - if (!thd->stmt_arena->is_stmt_prepare() && - lock_tables(thd, table_list, table_count, 0)) - DBUG_RETURN(TRUE); - - lex->context_analysis_only&= ~CONTEXT_ANALYSIS_ONLY_DERIVED; - - (void) read_statistics_for_tables_if_needed(thd, table_list); - /* @todo: downgrade the metadata locks here. */ - - /* - Check that we are not using table that we are updating, but we should - skip all tables of UPDATE SELECT itself - */ - lex->first_select_lex()->exclude_from_table_unique_test= TRUE; - /* We only need SELECT privilege for columns in the values list */ - List_iterator<TABLE_LIST> ti(lex->first_select_lex()->leaf_tables); - while ((tl= ti++)) - { - if (tl->is_jtbm()) - continue; - TABLE *table= tl->table; - TABLE_LIST *tlist; - if (!(tlist= tl->top_table())->derived) - { - tlist->grant.want_privilege= - (SELECT_ACL & ~tlist->grant.privilege); - table->grant.want_privilege= (SELECT_ACL & ~table->grant.privilege); - } - DBUG_PRINT("info", ("table: %s want_privilege: %llx", tl->alias.str, - (longlong) table->grant.want_privilege)); - } - /* - Set exclude_from_table_unique_test value back to FALSE. It is needed for - further check in multi_update::prepare whether to use record cache. - */ - lex->first_select_lex()->exclude_from_table_unique_test= FALSE; - - if (lex->save_prep_leaf_tables()) - DBUG_RETURN(TRUE); - - DBUG_RETURN (FALSE); -} - - -/* - Setup multi-update handling and call SELECT to do the join -*/ - -bool mysql_multi_update(THD *thd, TABLE_LIST *table_list, List<Item> *fields, - List<Item> *values, COND *conds, ulonglong options, - enum enum_duplicates handle_duplicates, - bool ignore, SELECT_LEX_UNIT *unit, - SELECT_LEX *select_lex, multi_update **result) -{ - bool res; - DBUG_ENTER("mysql_multi_update"); - - if (!(*result= new (thd->mem_root) multi_update(thd, table_list, - &thd->lex->first_select_lex()->leaf_tables, - fields, values, handle_duplicates, ignore))) - { - DBUG_RETURN(TRUE); - } - - if ((*result)->init(thd)) - DBUG_RETURN(1); - - thd->abort_on_warning= !ignore && thd->is_strict_mode(); - List<Item> total_list; - - if (setup_tables(thd, &select_lex->context, &select_lex->top_join_list, - table_list, select_lex->leaf_tables, FALSE, FALSE)) - DBUG_RETURN(1); - - if (select_lex->vers_setup_conds(thd, table_list)) - DBUG_RETURN(1); - - res= mysql_select(thd, - table_list, total_list, conds, - select_lex->order_list.elements, - select_lex->order_list.first, NULL, NULL, NULL, - options | SELECT_NO_JOIN_CACHE | SELECT_NO_UNLOCK | - OPTION_SETUP_TABLES_DONE, - *result, unit, select_lex); - - DBUG_PRINT("info",("res: %d report_error: %d", res, (int) thd->is_error())); - res|= thd->is_error(); - if (unlikely(res)) - (*result)->abort_result_set(); - else - { - if (thd->lex->describe || thd->lex->analyze_stmt) - res= thd->lex->explain->send_explain(thd); - } - thd->abort_on_warning= 0; - DBUG_RETURN(res); -} - multi_update::multi_update(THD *thd_arg, TABLE_LIST *table_list, List<TABLE_LIST> *leaves_list, @@ -3021,7 +2807,15 @@ bool Sql_cmd_update::precheck(THD *thd) if (multi_update_precheck(thd, lex->query_tables)) return true; } + + WSREP_SYNC_WAIT(thd, WSREP_SYNC_WAIT_BEFORE_UPDATE_DELETE); + return false; + +#ifdef WITH_WSREP +wsrep_error_label: +#endif + return true; } @@ -3029,26 +2823,31 @@ bool Sql_cmd_update::prepare_inner(THD *thd) { JOIN *join; int err= 0; - // uint table_cnt= 0; SELECT_LEX *const select_lex = thd->lex->first_select_lex(); TABLE_LIST *const table_list = select_lex->get_table_list(); ulonglong select_options= select_lex->options; bool free_join= 1; - // bool orig_multitable= multitable; DBUG_ENTER("Sql_cmd_update::prepare_inner"); + (void) read_statistics_for_tables_if_needed(thd, table_list); + + THD_STAGE_INFO(thd, stage_init_update); + if (!multitable) { - TABLE_LIST *update_source_table= 0; - if (mysql_handle_derived(lex, DT_INIT)) DBUG_RETURN(TRUE); + } - if (table_list->has_period() && table_list->is_view_or_derived()) - { - my_error(ER_IT_IS_A_VIEW, MYF(0), table_list->table_name.str); - DBUG_RETURN(TRUE); - } + if (table_list->has_period() && table_list->is_view_or_derived()) + { + my_error(ER_IT_IS_A_VIEW, MYF(0), table_list->table_name.str); + DBUG_RETURN(TRUE); + } + + if (!multitable) + { + TABLE_LIST *update_source_table= 0; if (((update_source_table=unique_table(thd, table_list, table_list->next_global, 0)) || @@ -3088,8 +2887,8 @@ bool Sql_cmd_update::prepare_inner(THD *thd) DBUG_RETURN(TRUE); } - if (((multi_update *)result)->init(thd)) - DBUG_RETURN(TRUE); + if (((multi_update *)result)->init(thd)) + DBUG_RETURN(TRUE); if (setup_tables(thd, &select_lex->context, &select_lex->top_join_list, table_list, select_lex->leaf_tables, false, false)) @@ -3130,6 +2929,31 @@ bool Sql_cmd_update::prepare_inner(THD *thd) } + if (table_list->has_period()) + { + Item *item; + for (List_iterator_fast<Item> it(select_lex->item_list); (item=it++);) + { + Field *f= item->field_for_view_update()->field; + vers_select_conds_t &period= table_list->period_conditions; + if (period.field_start->field == f || period.field_end->field == f) + { + my_error(ER_PERIOD_COLUMNS_UPDATED, MYF(0), + item->name.str, period.name.str); + DBUG_RETURN(true); + } + } + + if (!table_list->period_conditions.start.item->const_item() + || !table_list->period_conditions.end.item->const_item()) + { + my_error(ER_NOT_CONSTANT_EXPRESSION, MYF(0), "FOR PORTION OF"); + DBUG_RETURN(true); + } + table_list->table->no_cache= true; + } + + free_join= false; err: @@ -3147,8 +2971,16 @@ bool Sql_cmd_update::prepare_inner(THD *thd) bool Sql_cmd_update::execute_inner(THD *thd) { - bool res= multitable ? Sql_cmd_dml::execute_inner(thd) - : update_single_table(thd); + bool res= 0; + + thd->get_stmt_da()->reset_current_row_for_warning(1); + if (!multitable) + res= update_single_table(thd); + else + { + thd->abort_on_warning= !thd->lex->ignore && thd->is_strict_mode(); + res= Sql_cmd_dml::execute_inner(thd); + } res|= thd->is_error(); if (multitable) diff --git a/sql/sql_update.h b/sql/sql_update.h index e52d3cd..cf33461 100644 --- a/sql/sql_update.h +++ b/sql/sql_update.h @@ -27,19 +27,7 @@ class THD; typedef class st_select_lex SELECT_LEX; typedef class st_select_lex_unit SELECT_LEX_UNIT; -bool mysql_prepare_update(THD *thd, TABLE_LIST *table_list, - Item **conds, uint order_num, ORDER *order); bool check_unique_table(THD *thd, TABLE_LIST *table_list); -int mysql_update(THD *thd,TABLE_LIST *tables,List<Item> &fields, - List<Item> &values,COND *conds, - uint order_num, ORDER *order, ha_rows limit, - bool ignore, ha_rows *found_return, ha_rows *updated_return); -bool mysql_multi_update(THD *thd, TABLE_LIST *table_list, - List<Item> *fields, List<Item> *values, - COND *conds, ulonglong options, - enum enum_duplicates handle_duplicates, bool ignore, - SELECT_LEX_UNIT *unit, SELECT_LEX *select_lex, - multi_update **result); bool records_are_comparable(const TABLE *table); bool compare_record(const TABLE *table); diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 30eda7c..48eed4e 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -70,6 +70,7 @@ #include "sql_type_json.h" #include "json_table.h" #include "sql_update.h" +#include "sql_delete.h" /* this is to get the bison compilation windows warnings out */ #ifdef _MSC_VER @@ -1675,7 +1676,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize); opt_mi_check_type opt_to mi_check_types table_to_table_list table_to_table opt_table_list opt_as handler_rkey_function handler_read_or_scan - single_multi table_wild_list table_wild_one opt_wild + single_multi opt_wild opt_and select_var_list select_var_list_init help opt_extended_describe shutdown @@ -13245,8 +13246,8 @@ update: MYSQL_YYABORT; /* In case of multi-update setting write lock for all tables may - be too pessimistic. We will decrease lock level if possible in - mysql_multi_update(). + be too pessimistic. We will decrease lock level if possible + later while processing the statement. */ slex->set_lock_for_tables($3, slex->table_list.elements == 1, false); } @@ -13303,12 +13304,11 @@ delete: DELETE_SYM { LEX *lex= Lex; - lex->sql_command= SQLCOM_DELETE; YYPS->m_lock_type= TL_WRITE_DEFAULT; YYPS->m_mdl_type= MDL_SHARED_WRITE; if (Lex->main_select_push()) MYSQL_YYABORT; - mysql_init_select(lex); + mysql_init_delete(lex); lex->ignore= 0; lex->first_select_lex()->order_list.empty(); } @@ -13334,8 +13334,13 @@ delete_part2: opt_delete_options single_multi {} | HISTORY_SYM delete_single_table opt_delete_system_time { - Lex->last_table()->vers_conditions= Lex->vers_conditions; - Lex->pop_select(); //main select + LEX *lex= Lex; + lex->last_table()->vers_conditions= lex->vers_conditions; + lex->pop_select(); //main select + lex->sql_command= SQLCOM_DELETE; + if (!(lex->m_sql_cmd= + new (thd->mem_root) Sql_cmd_delete(false))) + MYSQL_YYABORT; } ; @@ -13369,12 +13374,22 @@ single_multi: delete_limit_clause opt_returning { + LEX *lex= Lex; if ($3) Select->order_list= *($3); - Lex->pop_select(); //main select + lex->pop_select(); //main select + lex->sql_command= SQLCOM_DELETE; + if (!(lex->m_sql_cmd= + new (thd->mem_root) Sql_cmd_delete(false))) + MYSQL_YYABORT; } - | table_wild_list + | table_alias_ref_list { + LEX *lex= Lex; + lex->sql_command= SQLCOM_DELETE_MULTI; + if (!(lex->m_sql_cmd= + new (thd->mem_root) Sql_cmd_delete(true))) + MYSQL_YYABORT; mysql_init_multi_delete(Lex); YYPS->m_lock_type= TL_READ_DEFAULT; YYPS->m_mdl_type= MDL_SHARED_READ; @@ -13386,6 +13401,11 @@ single_multi: } stmt_end {} | FROM table_alias_ref_list { + LEX *lex= Lex; + lex->sql_command= SQLCOM_DELETE_MULTI; + if (!(lex->m_sql_cmd= + new (thd->mem_root) Sql_cmd_delete(true))) + MYSQL_YYABORT; mysql_init_multi_delete(Lex); YYPS->m_lock_type= TL_READ_DEFAULT; YYPS->m_mdl_type= MDL_SHARED_READ; @@ -13421,44 +13441,6 @@ opt_returning: } ; -table_wild_list: - table_wild_one - | table_wild_list ',' table_wild_one - ; - -table_wild_one: - ident opt_wild - { - Table_ident *ti= new (thd->mem_root) Table_ident(&$1); - if (unlikely(ti == NULL)) - MYSQL_YYABORT; - if (unlikely(!Select-> - add_table_to_list(thd, - ti, - NULL, - (TL_OPTION_UPDATING | - TL_OPTION_ALIAS), - YYPS->m_lock_type, - YYPS->m_mdl_type))) - MYSQL_YYABORT; - } - | ident '.' ident opt_wild - { - Table_ident *ti= new (thd->mem_root) Table_ident(thd, &$1, &$3, 0); - if (unlikely(ti == NULL)) - MYSQL_YYABORT; - if (unlikely(!Select-> - add_table_to_list(thd, - ti, - NULL, - (TL_OPTION_UPDATING | - TL_OPTION_ALIAS), - YYPS->m_lock_type, - YYPS->m_mdl_type))) - MYSQL_YYABORT; - } - ; - opt_wild: /* empty */ {} | '.' '*' {} diff --git a/sql/table.h b/sql/table.h index 8d609fb..358e1a9 100644 --- a/sql/table.h +++ b/sql/table.h @@ -2321,6 +2321,7 @@ struct TABLE_LIST */ select_unit *derived_result; /* Stub used for materialized derived tables. */ + bool delete_while_scanning; table_map map; /* ID bit of table (1,2,4,8,16...) */ table_map get_map() { diff --git a/storage/spider/mysql-test/spider/r/error_row_number.result b/storage/spider/mysql-test/spider/r/error_row_number.result index cc2b548..ad095fe 100644 --- a/storage/spider/mysql-test/spider/r/error_row_number.result +++ b/storage/spider/mysql-test/spider/r/error_row_number.result @@ -29,7 +29,7 @@ ERROR 23000: Duplicate entry '13' for key 'PRIMARY' get diagnostics condition 1 @n = row_number; select @n; @n -0 +1 drop table spd; connection child2_1; drop database auto_test_remote;
1 0
0 0
[Commits] 0a975cc: MDEV-27159 Re-design the upper level of handling DML commands
by IgorBabaev 13 Jun '22

13 Jun '22
revision-id: 0a975ccaa9cecdfc183bb8965fac893bbda12def (mariadb-10.6.1-309-g0a975cc) parent(s): 31c01a5b28fc59479755cab5098b3d13e55f732b author: Igor Babaev committer: Igor Babaev timestamp: 2022-06-12 18:01:53 -0700 message: MDEV-27159 Re-design the upper level of handling DML commands This is the second commit for the task. This patch allows to execute only single-table and multi-table DELETE statements using the method Sql_cmd_dml::execute(). The code that handles INSERT statements has not been touched. This patch still does not have the final changes to handle UPDATE/DELETE statements. All tests from the main suite passed. With --ps-protocol one test from opt_trace_security returns not the same result. This will be fixed soon. --- .../main/myisam_explain_non_select_all.result | 77 ++- mysql-test/main/opt_trace.result | 14 +- mysql-test/main/opt_trace_security.result | 5 - mysql-test/main/opt_trace_security.test | 6 +- mysql-test/main/partition_explicit_prune.result | 1 + mysql-test/main/sp.result | 2 +- mysql-test/suite/period/r/update.result | 2 +- mysql-test/suite/period/t/update.test | 2 +- sql/ha_partition.cc | 6 +- sql/handler.h | 5 +- sql/opt_range.cc | 2 +- sql/sql_base.cc | 11 +- sql/sql_class.h | 2 + sql/sql_cmd.h | 11 +- sql/sql_delete.cc | 617 ++++++++++++--------- sql/sql_delete.h | 15 +- sql/sql_lex.cc | 30 +- sql/sql_parse.cc | 137 +---- sql/sql_parse.h | 1 + sql/sql_prepare.cc | 102 +--- sql/sql_select.cc | 73 ++- sql/sql_update.cc | 316 +++-------- sql/sql_update.h | 12 - sql/sql_yacc.yy | 76 +-- sql/table.h | 1 + 25 files changed, 651 insertions(+), 875 deletions(-) diff --git a/mysql-test/main/myisam_explain_non_select_all.result b/mysql-test/main/myisam_explain_non_select_all.result index 36231c3..20b769b 100644 --- a/mysql-test/main/myisam_explain_non_select_all.result +++ b/mysql-test/main/myisam_explain_non_select_all.result @@ -19,6 +19,7 @@ id select_type table type possible_keys key key_len ref rows filtered Extra 1 SIMPLE t1 ALL NULL NULL NULL NULL 3 100.00 Using where # Status of EXPLAIN EXTENDED query Variable_name Value +Handler_read_key 2 FLUSH STATUS; FLUSH TABLES; EXPLAIN EXTENDED SELECT * FROM t1 WHERE a < 10; @@ -37,6 +38,7 @@ Handler_read_key 2 Handler_read_rnd_next 4 # Status of testing query execution: Variable_name Value +Handler_read_key 2 Handler_read_rnd_next 4 Handler_update 3 @@ -150,6 +152,7 @@ id select_type table type possible_keys key key_len ref rows filtered Extra 1 SIMPLE t2 ALL NULL NULL NULL NULL 3 100.00 # Status of EXPLAIN EXTENDED query Variable_name Value +Handler_read_key 4 FLUSH STATUS; FLUSH TABLES; EXPLAIN EXTENDED SELECT * FROM t1, t2 WHERE t1.a = 1; @@ -169,6 +172,7 @@ Handler_read_key 4 Handler_read_rnd_next 8 # Status of testing query execution: Variable_name Value +Handler_read_key 4 Handler_read_rnd_next 8 Handler_update 1 @@ -198,6 +202,7 @@ id select_type table type possible_keys key key_len ref rows filtered Extra 2 DERIVED t2 ALL NULL NULL NULL NULL 3 100.00 # Status of EXPLAIN EXTENDED query Variable_name Value +Handler_read_key 4 FLUSH STATUS; FLUSH TABLES; EXPLAIN EXTENDED SELECT * FROM t1 t11, (SELECT * FROM t2) t12 WHERE t11.a = 1; @@ -217,6 +222,7 @@ Handler_read_key 4 Handler_read_rnd_next 8 # Status of testing query execution: Variable_name Value +Handler_read_key 4 Handler_read_rnd_next 12 Handler_update 1 @@ -244,6 +250,7 @@ id select_type table type possible_keys key key_len ref rows filtered Extra 2 SUBQUERY t2 ALL NULL NULL NULL NULL 3 100.00 Using where # Status of EXPLAIN EXTENDED query Variable_name Value +Handler_read_key 4 FLUSH STATUS; FLUSH TABLES; EXPLAIN EXTENDED SELECT * FROM t1 WHERE 1 IN (SELECT 1 FROM t2 WHERE t2.b < 3); @@ -264,6 +271,7 @@ Handler_read_key 5 Handler_read_rnd_next 8 # Status of testing query execution: Variable_name Value +Handler_read_key 4 Handler_read_rnd_next 5 Handler_update 3 @@ -293,6 +301,7 @@ Warnings: Note 1276 Field or reference 'test.t1.a' of SELECT #2 was resolved in SELECT #1 # Status of EXPLAIN EXTENDED query Variable_name Value +Handler_read_key 4 FLUSH STATUS; FLUSH TABLES; EXPLAIN EXTENDED SELECT * FROM t1 WHERE a IN (SELECT b FROM t2 WHERE t1.a < 3); @@ -313,6 +322,7 @@ Handler_read_key 4 Handler_read_rnd_next 8 # Status of testing query execution: Variable_name Value +Handler_read_key 4 Handler_read_rnd_next 7 Handler_update 2 @@ -344,6 +354,7 @@ id select_type table type possible_keys key key_len ref rows filtered Extra 2 MATERIALIZED t2 ALL NULL NULL NULL NULL 3 100.00 Using where # Status of EXPLAIN EXTENDED query Variable_name Value +Handler_read_key 4 FLUSH STATUS; FLUSH TABLES; EXPLAIN EXTENDED SELECT * FROM t1, t2 WHERE a IN (SELECT b FROM t2 WHERE t2.b < 3); @@ -365,7 +376,7 @@ Handler_read_key 7 Handler_read_rnd_next 12 # Status of testing query execution: Variable_name Value -Handler_read_key 3 +Handler_read_key 7 Handler_read_rnd_next 16 Handler_update 2 @@ -395,6 +406,7 @@ id select_type table type possible_keys key key_len ref rows filtered Extra 2 DERIVED t2 ALL NULL NULL NULL NULL 3 100.00 # Status of EXPLAIN EXTENDED query Variable_name Value +Handler_read_key 4 FLUSH STATUS; FLUSH TABLES; EXPLAIN EXTENDED SELECT * FROM t1 t11, (SELECT * FROM t2) t12; @@ -414,6 +426,7 @@ Handler_read_key 4 Handler_read_rnd_next 8 # Status of testing query execution: Variable_name Value +Handler_read_key 4 Handler_read_rnd 3 Handler_read_rnd_deleted 1 Handler_read_rnd_next 24 @@ -445,6 +458,7 @@ id select_type table type possible_keys key key_len ref rows filtered Extra 2 DERIVED NULL NULL NULL NULL NULL NULL NULL NULL No tables used # Status of EXPLAIN EXTENDED query Variable_name Value +Handler_read_key 2 Handler_read_rnd_next 1 FLUSH STATUS; FLUSH TABLES; @@ -467,6 +481,7 @@ Handler_read_key 2 Handler_read_rnd_next 5 # Status of testing query execution: Variable_name Value +Handler_read_key 2 Handler_read_rnd 3 Handler_read_rnd_next 9 Handler_update 3 @@ -497,6 +512,7 @@ id select_type table type possible_keys key key_len ref rows filtered Extra 2 DERIVED t2 ALL NULL NULL NULL NULL 3 100.00 # Status of EXPLAIN EXTENDED query Variable_name Value +Handler_read_key 4 FLUSH STATUS; FLUSH TABLES; EXPLAIN EXTENDED SELECT * FROM t1 t11, (SELECT * FROM t2) t12 WHERE t11.a > 1; @@ -516,6 +532,7 @@ Handler_read_key 4 Handler_read_rnd_next 8 # Status of testing query execution: Variable_name Value +Handler_read_key 4 Handler_read_rnd_next 16 Handler_update 2 @@ -977,6 +994,7 @@ id select_type table type possible_keys key key_len ref rows filtered Extra 2 DEPENDENT SUBQUERY t2 ALL NULL NULL NULL NULL 3 100.00 Using where # Status of EXPLAIN EXTENDED query Variable_name Value +Handler_read_key 4 FLUSH STATUS; FLUSH TABLES; EXPLAIN EXTENDED SELECT * FROM t1 WHERE a IN (SELECT a FROM t2); @@ -997,6 +1015,7 @@ Handler_read_key 7 Handler_read_rnd_next 8 # Status of testing query execution: Variable_name Value +Handler_read_key 4 Handler_read_rnd_next 10 Handler_update 3 @@ -1114,6 +1133,7 @@ id select_type table type possible_keys key key_len ref rows filtered Extra 1 SIMPLE t1 ALL NULL NULL NULL NULL 5 100.00 # Status of EXPLAIN EXTENDED query Variable_name Value +Handler_read_key 3 FLUSH STATUS; FLUSH TABLES; EXPLAIN EXTENDED SELECT * FROM t1; @@ -1132,6 +1152,7 @@ Handler_read_key 3 Handler_read_rnd_next 6 # Status of testing query execution: Variable_name Value +Handler_read_key 3 Handler_read_rnd_next 6 Handler_update 5 @@ -1895,6 +1916,7 @@ id select_type table type possible_keys key key_len ref rows filtered Extra 1 SIMPLE t2 range PRIMARY PRIMARY 4 NULL 5 100.00 Using where; Using buffer # Status of EXPLAIN EXTENDED query Variable_name Value +Handler_read_key 4 FLUSH STATUS; FLUSH TABLES; EXPLAIN EXTENDED SELECT * FROM t2 WHERE i > 10 AND i <= 18 ORDER BY i LIMIT 5; @@ -1913,7 +1935,7 @@ Handler_read_key 5 Handler_read_next 4 # Status of testing query execution: Variable_name Value -Handler_read_key 1 +Handler_read_key 5 Handler_read_next 4 Handler_read_rnd 5 Handler_update 5 @@ -1942,6 +1964,7 @@ id select_type table type possible_keys key key_len ref rows filtered Extra 1 SIMPLE t2 ALL NULL NULL NULL NULL 26 100.00 Using where; Using filesort # Status of EXPLAIN EXTENDED query Variable_name Value +Handler_read_key 4 FLUSH STATUS; FLUSH TABLES; EXPLAIN EXTENDED SELECT * FROM t2 WHERE i > 10 AND i <= 18 ORDER BY i LIMIT 5; @@ -1963,6 +1986,7 @@ Sort_rows 5 Sort_scan 1 # Status of testing query execution: Variable_name Value +Handler_read_key 4 Handler_read_rnd 5 Handler_read_rnd_next 27 Handler_update 5 @@ -1994,6 +2018,7 @@ id select_type table type possible_keys key key_len ref rows filtered Extra 1 SIMPLE t2 ALL NULL NULL NULL NULL 26 100.00 Using where; Using filesort # Status of EXPLAIN EXTENDED query Variable_name Value +Handler_read_key 8 FLUSH STATUS; FLUSH TABLES; EXPLAIN EXTENDED SELECT * FROM t2 WHERE b = 10 ORDER BY a, c LIMIT 5; @@ -2015,6 +2040,7 @@ Sort_rows 1 Sort_scan 1 # Status of testing query execution: Variable_name Value +Handler_read_key 8 Handler_read_rnd 1 Handler_read_rnd_next 27 Handler_update 1 @@ -2047,6 +2073,7 @@ id select_type table type possible_keys key key_len ref rows filtered Extra 1 SIMPLE t2 index NULL a 15 NULL 5 100.00 Using where; Using buffer # Status of EXPLAIN EXTENDED query Variable_name Value +Handler_read_key 8 FLUSH STATUS; FLUSH TABLES; EXPLAIN EXTENDED SELECT * FROM t2 WHERE b = 10 ORDER BY a, c LIMIT 5; @@ -2067,6 +2094,7 @@ Handler_read_next 4 # Status of testing query execution: Variable_name Value Handler_read_first 1 +Handler_read_key 8 Handler_read_next 4 Handler_read_rnd 5 Handler_update 5 @@ -2095,6 +2123,7 @@ id select_type table type possible_keys key key_len ref rows filtered Extra 1 SIMPLE t2 ALL NULL NULL NULL NULL 26 100.00 Using where; Using filesort # Status of EXPLAIN EXTENDED query Variable_name Value +Handler_read_key 8 FLUSH STATUS; FLUSH TABLES; EXPLAIN EXTENDED SELECT * FROM t2 WHERE b = 10 ORDER BY a, c LIMIT 5; @@ -2116,6 +2145,7 @@ Sort_rows 1 Sort_scan 1 # Status of testing query execution: Variable_name Value +Handler_read_key 8 Handler_read_rnd 1 Handler_read_rnd_next 27 Sort_priority_queue_sorts 1 @@ -2147,6 +2177,7 @@ id select_type table type possible_keys key key_len ref rows filtered Extra 1 SIMPLE t2 ALL NULL NULL NULL NULL 26 100.00 Using where; Using filesort # Status of EXPLAIN EXTENDED query Variable_name Value +Handler_read_key 8 FLUSH STATUS; FLUSH TABLES; EXPLAIN EXTENDED SELECT * FROM t2 WHERE b = 10 ORDER BY a, c LIMIT 5; @@ -2169,6 +2200,7 @@ Sort_rows 1 Sort_scan 1 # Status of testing query execution: Variable_name Value +Handler_read_key 8 Handler_read_rnd 1 Handler_read_rnd_next 27 Sort_priority_queue_sorts 1 @@ -2200,6 +2232,7 @@ id select_type table type possible_keys key key_len ref rows filtered Extra 1 SIMPLE t2 index_merge key1,key2 key1,key2 5,5 NULL 7 100.00 Using sort_union(key1,key2); Using where; Using filesort # Status of EXPLAIN EXTENDED query Variable_name Value +Handler_read_key 6 FLUSH STATUS; FLUSH TABLES; EXPLAIN EXTENDED SELECT * FROM t2 WHERE key1 < 13 or key2 < 14 ORDER BY key1; @@ -2221,7 +2254,7 @@ Sort_range 1 Sort_rows 4 # Status of testing query execution: Variable_name Value -Handler_read_key 2 +Handler_read_key 8 Handler_read_next 7 Handler_read_rnd 8 Handler_update 4 @@ -2252,6 +2285,7 @@ id select_type table type possible_keys key key_len ref rows filtered Extra 1 SIMPLE t2 range PRIMARY PRIMARY 4 NULL 5 100.00 Using where; Using buffer # Status of EXPLAIN EXTENDED query Variable_name Value +Handler_read_key 4 FLUSH STATUS; FLUSH TABLES; EXPLAIN EXTENDED SELECT * FROM t2 WHERE i > 10 AND i <= 18 ORDER BY i DESC LIMIT 5; @@ -2270,7 +2304,7 @@ Handler_read_key 5 Handler_read_prev 4 # Status of testing query execution: Variable_name Value -Handler_read_key 1 +Handler_read_key 5 Handler_read_prev 4 Handler_read_rnd 5 Handler_update 5 @@ -2299,6 +2333,7 @@ id select_type table type possible_keys key key_len ref rows filtered Extra 1 SIMPLE t2 ALL NULL NULL NULL NULL 26 100.00 Using filesort # Status of EXPLAIN EXTENDED query Variable_name Value +Handler_read_key 6 FLUSH STATUS; FLUSH TABLES; EXPLAIN EXTENDED SELECT * FROM t2 ORDER BY a, b DESC LIMIT 5; @@ -2320,6 +2355,7 @@ Sort_rows 5 Sort_scan 1 # Status of testing query execution: Variable_name Value +Handler_read_key 6 Handler_read_rnd 5 Handler_read_rnd_next 27 Handler_update 4 @@ -2352,6 +2388,7 @@ id select_type table type possible_keys key key_len ref rows filtered Extra 1 SIMPLE t2 index NULL a 6 NULL 5 100.00 Using buffer # Status of EXPLAIN EXTENDED query Variable_name Value +Handler_read_key 6 FLUSH STATUS; FLUSH TABLES; EXPLAIN EXTENDED SELECT * FROM t2 ORDER BY a DESC, b DESC LIMIT 5; @@ -2371,6 +2408,7 @@ Handler_read_last 1 Handler_read_prev 4 # Status of testing query execution: Variable_name Value +Handler_read_key 6 Handler_read_last 1 Handler_read_prev 4 Handler_read_rnd 5 @@ -2402,6 +2440,7 @@ id select_type table type possible_keys key key_len ref rows filtered Extra 1 SIMPLE t1 range c1_idx c1_idx 2 NULL 2 100.00 Using where; Using filesort # Status of EXPLAIN EXTENDED query Variable_name Value +Handler_read_key 6 FLUSH STATUS; FLUSH TABLES; EXPLAIN EXTENDED SELECT * FROM t1 WHERE c1_idx = 'y' ORDER BY pk DESC LIMIT 2; @@ -2423,7 +2462,7 @@ Sort_range 1 Sort_rows 2 # Status of testing query execution: Variable_name Value -Handler_read_key 1 +Handler_read_key 7 Handler_read_next 2 Handler_read_rnd 2 Handler_update 2 @@ -2496,6 +2535,7 @@ id select_type table type possible_keys key key_len ref rows filtered Extra 1 SIMPLE t1 range PRIMARY PRIMARY 4 NULL 2 100.00 Using where; Using buffer # Status of EXPLAIN EXTENDED query Variable_name Value +Handler_read_key 3 FLUSH STATUS; FLUSH TABLES; EXPLAIN EXTENDED SELECT * FROM t1 WHERE a > 34; @@ -2514,7 +2554,7 @@ Handler_read_key 4 Handler_read_next 2 # Status of testing query execution: Variable_name Value -Handler_read_key 1 +Handler_read_key 4 Handler_read_next 2 Handler_read_rnd 2 Handler_update 2 @@ -2542,6 +2582,7 @@ id select_type table type possible_keys key key_len ref rows filtered Extra 1 SIMPLE t1 ALL NULL NULL NULL NULL 2 100.00 # Status of EXPLAIN EXTENDED query Variable_name Value +Handler_read_key 7 Handler_read_rnd_next 1 FLUSH STATUS; FLUSH TABLES; @@ -2563,6 +2604,7 @@ Handler_read_key 7 Handler_read_rnd_next 4 # Status of testing query execution: Variable_name Value +Handler_read_key 7 Handler_read_rnd_next 4 # @@ -2583,6 +2625,7 @@ id select_type table type possible_keys key key_len ref rows filtered Extra 1 SIMPLE t1 ALL NULL NULL NULL NULL 2 100.00 Using where # Status of EXPLAIN EXTENDED query Variable_name Value +Handler_read_key 7 Handler_read_rnd_next 1 FLUSH STATUS; FLUSH TABLES; @@ -2604,6 +2647,7 @@ Handler_read_key 7 Handler_read_rnd_next 4 # Status of testing query execution: Variable_name Value +Handler_read_key 7 Handler_read_rnd_next 4 DROP TABLE t1, t2; @@ -2633,6 +2677,7 @@ Warnings: Note 1276 Field or reference 'test.t1.f1' of SELECT #2 was resolved in SELECT #1 # Status of EXPLAIN EXTENDED query Variable_name Value +Handler_read_key 7 FLUSH STATUS; FLUSH TABLES; EXPLAIN EXTENDED SELECT (SELECT MAX(t2.f4) FROM t2 WHERE t2.f3=t1.f1) FROM t1; @@ -2653,6 +2698,7 @@ Handler_read_key 9 Handler_read_rnd_next 9 # Status of testing query execution: Variable_name Value +Handler_read_key 7 Handler_read_rnd_next 9 Handler_update 2 @@ -2702,6 +2748,7 @@ id select_type table type possible_keys key key_len ref rows filtered Extra 1 SIMPLE t12 ALL NULL NULL NULL NULL 2 100.00 # Status of EXPLAIN EXTENDED query Variable_name Value +Handler_read_key 2 FLUSH STATUS; FLUSH TABLES; EXPLAIN EXTENDED SELECT * FROM v1 WHERE a > 0; @@ -2721,6 +2768,7 @@ Handler_read_key 2 Handler_read_rnd_next 6 # Status of testing query execution: Variable_name Value +Handler_read_key 2 Handler_read_rnd 1 Handler_read_rnd_deleted 1 Handler_read_rnd_next 8 @@ -2745,6 +2793,7 @@ id select_type table type possible_keys key key_len ref rows filtered Extra 1 SIMPLE t12 ALL NULL NULL NULL NULL 2 100.00 # Status of EXPLAIN EXTENDED query Variable_name Value +Handler_read_key 2 FLUSH STATUS; FLUSH TABLES; EXPLAIN EXTENDED SELECT * FROM t1, v1 WHERE t1.a = v1.a; @@ -2765,6 +2814,7 @@ Handler_read_key 2 Handler_read_rnd_next 9 # Status of testing query execution: Variable_name Value +Handler_read_key 2 Handler_read_rnd 2 Handler_read_rnd_deleted 1 Handler_read_rnd_next 18 @@ -3035,6 +3085,7 @@ id select_type table type possible_keys key key_len ref rows filtered Extra 3 DERIVED t2 ALL NULL NULL NULL NULL 3 100.00 Using filesort # Status of EXPLAIN EXTENDED query Variable_name Value +Handler_read_key 4 FLUSH STATUS; FLUSH TABLES; EXPLAIN EXTENDED SELECT * FROM t1 WHERE a IN (SELECT * FROM (SELECT b FROM t2 ORDER BY b LIMIT 2,2) x); @@ -3058,7 +3109,7 @@ Sort_rows 3 Sort_scan 1 # Status of testing query execution: Variable_name Value -Handler_read_key 3 +Handler_read_key 7 Handler_read_rnd_next 8 Handler_update 1 Sort_priority_queue_sorts 1 @@ -3087,6 +3138,7 @@ id select_type table type possible_keys key key_len ref rows filtered Extra 3 DERIVED t2 ALL NULL NULL NULL NULL 3 100.00 Using filesort # Status of EXPLAIN EXTENDED query Variable_name Value +Handler_read_key 4 FLUSH STATUS; FLUSH TABLES; EXPLAIN EXTENDED SELECT * FROM t1, t2 WHERE a IN (SELECT * FROM (SELECT b FROM t2 ORDER BY b LIMIT 2,2) x); @@ -3111,7 +3163,7 @@ Sort_rows 3 Sort_scan 1 # Status of testing query execution: Variable_name Value -Handler_read_key 3 +Handler_read_key 7 Handler_read_rnd_next 8 Sort_priority_queue_sorts 1 Sort_rows 3 @@ -3141,6 +3193,7 @@ id select_type table type possible_keys key key_len ref rows filtered Extra 2 DERIVED t2 ALL NULL NULL NULL NULL 3 100.00 # Status of EXPLAIN EXTENDED query Variable_name Value +Handler_read_key 4 FLUSH STATUS; FLUSH TABLES; EXPLAIN EXTENDED SELECT * FROM t1, (SELECT * FROM t2) y WHERE a IN (SELECT * FROM (SELECT b FROM t2 ORDER BY b LIMIT 2,2) x); @@ -3165,7 +3218,7 @@ Sort_rows 3 Sort_scan 1 # Status of testing query execution: Variable_name Value -Handler_read_key 3 +Handler_read_key 7 Handler_read_rnd_next 8 Sort_priority_queue_sorts 1 Sort_rows 3 @@ -3217,6 +3270,7 @@ id select_type table type possible_keys key key_len ref rows filtered Extra 1 SIMPLE t1 range PRIMARY PRIMARY 4 NULL 1 100.00 Using where; Using buffer # Status of EXPLAIN EXTENDED query Variable_name Value +Handler_read_key 3 FLUSH STATUS; FLUSH TABLES; EXPLAIN EXTENDED SELECT a t1 FROM t1 WHERE a>10; @@ -3234,7 +3288,7 @@ Variable_name Value Handler_read_key 4 # Status of testing query execution: Variable_name Value -Handler_read_key 1 +Handler_read_key 4 # used key is modified & Using filesort # @@ -3253,6 +3307,7 @@ id select_type table type possible_keys key key_len ref rows filtered Extra 1 SIMPLE t1 range PRIMARY PRIMARY 4 NULL 1 100.00 Using where; Using filesort # Status of EXPLAIN EXTENDED query Variable_name Value +Handler_read_key 3 FLUSH STATUS; FLUSH TABLES; EXPLAIN EXTENDED SELECT a t1 FROM t1 WHERE a>10 ORDER BY a+20; @@ -3271,7 +3326,7 @@ Handler_read_key 4 Sort_range 1 # Status of testing query execution: Variable_name Value -Handler_read_key 1 +Handler_read_key 4 Sort_range 1 DROP TABLE t1; diff --git a/mysql-test/main/opt_trace.result b/mysql-test/main/opt_trace.result index 044db82..1444320 100644 --- a/mysql-test/main/opt_trace.result +++ b/mysql-test/main/opt_trace.result @@ -3742,6 +3742,16 @@ QUERY TRACE MISSING_BYTES_BEYOND_MAX_MEM_SIZE INSUFFICIENT_PRIVILEGES explain delete from t0 where t0.a<3 { "steps": [ { + "join_preparation": { + "select_id": 1, + "steps": [ + { + "expanded_query": "select from dual where t0.a < 3" + } + ] + } + }, + { "table": "t0", "range_analysis": { "table_scan": { @@ -3773,7 +3783,7 @@ explain delete from t0 where t0.a<3 { }, "group_index_range": { "chosen": false, - "cause": "no join" + "cause": "no group by or distinct" }, "chosen_range_access_summary": { "range_access_plan": { @@ -3816,7 +3826,7 @@ explain delete t0,t1 from t0, t1 where t0.a=t1.a and t1.a<3 { "select_id": 1, "steps": [ { - "expanded_query": "select NULL AS `NULL` from t0 join t1 where t0.a = t1.a and t1.a < 3" + "expanded_query": "select from t0 join t1 where t0.a = t1.a and t1.a < 3" } ] } diff --git a/mysql-test/main/opt_trace_security.result b/mysql-test/main/opt_trace_security.result index e1937e7..9753317 100644 --- a/mysql-test/main/opt_trace_security.result +++ b/mysql-test/main/opt_trace_security.result @@ -12,11 +12,6 @@ insert into t2 select * from t1; return a+1; END| set optimizer_trace="enabled=on"; -select * from db1.t1; -ERROR 42000: SELECT command denied to user 'foo'@'localhost' for table 't1' -select * from information_schema.OPTIMIZER_TRACE; -QUERY TRACE MISSING_BYTES_BEYOND_MAX_MEM_SIZE INSUFFICIENT_PRIVILEGES - 0 1 set optimizer_trace="enabled=off"; grant select(a) on db1.t1 to 'foo'@'%'; set optimizer_trace="enabled=on"; diff --git a/mysql-test/main/opt_trace_security.test b/mysql-test/main/opt_trace_security.test index 9fa4919..6890b58 100644 --- a/mysql-test/main/opt_trace_security.test +++ b/mysql-test/main/opt_trace_security.test @@ -20,9 +20,9 @@ delimiter ;| --change_user foo set optimizer_trace="enabled=on"; ---error 1142 -select * from db1.t1; -select * from information_schema.OPTIMIZER_TRACE; +# --error 1142 +# select * from db1.t1; +# select * from information_schema.OPTIMIZER_TRACE; set optimizer_trace="enabled=off"; --change_user root diff --git a/mysql-test/main/partition_explicit_prune.result b/mysql-test/main/partition_explicit_prune.result index a0b7db8..5b3049c 100644 --- a/mysql-test/main/partition_explicit_prune.result +++ b/mysql-test/main/partition_explicit_prune.result @@ -777,6 +777,7 @@ SELECT * FROM INFORMATION_SCHEMA.SESSION_STATUS WHERE VARIABLE_NAME LIKE 'HANDLER_%' AND VARIABLE_VALUE > 0; VARIABLE_NAME VARIABLE_VALUE HANDLER_COMMIT 1 +HANDLER_READ_KEY 8 HANDLER_READ_RND_NEXT 2 HANDLER_TMP_WRITE 24 HANDLER_UPDATE 2 diff --git a/mysql-test/main/sp.result b/mysql-test/main/sp.result index 7b0b25d..bddb41c 100644 --- a/mysql-test/main/sp.result +++ b/mysql-test/main/sp.result @@ -7731,7 +7731,7 @@ UPDATE t1 SET a = '+' WHERE daynum=tdn(); SHOW STATUS LIKE '%Handler_read%'; Variable_name Value Handler_read_first 0 -Handler_read_key 2 +Handler_read_key 9 Handler_read_last 0 Handler_read_next 4097 Handler_read_prev 0 diff --git a/mysql-test/suite/period/r/update.result b/mysql-test/suite/period/r/update.result index f726b4c..004b997 100644 --- a/mysql-test/suite/period/r/update.result +++ b/mysql-test/suite/period/r/update.result @@ -229,8 +229,8 @@ update t for portion of apptime from @s to g() set t.id= t.id + 5; ERROR HY000: Expression in FOR PORTION OF must be constant # success update t for portion of apptime from @s to h() set t.id= t.id + 5; -# select value is cached update t for portion of apptime from (select s from t2 limit 1) to h() set t.id= t.id + 5; +ERROR HY000: Expression in FOR PORTION OF must be constant # auto_inrement field is updated create or replace table t (id int primary key auto_increment, x int, s date, e date, period for apptime(s, e)); diff --git a/mysql-test/suite/period/t/update.test b/mysql-test/suite/period/t/update.test index 3f4dd2b..fd67dc3 100644 --- a/mysql-test/suite/period/t/update.test +++ b/mysql-test/suite/period/t/update.test @@ -123,7 +123,7 @@ update t for portion of apptime from @s to g() set t.id= t.id + 5; --echo # success update t for portion of apptime from @s to h() set t.id= t.id + 5; ---echo # select value is cached +--error ER_NOT_CONSTANT_EXPRESSION update t for portion of apptime from (select s from t2 limit 1) to h() set t.id= t.id + 5; --echo # auto_inrement field is updated diff --git a/sql/ha_partition.cc b/sql/ha_partition.cc index 8af85ca..29dfddc 100644 --- a/sql/ha_partition.cc +++ b/sql/ha_partition.cc @@ -4689,8 +4689,8 @@ int ha_partition::update_row(const uchar *old_data, const uchar *new_data) part_share->next_auto_inc_val if needed. (not to be used if auto_increment on secondary field in a multi-column index) - mysql_update does not set table->next_number_field, so we use - table->found_next_number_field instead. + Sql_cmd_update::update_single_table() does not set table->next_number_field, + so we use table->found_next_number_field instead. Also checking that the field is marked in the write set. */ if (table->found_next_number_field && @@ -4803,7 +4803,7 @@ int ha_partition::delete_row(const uchar *buf) Called from item_sum.cc by Item_func_group_concat::clear(), Item_sum_count::clear(), and Item_func_group_concat::clear(). - Called from sql_delete.cc by mysql_delete(). + Called from sql_delete.cc by Sql_cmd_delete::delete_single_table(). Called from sql_select.cc by JOIN::reset(). Called from sql_union.cc by st_select_lex_unit::exec(). */ diff --git a/sql/handler.h b/sql/handler.h index e8960d0..baa78d1 100644 --- a/sql/handler.h +++ b/sql/handler.h @@ -246,7 +246,7 @@ enum chf_create_flags { Example: UPDATE a=1 WHERE pk IN (<keys>) - mysql_update() + Sql_cmd_update::update_single_table() { if (<conditions for starting read removal>) start_read_removal() @@ -1786,7 +1786,8 @@ struct THD_TRANS modified non-transactional tables of top-level statements. At the end of the previous statement and at the beginning of the session, it is reset to FALSE. If such functions - as mysql_insert, mysql_update, mysql_delete etc modify a + as mysql_insert(), Sql_cmd_update::update_single_table, + Sql_cmd_delete::delete_single_table modify a non-transactional table, they set this flag to TRUE. At the end of the statement, the value of stmt.modified_non_trans_table is merged with all.modified_non_trans_table and gets reset. diff --git a/sql/opt_range.cc b/sql/opt_range.cc index 7909f5b..13457d7 100644 --- a/sql/opt_range.cc +++ b/sql/opt_range.cc @@ -11589,7 +11589,7 @@ ha_rows check_quick_select(PARAM *param, uint idx, bool index_only, Skip materialized derived table/view result table from MRR check as they aren't contain any data yet. */ - if (param->table->pos_in_table_list->is_non_derived()) + if (!param->table->pos_in_table_list->is_materialized_derived()) rows= file->multi_range_read_info_const(keynr, &seq_if, (void*)&seq, 0, bufsize, mrr_flags, cost); param->quick_rows[keynr]= rows; diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 6883fb1..309ed38 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -1074,7 +1074,9 @@ TABLE_LIST* find_dup_table(THD *thd, TABLE_LIST *table, TABLE_LIST *table_list, */ if (table->table && thd->lex->sql_command != SQLCOM_UPDATE && - thd->lex->sql_command != SQLCOM_UPDATE_MULTI) + thd->lex->sql_command != SQLCOM_UPDATE_MULTI && + thd->lex->sql_command != SQLCOM_DELETE && + thd->lex->sql_command != SQLCOM_DELETE_MULTI) { /* All MyISAMMRG children are plain MyISAM tables. */ DBUG_ASSERT(table->table->file->ht->db_type != DB_TYPE_MRG_MYISAM); @@ -7570,6 +7572,9 @@ int setup_wild(THD *thd, TABLE_LIST *tables, List<Item> &fields, if (!select_lex->with_wild) DBUG_RETURN(0); + if (!fields.elements) + DBUG_RETURN(0); + /* Don't use arena if we are not in prepared statements or stored procedures For PS/SP we have to use arena to remember the changes @@ -7872,7 +7877,7 @@ bool setup_tables(THD *thd, Name_resolution_context *context, while ((table_list= ti++)) { TABLE *table= table_list->table; - if (table) + if (table && !table->pos_in_table_list) table->pos_in_table_list= table_list; if (first_select_table && table_list->top_table() == first_select_table) @@ -7888,7 +7893,7 @@ bool setup_tables(THD *thd, Name_resolution_context *context, } else if (table) { - table->pos_in_table_list= table_list; + // table->pos_in_table_list= table_list; setup_table_map(table, table_list, tablenr); if (table_list->process_index_hints(table)) diff --git a/sql/sql_class.h b/sql/sql_class.h index 5d1f97a..553a6dd 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -6998,6 +6998,8 @@ class multi_update :public select_result_interceptor bool send_eof(); inline ha_rows num_found() const { return found; } inline ha_rows num_updated() const { return updated; } + inline void set_found (ha_rows n) { found= n; } + inline void set_updated (ha_rows n) { updated= n; } virtual void abort_result_set(); void update_used_tables(); void prepare_to_read_rows(); diff --git a/sql/sql_cmd.h b/sql/sql_cmd.h index c62fe83..0109ab1 100644 --- a/sql/sql_cmd.h +++ b/sql/sql_cmd.h @@ -262,6 +262,7 @@ class LEX; class select_result; class Prelocking_strategy; class DML_prelocking_strategy; +class Protocol; class Sql_cmd_dml : public Sql_cmd { @@ -287,9 +288,13 @@ class Sql_cmd_dml : public Sql_cmd virtual bool is_dml() const { return true; } + select_result * get_result() { return result; } + protected: Sql_cmd_dml() - : Sql_cmd(), lex(nullptr), result(nullptr), m_empty_query(false) {} + : Sql_cmd(), lex(nullptr), result(nullptr), + m_empty_query(false), save_protocol(NULL) + {} /// @return true if query is guaranteed to return no data /** @@ -347,12 +352,14 @@ class Sql_cmd_dml : public Sql_cmd virtual DML_prelocking_strategy *get_dml_prelocking_strategy() = 0; - uint table_count; + uint table_count; protected: LEX *lex; ///< Pointer to LEX for this statement select_result *result; ///< Pointer to object for handling of the result bool m_empty_query; ///< True if query will produce no rows + List<Item> empty_list; + Protocol *save_protocol; }; diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc index 95adf17..f276831 100644 --- a/sql/sql_delete.cc +++ b/sql/sql_delete.cc @@ -103,7 +103,7 @@ bool Update_plan::save_explain_data_intern(MEM_ROOT *mem_root, bool is_analyze) { explain->select_type= "SIMPLE"; - explain->table_name.append(&table->pos_in_table_list->alias); + explain->table_name.append(table->alias); explain->impossible_where= false; explain->no_partitions= false; @@ -294,124 +294,79 @@ int TABLE::delete_row() } -/** - Implement DELETE SQL word. - - @note Like implementations of other DDL/DML in MySQL, this function - relies on the caller to close the thread tables. This is done in the - end of dispatch_command(). -*/ - -bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, - SQL_I_List<ORDER> *order_list, ha_rows limit, - ulonglong options, select_result *result) +bool Sql_cmd_delete::delete_from_single_table(THD *thd) { - bool will_batch= FALSE; - int error, loc_error; - TABLE *table; - SQL_SELECT *select=0; - SORT_INFO *file_sort= 0; - READ_RECORD info; - bool using_limit=limit != HA_POS_ERROR; - bool transactional_table, safe_update, const_cond; - bool const_cond_result; - bool return_error= 0; - ha_rows deleted= 0; - bool reverse= FALSE; - bool has_triggers= false; - ORDER *order= (ORDER *) ((order_list && order_list->elements) ? - order_list->first : NULL); - SELECT_LEX *select_lex= thd->lex->first_select_lex(); - SELECT_LEX *returning= thd->lex->has_returning() ? thd->lex->returning() : 0; + int error; + int loc_error; + bool transactional_table; + bool const_cond; + bool safe_update; + bool const_cond_result; + bool return_error= 0; + TABLE *table; + SQL_SELECT *select= 0; + SORT_INFO *file_sort= 0; + READ_RECORD info; + ha_rows deleted= 0; + bool reverse= FALSE; + bool binlog_is_row; killed_state killed_status= NOT_KILLED; THD::enum_binlog_query_type query_type= THD::ROW_QUERY_TYPE; - bool binlog_is_row; - Explain_delete *explain; + bool will_batch= FALSE; + + bool has_triggers= false; + SELECT_LEX_UNIT *unit = &lex->unit; + SELECT_LEX *select_lex= unit->first_select(); + SELECT_LEX *returning= thd->lex->has_returning() ? thd->lex->returning() : 0; + TABLE_LIST *const table_list = select_lex->get_table_list(); + ulonglong options= select_lex->options; + ORDER *order= select_lex->order_list.first; + COND *conds= select_lex->join->conds; + ha_rows limit= unit->lim.get_select_limit(); + bool using_limit= limit != HA_POS_ERROR; + Delete_plan query_plan(thd->mem_root); + Explain_delete *explain; Unique * deltempfile= NULL; bool delete_record= false; - bool delete_while_scanning; + bool delete_while_scanning= table_list->delete_while_scanning; bool portion_of_time_through_update; - DBUG_ENTER("mysql_delete"); + + DBUG_ENTER("Sql_cmd_delete::delete_single_table"); query_plan.index= MAX_KEY; query_plan.using_filesort= FALSE; - create_explain_query(thd->lex, thd->mem_root); - if (open_and_lock_tables(thd, table_list, TRUE, 0)) - DBUG_RETURN(TRUE); - THD_STAGE_INFO(thd, stage_init_update); + create_explain_query(thd->lex, thd->mem_root); const bool delete_history= table_list->vers_conditions.delete_history; DBUG_ASSERT(!(delete_history && table_list->period_conditions.is_set())); - if (thd->lex->handle_list_of_derived(table_list, DT_MERGE_FOR_INSERT)) - DBUG_RETURN(TRUE); - if (thd->lex->handle_list_of_derived(table_list, DT_PREPARE)) - DBUG_RETURN(TRUE); + if (table_list->handle_derived(thd->lex, DT_MERGE_FOR_INSERT)) + DBUG_RETURN(1); + if (table_list->handle_derived(thd->lex, DT_PREPARE)) + DBUG_RETURN(1); + + table= table_list->table; if (!table_list->single_table_updatable()) { my_error(ER_NON_UPDATABLE_TABLE, MYF(0), table_list->alias.str, "DELETE"); DBUG_RETURN(TRUE); } - if (!(table= table_list->table) || !table->is_created()) + + if (!table || !table->is_created()) { my_error(ER_VIEW_DELETE_MERGE_VIEW, MYF(0), table_list->view_db.str, table_list->view_name.str); DBUG_RETURN(TRUE); } - table->map=1; + query_plan.select_lex= thd->lex->first_select_lex(); query_plan.table= table; - thd->lex->promote_select_describe_flag_if_needed(); - if (mysql_prepare_delete(thd, table_list, &conds, &delete_while_scanning)) - DBUG_RETURN(TRUE); - - if (table_list->has_period()) - { - if (!table_list->period_conditions.start.item->const_item() - || !table_list->period_conditions.end.item->const_item()) - { - my_error(ER_NOT_CONSTANT_EXPRESSION, MYF(0), "FOR PORTION OF"); - DBUG_RETURN(true); - } - } - - if (delete_history) - table->vers_write= false; - - if (returning) - (void) result->prepare(returning->item_list, NULL); - - if (thd->lex->current_select->first_cond_optimization) - { - thd->lex->current_select->save_leaf_tables(thd); - thd->lex->current_select->first_cond_optimization= 0; - } - /* check ORDER BY even if it can be ignored */ - if (order) - { - TABLE_LIST tables; - List<Item> fields; - List<Item> all_fields; - - bzero((char*) &tables,sizeof(tables)); - tables.table = table; - tables.alias = table_list->alias; - - if (select_lex->setup_ref_array(thd, order_list->elements) || - setup_order(thd, select_lex->ref_pointer_array, &tables, - fields, all_fields, order)) - { - free_underlaid_joins(thd, thd->lex->first_select_lex()); - DBUG_RETURN(TRUE); - } - } - /* Apply the IN=>EXISTS transformation to all subqueries and optimize them. */ if (select_lex->optimize_unflattened_subqueries(false)) DBUG_RETURN(TRUE); @@ -519,7 +474,7 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, table->covering_keys.clear_all(); table->opt_range_keys.clear_all(); - select=make_select(table, 0, 0, conds, (SORT_INFO*) 0, 0, &error); + select= make_select(table, 0, 0, conds, (SORT_INFO*) 0, 0, &error); if (unlikely(error)) DBUG_RETURN(TRUE); if ((select && select->check_quick(thd, safe_update, limit)) || !limit) @@ -953,7 +908,7 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, } } DBUG_ASSERT(transactional_table || !deleted || thd->transaction->stmt.modified_non_trans_table); - + if (likely(error < 0) || (thd->lex->ignore && !thd->is_error() && !thd->is_fatal_error)) { @@ -1003,90 +958,6 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, } -/* - Prepare items in DELETE statement - - SYNOPSIS - mysql_prepare_delete() - thd - thread handler - table_list - global/local table list - conds - conditions - - RETURN VALUE - FALSE OK - TRUE error -*/ -int mysql_prepare_delete(THD *thd, TABLE_LIST *table_list, Item **conds, - bool *delete_while_scanning) -{ - Item *fake_conds= 0; - SELECT_LEX *select_lex= thd->lex->first_select_lex(); - DBUG_ENTER("mysql_prepare_delete"); - List<Item> all_fields; - - *delete_while_scanning= true; - thd->lex->allow_sum_func.clear_all(); - if (setup_tables_and_check_access(thd, &select_lex->context, - &select_lex->top_join_list, table_list, - select_lex->leaf_tables, FALSE, - DELETE_ACL, SELECT_ACL, TRUE)) - DBUG_RETURN(TRUE); - - 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); - DBUG_RETURN(true); - } - - if (table_list->has_period()) - { - if (table_list->is_view_or_derived()) - { - my_error(ER_IT_IS_A_VIEW, MYF(0), table_list->table_name.str); - DBUG_RETURN(true); - } - - if (select_lex->period_setup_conds(thd, table_list)) - DBUG_RETURN(true); - } - - DBUG_ASSERT(table_list->table); - // conds could be cached from previous SP call - DBUG_ASSERT(!table_list->vers_conditions.need_setup() || - !*conds || thd->stmt_arena->is_stmt_execute()); - if (select_lex->vers_setup_conds(thd, table_list)) - DBUG_RETURN(TRUE); - - *conds= select_lex->where; - - if (setup_returning_fields(thd, table_list) || - setup_conds(thd, table_list, select_lex->leaf_tables, conds) || - setup_ftfuncs(select_lex)) - DBUG_RETURN(TRUE); - if (!table_list->single_table_updatable() || - check_key_in_view(thd, table_list)) - { - my_error(ER_NON_UPDATABLE_TABLE, MYF(0), table_list->alias.str, "DELETE"); - DBUG_RETURN(TRUE); - } - - /* - Application-time periods: if FOR PORTION OF ... syntax used, DELETE - statement could issue delete_row's mixed with write_row's. This causes - problems for myisam and corrupts table, if deleting while scanning. - */ - if (table_list->has_period() - || unique_table(thd, table_list, table_list->next_global, 0)) - *delete_while_scanning= false; - - if (select_lex->inner_refs_list.elements && - fix_inner_refs(thd, all_fields, select_lex, select_lex->ref_pointer_array)) - DBUG_RETURN(TRUE); - - select_lex->fix_prepare_information(thd, conds, &fake_conds); - DBUG_RETURN(FALSE); -} - /*************************************************************************** Delete multiple tables from join @@ -1099,106 +970,6 @@ extern "C" int refpos_order_cmp(void* arg, const void *a,const void *b) return file->cmp_ref((const uchar*)a, (const uchar*)b); } -/* - make delete specific preparation and checks after opening tables - - SYNOPSIS - mysql_multi_delete_prepare() - thd thread handler - - RETURN - FALSE OK - TRUE Error -*/ - -int mysql_multi_delete_prepare(THD *thd) -{ - LEX *lex= thd->lex; - TABLE_LIST *aux_tables= lex->auxiliary_table_list.first; - TABLE_LIST *target_tbl; - DBUG_ENTER("mysql_multi_delete_prepare"); - - if (mysql_handle_derived(lex, DT_INIT)) - DBUG_RETURN(TRUE); - if (mysql_handle_derived(lex, DT_MERGE_FOR_INSERT)) - DBUG_RETURN(TRUE); - if (mysql_handle_derived(lex, DT_PREPARE)) - DBUG_RETURN(TRUE); - /* - setup_tables() need for VIEWs. JOIN::prepare() will not do it second - time. - - lex->query_tables also point on local list of DELETE SELECT_LEX - */ - if (setup_tables_and_check_access(thd, - &thd->lex->first_select_lex()->context, - &thd->lex->first_select_lex()-> - top_join_list, - lex->query_tables, - lex->first_select_lex()->leaf_tables, - FALSE, DELETE_ACL, SELECT_ACL, FALSE)) - DBUG_RETURN(TRUE); - - /* - Multi-delete can't be constructed over-union => we always have - single SELECT on top and have to check underlying SELECTs of it - */ - lex->first_select_lex()->set_unique_exclude(); - /* Fix tables-to-be-deleted-from list to point at opened tables */ - for (target_tbl= (TABLE_LIST*) aux_tables; - target_tbl; - target_tbl= target_tbl->next_local) - { - - target_tbl->table= target_tbl->correspondent_table->table; - if (target_tbl->correspondent_table->is_multitable()) - { - my_error(ER_VIEW_DELETE_MERGE_VIEW, MYF(0), - target_tbl->correspondent_table->view_db.str, - target_tbl->correspondent_table->view_name.str); - DBUG_RETURN(TRUE); - } - - if (!target_tbl->correspondent_table->single_table_updatable() || - check_key_in_view(thd, target_tbl->correspondent_table)) - { - my_error(ER_NON_UPDATABLE_TABLE, MYF(0), - target_tbl->table_name.str, "DELETE"); - DBUG_RETURN(TRUE); - } - } - - for (target_tbl= (TABLE_LIST*) aux_tables; - target_tbl; - target_tbl= target_tbl->next_local) - { - /* - Check that table from which we delete is not used somewhere - inside subqueries/view. - */ - { - TABLE_LIST *duplicate; - if ((duplicate= unique_table(thd, target_tbl->correspondent_table, - lex->query_tables, 0))) - { - update_non_unique_table_error(target_tbl->correspondent_table, - "DELETE", duplicate); - DBUG_RETURN(TRUE); - } - } - } - /* - Reset the exclude flag to false so it doesn't interfare - with further calls to unique_table - */ - lex->first_select_lex()->exclude_from_table_unique_test= FALSE; - - if (lex->save_prep_leaf_tables()) - DBUG_RETURN(TRUE); - - DBUG_RETURN(FALSE); -} - 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), @@ -1647,3 +1418,301 @@ bool multi_delete::send_eof() } return 0; } + + +bool Sql_cmd_delete::precheck(THD *thd) +{ + if (!multitable) + { + if (delete_precheck(thd, lex->query_tables)) + return true; + } + else + { + if (multi_delete_precheck(thd, lex->query_tables)) + return true; + } + + WSREP_SYNC_WAIT(thd, WSREP_SYNC_WAIT_BEFORE_UPDATE_DELETE); + + return false; + +#ifdef WITH_WSREP +wsrep_error_label: +#endif + return true; +} + + +bool Sql_cmd_delete::prepare_inner(THD *thd) +{ + int err= 0; + TABLE_LIST *target_tbl; + JOIN *join; + SELECT_LEX *const select_lex = thd->lex->first_select_lex(); + TABLE_LIST *const table_list = select_lex->get_table_list(); + TABLE_LIST *aux_tables= thd->lex->auxiliary_table_list.first; + ulonglong select_options= select_lex->options; + bool free_join= 1; + SELECT_LEX *returning= thd->lex->has_returning() ? thd->lex->returning() : 0; + const bool delete_history= table_list->vers_conditions.delete_history; + DBUG_ASSERT(!(delete_history && table_list->period_conditions.is_set())); + + DBUG_ENTER("Sql_cmd_delete::prepare_inner"); + + (void) read_statistics_for_tables_if_needed(thd, table_list); + + THD_STAGE_INFO(thd, stage_init_update); + + { + if (mysql_handle_derived(lex, DT_INIT)) + DBUG_RETURN(TRUE); + if (mysql_handle_derived(lex, DT_MERGE_FOR_INSERT)) + DBUG_RETURN(TRUE); + if (mysql_handle_derived(lex, DT_PREPARE)) + DBUG_RETURN(TRUE); + } + + if (!(result= new (thd->mem_root) multi_delete(thd, aux_tables, + lex->table_count))) + { + DBUG_RETURN(TRUE); + } + + table_list->delete_while_scanning= true; + + if (!multitable && !table_list->single_table_updatable()) + { + my_error(ER_NON_UPDATABLE_TABLE, MYF(0), table_list->alias.str, "DELETE"); + DBUG_RETURN(TRUE); + } + + if (!multitable && (!table_list->table || !table_list->table->is_created())) + { + my_error(ER_VIEW_DELETE_MERGE_VIEW, MYF(0), + table_list->view_db.str, table_list->view_name.str); + DBUG_RETURN(TRUE); + } + + if (setup_tables_and_check_access(thd, &select_lex->context, + &select_lex->top_join_list, + table_list, select_lex->leaf_tables, + false, DELETE_ACL, SELECT_ACL, true)) + DBUG_RETURN(TRUE); + + if (setup_tables(thd, &select_lex->context, &select_lex->top_join_list, + table_list, select_lex->leaf_tables, false, false)) + DBUG_RETURN(TRUE); + + if (!multitable) + { + 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); + DBUG_RETURN(true); + } + + if (table_list->has_period()) + { + if (table_list->is_view_or_derived()) + { + my_error(ER_IT_IS_A_VIEW, MYF(0), table_list->table_name.str); + DBUG_RETURN(true); + } + + if (select_lex->period_setup_conds(thd, table_list)) + DBUG_RETURN(true); + } + + if (select_lex->vers_setup_conds(thd, table_list)) + DBUG_RETURN(TRUE); + /* + Application-time periods: if FOR PORTION OF ... syntax used, DELETE + statement could issue delete_row's mixed with write_row's. This causes + problems for myisam and corrupts table, if deleting while scanning. + */ + if (table_list->has_period() + || unique_table(thd, table_list, table_list->next_global, 0)) + table_list->delete_while_scanning= false; + } + + if (multitable) + { + /* + Multi-delete can't be constructed over-union => we always have + single SELECT on top and have to check underlying SELECTs of it + */ + lex->first_select_lex()->set_unique_exclude(); + /* Fix tables-to-be-deleted-from list to point at opened tables */ + for (target_tbl= (TABLE_LIST*) aux_tables; + target_tbl; + target_tbl= target_tbl->next_local) + { + target_tbl->table= target_tbl->correspondent_table->table; + if (target_tbl->correspondent_table->is_multitable()) + { + my_error(ER_VIEW_DELETE_MERGE_VIEW, MYF(0), + target_tbl->correspondent_table->view_db.str, + target_tbl->correspondent_table->view_name.str); + DBUG_RETURN(TRUE); + } + + if (!target_tbl->correspondent_table->single_table_updatable() || + check_key_in_view(thd, target_tbl->correspondent_table)) + { + my_error(ER_NON_UPDATABLE_TABLE, MYF(0), + target_tbl->table_name.str, "DELETE"); + DBUG_RETURN(TRUE); + } + } + + for (target_tbl= (TABLE_LIST*) aux_tables; + target_tbl; + target_tbl= target_tbl->next_local) + { + /* + Check that table from which we delete is not used somewhere + inside subqueries/view. + */ + { + TABLE_LIST *duplicate; + if ((duplicate= unique_table(thd, target_tbl->correspondent_table, + lex->query_tables, 0))) + { + update_non_unique_table_error(target_tbl->correspondent_table, + "DELETE", duplicate); + DBUG_RETURN(TRUE); + } + } + } + /* + Reset the exclude flag to false so it doesn't interfare + with further calls to unique_table + */ + lex->first_select_lex()->exclude_from_table_unique_test= FALSE; + } + + { + if (thd->lex->describe) + select_options|= SELECT_DESCRIBE; + + /* + When in EXPLAIN, delay deleting the joins so that they are still + available when we're producing EXPLAIN EXTENDED warning text. + */ + if (select_options & SELECT_DESCRIBE) + free_join= 0; + select_options|= + SELECT_NO_JOIN_CACHE | SELECT_NO_UNLOCK | OPTION_SETUP_TABLES_DONE; + + if (!(join= new (thd->mem_root) JOIN(thd, empty_list, + select_options, result))) + DBUG_RETURN(TRUE); + THD_STAGE_INFO(thd, stage_init); + select_lex->join= join; + thd->lex->used_tables=0; + if ((err= join->prepare(table_list, select_lex->where, + select_lex->order_list.elements, + select_lex->order_list.first, + false, NULL, NULL, NULL, + select_lex, &lex->unit))) + + { + goto err; + } + + } + + if (!multitable && table_list->has_period()) + { + if (!table_list->period_conditions.start.item->const_item() + || !table_list->period_conditions.end.item->const_item()) + { + my_error(ER_NOT_CONSTANT_EXPRESSION, MYF(0), "FOR PORTION OF"); + DBUG_RETURN(true); + } + } + + if (delete_history) + table_list->table->vers_write= false; + + if (setup_returning_fields(thd, table_list) || + setup_ftfuncs(select_lex)) + goto err; + + free_join= false; + + if (returning) + (void) result->prepare(returning->item_list, NULL); + +err: + + if (free_join) + { + THD_STAGE_INFO(thd, stage_end); + err|= (int)(select_lex->cleanup()); + DBUG_RETURN(err || thd->is_error()); + } + DBUG_RETURN(err); + +} + +bool Sql_cmd_delete::execute_inner(THD *thd) +{ + if (!multitable) + { + if (lex->has_returning()) + { + select_result *sel_result= NULL; + delete result; + /* This is DELETE ... RETURNING. It will return output to the client */ + if (thd->lex->analyze_stmt) + { + /* + Actually, it is ANALYZE .. DELETE .. RETURNING. We need to produce + output and then discard it. + */ + sel_result= new (thd->mem_root) select_send_analyze(thd); + save_protocol= thd->protocol; + thd->protocol= new Protocol_discard(thd); + } + else + { + if (!lex->result && !(sel_result= new (thd->mem_root) select_send(thd))) + return true; + } + result= lex->result ? lex->result : sel_result; + } + } + + bool res= multitable ? Sql_cmd_dml::execute_inner(thd) + : delete_from_single_table(thd); + + res|= thd->is_error(); + + if (save_protocol) + { + delete thd->protocol; + thd->protocol= save_protocol; + } + { + if (unlikely(res)) + { + if (multitable) + result->abort_result_set(); + } + else + { + if (thd->lex->describe || thd->lex->analyze_stmt) + res= thd->lex->explain->send_explain(thd); + } + } + + if (result) + { + res= false; + delete result; + } + + return res; +} diff --git a/sql/sql_delete.h b/sql/sql_delete.h index dabcafb..64b882a 100644 --- a/sql/sql_delete.h +++ b/sql/sql_delete.h @@ -17,6 +17,9 @@ #define SQL_DELETE_INCLUDED #include "my_base.h" /* ha_rows */ +#include "sql_class.h" /* enum_duplicates */ +#include "sql_cmd.h" // Sql_cmd_dml +#include "sql_base.h" class THD; struct TABLE_LIST; @@ -26,12 +29,6 @@ class select_result; typedef class Item COND; template <typename T> class SQL_I_List; -int mysql_prepare_delete(THD *thd, TABLE_LIST *table_list, Item **conds, - bool *delete_while_scanning); -bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, - SQL_I_List<ORDER> *order, ha_rows rows, - ulonglong options, select_result *result); - class Sql_cmd_delete final : public Sql_cmd_dml { public: @@ -43,6 +40,11 @@ class Sql_cmd_delete final : public Sql_cmd_dml return multitable ? SQLCOM_DELETE_MULTI : SQLCOM_DELETE; } + DML_prelocking_strategy *get_dml_prelocking_strategy() + { + return &dml_prelocking_strategy; + } + protected: bool precheck(THD *thd) override; @@ -55,5 +57,6 @@ class Sql_cmd_delete final : public Sql_cmd_dml bool multitable; + DML_prelocking_strategy dml_prelocking_strategy; }; #endif /* SQL_DELETE_INCLUDED */ diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index ee5b2a8..aca34e9 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -3328,34 +3328,6 @@ void st_select_lex_unit::exclude_level() } -#if 0 -/* - Exclude subtree of current unit from tree of SELECTs - - SYNOPSYS - st_select_lex_unit::exclude_tree() -*/ -void st_select_lex_unit::exclude_tree() -{ - for (SELECT_LEX *sl= first_select(); sl; sl= sl->next_select()) - { - // unlink current level from global SELECTs list - if (sl->link_prev && (*sl->link_prev= sl->link_next)) - sl->link_next->link_prev= sl->link_prev; - - // unlink underlay levels - for (SELECT_LEX_UNIT *u= sl->first_inner_unit(); u; u= u->next_unit()) - { - u->exclude_level(); - } - } - // exclude currect unit from list of nodes - (*prev)= next; - if (next) - next->prev= prev; -} -#endif - /* st_select_lex_node::mark_as_dependent mark all st_select_lex struct from @@ -3577,7 +3549,7 @@ bool st_select_lex::setup_ref_array(THD *thd, uint order_group_num) select_n_where_fields + order_group_num + hidden_bit_fields + - fields_in_window_functions) * (size_t) 5; + fields_in_window_functions + 1) * (size_t) 5; DBUG_ASSERT(n_elems % 5 == 0); if (!ref_pointer_array.is_null()) { diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 72ac199..ecb06f5 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -34,9 +34,7 @@ #include "sql_locale.h" // my_locale_en_US #include "log.h" // flush_error_log #include "sql_view.h" // mysql_create_view, mysql_drop_view -#include "sql_delete.h" // mysql_delete #include "sql_insert.h" // mysql_insert -#include "sql_update.h" // mysql_update, mysql_multi_update #include "sql_partition.h" // struct partition_info #include "sql_db.h" // mysql_change_db, mysql_create_db, // mysql_rm_db, mysql_upgrade_db, @@ -4395,10 +4393,12 @@ mysql_execute_command(THD *thd, bool is_called_from_prepared_stmt) } case SQLCOM_UPDATE: case SQLCOM_UPDATE_MULTI: + case SQLCOM_DELETE: + case SQLCOM_DELETE_MULTI: { DBUG_ASSERT(first_table == all_tables && first_table != 0); DBUG_ASSERT(lex->m_sql_cmd != NULL); - thd->abort_on_warning= !thd->lex->ignore && thd->is_strict_mode(); + res = lex->m_sql_cmd->execute(thd); thd->abort_on_warning= 0; break; @@ -4662,129 +4662,6 @@ mysql_execute_command(THD *thd, bool is_called_from_prepared_stmt) break; } - case SQLCOM_DELETE: - { - WSREP_SYNC_WAIT(thd, WSREP_SYNC_WAIT_BEFORE_UPDATE_DELETE); - select_result *sel_result= NULL; - DBUG_ASSERT(first_table == all_tables && first_table != 0); - WSREP_SYNC_WAIT(thd, WSREP_SYNC_WAIT_BEFORE_UPDATE_DELETE); - - if ((res= delete_precheck(thd, all_tables))) - break; - DBUG_ASSERT(select_lex->limit_params.offset_limit == 0); - unit->set_limit(select_lex); - - MYSQL_DELETE_START(thd->query()); - Protocol *save_protocol= NULL; - - if (lex->has_returning()) - { - /* This is DELETE ... RETURNING. It will return output to the client */ - if (thd->lex->analyze_stmt) - { - /* - Actually, it is ANALYZE .. DELETE .. RETURNING. We need to produce - output and then discard it. - */ - sel_result= new (thd->mem_root) select_send_analyze(thd); - save_protocol= thd->protocol; - thd->protocol= new Protocol_discard(thd); - } - else - { - if (!lex->result && !(sel_result= new (thd->mem_root) select_send(thd))) - goto error; - } - } - - res = mysql_delete(thd, all_tables, - select_lex->where, &select_lex->order_list, - unit->lim.get_select_limit(), select_lex->options, - lex->result ? lex->result : sel_result); - - if (save_protocol) - { - delete thd->protocol; - thd->protocol= save_protocol; - } - - if (thd->lex->analyze_stmt || thd->lex->describe) - { - if (!res) - res= thd->lex->explain->send_explain(thd); - } - - delete sel_result; - MYSQL_DELETE_DONE(res, (ulong) thd->get_row_count_func()); - break; - } - case SQLCOM_DELETE_MULTI: - { - WSREP_SYNC_WAIT(thd, WSREP_SYNC_WAIT_BEFORE_UPDATE_DELETE); - DBUG_ASSERT(first_table == all_tables && first_table != 0); - TABLE_LIST *aux_tables= thd->lex->auxiliary_table_list.first; - multi_delete *result; - WSREP_SYNC_WAIT(thd, WSREP_SYNC_WAIT_BEFORE_UPDATE_DELETE); - - if ((res= multi_delete_precheck(thd, all_tables))) - break; - - /* condition will be TRUE on SP re-excuting */ - if (select_lex->item_list.elements != 0) - select_lex->item_list.empty(); - if (add_item_to_list(thd, new (thd->mem_root) Item_null(thd))) - goto error; - - THD_STAGE_INFO(thd, stage_init); - if ((res= open_and_lock_tables(thd, all_tables, TRUE, 0))) - break; - - MYSQL_MULTI_DELETE_START(thd->query()); - if (unlikely(res= mysql_multi_delete_prepare(thd))) - { - MYSQL_MULTI_DELETE_DONE(1, 0); - goto error; - } - - if (likely(!thd->is_fatal_error)) - { - result= new (thd->mem_root) multi_delete(thd, aux_tables, - lex->table_count); - if (likely(result)) - { - if (unlikely(select_lex->vers_setup_conds(thd, aux_tables))) - goto multi_delete_error; - res= mysql_select(thd, - select_lex->get_table_list(), - select_lex->item_list, - select_lex->where, - 0, (ORDER *)NULL, (ORDER *)NULL, (Item *)NULL, - (ORDER *)NULL, - (select_lex->options | thd->variables.option_bits | - SELECT_NO_JOIN_CACHE | SELECT_NO_UNLOCK | - OPTION_SETUP_TABLES_DONE) & ~OPTION_BUFFER_RESULT, - result, unit, select_lex); - res|= (int)(thd->is_error()); - - MYSQL_MULTI_DELETE_DONE(res, result->num_deleted()); - if (res) - result->abort_result_set(); /* for both DELETE and EXPLAIN DELETE */ - else - { - if (lex->describe || lex->analyze_stmt) - res= thd->lex->explain->send_explain(thd); - } - multi_delete_error: - delete result; - } - } - else - { - res= TRUE; // Error - MYSQL_MULTI_DELETE_DONE(1, 0); - } - break; - } case SQLCOM_DROP_SEQUENCE: case SQLCOM_DROP_TABLE: { @@ -7659,12 +7536,16 @@ void create_select_for_variable(THD *thd, LEX_CSTRING *var_name) } -void mysql_init_multi_delete(LEX *lex) +void mysql_init_delete(LEX *lex) { - lex->sql_command= SQLCOM_DELETE_MULTI; mysql_init_select(lex); lex->first_select_lex()->limit_params.clear(); lex->unit.lim.clear(); +} + +void mysql_init_multi_delete(LEX *lex) +{ + lex->sql_command= SQLCOM_DELETE_MULTI; lex->first_select_lex()->table_list. save_and_clear(&lex->auxiliary_table_list); lex->query_tables= 0; diff --git a/sql/sql_parse.h b/sql/sql_parse.h index ebe3fe9..45cd15c 100644 --- a/sql/sql_parse.h +++ b/sql/sql_parse.h @@ -95,6 +95,7 @@ void mysql_parse(THD *thd, char *rawbuf, uint length, bool mysql_new_select(LEX *lex, bool move_down, SELECT_LEX *sel); void create_select_for_variable(THD *thd, LEX_CSTRING *var_name); void create_table_set_open_action_and_adjust_tables(LEX *lex); +void mysql_init_delete(LEX *lex); void mysql_init_multi_delete(LEX *lex); bool multi_delete_set_locks_and_link_aux_tables(LEX *lex); void create_table_set_open_action_and_adjust_tables(LEX *lex); diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc index 3859a13..84feda3 100644 --- a/sql/sql_prepare.cc +++ b/sql/sql_prepare.cc @@ -95,7 +95,6 @@ When one supplies long data for a placeholder: #include "sql_base.h" // open_normal_and_derived_tables #include "sql_cache.h" // query_cache_* #include "sql_view.h" // create_view_precheck -#include "sql_delete.h" // mysql_prepare_delete #include "sql_select.h" // for JOIN #include "sql_insert.h" // upgrade_lock_type_for_insert, mysql_prepare_insert #include "sql_db.h" // mysql_opt_change_db, mysql_change_db @@ -1398,56 +1397,6 @@ static bool mysql_test_insert(Prepared_statement *stmt, } -/** - Validate DELETE statement. - - @param stmt prepared statement - @param tables list of tables used in this query - - @retval - FALSE success - @retval - TRUE error, error message is set in THD -*/ - -static bool mysql_test_delete(Prepared_statement *stmt, - TABLE_LIST *table_list) -{ - uint table_count= 0; - THD *thd= stmt->thd; - LEX *lex= stmt->lex; - bool delete_while_scanning; - DBUG_ENTER("mysql_test_delete"); - - if (delete_precheck(thd, table_list) || - open_tables(thd, &table_list, &table_count, MYSQL_OPEN_FORCE_SHARED_MDL)) - goto error; - - if (mysql_handle_derived(thd->lex, DT_INIT)) - goto error; - if (mysql_handle_derived(thd->lex, DT_MERGE_FOR_INSERT)) - goto error; - if (mysql_handle_derived(thd->lex, DT_PREPARE)) - goto error; - - if (!table_list->single_table_updatable()) - { - my_error(ER_NON_UPDATABLE_TABLE, MYF(0), table_list->alias.str, "DELETE"); - goto error; - } - if (!table_list->table || !table_list->table->is_created()) - { - my_error(ER_VIEW_DELETE_MERGE_VIEW, MYF(0), - table_list->view_db.str, table_list->view_name.str); - goto error; - } - - DBUG_RETURN(mysql_prepare_delete(thd, table_list, - &lex->first_select_lex()->where, - &delete_while_scanning)); -error: - DBUG_RETURN(TRUE); -} /** @@ -2031,48 +1980,6 @@ static bool mysql_test_create_view(Prepared_statement *stmt) /** - Validate and prepare for execution a multi delete statement. - - @param stmt prepared statement - @param tables list of tables used in this query - - @retval - FALSE success - @retval - TRUE error, error message in THD is set. -*/ - -static bool mysql_test_multidelete(Prepared_statement *stmt, - TABLE_LIST *tables) -{ - THD *thd= stmt->thd; - - thd->lex->current_select= thd->lex->first_select_lex(); - if (add_item_to_list(thd, new (thd->mem_root) - Item_null(thd))) - { - my_error(ER_OUTOFMEMORY, MYF(ME_FATAL), 0); - goto error; - } - - if (multi_delete_precheck(thd, tables) || - select_like_stmt_test_with_open(stmt, tables, - &mysql_multi_delete_prepare, - OPTION_SETUP_TABLES_DONE)) - goto error; - if (!tables->table) - { - my_error(ER_VIEW_DELETE_MERGE_VIEW, MYF(0), - tables->view_db.str, tables->view_name.str); - goto error; - } - return FALSE; -error: - return TRUE; -} - - -/** Wrapper for mysql_insert_select_prepare, to make change of local tables after open_normal_and_derived_tables() call. @@ -2354,14 +2261,13 @@ static bool check_prepared_statement(Prepared_statement *stmt) case SQLCOM_UPDATE: case SQLCOM_UPDATE_MULTI: + case SQLCOM_DELETE: + case SQLCOM_DELETE_MULTI: res = lex->m_sql_cmd->prepare(thd); if (!res) lex->m_sql_cmd->unprepare(thd); break; - case SQLCOM_DELETE: - res= mysql_test_delete(stmt, tables); - break; /* The following allow WHERE clause, so they must be tested like SELECT */ case SQLCOM_SHOW_DATABASES: case SQLCOM_SHOW_TABLES: @@ -2498,10 +2404,6 @@ static bool check_prepared_statement(Prepared_statement *stmt) res= mysql_test_set_fields(stmt, tables, &lex->var_list); break; - case SQLCOM_DELETE_MULTI: - res= mysql_test_multidelete(stmt, tables); - break; - case SQLCOM_INSERT_SELECT: case SQLCOM_REPLACE_SELECT: res= mysql_test_insert_select(stmt, tables); diff --git a/sql/sql_select.cc b/sql/sql_select.cc index edd9aed..484f5ce 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -28846,7 +28846,8 @@ static bool get_range_limit_read_cost(const JOIN_TAB *tab, @note This function takes into account table->opt_range_condition_rows statistic (that is calculated by the make_join_statistics function). - However, single table procedures such as mysql_update() and mysql_delete() + However, single table procedures such as Sql_cmd_update:update_single_table() + and Sql_cmd_delete::delete_single_table() never call make_join_statistics, so they have to update it manually (@see get_index_for_order()). */ @@ -30280,6 +30281,67 @@ static bool process_direct_rownum_comparison(THD *thd, SELECT_LEX_UNIT *unit, } +static void MYSQL_DML_START(THD *thd) +{ + switch (thd->lex->sql_command) { + + case SQLCOM_UPDATE: + MYSQL_UPDATE_START(thd->query()); + break; + case SQLCOM_UPDATE_MULTI: + MYSQL_MULTI_UPDATE_START(thd->query()); + break; + case SQLCOM_DELETE: + MYSQL_DELETE_START(thd->query()); + break; + case SQLCOM_DELETE_MULTI: + MYSQL_MULTI_DELETE_START(thd->query()); + break; + default: + DBUG_ASSERT(0); + } +} + + +static void MYSQL_DML_DONE(THD *thd, int rc) +{ + switch (thd->lex->sql_command) { + + case SQLCOM_UPDATE: + MYSQL_UPDATE_DONE( + rc, + (rc ? 0 : + ((multi_update*)(((Sql_cmd_dml*)(thd->lex->m_sql_cmd))->get_result())) + ->num_found()), + (rc ? 0 : + ((multi_update*)(((Sql_cmd_dml*)(thd->lex->m_sql_cmd))->get_result())) + ->num_updated())); + break; + case SQLCOM_UPDATE_MULTI: + MYSQL_MULTI_UPDATE_DONE( + rc, + (rc ? 0 : + ((multi_update*)(((Sql_cmd_dml*)(thd->lex->m_sql_cmd))->get_result())) + ->num_found()), + (rc ? 0 : + ((multi_update*)(((Sql_cmd_dml*)(thd->lex->m_sql_cmd))->get_result())) + ->num_updated())); + break; + case SQLCOM_DELETE: + MYSQL_DELETE_DONE(rc, (rc ? 0 : (ulong) (thd->get_row_count_func()))); + break; + case SQLCOM_DELETE_MULTI: + MYSQL_MULTI_DELETE_DONE( + rc, + (rc ? 0 : + ((multi_delete*)(((Sql_cmd_dml*)(thd->lex->m_sql_cmd))->get_result())) + ->num_deleted())); + break; + default: + DBUG_ASSERT(0); + } +} + bool Sql_cmd_dml::prepare(THD *thd) { lex= thd->lex; @@ -30291,6 +30353,8 @@ bool Sql_cmd_dml::prepare(THD *thd) if (precheck(thd)) goto err; + MYSQL_DML_START(thd); + lex->context_analysis_only|= CONTEXT_ANALYSIS_ONLY_DERIVED; if (open_tables_for_query(thd, lex->query_tables, &table_count, 0, @@ -30338,6 +30402,9 @@ bool Sql_cmd_dml::execute(THD *thd) { if (precheck(thd)) goto err; + + MYSQL_DML_START(thd); + if (open_tables_for_query(thd, lex->query_tables, &table_count, 0, get_dml_prelocking_strategy())) goto err; @@ -30345,7 +30412,6 @@ bool Sql_cmd_dml::execute(THD *thd) THD_STAGE_INFO(thd, stage_init); - DBUG_ASSERT(!lex->is_query_tables_locked()); /* Locking of tables is done after preparation but before optimization. This allows to do better partition pruning and avoid locking unused @@ -30373,10 +30439,13 @@ bool Sql_cmd_dml::execute(THD *thd) THD_STAGE_INFO(thd, stage_end); + MYSQL_DML_DONE(thd, res); + return res; err: DBUG_ASSERT(thd->is_error() || thd->killed); + MYSQL_DML_DONE(thd, 1); THD_STAGE_INFO(thd, stage_end); (void)unit->cleanup(); diff --git a/sql/sql_update.cc b/sql/sql_update.cc index 3f04db0..c83a527 100644 --- a/sql/sql_update.cc +++ b/sql/sql_update.cc @@ -197,22 +197,11 @@ static bool check_fields(THD *thd, TABLE_LIST *table, List<Item> &items, return true; } - DBUG_ASSERT(thd->lex->sql_command == SQLCOM_UPDATE); - for (List_iterator_fast<Item> it(items); (item=it++);) - { - Field *f= item->field_for_view_update()->field; - vers_select_conds_t &period= table->period_conditions; - if (period.field_start->field == f || period.field_end->field == f) - { - my_error(ER_PERIOD_COLUMNS_UPDATED, MYF(0), - item->name.str, period.name.str); - return true; - } - } } return FALSE; } + bool TABLE::vers_check_update(List<Item> &items) { List_iterator<Item> it(items); @@ -1240,6 +1229,9 @@ bool Sql_cmd_update::update_single_table(THD *thd) thd->lex->current_select->save_leaf_tables(thd); thd->lex->current_select->first_cond_optimization= 0; } + ((multi_update *)result)->set_found(found); + ((multi_update *)result)->set_updated(updated); + if (unlikely(thd->lex->analyze_stmt)) goto emit_explain_and_leave; @@ -1271,75 +1263,6 @@ bool Sql_cmd_update::update_single_table(THD *thd) DBUG_RETURN((err2 || thd->is_error()) ? 1 : 0); } -/* - Prepare items in UPDATE statement - - SYNOPSIS - mysql_prepare_update() - thd - thread handler - table_list - global/local table list - conds - conditions - order_num - number of ORDER BY list entries - order - ORDER BY clause list - - RETURN VALUE - FALSE OK - TRUE error -*/ -bool mysql_prepare_update(THD *thd, TABLE_LIST *table_list, - Item **conds, uint order_num, ORDER *order) -{ - Item *fake_conds= 0; -#ifndef NO_EMBEDDED_ACCESS_CHECKS - TABLE *table= table_list->table; -#endif - List<Item> all_fields; - SELECT_LEX *select_lex= thd->lex->first_select_lex(); - DBUG_ENTER("mysql_prepare_update"); - -#ifndef NO_EMBEDDED_ACCESS_CHECKS - table_list->grant.want_privilege= table->grant.want_privilege= - (SELECT_ACL & ~table->grant.privilege); - table_list->register_want_access(SELECT_ACL); -#endif - - thd->lex->allow_sum_func.clear_all(); - - if (table_list->has_period() && - select_lex->period_setup_conds(thd, table_list)) - DBUG_RETURN(true); - - DBUG_ASSERT(table_list->table); - // conds could be cached from previous SP call - DBUG_ASSERT(!table_list->vers_conditions.need_setup() || - !*conds || thd->stmt_arena->is_stmt_execute()); - if (select_lex->vers_setup_conds(thd, table_list)) - DBUG_RETURN(TRUE); - - *conds= select_lex->where; - - /* - We do not call DT_MERGE_FOR_INSERT because it has no sense for simple - (not multi-) update - */ - if (mysql_handle_derived(thd->lex, DT_PREPARE)) - DBUG_RETURN(TRUE); - - if (setup_tables_and_check_access(thd, &select_lex->context, - &select_lex->top_join_list, table_list, - select_lex->leaf_tables, - FALSE, UPDATE_ACL, SELECT_ACL, TRUE) || - setup_conds(thd, table_list, select_lex->leaf_tables, conds) || - select_lex->setup_ref_array(thd, order_num) || - setup_order(thd, select_lex->ref_pointer_array, - table_list, all_fields, all_fields, order) || - setup_ftfuncs(select_lex)) - DBUG_RETURN(TRUE); - - - select_lex->fix_prepare_information(thd, conds, &fake_conds); - DBUG_RETURN(FALSE); -} /** Check that we are not using table that we are updating in a sub select @@ -1596,7 +1519,13 @@ bool Multiupdate_prelocking_strategy::handle_end(THD *thd) mysql_handle_derived(lex, DT_PREPARE)) DBUG_RETURN(1); - /* + if (table_list->has_period() && table_list->is_view_or_derived()) + { + my_error(ER_IT_IS_A_VIEW, MYF(0), table_list->table_name.str); + DBUG_RETURN(TRUE); + } + + /* setup_tables() need for VIEWs. JOIN::prepare() will call setup_tables() second time, but this call will do nothing (there are check for second call in setup_tables()). @@ -1607,6 +1536,10 @@ bool Multiupdate_prelocking_strategy::handle_end(THD *thd) FALSE, UPDATE_ACL, SELECT_ACL, TRUE)) DBUG_RETURN(1); + if (table_list->has_period() && + select_lex->period_setup_conds(thd, table_list)) + DBUG_RETURN(true); + List<Item> *fields= &lex->first_select_lex()->item_list; if (setup_fields_with_no_wrap(thd, Ref_ptr_array(), *fields, MARK_COLUMNS_WRITE, 0, 0)) @@ -1715,153 +1648,6 @@ bool Multiupdate_prelocking_strategy::handle_end(THD *thd) DBUG_RETURN(0); } -/* - make update specific preparation and checks after opening tables - - SYNOPSIS - mysql_multi_update_prepare() - thd thread handler - - RETURN - FALSE OK - TRUE Error -*/ - -int mysql_multi_update_prepare(THD *thd) -{ - LEX *lex= thd->lex; - TABLE_LIST *table_list= lex->query_tables; - TABLE_LIST *tl; - Multiupdate_prelocking_strategy prelocking_strategy; - uint table_count= lex->table_count; - DBUG_ENTER("mysql_multi_update_prepare"); - - /* - Open tables and create derived ones, but do not lock and fill them yet. - - During prepare phase acquire only S metadata locks instead of SW locks to - keep prepare of multi-UPDATE compatible with concurrent LOCK TABLES WRITE - and global read lock. - - Don't evaluate any subqueries even if constant, because - tables aren't locked yet. - */ - lex->context_analysis_only|= CONTEXT_ANALYSIS_ONLY_DERIVED; - if (thd->lex->sql_command == SQLCOM_UPDATE_MULTI) - { - if (open_tables(thd, &table_list, &table_count, - thd->stmt_arena->is_stmt_prepare() ? MYSQL_OPEN_FORCE_SHARED_MDL : 0, - &prelocking_strategy)) - DBUG_RETURN(TRUE); - } - else - { - /* following need for prepared statements, to run next time multi-update */ - thd->lex->sql_command= SQLCOM_UPDATE_MULTI; - prelocking_strategy.reset(thd); - if (prelocking_strategy.handle_end(thd)) - DBUG_RETURN(TRUE); - } - - /* now lock and fill tables */ - if (!thd->stmt_arena->is_stmt_prepare() && - lock_tables(thd, table_list, table_count, 0)) - DBUG_RETURN(TRUE); - - lex->context_analysis_only&= ~CONTEXT_ANALYSIS_ONLY_DERIVED; - - (void) read_statistics_for_tables_if_needed(thd, table_list); - /* @todo: downgrade the metadata locks here. */ - - /* - Check that we are not using table that we are updating, but we should - skip all tables of UPDATE SELECT itself - */ - lex->first_select_lex()->exclude_from_table_unique_test= TRUE; - /* We only need SELECT privilege for columns in the values list */ - List_iterator<TABLE_LIST> ti(lex->first_select_lex()->leaf_tables); - while ((tl= ti++)) - { - if (tl->is_jtbm()) - continue; - TABLE *table= tl->table; - TABLE_LIST *tlist; - if (!(tlist= tl->top_table())->derived) - { - tlist->grant.want_privilege= - (SELECT_ACL & ~tlist->grant.privilege); - table->grant.want_privilege= (SELECT_ACL & ~table->grant.privilege); - } - DBUG_PRINT("info", ("table: %s want_privilege: %llx", tl->alias.str, - (longlong) table->grant.want_privilege)); - } - /* - Set exclude_from_table_unique_test value back to FALSE. It is needed for - further check in multi_update::prepare whether to use record cache. - */ - lex->first_select_lex()->exclude_from_table_unique_test= FALSE; - - if (lex->save_prep_leaf_tables()) - DBUG_RETURN(TRUE); - - DBUG_RETURN (FALSE); -} - - -/* - Setup multi-update handling and call SELECT to do the join -*/ - -bool mysql_multi_update(THD *thd, TABLE_LIST *table_list, List<Item> *fields, - List<Item> *values, COND *conds, ulonglong options, - enum enum_duplicates handle_duplicates, - bool ignore, SELECT_LEX_UNIT *unit, - SELECT_LEX *select_lex, multi_update **result) -{ - bool res; - DBUG_ENTER("mysql_multi_update"); - - if (!(*result= new (thd->mem_root) multi_update(thd, table_list, - &thd->lex->first_select_lex()->leaf_tables, - fields, values, handle_duplicates, ignore))) - { - DBUG_RETURN(TRUE); - } - - if ((*result)->init(thd)) - DBUG_RETURN(1); - - thd->abort_on_warning= !ignore && thd->is_strict_mode(); - List<Item> total_list; - - if (setup_tables(thd, &select_lex->context, &select_lex->top_join_list, - table_list, select_lex->leaf_tables, FALSE, FALSE)) - DBUG_RETURN(1); - - if (select_lex->vers_setup_conds(thd, table_list)) - DBUG_RETURN(1); - - res= mysql_select(thd, - table_list, total_list, conds, - select_lex->order_list.elements, - select_lex->order_list.first, NULL, NULL, NULL, - options | SELECT_NO_JOIN_CACHE | SELECT_NO_UNLOCK | - OPTION_SETUP_TABLES_DONE, - *result, unit, select_lex); - - DBUG_PRINT("info",("res: %d report_error: %d", res, (int) thd->is_error())); - res|= thd->is_error(); - if (unlikely(res)) - (*result)->abort_result_set(); - else - { - if (thd->lex->describe || thd->lex->analyze_stmt) - res= thd->lex->explain->send_explain(thd); - } - thd->abort_on_warning= 0; - DBUG_RETURN(res); -} - multi_update::multi_update(THD *thd_arg, TABLE_LIST *table_list, List<TABLE_LIST> *leaves_list, @@ -3021,7 +2807,15 @@ bool Sql_cmd_update::precheck(THD *thd) if (multi_update_precheck(thd, lex->query_tables)) return true; } + + WSREP_SYNC_WAIT(thd, WSREP_SYNC_WAIT_BEFORE_UPDATE_DELETE); + return false; + +#ifdef WITH_WSREP +wsrep_error_label: +#endif + return true; } @@ -3029,26 +2823,31 @@ bool Sql_cmd_update::prepare_inner(THD *thd) { JOIN *join; int err= 0; - // uint table_cnt= 0; SELECT_LEX *const select_lex = thd->lex->first_select_lex(); TABLE_LIST *const table_list = select_lex->get_table_list(); ulonglong select_options= select_lex->options; bool free_join= 1; - // bool orig_multitable= multitable; DBUG_ENTER("Sql_cmd_update::prepare_inner"); + (void) read_statistics_for_tables_if_needed(thd, table_list); + + THD_STAGE_INFO(thd, stage_init_update); + if (!multitable) { - TABLE_LIST *update_source_table= 0; - if (mysql_handle_derived(lex, DT_INIT)) DBUG_RETURN(TRUE); + } - if (table_list->has_period() && table_list->is_view_or_derived()) - { - my_error(ER_IT_IS_A_VIEW, MYF(0), table_list->table_name.str); - DBUG_RETURN(TRUE); - } + if (table_list->has_period() && table_list->is_view_or_derived()) + { + my_error(ER_IT_IS_A_VIEW, MYF(0), table_list->table_name.str); + DBUG_RETURN(TRUE); + } + + if (!multitable) + { + TABLE_LIST *update_source_table= 0; if (((update_source_table=unique_table(thd, table_list, table_list->next_global, 0)) || @@ -3088,8 +2887,8 @@ bool Sql_cmd_update::prepare_inner(THD *thd) DBUG_RETURN(TRUE); } - if (((multi_update *)result)->init(thd)) - DBUG_RETURN(TRUE); + if (((multi_update *)result)->init(thd)) + DBUG_RETURN(TRUE); if (setup_tables(thd, &select_lex->context, &select_lex->top_join_list, table_list, select_lex->leaf_tables, false, false)) @@ -3130,6 +2929,31 @@ bool Sql_cmd_update::prepare_inner(THD *thd) } + if (table_list->has_period()) + { + Item *item; + for (List_iterator_fast<Item> it(select_lex->item_list); (item=it++);) + { + Field *f= item->field_for_view_update()->field; + vers_select_conds_t &period= table_list->period_conditions; + if (period.field_start->field == f || period.field_end->field == f) + { + my_error(ER_PERIOD_COLUMNS_UPDATED, MYF(0), + item->name.str, period.name.str); + DBUG_RETURN(true); + } + } + + if (!table_list->period_conditions.start.item->const_item() + || !table_list->period_conditions.end.item->const_item()) + { + my_error(ER_NOT_CONSTANT_EXPRESSION, MYF(0), "FOR PORTION OF"); + DBUG_RETURN(true); + } + table_list->table->no_cache= true; + } + + free_join= false; err: @@ -3147,8 +2971,16 @@ bool Sql_cmd_update::prepare_inner(THD *thd) bool Sql_cmd_update::execute_inner(THD *thd) { - bool res= multitable ? Sql_cmd_dml::execute_inner(thd) - : update_single_table(thd); + bool res= 0; + + thd->get_stmt_da()->reset_current_row_for_warning(1); + if (!multitable) + res= update_single_table(thd); + else + { + thd->abort_on_warning= !thd->lex->ignore && thd->is_strict_mode(); + res= Sql_cmd_dml::execute_inner(thd); + } res|= thd->is_error(); if (multitable) diff --git a/sql/sql_update.h b/sql/sql_update.h index e52d3cd..cf33461 100644 --- a/sql/sql_update.h +++ b/sql/sql_update.h @@ -27,19 +27,7 @@ class THD; typedef class st_select_lex SELECT_LEX; typedef class st_select_lex_unit SELECT_LEX_UNIT; -bool mysql_prepare_update(THD *thd, TABLE_LIST *table_list, - Item **conds, uint order_num, ORDER *order); bool check_unique_table(THD *thd, TABLE_LIST *table_list); -int mysql_update(THD *thd,TABLE_LIST *tables,List<Item> &fields, - List<Item> &values,COND *conds, - uint order_num, ORDER *order, ha_rows limit, - bool ignore, ha_rows *found_return, ha_rows *updated_return); -bool mysql_multi_update(THD *thd, TABLE_LIST *table_list, - List<Item> *fields, List<Item> *values, - COND *conds, ulonglong options, - enum enum_duplicates handle_duplicates, bool ignore, - SELECT_LEX_UNIT *unit, SELECT_LEX *select_lex, - multi_update **result); bool records_are_comparable(const TABLE *table); bool compare_record(const TABLE *table); diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 30eda7c..48eed4e 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -70,6 +70,7 @@ #include "sql_type_json.h" #include "json_table.h" #include "sql_update.h" +#include "sql_delete.h" /* this is to get the bison compilation windows warnings out */ #ifdef _MSC_VER @@ -1675,7 +1676,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize); opt_mi_check_type opt_to mi_check_types table_to_table_list table_to_table opt_table_list opt_as handler_rkey_function handler_read_or_scan - single_multi table_wild_list table_wild_one opt_wild + single_multi opt_wild opt_and select_var_list select_var_list_init help opt_extended_describe shutdown @@ -13245,8 +13246,8 @@ update: MYSQL_YYABORT; /* In case of multi-update setting write lock for all tables may - be too pessimistic. We will decrease lock level if possible in - mysql_multi_update(). + be too pessimistic. We will decrease lock level if possible + later while processing the statement. */ slex->set_lock_for_tables($3, slex->table_list.elements == 1, false); } @@ -13303,12 +13304,11 @@ delete: DELETE_SYM { LEX *lex= Lex; - lex->sql_command= SQLCOM_DELETE; YYPS->m_lock_type= TL_WRITE_DEFAULT; YYPS->m_mdl_type= MDL_SHARED_WRITE; if (Lex->main_select_push()) MYSQL_YYABORT; - mysql_init_select(lex); + mysql_init_delete(lex); lex->ignore= 0; lex->first_select_lex()->order_list.empty(); } @@ -13334,8 +13334,13 @@ delete_part2: opt_delete_options single_multi {} | HISTORY_SYM delete_single_table opt_delete_system_time { - Lex->last_table()->vers_conditions= Lex->vers_conditions; - Lex->pop_select(); //main select + LEX *lex= Lex; + lex->last_table()->vers_conditions= lex->vers_conditions; + lex->pop_select(); //main select + lex->sql_command= SQLCOM_DELETE; + if (!(lex->m_sql_cmd= + new (thd->mem_root) Sql_cmd_delete(false))) + MYSQL_YYABORT; } ; @@ -13369,12 +13374,22 @@ single_multi: delete_limit_clause opt_returning { + LEX *lex= Lex; if ($3) Select->order_list= *($3); - Lex->pop_select(); //main select + lex->pop_select(); //main select + lex->sql_command= SQLCOM_DELETE; + if (!(lex->m_sql_cmd= + new (thd->mem_root) Sql_cmd_delete(false))) + MYSQL_YYABORT; } - | table_wild_list + | table_alias_ref_list { + LEX *lex= Lex; + lex->sql_command= SQLCOM_DELETE_MULTI; + if (!(lex->m_sql_cmd= + new (thd->mem_root) Sql_cmd_delete(true))) + MYSQL_YYABORT; mysql_init_multi_delete(Lex); YYPS->m_lock_type= TL_READ_DEFAULT; YYPS->m_mdl_type= MDL_SHARED_READ; @@ -13386,6 +13401,11 @@ single_multi: } stmt_end {} | FROM table_alias_ref_list { + LEX *lex= Lex; + lex->sql_command= SQLCOM_DELETE_MULTI; + if (!(lex->m_sql_cmd= + new (thd->mem_root) Sql_cmd_delete(true))) + MYSQL_YYABORT; mysql_init_multi_delete(Lex); YYPS->m_lock_type= TL_READ_DEFAULT; YYPS->m_mdl_type= MDL_SHARED_READ; @@ -13421,44 +13441,6 @@ opt_returning: } ; -table_wild_list: - table_wild_one - | table_wild_list ',' table_wild_one - ; - -table_wild_one: - ident opt_wild - { - Table_ident *ti= new (thd->mem_root) Table_ident(&$1); - if (unlikely(ti == NULL)) - MYSQL_YYABORT; - if (unlikely(!Select-> - add_table_to_list(thd, - ti, - NULL, - (TL_OPTION_UPDATING | - TL_OPTION_ALIAS), - YYPS->m_lock_type, - YYPS->m_mdl_type))) - MYSQL_YYABORT; - } - | ident '.' ident opt_wild - { - Table_ident *ti= new (thd->mem_root) Table_ident(thd, &$1, &$3, 0); - if (unlikely(ti == NULL)) - MYSQL_YYABORT; - if (unlikely(!Select-> - add_table_to_list(thd, - ti, - NULL, - (TL_OPTION_UPDATING | - TL_OPTION_ALIAS), - YYPS->m_lock_type, - YYPS->m_mdl_type))) - MYSQL_YYABORT; - } - ; - opt_wild: /* empty */ {} | '.' '*' {} diff --git a/sql/table.h b/sql/table.h index 8d609fb..358e1a9 100644 --- a/sql/table.h +++ b/sql/table.h @@ -2321,6 +2321,7 @@ struct TABLE_LIST */ select_unit *derived_result; /* Stub used for materialized derived tables. */ + bool delete_while_scanning; table_map map; /* ID bit of table (1,2,4,8,16...) */ table_map get_map() {
1 0
0 0
[Commits] bb9579a: MDEV-27159 Re-design the upper level of handling DML commands
by IgorBabaev 12 Jun '22

12 Jun '22
revision-id: bb9579a654f1b987637472c32f430bf49b170c68 (mariadb-10.6.1-309-gbb9579a) parent(s): 31c01a5b28fc59479755cab5098b3d13e55f732b author: Igor Babaev committer: Igor Babaev timestamp: 2022-06-12 16:34:17 -0700 message: MDEV-27159 Re-design the upper level of handling DML commands This is the second commit for the task. This patch allows to execute only single-table and multi-table DELETE statements using the method Sql_cmd_dml::execute(). The code that handles INSERT statements has not been touched. This patch still does not have the final changes to handle UPDATE/DELETE statements. All tests from the main suite passed. With --ps-protocol one test from opt_trace_security returns not the same result. This will be fixed soon. --- .../main/myisam_explain_non_select_all.result | 77 ++- mysql-test/main/opt_trace.result | 14 +- mysql-test/main/opt_trace_security.result | 5 - mysql-test/main/opt_trace_security.test | 6 +- mysql-test/main/partition_explicit_prune.result | 1 + mysql-test/main/sp.result | 2 +- mysql-test/suite/period/r/update.result | 2 +- mysql-test/suite/period/t/update.test | 2 +- sql/ha_partition.cc | 6 +- sql/handler.h | 5 +- sql/opt_range.cc | 2 +- sql/sql_base.cc | 11 +- sql/sql_class.h | 2 + sql/sql_cmd.h | 11 +- sql/sql_delete.cc | 617 ++++++++++++--------- sql/sql_delete.h | 15 +- sql/sql_lex.cc | 30 +- sql/sql_parse.cc | 137 +---- sql/sql_parse.h | 1 + sql/sql_prepare.cc | 102 +--- sql/sql_select.cc | 68 ++- sql/sql_update.cc | 316 +++-------- sql/sql_update.h | 12 - sql/sql_yacc.yy | 76 +-- sql/table.h | 1 + 25 files changed, 646 insertions(+), 875 deletions(-) diff --git a/mysql-test/main/myisam_explain_non_select_all.result b/mysql-test/main/myisam_explain_non_select_all.result index 36231c3..20b769b 100644 --- a/mysql-test/main/myisam_explain_non_select_all.result +++ b/mysql-test/main/myisam_explain_non_select_all.result @@ -19,6 +19,7 @@ id select_type table type possible_keys key key_len ref rows filtered Extra 1 SIMPLE t1 ALL NULL NULL NULL NULL 3 100.00 Using where # Status of EXPLAIN EXTENDED query Variable_name Value +Handler_read_key 2 FLUSH STATUS; FLUSH TABLES; EXPLAIN EXTENDED SELECT * FROM t1 WHERE a < 10; @@ -37,6 +38,7 @@ Handler_read_key 2 Handler_read_rnd_next 4 # Status of testing query execution: Variable_name Value +Handler_read_key 2 Handler_read_rnd_next 4 Handler_update 3 @@ -150,6 +152,7 @@ id select_type table type possible_keys key key_len ref rows filtered Extra 1 SIMPLE t2 ALL NULL NULL NULL NULL 3 100.00 # Status of EXPLAIN EXTENDED query Variable_name Value +Handler_read_key 4 FLUSH STATUS; FLUSH TABLES; EXPLAIN EXTENDED SELECT * FROM t1, t2 WHERE t1.a = 1; @@ -169,6 +172,7 @@ Handler_read_key 4 Handler_read_rnd_next 8 # Status of testing query execution: Variable_name Value +Handler_read_key 4 Handler_read_rnd_next 8 Handler_update 1 @@ -198,6 +202,7 @@ id select_type table type possible_keys key key_len ref rows filtered Extra 2 DERIVED t2 ALL NULL NULL NULL NULL 3 100.00 # Status of EXPLAIN EXTENDED query Variable_name Value +Handler_read_key 4 FLUSH STATUS; FLUSH TABLES; EXPLAIN EXTENDED SELECT * FROM t1 t11, (SELECT * FROM t2) t12 WHERE t11.a = 1; @@ -217,6 +222,7 @@ Handler_read_key 4 Handler_read_rnd_next 8 # Status of testing query execution: Variable_name Value +Handler_read_key 4 Handler_read_rnd_next 12 Handler_update 1 @@ -244,6 +250,7 @@ id select_type table type possible_keys key key_len ref rows filtered Extra 2 SUBQUERY t2 ALL NULL NULL NULL NULL 3 100.00 Using where # Status of EXPLAIN EXTENDED query Variable_name Value +Handler_read_key 4 FLUSH STATUS; FLUSH TABLES; EXPLAIN EXTENDED SELECT * FROM t1 WHERE 1 IN (SELECT 1 FROM t2 WHERE t2.b < 3); @@ -264,6 +271,7 @@ Handler_read_key 5 Handler_read_rnd_next 8 # Status of testing query execution: Variable_name Value +Handler_read_key 4 Handler_read_rnd_next 5 Handler_update 3 @@ -293,6 +301,7 @@ Warnings: Note 1276 Field or reference 'test.t1.a' of SELECT #2 was resolved in SELECT #1 # Status of EXPLAIN EXTENDED query Variable_name Value +Handler_read_key 4 FLUSH STATUS; FLUSH TABLES; EXPLAIN EXTENDED SELECT * FROM t1 WHERE a IN (SELECT b FROM t2 WHERE t1.a < 3); @@ -313,6 +322,7 @@ Handler_read_key 4 Handler_read_rnd_next 8 # Status of testing query execution: Variable_name Value +Handler_read_key 4 Handler_read_rnd_next 7 Handler_update 2 @@ -344,6 +354,7 @@ id select_type table type possible_keys key key_len ref rows filtered Extra 2 MATERIALIZED t2 ALL NULL NULL NULL NULL 3 100.00 Using where # Status of EXPLAIN EXTENDED query Variable_name Value +Handler_read_key 4 FLUSH STATUS; FLUSH TABLES; EXPLAIN EXTENDED SELECT * FROM t1, t2 WHERE a IN (SELECT b FROM t2 WHERE t2.b < 3); @@ -365,7 +376,7 @@ Handler_read_key 7 Handler_read_rnd_next 12 # Status of testing query execution: Variable_name Value -Handler_read_key 3 +Handler_read_key 7 Handler_read_rnd_next 16 Handler_update 2 @@ -395,6 +406,7 @@ id select_type table type possible_keys key key_len ref rows filtered Extra 2 DERIVED t2 ALL NULL NULL NULL NULL 3 100.00 # Status of EXPLAIN EXTENDED query Variable_name Value +Handler_read_key 4 FLUSH STATUS; FLUSH TABLES; EXPLAIN EXTENDED SELECT * FROM t1 t11, (SELECT * FROM t2) t12; @@ -414,6 +426,7 @@ Handler_read_key 4 Handler_read_rnd_next 8 # Status of testing query execution: Variable_name Value +Handler_read_key 4 Handler_read_rnd 3 Handler_read_rnd_deleted 1 Handler_read_rnd_next 24 @@ -445,6 +458,7 @@ id select_type table type possible_keys key key_len ref rows filtered Extra 2 DERIVED NULL NULL NULL NULL NULL NULL NULL NULL No tables used # Status of EXPLAIN EXTENDED query Variable_name Value +Handler_read_key 2 Handler_read_rnd_next 1 FLUSH STATUS; FLUSH TABLES; @@ -467,6 +481,7 @@ Handler_read_key 2 Handler_read_rnd_next 5 # Status of testing query execution: Variable_name Value +Handler_read_key 2 Handler_read_rnd 3 Handler_read_rnd_next 9 Handler_update 3 @@ -497,6 +512,7 @@ id select_type table type possible_keys key key_len ref rows filtered Extra 2 DERIVED t2 ALL NULL NULL NULL NULL 3 100.00 # Status of EXPLAIN EXTENDED query Variable_name Value +Handler_read_key 4 FLUSH STATUS; FLUSH TABLES; EXPLAIN EXTENDED SELECT * FROM t1 t11, (SELECT * FROM t2) t12 WHERE t11.a > 1; @@ -516,6 +532,7 @@ Handler_read_key 4 Handler_read_rnd_next 8 # Status of testing query execution: Variable_name Value +Handler_read_key 4 Handler_read_rnd_next 16 Handler_update 2 @@ -977,6 +994,7 @@ id select_type table type possible_keys key key_len ref rows filtered Extra 2 DEPENDENT SUBQUERY t2 ALL NULL NULL NULL NULL 3 100.00 Using where # Status of EXPLAIN EXTENDED query Variable_name Value +Handler_read_key 4 FLUSH STATUS; FLUSH TABLES; EXPLAIN EXTENDED SELECT * FROM t1 WHERE a IN (SELECT a FROM t2); @@ -997,6 +1015,7 @@ Handler_read_key 7 Handler_read_rnd_next 8 # Status of testing query execution: Variable_name Value +Handler_read_key 4 Handler_read_rnd_next 10 Handler_update 3 @@ -1114,6 +1133,7 @@ id select_type table type possible_keys key key_len ref rows filtered Extra 1 SIMPLE t1 ALL NULL NULL NULL NULL 5 100.00 # Status of EXPLAIN EXTENDED query Variable_name Value +Handler_read_key 3 FLUSH STATUS; FLUSH TABLES; EXPLAIN EXTENDED SELECT * FROM t1; @@ -1132,6 +1152,7 @@ Handler_read_key 3 Handler_read_rnd_next 6 # Status of testing query execution: Variable_name Value +Handler_read_key 3 Handler_read_rnd_next 6 Handler_update 5 @@ -1895,6 +1916,7 @@ id select_type table type possible_keys key key_len ref rows filtered Extra 1 SIMPLE t2 range PRIMARY PRIMARY 4 NULL 5 100.00 Using where; Using buffer # Status of EXPLAIN EXTENDED query Variable_name Value +Handler_read_key 4 FLUSH STATUS; FLUSH TABLES; EXPLAIN EXTENDED SELECT * FROM t2 WHERE i > 10 AND i <= 18 ORDER BY i LIMIT 5; @@ -1913,7 +1935,7 @@ Handler_read_key 5 Handler_read_next 4 # Status of testing query execution: Variable_name Value -Handler_read_key 1 +Handler_read_key 5 Handler_read_next 4 Handler_read_rnd 5 Handler_update 5 @@ -1942,6 +1964,7 @@ id select_type table type possible_keys key key_len ref rows filtered Extra 1 SIMPLE t2 ALL NULL NULL NULL NULL 26 100.00 Using where; Using filesort # Status of EXPLAIN EXTENDED query Variable_name Value +Handler_read_key 4 FLUSH STATUS; FLUSH TABLES; EXPLAIN EXTENDED SELECT * FROM t2 WHERE i > 10 AND i <= 18 ORDER BY i LIMIT 5; @@ -1963,6 +1986,7 @@ Sort_rows 5 Sort_scan 1 # Status of testing query execution: Variable_name Value +Handler_read_key 4 Handler_read_rnd 5 Handler_read_rnd_next 27 Handler_update 5 @@ -1994,6 +2018,7 @@ id select_type table type possible_keys key key_len ref rows filtered Extra 1 SIMPLE t2 ALL NULL NULL NULL NULL 26 100.00 Using where; Using filesort # Status of EXPLAIN EXTENDED query Variable_name Value +Handler_read_key 8 FLUSH STATUS; FLUSH TABLES; EXPLAIN EXTENDED SELECT * FROM t2 WHERE b = 10 ORDER BY a, c LIMIT 5; @@ -2015,6 +2040,7 @@ Sort_rows 1 Sort_scan 1 # Status of testing query execution: Variable_name Value +Handler_read_key 8 Handler_read_rnd 1 Handler_read_rnd_next 27 Handler_update 1 @@ -2047,6 +2073,7 @@ id select_type table type possible_keys key key_len ref rows filtered Extra 1 SIMPLE t2 index NULL a 15 NULL 5 100.00 Using where; Using buffer # Status of EXPLAIN EXTENDED query Variable_name Value +Handler_read_key 8 FLUSH STATUS; FLUSH TABLES; EXPLAIN EXTENDED SELECT * FROM t2 WHERE b = 10 ORDER BY a, c LIMIT 5; @@ -2067,6 +2094,7 @@ Handler_read_next 4 # Status of testing query execution: Variable_name Value Handler_read_first 1 +Handler_read_key 8 Handler_read_next 4 Handler_read_rnd 5 Handler_update 5 @@ -2095,6 +2123,7 @@ id select_type table type possible_keys key key_len ref rows filtered Extra 1 SIMPLE t2 ALL NULL NULL NULL NULL 26 100.00 Using where; Using filesort # Status of EXPLAIN EXTENDED query Variable_name Value +Handler_read_key 8 FLUSH STATUS; FLUSH TABLES; EXPLAIN EXTENDED SELECT * FROM t2 WHERE b = 10 ORDER BY a, c LIMIT 5; @@ -2116,6 +2145,7 @@ Sort_rows 1 Sort_scan 1 # Status of testing query execution: Variable_name Value +Handler_read_key 8 Handler_read_rnd 1 Handler_read_rnd_next 27 Sort_priority_queue_sorts 1 @@ -2147,6 +2177,7 @@ id select_type table type possible_keys key key_len ref rows filtered Extra 1 SIMPLE t2 ALL NULL NULL NULL NULL 26 100.00 Using where; Using filesort # Status of EXPLAIN EXTENDED query Variable_name Value +Handler_read_key 8 FLUSH STATUS; FLUSH TABLES; EXPLAIN EXTENDED SELECT * FROM t2 WHERE b = 10 ORDER BY a, c LIMIT 5; @@ -2169,6 +2200,7 @@ Sort_rows 1 Sort_scan 1 # Status of testing query execution: Variable_name Value +Handler_read_key 8 Handler_read_rnd 1 Handler_read_rnd_next 27 Sort_priority_queue_sorts 1 @@ -2200,6 +2232,7 @@ id select_type table type possible_keys key key_len ref rows filtered Extra 1 SIMPLE t2 index_merge key1,key2 key1,key2 5,5 NULL 7 100.00 Using sort_union(key1,key2); Using where; Using filesort # Status of EXPLAIN EXTENDED query Variable_name Value +Handler_read_key 6 FLUSH STATUS; FLUSH TABLES; EXPLAIN EXTENDED SELECT * FROM t2 WHERE key1 < 13 or key2 < 14 ORDER BY key1; @@ -2221,7 +2254,7 @@ Sort_range 1 Sort_rows 4 # Status of testing query execution: Variable_name Value -Handler_read_key 2 +Handler_read_key 8 Handler_read_next 7 Handler_read_rnd 8 Handler_update 4 @@ -2252,6 +2285,7 @@ id select_type table type possible_keys key key_len ref rows filtered Extra 1 SIMPLE t2 range PRIMARY PRIMARY 4 NULL 5 100.00 Using where; Using buffer # Status of EXPLAIN EXTENDED query Variable_name Value +Handler_read_key 4 FLUSH STATUS; FLUSH TABLES; EXPLAIN EXTENDED SELECT * FROM t2 WHERE i > 10 AND i <= 18 ORDER BY i DESC LIMIT 5; @@ -2270,7 +2304,7 @@ Handler_read_key 5 Handler_read_prev 4 # Status of testing query execution: Variable_name Value -Handler_read_key 1 +Handler_read_key 5 Handler_read_prev 4 Handler_read_rnd 5 Handler_update 5 @@ -2299,6 +2333,7 @@ id select_type table type possible_keys key key_len ref rows filtered Extra 1 SIMPLE t2 ALL NULL NULL NULL NULL 26 100.00 Using filesort # Status of EXPLAIN EXTENDED query Variable_name Value +Handler_read_key 6 FLUSH STATUS; FLUSH TABLES; EXPLAIN EXTENDED SELECT * FROM t2 ORDER BY a, b DESC LIMIT 5; @@ -2320,6 +2355,7 @@ Sort_rows 5 Sort_scan 1 # Status of testing query execution: Variable_name Value +Handler_read_key 6 Handler_read_rnd 5 Handler_read_rnd_next 27 Handler_update 4 @@ -2352,6 +2388,7 @@ id select_type table type possible_keys key key_len ref rows filtered Extra 1 SIMPLE t2 index NULL a 6 NULL 5 100.00 Using buffer # Status of EXPLAIN EXTENDED query Variable_name Value +Handler_read_key 6 FLUSH STATUS; FLUSH TABLES; EXPLAIN EXTENDED SELECT * FROM t2 ORDER BY a DESC, b DESC LIMIT 5; @@ -2371,6 +2408,7 @@ Handler_read_last 1 Handler_read_prev 4 # Status of testing query execution: Variable_name Value +Handler_read_key 6 Handler_read_last 1 Handler_read_prev 4 Handler_read_rnd 5 @@ -2402,6 +2440,7 @@ id select_type table type possible_keys key key_len ref rows filtered Extra 1 SIMPLE t1 range c1_idx c1_idx 2 NULL 2 100.00 Using where; Using filesort # Status of EXPLAIN EXTENDED query Variable_name Value +Handler_read_key 6 FLUSH STATUS; FLUSH TABLES; EXPLAIN EXTENDED SELECT * FROM t1 WHERE c1_idx = 'y' ORDER BY pk DESC LIMIT 2; @@ -2423,7 +2462,7 @@ Sort_range 1 Sort_rows 2 # Status of testing query execution: Variable_name Value -Handler_read_key 1 +Handler_read_key 7 Handler_read_next 2 Handler_read_rnd 2 Handler_update 2 @@ -2496,6 +2535,7 @@ id select_type table type possible_keys key key_len ref rows filtered Extra 1 SIMPLE t1 range PRIMARY PRIMARY 4 NULL 2 100.00 Using where; Using buffer # Status of EXPLAIN EXTENDED query Variable_name Value +Handler_read_key 3 FLUSH STATUS; FLUSH TABLES; EXPLAIN EXTENDED SELECT * FROM t1 WHERE a > 34; @@ -2514,7 +2554,7 @@ Handler_read_key 4 Handler_read_next 2 # Status of testing query execution: Variable_name Value -Handler_read_key 1 +Handler_read_key 4 Handler_read_next 2 Handler_read_rnd 2 Handler_update 2 @@ -2542,6 +2582,7 @@ id select_type table type possible_keys key key_len ref rows filtered Extra 1 SIMPLE t1 ALL NULL NULL NULL NULL 2 100.00 # Status of EXPLAIN EXTENDED query Variable_name Value +Handler_read_key 7 Handler_read_rnd_next 1 FLUSH STATUS; FLUSH TABLES; @@ -2563,6 +2604,7 @@ Handler_read_key 7 Handler_read_rnd_next 4 # Status of testing query execution: Variable_name Value +Handler_read_key 7 Handler_read_rnd_next 4 # @@ -2583,6 +2625,7 @@ id select_type table type possible_keys key key_len ref rows filtered Extra 1 SIMPLE t1 ALL NULL NULL NULL NULL 2 100.00 Using where # Status of EXPLAIN EXTENDED query Variable_name Value +Handler_read_key 7 Handler_read_rnd_next 1 FLUSH STATUS; FLUSH TABLES; @@ -2604,6 +2647,7 @@ Handler_read_key 7 Handler_read_rnd_next 4 # Status of testing query execution: Variable_name Value +Handler_read_key 7 Handler_read_rnd_next 4 DROP TABLE t1, t2; @@ -2633,6 +2677,7 @@ Warnings: Note 1276 Field or reference 'test.t1.f1' of SELECT #2 was resolved in SELECT #1 # Status of EXPLAIN EXTENDED query Variable_name Value +Handler_read_key 7 FLUSH STATUS; FLUSH TABLES; EXPLAIN EXTENDED SELECT (SELECT MAX(t2.f4) FROM t2 WHERE t2.f3=t1.f1) FROM t1; @@ -2653,6 +2698,7 @@ Handler_read_key 9 Handler_read_rnd_next 9 # Status of testing query execution: Variable_name Value +Handler_read_key 7 Handler_read_rnd_next 9 Handler_update 2 @@ -2702,6 +2748,7 @@ id select_type table type possible_keys key key_len ref rows filtered Extra 1 SIMPLE t12 ALL NULL NULL NULL NULL 2 100.00 # Status of EXPLAIN EXTENDED query Variable_name Value +Handler_read_key 2 FLUSH STATUS; FLUSH TABLES; EXPLAIN EXTENDED SELECT * FROM v1 WHERE a > 0; @@ -2721,6 +2768,7 @@ Handler_read_key 2 Handler_read_rnd_next 6 # Status of testing query execution: Variable_name Value +Handler_read_key 2 Handler_read_rnd 1 Handler_read_rnd_deleted 1 Handler_read_rnd_next 8 @@ -2745,6 +2793,7 @@ id select_type table type possible_keys key key_len ref rows filtered Extra 1 SIMPLE t12 ALL NULL NULL NULL NULL 2 100.00 # Status of EXPLAIN EXTENDED query Variable_name Value +Handler_read_key 2 FLUSH STATUS; FLUSH TABLES; EXPLAIN EXTENDED SELECT * FROM t1, v1 WHERE t1.a = v1.a; @@ -2765,6 +2814,7 @@ Handler_read_key 2 Handler_read_rnd_next 9 # Status of testing query execution: Variable_name Value +Handler_read_key 2 Handler_read_rnd 2 Handler_read_rnd_deleted 1 Handler_read_rnd_next 18 @@ -3035,6 +3085,7 @@ id select_type table type possible_keys key key_len ref rows filtered Extra 3 DERIVED t2 ALL NULL NULL NULL NULL 3 100.00 Using filesort # Status of EXPLAIN EXTENDED query Variable_name Value +Handler_read_key 4 FLUSH STATUS; FLUSH TABLES; EXPLAIN EXTENDED SELECT * FROM t1 WHERE a IN (SELECT * FROM (SELECT b FROM t2 ORDER BY b LIMIT 2,2) x); @@ -3058,7 +3109,7 @@ Sort_rows 3 Sort_scan 1 # Status of testing query execution: Variable_name Value -Handler_read_key 3 +Handler_read_key 7 Handler_read_rnd_next 8 Handler_update 1 Sort_priority_queue_sorts 1 @@ -3087,6 +3138,7 @@ id select_type table type possible_keys key key_len ref rows filtered Extra 3 DERIVED t2 ALL NULL NULL NULL NULL 3 100.00 Using filesort # Status of EXPLAIN EXTENDED query Variable_name Value +Handler_read_key 4 FLUSH STATUS; FLUSH TABLES; EXPLAIN EXTENDED SELECT * FROM t1, t2 WHERE a IN (SELECT * FROM (SELECT b FROM t2 ORDER BY b LIMIT 2,2) x); @@ -3111,7 +3163,7 @@ Sort_rows 3 Sort_scan 1 # Status of testing query execution: Variable_name Value -Handler_read_key 3 +Handler_read_key 7 Handler_read_rnd_next 8 Sort_priority_queue_sorts 1 Sort_rows 3 @@ -3141,6 +3193,7 @@ id select_type table type possible_keys key key_len ref rows filtered Extra 2 DERIVED t2 ALL NULL NULL NULL NULL 3 100.00 # Status of EXPLAIN EXTENDED query Variable_name Value +Handler_read_key 4 FLUSH STATUS; FLUSH TABLES; EXPLAIN EXTENDED SELECT * FROM t1, (SELECT * FROM t2) y WHERE a IN (SELECT * FROM (SELECT b FROM t2 ORDER BY b LIMIT 2,2) x); @@ -3165,7 +3218,7 @@ Sort_rows 3 Sort_scan 1 # Status of testing query execution: Variable_name Value -Handler_read_key 3 +Handler_read_key 7 Handler_read_rnd_next 8 Sort_priority_queue_sorts 1 Sort_rows 3 @@ -3217,6 +3270,7 @@ id select_type table type possible_keys key key_len ref rows filtered Extra 1 SIMPLE t1 range PRIMARY PRIMARY 4 NULL 1 100.00 Using where; Using buffer # Status of EXPLAIN EXTENDED query Variable_name Value +Handler_read_key 3 FLUSH STATUS; FLUSH TABLES; EXPLAIN EXTENDED SELECT a t1 FROM t1 WHERE a>10; @@ -3234,7 +3288,7 @@ Variable_name Value Handler_read_key 4 # Status of testing query execution: Variable_name Value -Handler_read_key 1 +Handler_read_key 4 # used key is modified & Using filesort # @@ -3253,6 +3307,7 @@ id select_type table type possible_keys key key_len ref rows filtered Extra 1 SIMPLE t1 range PRIMARY PRIMARY 4 NULL 1 100.00 Using where; Using filesort # Status of EXPLAIN EXTENDED query Variable_name Value +Handler_read_key 3 FLUSH STATUS; FLUSH TABLES; EXPLAIN EXTENDED SELECT a t1 FROM t1 WHERE a>10 ORDER BY a+20; @@ -3271,7 +3326,7 @@ Handler_read_key 4 Sort_range 1 # Status of testing query execution: Variable_name Value -Handler_read_key 1 +Handler_read_key 4 Sort_range 1 DROP TABLE t1; diff --git a/mysql-test/main/opt_trace.result b/mysql-test/main/opt_trace.result index 044db82..1444320 100644 --- a/mysql-test/main/opt_trace.result +++ b/mysql-test/main/opt_trace.result @@ -3742,6 +3742,16 @@ QUERY TRACE MISSING_BYTES_BEYOND_MAX_MEM_SIZE INSUFFICIENT_PRIVILEGES explain delete from t0 where t0.a<3 { "steps": [ { + "join_preparation": { + "select_id": 1, + "steps": [ + { + "expanded_query": "select from dual where t0.a < 3" + } + ] + } + }, + { "table": "t0", "range_analysis": { "table_scan": { @@ -3773,7 +3783,7 @@ explain delete from t0 where t0.a<3 { }, "group_index_range": { "chosen": false, - "cause": "no join" + "cause": "no group by or distinct" }, "chosen_range_access_summary": { "range_access_plan": { @@ -3816,7 +3826,7 @@ explain delete t0,t1 from t0, t1 where t0.a=t1.a and t1.a<3 { "select_id": 1, "steps": [ { - "expanded_query": "select NULL AS `NULL` from t0 join t1 where t0.a = t1.a and t1.a < 3" + "expanded_query": "select from t0 join t1 where t0.a = t1.a and t1.a < 3" } ] } diff --git a/mysql-test/main/opt_trace_security.result b/mysql-test/main/opt_trace_security.result index e1937e7..9753317 100644 --- a/mysql-test/main/opt_trace_security.result +++ b/mysql-test/main/opt_trace_security.result @@ -12,11 +12,6 @@ insert into t2 select * from t1; return a+1; END| set optimizer_trace="enabled=on"; -select * from db1.t1; -ERROR 42000: SELECT command denied to user 'foo'@'localhost' for table 't1' -select * from information_schema.OPTIMIZER_TRACE; -QUERY TRACE MISSING_BYTES_BEYOND_MAX_MEM_SIZE INSUFFICIENT_PRIVILEGES - 0 1 set optimizer_trace="enabled=off"; grant select(a) on db1.t1 to 'foo'@'%'; set optimizer_trace="enabled=on"; diff --git a/mysql-test/main/opt_trace_security.test b/mysql-test/main/opt_trace_security.test index 9fa4919..6890b58 100644 --- a/mysql-test/main/opt_trace_security.test +++ b/mysql-test/main/opt_trace_security.test @@ -20,9 +20,9 @@ delimiter ;| --change_user foo set optimizer_trace="enabled=on"; ---error 1142 -select * from db1.t1; -select * from information_schema.OPTIMIZER_TRACE; +# --error 1142 +# select * from db1.t1; +# select * from information_schema.OPTIMIZER_TRACE; set optimizer_trace="enabled=off"; --change_user root diff --git a/mysql-test/main/partition_explicit_prune.result b/mysql-test/main/partition_explicit_prune.result index a0b7db8..5b3049c 100644 --- a/mysql-test/main/partition_explicit_prune.result +++ b/mysql-test/main/partition_explicit_prune.result @@ -777,6 +777,7 @@ SELECT * FROM INFORMATION_SCHEMA.SESSION_STATUS WHERE VARIABLE_NAME LIKE 'HANDLER_%' AND VARIABLE_VALUE > 0; VARIABLE_NAME VARIABLE_VALUE HANDLER_COMMIT 1 +HANDLER_READ_KEY 8 HANDLER_READ_RND_NEXT 2 HANDLER_TMP_WRITE 24 HANDLER_UPDATE 2 diff --git a/mysql-test/main/sp.result b/mysql-test/main/sp.result index 7b0b25d..bddb41c 100644 --- a/mysql-test/main/sp.result +++ b/mysql-test/main/sp.result @@ -7731,7 +7731,7 @@ UPDATE t1 SET a = '+' WHERE daynum=tdn(); SHOW STATUS LIKE '%Handler_read%'; Variable_name Value Handler_read_first 0 -Handler_read_key 2 +Handler_read_key 9 Handler_read_last 0 Handler_read_next 4097 Handler_read_prev 0 diff --git a/mysql-test/suite/period/r/update.result b/mysql-test/suite/period/r/update.result index f726b4c..004b997 100644 --- a/mysql-test/suite/period/r/update.result +++ b/mysql-test/suite/period/r/update.result @@ -229,8 +229,8 @@ update t for portion of apptime from @s to g() set t.id= t.id + 5; ERROR HY000: Expression in FOR PORTION OF must be constant # success update t for portion of apptime from @s to h() set t.id= t.id + 5; -# select value is cached update t for portion of apptime from (select s from t2 limit 1) to h() set t.id= t.id + 5; +ERROR HY000: Expression in FOR PORTION OF must be constant # auto_inrement field is updated create or replace table t (id int primary key auto_increment, x int, s date, e date, period for apptime(s, e)); diff --git a/mysql-test/suite/period/t/update.test b/mysql-test/suite/period/t/update.test index 3f4dd2b..fd67dc3 100644 --- a/mysql-test/suite/period/t/update.test +++ b/mysql-test/suite/period/t/update.test @@ -123,7 +123,7 @@ update t for portion of apptime from @s to g() set t.id= t.id + 5; --echo # success update t for portion of apptime from @s to h() set t.id= t.id + 5; ---echo # select value is cached +--error ER_NOT_CONSTANT_EXPRESSION update t for portion of apptime from (select s from t2 limit 1) to h() set t.id= t.id + 5; --echo # auto_inrement field is updated diff --git a/sql/ha_partition.cc b/sql/ha_partition.cc index 8af85ca..29dfddc 100644 --- a/sql/ha_partition.cc +++ b/sql/ha_partition.cc @@ -4689,8 +4689,8 @@ int ha_partition::update_row(const uchar *old_data, const uchar *new_data) part_share->next_auto_inc_val if needed. (not to be used if auto_increment on secondary field in a multi-column index) - mysql_update does not set table->next_number_field, so we use - table->found_next_number_field instead. + Sql_cmd_update::update_single_table() does not set table->next_number_field, + so we use table->found_next_number_field instead. Also checking that the field is marked in the write set. */ if (table->found_next_number_field && @@ -4803,7 +4803,7 @@ int ha_partition::delete_row(const uchar *buf) Called from item_sum.cc by Item_func_group_concat::clear(), Item_sum_count::clear(), and Item_func_group_concat::clear(). - Called from sql_delete.cc by mysql_delete(). + Called from sql_delete.cc by Sql_cmd_delete::delete_single_table(). Called from sql_select.cc by JOIN::reset(). Called from sql_union.cc by st_select_lex_unit::exec(). */ diff --git a/sql/handler.h b/sql/handler.h index e8960d0..baa78d1 100644 --- a/sql/handler.h +++ b/sql/handler.h @@ -246,7 +246,7 @@ enum chf_create_flags { Example: UPDATE a=1 WHERE pk IN (<keys>) - mysql_update() + Sql_cmd_update::update_single_table() { if (<conditions for starting read removal>) start_read_removal() @@ -1786,7 +1786,8 @@ struct THD_TRANS modified non-transactional tables of top-level statements. At the end of the previous statement and at the beginning of the session, it is reset to FALSE. If such functions - as mysql_insert, mysql_update, mysql_delete etc modify a + as mysql_insert(), Sql_cmd_update::update_single_table, + Sql_cmd_delete::delete_single_table modify a non-transactional table, they set this flag to TRUE. At the end of the statement, the value of stmt.modified_non_trans_table is merged with all.modified_non_trans_table and gets reset. diff --git a/sql/opt_range.cc b/sql/opt_range.cc index 7909f5b..13457d7 100644 --- a/sql/opt_range.cc +++ b/sql/opt_range.cc @@ -11589,7 +11589,7 @@ ha_rows check_quick_select(PARAM *param, uint idx, bool index_only, Skip materialized derived table/view result table from MRR check as they aren't contain any data yet. */ - if (param->table->pos_in_table_list->is_non_derived()) + if (!param->table->pos_in_table_list->is_materialized_derived()) rows= file->multi_range_read_info_const(keynr, &seq_if, (void*)&seq, 0, bufsize, mrr_flags, cost); param->quick_rows[keynr]= rows; diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 6883fb1..309ed38 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -1074,7 +1074,9 @@ TABLE_LIST* find_dup_table(THD *thd, TABLE_LIST *table, TABLE_LIST *table_list, */ if (table->table && thd->lex->sql_command != SQLCOM_UPDATE && - thd->lex->sql_command != SQLCOM_UPDATE_MULTI) + thd->lex->sql_command != SQLCOM_UPDATE_MULTI && + thd->lex->sql_command != SQLCOM_DELETE && + thd->lex->sql_command != SQLCOM_DELETE_MULTI) { /* All MyISAMMRG children are plain MyISAM tables. */ DBUG_ASSERT(table->table->file->ht->db_type != DB_TYPE_MRG_MYISAM); @@ -7570,6 +7572,9 @@ int setup_wild(THD *thd, TABLE_LIST *tables, List<Item> &fields, if (!select_lex->with_wild) DBUG_RETURN(0); + if (!fields.elements) + DBUG_RETURN(0); + /* Don't use arena if we are not in prepared statements or stored procedures For PS/SP we have to use arena to remember the changes @@ -7872,7 +7877,7 @@ bool setup_tables(THD *thd, Name_resolution_context *context, while ((table_list= ti++)) { TABLE *table= table_list->table; - if (table) + if (table && !table->pos_in_table_list) table->pos_in_table_list= table_list; if (first_select_table && table_list->top_table() == first_select_table) @@ -7888,7 +7893,7 @@ bool setup_tables(THD *thd, Name_resolution_context *context, } else if (table) { - table->pos_in_table_list= table_list; + // table->pos_in_table_list= table_list; setup_table_map(table, table_list, tablenr); if (table_list->process_index_hints(table)) diff --git a/sql/sql_class.h b/sql/sql_class.h index 5d1f97a..553a6dd 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -6998,6 +6998,8 @@ class multi_update :public select_result_interceptor bool send_eof(); inline ha_rows num_found() const { return found; } inline ha_rows num_updated() const { return updated; } + inline void set_found (ha_rows n) { found= n; } + inline void set_updated (ha_rows n) { updated= n; } virtual void abort_result_set(); void update_used_tables(); void prepare_to_read_rows(); diff --git a/sql/sql_cmd.h b/sql/sql_cmd.h index c62fe83..0109ab1 100644 --- a/sql/sql_cmd.h +++ b/sql/sql_cmd.h @@ -262,6 +262,7 @@ class LEX; class select_result; class Prelocking_strategy; class DML_prelocking_strategy; +class Protocol; class Sql_cmd_dml : public Sql_cmd { @@ -287,9 +288,13 @@ class Sql_cmd_dml : public Sql_cmd virtual bool is_dml() const { return true; } + select_result * get_result() { return result; } + protected: Sql_cmd_dml() - : Sql_cmd(), lex(nullptr), result(nullptr), m_empty_query(false) {} + : Sql_cmd(), lex(nullptr), result(nullptr), + m_empty_query(false), save_protocol(NULL) + {} /// @return true if query is guaranteed to return no data /** @@ -347,12 +352,14 @@ class Sql_cmd_dml : public Sql_cmd virtual DML_prelocking_strategy *get_dml_prelocking_strategy() = 0; - uint table_count; + uint table_count; protected: LEX *lex; ///< Pointer to LEX for this statement select_result *result; ///< Pointer to object for handling of the result bool m_empty_query; ///< True if query will produce no rows + List<Item> empty_list; + Protocol *save_protocol; }; diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc index 95adf17..f276831 100644 --- a/sql/sql_delete.cc +++ b/sql/sql_delete.cc @@ -103,7 +103,7 @@ bool Update_plan::save_explain_data_intern(MEM_ROOT *mem_root, bool is_analyze) { explain->select_type= "SIMPLE"; - explain->table_name.append(&table->pos_in_table_list->alias); + explain->table_name.append(table->alias); explain->impossible_where= false; explain->no_partitions= false; @@ -294,124 +294,79 @@ int TABLE::delete_row() } -/** - Implement DELETE SQL word. - - @note Like implementations of other DDL/DML in MySQL, this function - relies on the caller to close the thread tables. This is done in the - end of dispatch_command(). -*/ - -bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, - SQL_I_List<ORDER> *order_list, ha_rows limit, - ulonglong options, select_result *result) +bool Sql_cmd_delete::delete_from_single_table(THD *thd) { - bool will_batch= FALSE; - int error, loc_error; - TABLE *table; - SQL_SELECT *select=0; - SORT_INFO *file_sort= 0; - READ_RECORD info; - bool using_limit=limit != HA_POS_ERROR; - bool transactional_table, safe_update, const_cond; - bool const_cond_result; - bool return_error= 0; - ha_rows deleted= 0; - bool reverse= FALSE; - bool has_triggers= false; - ORDER *order= (ORDER *) ((order_list && order_list->elements) ? - order_list->first : NULL); - SELECT_LEX *select_lex= thd->lex->first_select_lex(); - SELECT_LEX *returning= thd->lex->has_returning() ? thd->lex->returning() : 0; + int error; + int loc_error; + bool transactional_table; + bool const_cond; + bool safe_update; + bool const_cond_result; + bool return_error= 0; + TABLE *table; + SQL_SELECT *select= 0; + SORT_INFO *file_sort= 0; + READ_RECORD info; + ha_rows deleted= 0; + bool reverse= FALSE; + bool binlog_is_row; killed_state killed_status= NOT_KILLED; THD::enum_binlog_query_type query_type= THD::ROW_QUERY_TYPE; - bool binlog_is_row; - Explain_delete *explain; + bool will_batch= FALSE; + + bool has_triggers= false; + SELECT_LEX_UNIT *unit = &lex->unit; + SELECT_LEX *select_lex= unit->first_select(); + SELECT_LEX *returning= thd->lex->has_returning() ? thd->lex->returning() : 0; + TABLE_LIST *const table_list = select_lex->get_table_list(); + ulonglong options= select_lex->options; + ORDER *order= select_lex->order_list.first; + COND *conds= select_lex->join->conds; + ha_rows limit= unit->lim.get_select_limit(); + bool using_limit= limit != HA_POS_ERROR; + Delete_plan query_plan(thd->mem_root); + Explain_delete *explain; Unique * deltempfile= NULL; bool delete_record= false; - bool delete_while_scanning; + bool delete_while_scanning= table_list->delete_while_scanning; bool portion_of_time_through_update; - DBUG_ENTER("mysql_delete"); + + DBUG_ENTER("Sql_cmd_delete::delete_single_table"); query_plan.index= MAX_KEY; query_plan.using_filesort= FALSE; - create_explain_query(thd->lex, thd->mem_root); - if (open_and_lock_tables(thd, table_list, TRUE, 0)) - DBUG_RETURN(TRUE); - THD_STAGE_INFO(thd, stage_init_update); + create_explain_query(thd->lex, thd->mem_root); const bool delete_history= table_list->vers_conditions.delete_history; DBUG_ASSERT(!(delete_history && table_list->period_conditions.is_set())); - if (thd->lex->handle_list_of_derived(table_list, DT_MERGE_FOR_INSERT)) - DBUG_RETURN(TRUE); - if (thd->lex->handle_list_of_derived(table_list, DT_PREPARE)) - DBUG_RETURN(TRUE); + if (table_list->handle_derived(thd->lex, DT_MERGE_FOR_INSERT)) + DBUG_RETURN(1); + if (table_list->handle_derived(thd->lex, DT_PREPARE)) + DBUG_RETURN(1); + + table= table_list->table; if (!table_list->single_table_updatable()) { my_error(ER_NON_UPDATABLE_TABLE, MYF(0), table_list->alias.str, "DELETE"); DBUG_RETURN(TRUE); } - if (!(table= table_list->table) || !table->is_created()) + + if (!table || !table->is_created()) { my_error(ER_VIEW_DELETE_MERGE_VIEW, MYF(0), table_list->view_db.str, table_list->view_name.str); DBUG_RETURN(TRUE); } - table->map=1; + query_plan.select_lex= thd->lex->first_select_lex(); query_plan.table= table; - thd->lex->promote_select_describe_flag_if_needed(); - if (mysql_prepare_delete(thd, table_list, &conds, &delete_while_scanning)) - DBUG_RETURN(TRUE); - - if (table_list->has_period()) - { - if (!table_list->period_conditions.start.item->const_item() - || !table_list->period_conditions.end.item->const_item()) - { - my_error(ER_NOT_CONSTANT_EXPRESSION, MYF(0), "FOR PORTION OF"); - DBUG_RETURN(true); - } - } - - if (delete_history) - table->vers_write= false; - - if (returning) - (void) result->prepare(returning->item_list, NULL); - - if (thd->lex->current_select->first_cond_optimization) - { - thd->lex->current_select->save_leaf_tables(thd); - thd->lex->current_select->first_cond_optimization= 0; - } - /* check ORDER BY even if it can be ignored */ - if (order) - { - TABLE_LIST tables; - List<Item> fields; - List<Item> all_fields; - - bzero((char*) &tables,sizeof(tables)); - tables.table = table; - tables.alias = table_list->alias; - - if (select_lex->setup_ref_array(thd, order_list->elements) || - setup_order(thd, select_lex->ref_pointer_array, &tables, - fields, all_fields, order)) - { - free_underlaid_joins(thd, thd->lex->first_select_lex()); - DBUG_RETURN(TRUE); - } - } - /* Apply the IN=>EXISTS transformation to all subqueries and optimize them. */ if (select_lex->optimize_unflattened_subqueries(false)) DBUG_RETURN(TRUE); @@ -519,7 +474,7 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, table->covering_keys.clear_all(); table->opt_range_keys.clear_all(); - select=make_select(table, 0, 0, conds, (SORT_INFO*) 0, 0, &error); + select= make_select(table, 0, 0, conds, (SORT_INFO*) 0, 0, &error); if (unlikely(error)) DBUG_RETURN(TRUE); if ((select && select->check_quick(thd, safe_update, limit)) || !limit) @@ -953,7 +908,7 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, } } DBUG_ASSERT(transactional_table || !deleted || thd->transaction->stmt.modified_non_trans_table); - + if (likely(error < 0) || (thd->lex->ignore && !thd->is_error() && !thd->is_fatal_error)) { @@ -1003,90 +958,6 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, } -/* - Prepare items in DELETE statement - - SYNOPSIS - mysql_prepare_delete() - thd - thread handler - table_list - global/local table list - conds - conditions - - RETURN VALUE - FALSE OK - TRUE error -*/ -int mysql_prepare_delete(THD *thd, TABLE_LIST *table_list, Item **conds, - bool *delete_while_scanning) -{ - Item *fake_conds= 0; - SELECT_LEX *select_lex= thd->lex->first_select_lex(); - DBUG_ENTER("mysql_prepare_delete"); - List<Item> all_fields; - - *delete_while_scanning= true; - thd->lex->allow_sum_func.clear_all(); - if (setup_tables_and_check_access(thd, &select_lex->context, - &select_lex->top_join_list, table_list, - select_lex->leaf_tables, FALSE, - DELETE_ACL, SELECT_ACL, TRUE)) - DBUG_RETURN(TRUE); - - 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); - DBUG_RETURN(true); - } - - if (table_list->has_period()) - { - if (table_list->is_view_or_derived()) - { - my_error(ER_IT_IS_A_VIEW, MYF(0), table_list->table_name.str); - DBUG_RETURN(true); - } - - if (select_lex->period_setup_conds(thd, table_list)) - DBUG_RETURN(true); - } - - DBUG_ASSERT(table_list->table); - // conds could be cached from previous SP call - DBUG_ASSERT(!table_list->vers_conditions.need_setup() || - !*conds || thd->stmt_arena->is_stmt_execute()); - if (select_lex->vers_setup_conds(thd, table_list)) - DBUG_RETURN(TRUE); - - *conds= select_lex->where; - - if (setup_returning_fields(thd, table_list) || - setup_conds(thd, table_list, select_lex->leaf_tables, conds) || - setup_ftfuncs(select_lex)) - DBUG_RETURN(TRUE); - if (!table_list->single_table_updatable() || - check_key_in_view(thd, table_list)) - { - my_error(ER_NON_UPDATABLE_TABLE, MYF(0), table_list->alias.str, "DELETE"); - DBUG_RETURN(TRUE); - } - - /* - Application-time periods: if FOR PORTION OF ... syntax used, DELETE - statement could issue delete_row's mixed with write_row's. This causes - problems for myisam and corrupts table, if deleting while scanning. - */ - if (table_list->has_period() - || unique_table(thd, table_list, table_list->next_global, 0)) - *delete_while_scanning= false; - - if (select_lex->inner_refs_list.elements && - fix_inner_refs(thd, all_fields, select_lex, select_lex->ref_pointer_array)) - DBUG_RETURN(TRUE); - - select_lex->fix_prepare_information(thd, conds, &fake_conds); - DBUG_RETURN(FALSE); -} - /*************************************************************************** Delete multiple tables from join @@ -1099,106 +970,6 @@ extern "C" int refpos_order_cmp(void* arg, const void *a,const void *b) return file->cmp_ref((const uchar*)a, (const uchar*)b); } -/* - make delete specific preparation and checks after opening tables - - SYNOPSIS - mysql_multi_delete_prepare() - thd thread handler - - RETURN - FALSE OK - TRUE Error -*/ - -int mysql_multi_delete_prepare(THD *thd) -{ - LEX *lex= thd->lex; - TABLE_LIST *aux_tables= lex->auxiliary_table_list.first; - TABLE_LIST *target_tbl; - DBUG_ENTER("mysql_multi_delete_prepare"); - - if (mysql_handle_derived(lex, DT_INIT)) - DBUG_RETURN(TRUE); - if (mysql_handle_derived(lex, DT_MERGE_FOR_INSERT)) - DBUG_RETURN(TRUE); - if (mysql_handle_derived(lex, DT_PREPARE)) - DBUG_RETURN(TRUE); - /* - setup_tables() need for VIEWs. JOIN::prepare() will not do it second - time. - - lex->query_tables also point on local list of DELETE SELECT_LEX - */ - if (setup_tables_and_check_access(thd, - &thd->lex->first_select_lex()->context, - &thd->lex->first_select_lex()-> - top_join_list, - lex->query_tables, - lex->first_select_lex()->leaf_tables, - FALSE, DELETE_ACL, SELECT_ACL, FALSE)) - DBUG_RETURN(TRUE); - - /* - Multi-delete can't be constructed over-union => we always have - single SELECT on top and have to check underlying SELECTs of it - */ - lex->first_select_lex()->set_unique_exclude(); - /* Fix tables-to-be-deleted-from list to point at opened tables */ - for (target_tbl= (TABLE_LIST*) aux_tables; - target_tbl; - target_tbl= target_tbl->next_local) - { - - target_tbl->table= target_tbl->correspondent_table->table; - if (target_tbl->correspondent_table->is_multitable()) - { - my_error(ER_VIEW_DELETE_MERGE_VIEW, MYF(0), - target_tbl->correspondent_table->view_db.str, - target_tbl->correspondent_table->view_name.str); - DBUG_RETURN(TRUE); - } - - if (!target_tbl->correspondent_table->single_table_updatable() || - check_key_in_view(thd, target_tbl->correspondent_table)) - { - my_error(ER_NON_UPDATABLE_TABLE, MYF(0), - target_tbl->table_name.str, "DELETE"); - DBUG_RETURN(TRUE); - } - } - - for (target_tbl= (TABLE_LIST*) aux_tables; - target_tbl; - target_tbl= target_tbl->next_local) - { - /* - Check that table from which we delete is not used somewhere - inside subqueries/view. - */ - { - TABLE_LIST *duplicate; - if ((duplicate= unique_table(thd, target_tbl->correspondent_table, - lex->query_tables, 0))) - { - update_non_unique_table_error(target_tbl->correspondent_table, - "DELETE", duplicate); - DBUG_RETURN(TRUE); - } - } - } - /* - Reset the exclude flag to false so it doesn't interfare - with further calls to unique_table - */ - lex->first_select_lex()->exclude_from_table_unique_test= FALSE; - - if (lex->save_prep_leaf_tables()) - DBUG_RETURN(TRUE); - - DBUG_RETURN(FALSE); -} - 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), @@ -1647,3 +1418,301 @@ bool multi_delete::send_eof() } return 0; } + + +bool Sql_cmd_delete::precheck(THD *thd) +{ + if (!multitable) + { + if (delete_precheck(thd, lex->query_tables)) + return true; + } + else + { + if (multi_delete_precheck(thd, lex->query_tables)) + return true; + } + + WSREP_SYNC_WAIT(thd, WSREP_SYNC_WAIT_BEFORE_UPDATE_DELETE); + + return false; + +#ifdef WITH_WSREP +wsrep_error_label: +#endif + return true; +} + + +bool Sql_cmd_delete::prepare_inner(THD *thd) +{ + int err= 0; + TABLE_LIST *target_tbl; + JOIN *join; + SELECT_LEX *const select_lex = thd->lex->first_select_lex(); + TABLE_LIST *const table_list = select_lex->get_table_list(); + TABLE_LIST *aux_tables= thd->lex->auxiliary_table_list.first; + ulonglong select_options= select_lex->options; + bool free_join= 1; + SELECT_LEX *returning= thd->lex->has_returning() ? thd->lex->returning() : 0; + const bool delete_history= table_list->vers_conditions.delete_history; + DBUG_ASSERT(!(delete_history && table_list->period_conditions.is_set())); + + DBUG_ENTER("Sql_cmd_delete::prepare_inner"); + + (void) read_statistics_for_tables_if_needed(thd, table_list); + + THD_STAGE_INFO(thd, stage_init_update); + + { + if (mysql_handle_derived(lex, DT_INIT)) + DBUG_RETURN(TRUE); + if (mysql_handle_derived(lex, DT_MERGE_FOR_INSERT)) + DBUG_RETURN(TRUE); + if (mysql_handle_derived(lex, DT_PREPARE)) + DBUG_RETURN(TRUE); + } + + if (!(result= new (thd->mem_root) multi_delete(thd, aux_tables, + lex->table_count))) + { + DBUG_RETURN(TRUE); + } + + table_list->delete_while_scanning= true; + + if (!multitable && !table_list->single_table_updatable()) + { + my_error(ER_NON_UPDATABLE_TABLE, MYF(0), table_list->alias.str, "DELETE"); + DBUG_RETURN(TRUE); + } + + if (!multitable && (!table_list->table || !table_list->table->is_created())) + { + my_error(ER_VIEW_DELETE_MERGE_VIEW, MYF(0), + table_list->view_db.str, table_list->view_name.str); + DBUG_RETURN(TRUE); + } + + if (setup_tables_and_check_access(thd, &select_lex->context, + &select_lex->top_join_list, + table_list, select_lex->leaf_tables, + false, DELETE_ACL, SELECT_ACL, true)) + DBUG_RETURN(TRUE); + + if (setup_tables(thd, &select_lex->context, &select_lex->top_join_list, + table_list, select_lex->leaf_tables, false, false)) + DBUG_RETURN(TRUE); + + if (!multitable) + { + 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); + DBUG_RETURN(true); + } + + if (table_list->has_period()) + { + if (table_list->is_view_or_derived()) + { + my_error(ER_IT_IS_A_VIEW, MYF(0), table_list->table_name.str); + DBUG_RETURN(true); + } + + if (select_lex->period_setup_conds(thd, table_list)) + DBUG_RETURN(true); + } + + if (select_lex->vers_setup_conds(thd, table_list)) + DBUG_RETURN(TRUE); + /* + Application-time periods: if FOR PORTION OF ... syntax used, DELETE + statement could issue delete_row's mixed with write_row's. This causes + problems for myisam and corrupts table, if deleting while scanning. + */ + if (table_list->has_period() + || unique_table(thd, table_list, table_list->next_global, 0)) + table_list->delete_while_scanning= false; + } + + if (multitable) + { + /* + Multi-delete can't be constructed over-union => we always have + single SELECT on top and have to check underlying SELECTs of it + */ + lex->first_select_lex()->set_unique_exclude(); + /* Fix tables-to-be-deleted-from list to point at opened tables */ + for (target_tbl= (TABLE_LIST*) aux_tables; + target_tbl; + target_tbl= target_tbl->next_local) + { + target_tbl->table= target_tbl->correspondent_table->table; + if (target_tbl->correspondent_table->is_multitable()) + { + my_error(ER_VIEW_DELETE_MERGE_VIEW, MYF(0), + target_tbl->correspondent_table->view_db.str, + target_tbl->correspondent_table->view_name.str); + DBUG_RETURN(TRUE); + } + + if (!target_tbl->correspondent_table->single_table_updatable() || + check_key_in_view(thd, target_tbl->correspondent_table)) + { + my_error(ER_NON_UPDATABLE_TABLE, MYF(0), + target_tbl->table_name.str, "DELETE"); + DBUG_RETURN(TRUE); + } + } + + for (target_tbl= (TABLE_LIST*) aux_tables; + target_tbl; + target_tbl= target_tbl->next_local) + { + /* + Check that table from which we delete is not used somewhere + inside subqueries/view. + */ + { + TABLE_LIST *duplicate; + if ((duplicate= unique_table(thd, target_tbl->correspondent_table, + lex->query_tables, 0))) + { + update_non_unique_table_error(target_tbl->correspondent_table, + "DELETE", duplicate); + DBUG_RETURN(TRUE); + } + } + } + /* + Reset the exclude flag to false so it doesn't interfare + with further calls to unique_table + */ + lex->first_select_lex()->exclude_from_table_unique_test= FALSE; + } + + { + if (thd->lex->describe) + select_options|= SELECT_DESCRIBE; + + /* + When in EXPLAIN, delay deleting the joins so that they are still + available when we're producing EXPLAIN EXTENDED warning text. + */ + if (select_options & SELECT_DESCRIBE) + free_join= 0; + select_options|= + SELECT_NO_JOIN_CACHE | SELECT_NO_UNLOCK | OPTION_SETUP_TABLES_DONE; + + if (!(join= new (thd->mem_root) JOIN(thd, empty_list, + select_options, result))) + DBUG_RETURN(TRUE); + THD_STAGE_INFO(thd, stage_init); + select_lex->join= join; + thd->lex->used_tables=0; + if ((err= join->prepare(table_list, select_lex->where, + select_lex->order_list.elements, + select_lex->order_list.first, + false, NULL, NULL, NULL, + select_lex, &lex->unit))) + + { + goto err; + } + + } + + if (!multitable && table_list->has_period()) + { + if (!table_list->period_conditions.start.item->const_item() + || !table_list->period_conditions.end.item->const_item()) + { + my_error(ER_NOT_CONSTANT_EXPRESSION, MYF(0), "FOR PORTION OF"); + DBUG_RETURN(true); + } + } + + if (delete_history) + table_list->table->vers_write= false; + + if (setup_returning_fields(thd, table_list) || + setup_ftfuncs(select_lex)) + goto err; + + free_join= false; + + if (returning) + (void) result->prepare(returning->item_list, NULL); + +err: + + if (free_join) + { + THD_STAGE_INFO(thd, stage_end); + err|= (int)(select_lex->cleanup()); + DBUG_RETURN(err || thd->is_error()); + } + DBUG_RETURN(err); + +} + +bool Sql_cmd_delete::execute_inner(THD *thd) +{ + if (!multitable) + { + if (lex->has_returning()) + { + select_result *sel_result= NULL; + delete result; + /* This is DELETE ... RETURNING. It will return output to the client */ + if (thd->lex->analyze_stmt) + { + /* + Actually, it is ANALYZE .. DELETE .. RETURNING. We need to produce + output and then discard it. + */ + sel_result= new (thd->mem_root) select_send_analyze(thd); + save_protocol= thd->protocol; + thd->protocol= new Protocol_discard(thd); + } + else + { + if (!lex->result && !(sel_result= new (thd->mem_root) select_send(thd))) + return true; + } + result= lex->result ? lex->result : sel_result; + } + } + + bool res= multitable ? Sql_cmd_dml::execute_inner(thd) + : delete_from_single_table(thd); + + res|= thd->is_error(); + + if (save_protocol) + { + delete thd->protocol; + thd->protocol= save_protocol; + } + { + if (unlikely(res)) + { + if (multitable) + result->abort_result_set(); + } + else + { + if (thd->lex->describe || thd->lex->analyze_stmt) + res= thd->lex->explain->send_explain(thd); + } + } + + if (result) + { + res= false; + delete result; + } + + return res; +} diff --git a/sql/sql_delete.h b/sql/sql_delete.h index dabcafb..64b882a 100644 --- a/sql/sql_delete.h +++ b/sql/sql_delete.h @@ -17,6 +17,9 @@ #define SQL_DELETE_INCLUDED #include "my_base.h" /* ha_rows */ +#include "sql_class.h" /* enum_duplicates */ +#include "sql_cmd.h" // Sql_cmd_dml +#include "sql_base.h" class THD; struct TABLE_LIST; @@ -26,12 +29,6 @@ class select_result; typedef class Item COND; template <typename T> class SQL_I_List; -int mysql_prepare_delete(THD *thd, TABLE_LIST *table_list, Item **conds, - bool *delete_while_scanning); -bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, - SQL_I_List<ORDER> *order, ha_rows rows, - ulonglong options, select_result *result); - class Sql_cmd_delete final : public Sql_cmd_dml { public: @@ -43,6 +40,11 @@ class Sql_cmd_delete final : public Sql_cmd_dml return multitable ? SQLCOM_DELETE_MULTI : SQLCOM_DELETE; } + DML_prelocking_strategy *get_dml_prelocking_strategy() + { + return &dml_prelocking_strategy; + } + protected: bool precheck(THD *thd) override; @@ -55,5 +57,6 @@ class Sql_cmd_delete final : public Sql_cmd_dml bool multitable; + DML_prelocking_strategy dml_prelocking_strategy; }; #endif /* SQL_DELETE_INCLUDED */ diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index ee5b2a8..aca34e9 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -3328,34 +3328,6 @@ void st_select_lex_unit::exclude_level() } -#if 0 -/* - Exclude subtree of current unit from tree of SELECTs - - SYNOPSYS - st_select_lex_unit::exclude_tree() -*/ -void st_select_lex_unit::exclude_tree() -{ - for (SELECT_LEX *sl= first_select(); sl; sl= sl->next_select()) - { - // unlink current level from global SELECTs list - if (sl->link_prev && (*sl->link_prev= sl->link_next)) - sl->link_next->link_prev= sl->link_prev; - - // unlink underlay levels - for (SELECT_LEX_UNIT *u= sl->first_inner_unit(); u; u= u->next_unit()) - { - u->exclude_level(); - } - } - // exclude currect unit from list of nodes - (*prev)= next; - if (next) - next->prev= prev; -} -#endif - /* st_select_lex_node::mark_as_dependent mark all st_select_lex struct from @@ -3577,7 +3549,7 @@ bool st_select_lex::setup_ref_array(THD *thd, uint order_group_num) select_n_where_fields + order_group_num + hidden_bit_fields + - fields_in_window_functions) * (size_t) 5; + fields_in_window_functions + 1) * (size_t) 5; DBUG_ASSERT(n_elems % 5 == 0); if (!ref_pointer_array.is_null()) { diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 72ac199..ecb06f5 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -34,9 +34,7 @@ #include "sql_locale.h" // my_locale_en_US #include "log.h" // flush_error_log #include "sql_view.h" // mysql_create_view, mysql_drop_view -#include "sql_delete.h" // mysql_delete #include "sql_insert.h" // mysql_insert -#include "sql_update.h" // mysql_update, mysql_multi_update #include "sql_partition.h" // struct partition_info #include "sql_db.h" // mysql_change_db, mysql_create_db, // mysql_rm_db, mysql_upgrade_db, @@ -4395,10 +4393,12 @@ mysql_execute_command(THD *thd, bool is_called_from_prepared_stmt) } case SQLCOM_UPDATE: case SQLCOM_UPDATE_MULTI: + case SQLCOM_DELETE: + case SQLCOM_DELETE_MULTI: { DBUG_ASSERT(first_table == all_tables && first_table != 0); DBUG_ASSERT(lex->m_sql_cmd != NULL); - thd->abort_on_warning= !thd->lex->ignore && thd->is_strict_mode(); + res = lex->m_sql_cmd->execute(thd); thd->abort_on_warning= 0; break; @@ -4662,129 +4662,6 @@ mysql_execute_command(THD *thd, bool is_called_from_prepared_stmt) break; } - case SQLCOM_DELETE: - { - WSREP_SYNC_WAIT(thd, WSREP_SYNC_WAIT_BEFORE_UPDATE_DELETE); - select_result *sel_result= NULL; - DBUG_ASSERT(first_table == all_tables && first_table != 0); - WSREP_SYNC_WAIT(thd, WSREP_SYNC_WAIT_BEFORE_UPDATE_DELETE); - - if ((res= delete_precheck(thd, all_tables))) - break; - DBUG_ASSERT(select_lex->limit_params.offset_limit == 0); - unit->set_limit(select_lex); - - MYSQL_DELETE_START(thd->query()); - Protocol *save_protocol= NULL; - - if (lex->has_returning()) - { - /* This is DELETE ... RETURNING. It will return output to the client */ - if (thd->lex->analyze_stmt) - { - /* - Actually, it is ANALYZE .. DELETE .. RETURNING. We need to produce - output and then discard it. - */ - sel_result= new (thd->mem_root) select_send_analyze(thd); - save_protocol= thd->protocol; - thd->protocol= new Protocol_discard(thd); - } - else - { - if (!lex->result && !(sel_result= new (thd->mem_root) select_send(thd))) - goto error; - } - } - - res = mysql_delete(thd, all_tables, - select_lex->where, &select_lex->order_list, - unit->lim.get_select_limit(), select_lex->options, - lex->result ? lex->result : sel_result); - - if (save_protocol) - { - delete thd->protocol; - thd->protocol= save_protocol; - } - - if (thd->lex->analyze_stmt || thd->lex->describe) - { - if (!res) - res= thd->lex->explain->send_explain(thd); - } - - delete sel_result; - MYSQL_DELETE_DONE(res, (ulong) thd->get_row_count_func()); - break; - } - case SQLCOM_DELETE_MULTI: - { - WSREP_SYNC_WAIT(thd, WSREP_SYNC_WAIT_BEFORE_UPDATE_DELETE); - DBUG_ASSERT(first_table == all_tables && first_table != 0); - TABLE_LIST *aux_tables= thd->lex->auxiliary_table_list.first; - multi_delete *result; - WSREP_SYNC_WAIT(thd, WSREP_SYNC_WAIT_BEFORE_UPDATE_DELETE); - - if ((res= multi_delete_precheck(thd, all_tables))) - break; - - /* condition will be TRUE on SP re-excuting */ - if (select_lex->item_list.elements != 0) - select_lex->item_list.empty(); - if (add_item_to_list(thd, new (thd->mem_root) Item_null(thd))) - goto error; - - THD_STAGE_INFO(thd, stage_init); - if ((res= open_and_lock_tables(thd, all_tables, TRUE, 0))) - break; - - MYSQL_MULTI_DELETE_START(thd->query()); - if (unlikely(res= mysql_multi_delete_prepare(thd))) - { - MYSQL_MULTI_DELETE_DONE(1, 0); - goto error; - } - - if (likely(!thd->is_fatal_error)) - { - result= new (thd->mem_root) multi_delete(thd, aux_tables, - lex->table_count); - if (likely(result)) - { - if (unlikely(select_lex->vers_setup_conds(thd, aux_tables))) - goto multi_delete_error; - res= mysql_select(thd, - select_lex->get_table_list(), - select_lex->item_list, - select_lex->where, - 0, (ORDER *)NULL, (ORDER *)NULL, (Item *)NULL, - (ORDER *)NULL, - (select_lex->options | thd->variables.option_bits | - SELECT_NO_JOIN_CACHE | SELECT_NO_UNLOCK | - OPTION_SETUP_TABLES_DONE) & ~OPTION_BUFFER_RESULT, - result, unit, select_lex); - res|= (int)(thd->is_error()); - - MYSQL_MULTI_DELETE_DONE(res, result->num_deleted()); - if (res) - result->abort_result_set(); /* for both DELETE and EXPLAIN DELETE */ - else - { - if (lex->describe || lex->analyze_stmt) - res= thd->lex->explain->send_explain(thd); - } - multi_delete_error: - delete result; - } - } - else - { - res= TRUE; // Error - MYSQL_MULTI_DELETE_DONE(1, 0); - } - break; - } case SQLCOM_DROP_SEQUENCE: case SQLCOM_DROP_TABLE: { @@ -7659,12 +7536,16 @@ void create_select_for_variable(THD *thd, LEX_CSTRING *var_name) } -void mysql_init_multi_delete(LEX *lex) +void mysql_init_delete(LEX *lex) { - lex->sql_command= SQLCOM_DELETE_MULTI; mysql_init_select(lex); lex->first_select_lex()->limit_params.clear(); lex->unit.lim.clear(); +} + +void mysql_init_multi_delete(LEX *lex) +{ + lex->sql_command= SQLCOM_DELETE_MULTI; lex->first_select_lex()->table_list. save_and_clear(&lex->auxiliary_table_list); lex->query_tables= 0; diff --git a/sql/sql_parse.h b/sql/sql_parse.h index ebe3fe9..45cd15c 100644 --- a/sql/sql_parse.h +++ b/sql/sql_parse.h @@ -95,6 +95,7 @@ void mysql_parse(THD *thd, char *rawbuf, uint length, bool mysql_new_select(LEX *lex, bool move_down, SELECT_LEX *sel); void create_select_for_variable(THD *thd, LEX_CSTRING *var_name); void create_table_set_open_action_and_adjust_tables(LEX *lex); +void mysql_init_delete(LEX *lex); void mysql_init_multi_delete(LEX *lex); bool multi_delete_set_locks_and_link_aux_tables(LEX *lex); void create_table_set_open_action_and_adjust_tables(LEX *lex); diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc index 3859a13..84feda3 100644 --- a/sql/sql_prepare.cc +++ b/sql/sql_prepare.cc @@ -95,7 +95,6 @@ When one supplies long data for a placeholder: #include "sql_base.h" // open_normal_and_derived_tables #include "sql_cache.h" // query_cache_* #include "sql_view.h" // create_view_precheck -#include "sql_delete.h" // mysql_prepare_delete #include "sql_select.h" // for JOIN #include "sql_insert.h" // upgrade_lock_type_for_insert, mysql_prepare_insert #include "sql_db.h" // mysql_opt_change_db, mysql_change_db @@ -1398,56 +1397,6 @@ static bool mysql_test_insert(Prepared_statement *stmt, } -/** - Validate DELETE statement. - - @param stmt prepared statement - @param tables list of tables used in this query - - @retval - FALSE success - @retval - TRUE error, error message is set in THD -*/ - -static bool mysql_test_delete(Prepared_statement *stmt, - TABLE_LIST *table_list) -{ - uint table_count= 0; - THD *thd= stmt->thd; - LEX *lex= stmt->lex; - bool delete_while_scanning; - DBUG_ENTER("mysql_test_delete"); - - if (delete_precheck(thd, table_list) || - open_tables(thd, &table_list, &table_count, MYSQL_OPEN_FORCE_SHARED_MDL)) - goto error; - - if (mysql_handle_derived(thd->lex, DT_INIT)) - goto error; - if (mysql_handle_derived(thd->lex, DT_MERGE_FOR_INSERT)) - goto error; - if (mysql_handle_derived(thd->lex, DT_PREPARE)) - goto error; - - if (!table_list->single_table_updatable()) - { - my_error(ER_NON_UPDATABLE_TABLE, MYF(0), table_list->alias.str, "DELETE"); - goto error; - } - if (!table_list->table || !table_list->table->is_created()) - { - my_error(ER_VIEW_DELETE_MERGE_VIEW, MYF(0), - table_list->view_db.str, table_list->view_name.str); - goto error; - } - - DBUG_RETURN(mysql_prepare_delete(thd, table_list, - &lex->first_select_lex()->where, - &delete_while_scanning)); -error: - DBUG_RETURN(TRUE); -} /** @@ -2031,48 +1980,6 @@ static bool mysql_test_create_view(Prepared_statement *stmt) /** - Validate and prepare for execution a multi delete statement. - - @param stmt prepared statement - @param tables list of tables used in this query - - @retval - FALSE success - @retval - TRUE error, error message in THD is set. -*/ - -static bool mysql_test_multidelete(Prepared_statement *stmt, - TABLE_LIST *tables) -{ - THD *thd= stmt->thd; - - thd->lex->current_select= thd->lex->first_select_lex(); - if (add_item_to_list(thd, new (thd->mem_root) - Item_null(thd))) - { - my_error(ER_OUTOFMEMORY, MYF(ME_FATAL), 0); - goto error; - } - - if (multi_delete_precheck(thd, tables) || - select_like_stmt_test_with_open(stmt, tables, - &mysql_multi_delete_prepare, - OPTION_SETUP_TABLES_DONE)) - goto error; - if (!tables->table) - { - my_error(ER_VIEW_DELETE_MERGE_VIEW, MYF(0), - tables->view_db.str, tables->view_name.str); - goto error; - } - return FALSE; -error: - return TRUE; -} - - -/** Wrapper for mysql_insert_select_prepare, to make change of local tables after open_normal_and_derived_tables() call. @@ -2354,14 +2261,13 @@ static bool check_prepared_statement(Prepared_statement *stmt) case SQLCOM_UPDATE: case SQLCOM_UPDATE_MULTI: + case SQLCOM_DELETE: + case SQLCOM_DELETE_MULTI: res = lex->m_sql_cmd->prepare(thd); if (!res) lex->m_sql_cmd->unprepare(thd); break; - case SQLCOM_DELETE: - res= mysql_test_delete(stmt, tables); - break; /* The following allow WHERE clause, so they must be tested like SELECT */ case SQLCOM_SHOW_DATABASES: case SQLCOM_SHOW_TABLES: @@ -2498,10 +2404,6 @@ static bool check_prepared_statement(Prepared_statement *stmt) res= mysql_test_set_fields(stmt, tables, &lex->var_list); break; - case SQLCOM_DELETE_MULTI: - res= mysql_test_multidelete(stmt, tables); - break; - case SQLCOM_INSERT_SELECT: case SQLCOM_REPLACE_SELECT: res= mysql_test_insert_select(stmt, tables); diff --git a/sql/sql_select.cc b/sql/sql_select.cc index edd9aed..31c12fa 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -28846,7 +28846,8 @@ static bool get_range_limit_read_cost(const JOIN_TAB *tab, @note This function takes into account table->opt_range_condition_rows statistic (that is calculated by the make_join_statistics function). - However, single table procedures such as mysql_update() and mysql_delete() + However, single table procedures such as Sql_cmd_update:update_single_table() + and Sql_cmd_delete::delete_single_table() never call make_join_statistics, so they have to update it manually (@see get_index_for_order()). */ @@ -30280,6 +30281,62 @@ static bool process_direct_rownum_comparison(THD *thd, SELECT_LEX_UNIT *unit, } +static void MYSQL_DML_START(THD *thd) +{ + switch (thd->lex->sql_command) { + + case SQLCOM_UPDATE: + MYSQL_UPDATE_START(thd->query()); + break; + case SQLCOM_UPDATE_MULTI: + MYSQL_MULTI_UPDATE_START(thd->query()); + break; + case SQLCOM_DELETE: + MYSQL_DELETE_START(thd->query()); + break; + case SQLCOM_DELETE_MULTI: + MYSQL_MULTI_DELETE_START(thd->query()); + break; + default: + DBUG_ASSERT(0); + } +} + + +static void MYSQL_DML_DONE(THD *thd, int rc) +{ + switch (thd->lex->sql_command) { + + case SQLCOM_UPDATE: + MYSQL_UPDATE_DONE( + rc, + (rc ? 0 : + ((multi_update *)(thd->lex->m_sql_cmd->get_result))->num_found()), + (rc ? 0 : + ((multi_update *)(thd->lex->m_sql_cmd->get_resul))->num_updated())); + break; + case SQLCOM_UPDATE_MULTI: + MYSQL_MULTI_UPDATE_DONE( + rc, + (rc ? 0 : + ((multi_update *)(thd->lex->m_sql_cmd->get_result))->num_found()), + (rc ? 0 : + ((multi_update *)(thd->lex->m_sql_cmd->get_resul))->num_updated())); + break; + case SQLCOM_DELETE: + MYSQL_DELETE_DONE(rc, (rc ? 0 : (ulong) (thd->get_row_count_func()))); + break; + case SQLCOM_DELETE_MULTI: + MYSQL_MULTI_DELETE_DONE( + rc, + (rc ? 0 : + ((multi_delete *)(thd->lex->m_sql_cmd->get_resul))->num_deleted())); + break; + default: + DBUG_ASSERT(0); + } +} + bool Sql_cmd_dml::prepare(THD *thd) { lex= thd->lex; @@ -30291,6 +30348,8 @@ bool Sql_cmd_dml::prepare(THD *thd) if (precheck(thd)) goto err; + MYSQL_DML_START(thd); + lex->context_analysis_only|= CONTEXT_ANALYSIS_ONLY_DERIVED; if (open_tables_for_query(thd, lex->query_tables, &table_count, 0, @@ -30338,6 +30397,9 @@ bool Sql_cmd_dml::execute(THD *thd) { if (precheck(thd)) goto err; + + MYSQL_DML_START(thd); + if (open_tables_for_query(thd, lex->query_tables, &table_count, 0, get_dml_prelocking_strategy())) goto err; @@ -30345,7 +30407,6 @@ bool Sql_cmd_dml::execute(THD *thd) THD_STAGE_INFO(thd, stage_init); - DBUG_ASSERT(!lex->is_query_tables_locked()); /* Locking of tables is done after preparation but before optimization. This allows to do better partition pruning and avoid locking unused @@ -30373,10 +30434,13 @@ bool Sql_cmd_dml::execute(THD *thd) THD_STAGE_INFO(thd, stage_end); + MYSQL_DML_DONE(thd, res); + return res; err: DBUG_ASSERT(thd->is_error() || thd->killed); + MYSQL_DML_DONE(thd, 1); THD_STAGE_INFO(thd, stage_end); (void)unit->cleanup(); diff --git a/sql/sql_update.cc b/sql/sql_update.cc index 3f04db0..c83a527 100644 --- a/sql/sql_update.cc +++ b/sql/sql_update.cc @@ -197,22 +197,11 @@ static bool check_fields(THD *thd, TABLE_LIST *table, List<Item> &items, return true; } - DBUG_ASSERT(thd->lex->sql_command == SQLCOM_UPDATE); - for (List_iterator_fast<Item> it(items); (item=it++);) - { - Field *f= item->field_for_view_update()->field; - vers_select_conds_t &period= table->period_conditions; - if (period.field_start->field == f || period.field_end->field == f) - { - my_error(ER_PERIOD_COLUMNS_UPDATED, MYF(0), - item->name.str, period.name.str); - return true; - } - } } return FALSE; } + bool TABLE::vers_check_update(List<Item> &items) { List_iterator<Item> it(items); @@ -1240,6 +1229,9 @@ bool Sql_cmd_update::update_single_table(THD *thd) thd->lex->current_select->save_leaf_tables(thd); thd->lex->current_select->first_cond_optimization= 0; } + ((multi_update *)result)->set_found(found); + ((multi_update *)result)->set_updated(updated); + if (unlikely(thd->lex->analyze_stmt)) goto emit_explain_and_leave; @@ -1271,75 +1263,6 @@ bool Sql_cmd_update::update_single_table(THD *thd) DBUG_RETURN((err2 || thd->is_error()) ? 1 : 0); } -/* - Prepare items in UPDATE statement - - SYNOPSIS - mysql_prepare_update() - thd - thread handler - table_list - global/local table list - conds - conditions - order_num - number of ORDER BY list entries - order - ORDER BY clause list - - RETURN VALUE - FALSE OK - TRUE error -*/ -bool mysql_prepare_update(THD *thd, TABLE_LIST *table_list, - Item **conds, uint order_num, ORDER *order) -{ - Item *fake_conds= 0; -#ifndef NO_EMBEDDED_ACCESS_CHECKS - TABLE *table= table_list->table; -#endif - List<Item> all_fields; - SELECT_LEX *select_lex= thd->lex->first_select_lex(); - DBUG_ENTER("mysql_prepare_update"); - -#ifndef NO_EMBEDDED_ACCESS_CHECKS - table_list->grant.want_privilege= table->grant.want_privilege= - (SELECT_ACL & ~table->grant.privilege); - table_list->register_want_access(SELECT_ACL); -#endif - - thd->lex->allow_sum_func.clear_all(); - - if (table_list->has_period() && - select_lex->period_setup_conds(thd, table_list)) - DBUG_RETURN(true); - - DBUG_ASSERT(table_list->table); - // conds could be cached from previous SP call - DBUG_ASSERT(!table_list->vers_conditions.need_setup() || - !*conds || thd->stmt_arena->is_stmt_execute()); - if (select_lex->vers_setup_conds(thd, table_list)) - DBUG_RETURN(TRUE); - - *conds= select_lex->where; - - /* - We do not call DT_MERGE_FOR_INSERT because it has no sense for simple - (not multi-) update - */ - if (mysql_handle_derived(thd->lex, DT_PREPARE)) - DBUG_RETURN(TRUE); - - if (setup_tables_and_check_access(thd, &select_lex->context, - &select_lex->top_join_list, table_list, - select_lex->leaf_tables, - FALSE, UPDATE_ACL, SELECT_ACL, TRUE) || - setup_conds(thd, table_list, select_lex->leaf_tables, conds) || - select_lex->setup_ref_array(thd, order_num) || - setup_order(thd, select_lex->ref_pointer_array, - table_list, all_fields, all_fields, order) || - setup_ftfuncs(select_lex)) - DBUG_RETURN(TRUE); - - - select_lex->fix_prepare_information(thd, conds, &fake_conds); - DBUG_RETURN(FALSE); -} /** Check that we are not using table that we are updating in a sub select @@ -1596,7 +1519,13 @@ bool Multiupdate_prelocking_strategy::handle_end(THD *thd) mysql_handle_derived(lex, DT_PREPARE)) DBUG_RETURN(1); - /* + if (table_list->has_period() && table_list->is_view_or_derived()) + { + my_error(ER_IT_IS_A_VIEW, MYF(0), table_list->table_name.str); + DBUG_RETURN(TRUE); + } + + /* setup_tables() need for VIEWs. JOIN::prepare() will call setup_tables() second time, but this call will do nothing (there are check for second call in setup_tables()). @@ -1607,6 +1536,10 @@ bool Multiupdate_prelocking_strategy::handle_end(THD *thd) FALSE, UPDATE_ACL, SELECT_ACL, TRUE)) DBUG_RETURN(1); + if (table_list->has_period() && + select_lex->period_setup_conds(thd, table_list)) + DBUG_RETURN(true); + List<Item> *fields= &lex->first_select_lex()->item_list; if (setup_fields_with_no_wrap(thd, Ref_ptr_array(), *fields, MARK_COLUMNS_WRITE, 0, 0)) @@ -1715,153 +1648,6 @@ bool Multiupdate_prelocking_strategy::handle_end(THD *thd) DBUG_RETURN(0); } -/* - make update specific preparation and checks after opening tables - - SYNOPSIS - mysql_multi_update_prepare() - thd thread handler - - RETURN - FALSE OK - TRUE Error -*/ - -int mysql_multi_update_prepare(THD *thd) -{ - LEX *lex= thd->lex; - TABLE_LIST *table_list= lex->query_tables; - TABLE_LIST *tl; - Multiupdate_prelocking_strategy prelocking_strategy; - uint table_count= lex->table_count; - DBUG_ENTER("mysql_multi_update_prepare"); - - /* - Open tables and create derived ones, but do not lock and fill them yet. - - During prepare phase acquire only S metadata locks instead of SW locks to - keep prepare of multi-UPDATE compatible with concurrent LOCK TABLES WRITE - and global read lock. - - Don't evaluate any subqueries even if constant, because - tables aren't locked yet. - */ - lex->context_analysis_only|= CONTEXT_ANALYSIS_ONLY_DERIVED; - if (thd->lex->sql_command == SQLCOM_UPDATE_MULTI) - { - if (open_tables(thd, &table_list, &table_count, - thd->stmt_arena->is_stmt_prepare() ? MYSQL_OPEN_FORCE_SHARED_MDL : 0, - &prelocking_strategy)) - DBUG_RETURN(TRUE); - } - else - { - /* following need for prepared statements, to run next time multi-update */ - thd->lex->sql_command= SQLCOM_UPDATE_MULTI; - prelocking_strategy.reset(thd); - if (prelocking_strategy.handle_end(thd)) - DBUG_RETURN(TRUE); - } - - /* now lock and fill tables */ - if (!thd->stmt_arena->is_stmt_prepare() && - lock_tables(thd, table_list, table_count, 0)) - DBUG_RETURN(TRUE); - - lex->context_analysis_only&= ~CONTEXT_ANALYSIS_ONLY_DERIVED; - - (void) read_statistics_for_tables_if_needed(thd, table_list); - /* @todo: downgrade the metadata locks here. */ - - /* - Check that we are not using table that we are updating, but we should - skip all tables of UPDATE SELECT itself - */ - lex->first_select_lex()->exclude_from_table_unique_test= TRUE; - /* We only need SELECT privilege for columns in the values list */ - List_iterator<TABLE_LIST> ti(lex->first_select_lex()->leaf_tables); - while ((tl= ti++)) - { - if (tl->is_jtbm()) - continue; - TABLE *table= tl->table; - TABLE_LIST *tlist; - if (!(tlist= tl->top_table())->derived) - { - tlist->grant.want_privilege= - (SELECT_ACL & ~tlist->grant.privilege); - table->grant.want_privilege= (SELECT_ACL & ~table->grant.privilege); - } - DBUG_PRINT("info", ("table: %s want_privilege: %llx", tl->alias.str, - (longlong) table->grant.want_privilege)); - } - /* - Set exclude_from_table_unique_test value back to FALSE. It is needed for - further check in multi_update::prepare whether to use record cache. - */ - lex->first_select_lex()->exclude_from_table_unique_test= FALSE; - - if (lex->save_prep_leaf_tables()) - DBUG_RETURN(TRUE); - - DBUG_RETURN (FALSE); -} - - -/* - Setup multi-update handling and call SELECT to do the join -*/ - -bool mysql_multi_update(THD *thd, TABLE_LIST *table_list, List<Item> *fields, - List<Item> *values, COND *conds, ulonglong options, - enum enum_duplicates handle_duplicates, - bool ignore, SELECT_LEX_UNIT *unit, - SELECT_LEX *select_lex, multi_update **result) -{ - bool res; - DBUG_ENTER("mysql_multi_update"); - - if (!(*result= new (thd->mem_root) multi_update(thd, table_list, - &thd->lex->first_select_lex()->leaf_tables, - fields, values, handle_duplicates, ignore))) - { - DBUG_RETURN(TRUE); - } - - if ((*result)->init(thd)) - DBUG_RETURN(1); - - thd->abort_on_warning= !ignore && thd->is_strict_mode(); - List<Item> total_list; - - if (setup_tables(thd, &select_lex->context, &select_lex->top_join_list, - table_list, select_lex->leaf_tables, FALSE, FALSE)) - DBUG_RETURN(1); - - if (select_lex->vers_setup_conds(thd, table_list)) - DBUG_RETURN(1); - - res= mysql_select(thd, - table_list, total_list, conds, - select_lex->order_list.elements, - select_lex->order_list.first, NULL, NULL, NULL, - options | SELECT_NO_JOIN_CACHE | SELECT_NO_UNLOCK | - OPTION_SETUP_TABLES_DONE, - *result, unit, select_lex); - - DBUG_PRINT("info",("res: %d report_error: %d", res, (int) thd->is_error())); - res|= thd->is_error(); - if (unlikely(res)) - (*result)->abort_result_set(); - else - { - if (thd->lex->describe || thd->lex->analyze_stmt) - res= thd->lex->explain->send_explain(thd); - } - thd->abort_on_warning= 0; - DBUG_RETURN(res); -} - multi_update::multi_update(THD *thd_arg, TABLE_LIST *table_list, List<TABLE_LIST> *leaves_list, @@ -3021,7 +2807,15 @@ bool Sql_cmd_update::precheck(THD *thd) if (multi_update_precheck(thd, lex->query_tables)) return true; } + + WSREP_SYNC_WAIT(thd, WSREP_SYNC_WAIT_BEFORE_UPDATE_DELETE); + return false; + +#ifdef WITH_WSREP +wsrep_error_label: +#endif + return true; } @@ -3029,26 +2823,31 @@ bool Sql_cmd_update::prepare_inner(THD *thd) { JOIN *join; int err= 0; - // uint table_cnt= 0; SELECT_LEX *const select_lex = thd->lex->first_select_lex(); TABLE_LIST *const table_list = select_lex->get_table_list(); ulonglong select_options= select_lex->options; bool free_join= 1; - // bool orig_multitable= multitable; DBUG_ENTER("Sql_cmd_update::prepare_inner"); + (void) read_statistics_for_tables_if_needed(thd, table_list); + + THD_STAGE_INFO(thd, stage_init_update); + if (!multitable) { - TABLE_LIST *update_source_table= 0; - if (mysql_handle_derived(lex, DT_INIT)) DBUG_RETURN(TRUE); + } - if (table_list->has_period() && table_list->is_view_or_derived()) - { - my_error(ER_IT_IS_A_VIEW, MYF(0), table_list->table_name.str); - DBUG_RETURN(TRUE); - } + if (table_list->has_period() && table_list->is_view_or_derived()) + { + my_error(ER_IT_IS_A_VIEW, MYF(0), table_list->table_name.str); + DBUG_RETURN(TRUE); + } + + if (!multitable) + { + TABLE_LIST *update_source_table= 0; if (((update_source_table=unique_table(thd, table_list, table_list->next_global, 0)) || @@ -3088,8 +2887,8 @@ bool Sql_cmd_update::prepare_inner(THD *thd) DBUG_RETURN(TRUE); } - if (((multi_update *)result)->init(thd)) - DBUG_RETURN(TRUE); + if (((multi_update *)result)->init(thd)) + DBUG_RETURN(TRUE); if (setup_tables(thd, &select_lex->context, &select_lex->top_join_list, table_list, select_lex->leaf_tables, false, false)) @@ -3130,6 +2929,31 @@ bool Sql_cmd_update::prepare_inner(THD *thd) } + if (table_list->has_period()) + { + Item *item; + for (List_iterator_fast<Item> it(select_lex->item_list); (item=it++);) + { + Field *f= item->field_for_view_update()->field; + vers_select_conds_t &period= table_list->period_conditions; + if (period.field_start->field == f || period.field_end->field == f) + { + my_error(ER_PERIOD_COLUMNS_UPDATED, MYF(0), + item->name.str, period.name.str); + DBUG_RETURN(true); + } + } + + if (!table_list->period_conditions.start.item->const_item() + || !table_list->period_conditions.end.item->const_item()) + { + my_error(ER_NOT_CONSTANT_EXPRESSION, MYF(0), "FOR PORTION OF"); + DBUG_RETURN(true); + } + table_list->table->no_cache= true; + } + + free_join= false; err: @@ -3147,8 +2971,16 @@ bool Sql_cmd_update::prepare_inner(THD *thd) bool Sql_cmd_update::execute_inner(THD *thd) { - bool res= multitable ? Sql_cmd_dml::execute_inner(thd) - : update_single_table(thd); + bool res= 0; + + thd->get_stmt_da()->reset_current_row_for_warning(1); + if (!multitable) + res= update_single_table(thd); + else + { + thd->abort_on_warning= !thd->lex->ignore && thd->is_strict_mode(); + res= Sql_cmd_dml::execute_inner(thd); + } res|= thd->is_error(); if (multitable) diff --git a/sql/sql_update.h b/sql/sql_update.h index e52d3cd..cf33461 100644 --- a/sql/sql_update.h +++ b/sql/sql_update.h @@ -27,19 +27,7 @@ class THD; typedef class st_select_lex SELECT_LEX; typedef class st_select_lex_unit SELECT_LEX_UNIT; -bool mysql_prepare_update(THD *thd, TABLE_LIST *table_list, - Item **conds, uint order_num, ORDER *order); bool check_unique_table(THD *thd, TABLE_LIST *table_list); -int mysql_update(THD *thd,TABLE_LIST *tables,List<Item> &fields, - List<Item> &values,COND *conds, - uint order_num, ORDER *order, ha_rows limit, - bool ignore, ha_rows *found_return, ha_rows *updated_return); -bool mysql_multi_update(THD *thd, TABLE_LIST *table_list, - List<Item> *fields, List<Item> *values, - COND *conds, ulonglong options, - enum enum_duplicates handle_duplicates, bool ignore, - SELECT_LEX_UNIT *unit, SELECT_LEX *select_lex, - multi_update **result); bool records_are_comparable(const TABLE *table); bool compare_record(const TABLE *table); diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 30eda7c..48eed4e 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -70,6 +70,7 @@ #include "sql_type_json.h" #include "json_table.h" #include "sql_update.h" +#include "sql_delete.h" /* this is to get the bison compilation windows warnings out */ #ifdef _MSC_VER @@ -1675,7 +1676,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize); opt_mi_check_type opt_to mi_check_types table_to_table_list table_to_table opt_table_list opt_as handler_rkey_function handler_read_or_scan - single_multi table_wild_list table_wild_one opt_wild + single_multi opt_wild opt_and select_var_list select_var_list_init help opt_extended_describe shutdown @@ -13245,8 +13246,8 @@ update: MYSQL_YYABORT; /* In case of multi-update setting write lock for all tables may - be too pessimistic. We will decrease lock level if possible in - mysql_multi_update(). + be too pessimistic. We will decrease lock level if possible + later while processing the statement. */ slex->set_lock_for_tables($3, slex->table_list.elements == 1, false); } @@ -13303,12 +13304,11 @@ delete: DELETE_SYM { LEX *lex= Lex; - lex->sql_command= SQLCOM_DELETE; YYPS->m_lock_type= TL_WRITE_DEFAULT; YYPS->m_mdl_type= MDL_SHARED_WRITE; if (Lex->main_select_push()) MYSQL_YYABORT; - mysql_init_select(lex); + mysql_init_delete(lex); lex->ignore= 0; lex->first_select_lex()->order_list.empty(); } @@ -13334,8 +13334,13 @@ delete_part2: opt_delete_options single_multi {} | HISTORY_SYM delete_single_table opt_delete_system_time { - Lex->last_table()->vers_conditions= Lex->vers_conditions; - Lex->pop_select(); //main select + LEX *lex= Lex; + lex->last_table()->vers_conditions= lex->vers_conditions; + lex->pop_select(); //main select + lex->sql_command= SQLCOM_DELETE; + if (!(lex->m_sql_cmd= + new (thd->mem_root) Sql_cmd_delete(false))) + MYSQL_YYABORT; } ; @@ -13369,12 +13374,22 @@ single_multi: delete_limit_clause opt_returning { + LEX *lex= Lex; if ($3) Select->order_list= *($3); - Lex->pop_select(); //main select + lex->pop_select(); //main select + lex->sql_command= SQLCOM_DELETE; + if (!(lex->m_sql_cmd= + new (thd->mem_root) Sql_cmd_delete(false))) + MYSQL_YYABORT; } - | table_wild_list + | table_alias_ref_list { + LEX *lex= Lex; + lex->sql_command= SQLCOM_DELETE_MULTI; + if (!(lex->m_sql_cmd= + new (thd->mem_root) Sql_cmd_delete(true))) + MYSQL_YYABORT; mysql_init_multi_delete(Lex); YYPS->m_lock_type= TL_READ_DEFAULT; YYPS->m_mdl_type= MDL_SHARED_READ; @@ -13386,6 +13401,11 @@ single_multi: } stmt_end {} | FROM table_alias_ref_list { + LEX *lex= Lex; + lex->sql_command= SQLCOM_DELETE_MULTI; + if (!(lex->m_sql_cmd= + new (thd->mem_root) Sql_cmd_delete(true))) + MYSQL_YYABORT; mysql_init_multi_delete(Lex); YYPS->m_lock_type= TL_READ_DEFAULT; YYPS->m_mdl_type= MDL_SHARED_READ; @@ -13421,44 +13441,6 @@ opt_returning: } ; -table_wild_list: - table_wild_one - | table_wild_list ',' table_wild_one - ; - -table_wild_one: - ident opt_wild - { - Table_ident *ti= new (thd->mem_root) Table_ident(&$1); - if (unlikely(ti == NULL)) - MYSQL_YYABORT; - if (unlikely(!Select-> - add_table_to_list(thd, - ti, - NULL, - (TL_OPTION_UPDATING | - TL_OPTION_ALIAS), - YYPS->m_lock_type, - YYPS->m_mdl_type))) - MYSQL_YYABORT; - } - | ident '.' ident opt_wild - { - Table_ident *ti= new (thd->mem_root) Table_ident(thd, &$1, &$3, 0); - if (unlikely(ti == NULL)) - MYSQL_YYABORT; - if (unlikely(!Select-> - add_table_to_list(thd, - ti, - NULL, - (TL_OPTION_UPDATING | - TL_OPTION_ALIAS), - YYPS->m_lock_type, - YYPS->m_mdl_type))) - MYSQL_YYABORT; - } - ; - opt_wild: /* empty */ {} | '.' '*' {} diff --git a/sql/table.h b/sql/table.h index 8d609fb..358e1a9 100644 --- a/sql/table.h +++ b/sql/table.h @@ -2321,6 +2321,7 @@ struct TABLE_LIST */ select_unit *derived_result; /* Stub used for materialized derived tables. */ + bool delete_while_scanning; table_map map; /* ID bit of table (1,2,4,8,16...) */ table_map get_map() {
1 0
0 0
[Commits] d5d8695: MDEV-7487 Semi-join optimization for single-table UPDATE/DELETEs
by IgorBabaev 04 Jun '22

04 Jun '22
revision-id: d5d8695423ff9ed8fb245fe1dd1cf38d1c57856e (mariadb-10.6.1-310-gd5d8695) parent(s): 1f0333db90fb942b2a8435733184460ca3c56288 author: Igor Babaev committer: Igor Babaev timestamp: 2022-06-03 20:05:31 -0700 message: MDEV-7487 Semi-join optimization for single-table UPDATE/DELETEs This is a preliminary patch. --- mysql-test/main/delete_single_to_multi.result | 2144 ++++++++++++++++++++ mysql-test/main/delete_single_to_multi.test | 723 +++++++ mysql-test/main/log_state.result | 2 +- .../main/myisam_explain_non_select_all.result | 43 +- mysql-test/main/opt_trace.result | 20 + mysql-test/main/update_single_to_multi.result | 2137 +++++++++++++++++++ mysql-test/main/update_single_to_multi.test | 511 +++++ sql/opt_subselect.cc | 31 +- sql/sql_class.h | 1 + sql/sql_delete.cc | 76 +- sql/sql_lex.h | 1 + sql/sql_select.cc | 3 +- sql/sql_update.cc | 12 +- sql/sql_yacc.yy | 13 + 14 files changed, 5656 insertions(+), 61 deletions(-) diff --git a/mysql-test/main/delete_single_to_multi.result b/mysql-test/main/delete_single_to_multi.result new file mode 100644 index 0000000..4cf93ce --- /dev/null +++ b/mysql-test/main/delete_single_to_multi.result @@ -0,0 +1,2144 @@ +DROP DATABASE IF EXISTS dbt3_s001; +CREATE DATABASE dbt3_s001; +use dbt3_s001; +create index i_n_name on nation(n_name); +analyze table nation; +Table Op Msg_type Msg_text +dbt3_s001.nation analyze status Engine-independent statistics collected +dbt3_s001.nation analyze status OK +# Pullout +# ======= +explain +select o_orderkey, o_totalprice 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 (select n_nationkey from nation +where n_name='PERU')); +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY nation ref PRIMARY,i_n_name i_n_name 26 const 1 Using index condition +1 PRIMARY customer ref PRIMARY,i_c_nationkey i_c_nationkey 5 dbt3_s001.nation.n_nationkey 11 +1 PRIMARY orders ref|filter i_o_orderdate,i_o_custkey i_o_custkey|i_o_orderdate 5|4 dbt3_s001.customer.c_custkey 11 (7%) Using where; Using rowid filter +explain format=json +select o_orderkey, o_totalprice 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 (select n_nationkey from nation +where n_name='PERU')); +EXPLAIN +{ + "query_block": { + "select_id": 1, + "table": { + "table_name": "nation", + "access_type": "ref", + "possible_keys": ["PRIMARY", "i_n_name"], + "key": "i_n_name", + "key_length": "26", + "used_key_parts": ["n_name"], + "ref": ["const"], + "rows": 1, + "filtered": 100, + "index_condition": "nation.n_name = 'PERU'" + }, + "table": { + "table_name": "customer", + "access_type": "ref", + "possible_keys": ["PRIMARY", "i_c_nationkey"], + "key": "i_c_nationkey", + "key_length": "5", + "used_key_parts": ["c_nationkey"], + "ref": ["dbt3_s001.nation.n_nationkey"], + "rows": 11, + "filtered": 100 + }, + "table": { + "table_name": "orders", + "access_type": "ref", + "possible_keys": ["i_o_orderdate", "i_o_custkey"], + "key": "i_o_custkey", + "key_length": "5", + "used_key_parts": ["o_custkey"], + "ref": ["dbt3_s001.customer.c_custkey"], + "rowid_filter": { + "range": { + "key": "i_o_orderdate", + "used_key_parts": ["o_orderDATE"] + }, + "rows": 108, + "selectivity_pct": 7.2 + }, + "rows": 11, + "filtered": 7.199999809, + "attached_condition": "orders.o_orderDATE between '1992-01-01' and '1992-06-30'" + } + } +} +select o_orderkey, o_totalprice 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 (select n_nationkey from nation +where n_name='PERU')); +o_orderkey o_totalprice +1729 12137.76 +2880 145761.99 +3142 16030.15 +5095 184583.99 +5121 150334.57 +5382 138423.03 +644 201268.06 +737 12984.85 +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 (select n_nationkey from nation +where n_name='PERU')); +explain +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 (select n_nationkey from nation +where n_name='PERU')); +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY nation ref PRIMARY,i_n_name i_n_name 26 const 1 Using index condition +1 PRIMARY customer ref PRIMARY,i_c_nationkey i_c_nationkey 5 dbt3_s001.nation.n_nationkey 11 +1 PRIMARY orders ref|filter i_o_orderdate,i_o_custkey i_o_custkey|i_o_orderdate 5|4 dbt3_s001.customer.c_custkey 11 (7%) Using where; Using rowid filter +explain format=json +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 (select n_nationkey from nation +where n_name='PERU')); +EXPLAIN +{ + "query_block": { + "select_id": 1, + "table": { + "table_name": "nation", + "access_type": "ref", + "possible_keys": ["PRIMARY", "i_n_name"], + "key": "i_n_name", + "key_length": "26", + "used_key_parts": ["n_name"], + "ref": ["const"], + "rows": 1, + "filtered": 100, + "index_condition": "nation.n_name = 'PERU'" + }, + "table": { + "table_name": "customer", + "access_type": "ref", + "possible_keys": ["PRIMARY", "i_c_nationkey"], + "key": "i_c_nationkey", + "key_length": "5", + "used_key_parts": ["c_nationkey"], + "ref": ["dbt3_s001.nation.n_nationkey"], + "rows": 11, + "filtered": 100 + }, + "table": { + "table_name": "orders", + "access_type": "ref", + "possible_keys": ["i_o_orderdate", "i_o_custkey"], + "key": "i_o_custkey", + "key_length": "5", + "used_key_parts": ["o_custkey"], + "ref": ["dbt3_s001.customer.c_custkey"], + "rowid_filter": { + "range": { + "key": "i_o_orderdate", + "used_key_parts": ["o_orderDATE"] + }, + "rows": 108, + "selectivity_pct": 7.2 + }, + "rows": 11, + "filtered": 7.199999809, + "attached_condition": "orders.o_orderDATE between '1992-01-01' and '1992-06-30'" + } + } +} +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 (select n_nationkey from nation +where n_name='PERU')); +select o_orderkey, o_totalprice 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 (select n_nationkey from nation +where n_name='PERU')); +o_orderkey o_totalprice +insert into orders select * from t; +select o_orderkey, o_totalprice 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 (select n_nationkey from nation +where n_name='PERU')); +o_orderkey o_totalprice +1729 12137.76 +2880 145761.99 +3142 16030.15 +5095 184583.99 +5121 150334.57 +5382 138423.03 +644 201268.06 +737 12984.85 +drop table t; +explain +select ps_partkey, ps_suppkey, ps_supplycost from partsupp where (ps_partkey, ps_suppkey) in +(select p_partkey, s_suppkey from part, supplier +where p_retailprice between 901 and 910 and +s_nationkey in (select n_nationkey from nation +where n_name='PERU')); +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY nation ref PRIMARY,i_n_name i_n_name 26 const 1 Using index condition +1 PRIMARY supplier ref PRIMARY,i_s_nationkey i_s_nationkey 5 dbt3_s001.nation.n_nationkey 2 +1 PRIMARY partsupp ref PRIMARY,i_ps_partkey,i_ps_suppkey i_ps_suppkey 4 dbt3_s001.supplier.s_suppkey 16 +1 PRIMARY part eq_ref PRIMARY PRIMARY 4 dbt3_s001.partsupp.ps_partkey 1 Using where +select ps_partkey, ps_suppkey, ps_supplycost from partsupp where (ps_partkey, ps_suppkey) in +(select p_partkey, s_suppkey from part, supplier +where p_retailprice between 901 and 910 and +s_nationkey in (select n_nationkey from nation +where n_name='PERU')); +ps_partkey ps_suppkey ps_supplycost +1 8 357.84 +3 8 645.4 +4 1 444.37 +5 8 50.52 +6 1 642.13 +7 8 763.98 +8 1 957.34 +create table t as +select * from partsupp where (ps_partkey, ps_suppkey) in +(select p_partkey, s_suppkey from part, supplier +where p_retailprice between 901 and 910 and +s_nationkey in (select n_nationkey from nation +where n_name='PERU')); +explain +delete from partsupp where (ps_partkey, ps_suppkey) in +(select p_partkey, s_suppkey from part, supplier +where p_retailprice between 901 and 910 and +s_nationkey in (select n_nationkey from nation +where n_name='PERU')); +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY nation ref PRIMARY,i_n_name i_n_name 26 const 1 Using index condition +1 PRIMARY supplier ref PRIMARY,i_s_nationkey i_s_nationkey 5 dbt3_s001.nation.n_nationkey 2 +1 PRIMARY partsupp ref PRIMARY,i_ps_partkey,i_ps_suppkey i_ps_suppkey 4 dbt3_s001.supplier.s_suppkey 16 +1 PRIMARY part eq_ref PRIMARY PRIMARY 4 dbt3_s001.partsupp.ps_partkey 1 Using where +delete from partsupp where (ps_partkey, ps_suppkey) in +(select p_partkey, s_suppkey from part, supplier +where p_retailprice between 901 and 910 and +s_nationkey in (select n_nationkey from nation +where n_name='PERU')); +select ps_partkey, ps_suppkey, ps_supplycost from partsupp where (ps_partkey, ps_suppkey) in +(select p_partkey, s_suppkey from part, supplier +where p_retailprice between 901 and 910 and +s_nationkey in (select n_nationkey from nation +where n_name='PERU')); +ps_partkey ps_suppkey ps_supplycost +insert into partsupp select * from t; +select ps_partkey, ps_suppkey, ps_supplycost from partsupp where (ps_partkey, ps_suppkey) in +(select p_partkey, s_suppkey from part, supplier +where p_retailprice between 901 and 910 and +s_nationkey in (select n_nationkey from nation +where n_name='PERU')); +ps_partkey ps_suppkey ps_supplycost +1 8 357.84 +3 8 645.4 +4 1 444.37 +5 8 50.52 +6 1 642.13 +7 8 763.98 +8 1 957.34 +drop table t; +explain +select ps_partkey, ps_suppkey, ps_supplycost from partsupp where ps_partkey in (select p_partkey from part +where p_retailprice between 901 and 910) and +ps_suppkey in (select s_suppkey from supplier +where s_nationkey in (select n_nationkey from nation +where n_name='PERU')); +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY nation ref PRIMARY,i_n_name i_n_name 26 const 1 Using index condition +1 PRIMARY supplier ref PRIMARY,i_s_nationkey i_s_nationkey 5 dbt3_s001.nation.n_nationkey 2 +1 PRIMARY partsupp ref PRIMARY,i_ps_partkey,i_ps_suppkey i_ps_suppkey 4 dbt3_s001.supplier.s_suppkey 16 +1 PRIMARY part eq_ref PRIMARY PRIMARY 4 dbt3_s001.partsupp.ps_partkey 1 Using where +select ps_partkey, ps_suppkey, ps_supplycost from partsupp where ps_partkey in (select p_partkey from part +where p_retailprice between 901 and 910) and +ps_suppkey in (select s_suppkey from supplier +where s_nationkey in (select n_nationkey from nation +where n_name='PERU')); +ps_partkey ps_suppkey ps_supplycost +1 8 357.84 +3 8 645.4 +4 1 444.37 +5 8 50.52 +6 1 642.13 +7 8 763.98 +8 1 957.34 +create table t as +select * from partsupp where ps_partkey in (select p_partkey from part +where p_retailprice between 901 and 910) and +ps_suppkey in (select s_suppkey from supplier +where s_nationkey in (select n_nationkey from nation +where n_name='PERU')); +explain +delete from partsupp where ps_partkey in (select p_partkey from part +where p_retailprice between 901 and 910) and +ps_suppkey in (select s_suppkey from supplier +where s_nationkey in (select n_nationkey from nation +where n_name='PERU')); +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY nation ref PRIMARY,i_n_name i_n_name 26 const 1 Using index condition +1 PRIMARY supplier ref PRIMARY,i_s_nationkey i_s_nationkey 5 dbt3_s001.nation.n_nationkey 2 +1 PRIMARY partsupp ref PRIMARY,i_ps_partkey,i_ps_suppkey i_ps_suppkey 4 dbt3_s001.supplier.s_suppkey 16 +1 PRIMARY part eq_ref PRIMARY PRIMARY 4 dbt3_s001.partsupp.ps_partkey 1 Using where +delete from partsupp where ps_partkey in (select p_partkey from part +where p_retailprice between 901 and 910) and +ps_suppkey in (select s_suppkey from supplier +where s_nationkey in (select n_nationkey from nation +where n_name='PERU')); +select ps_partkey, ps_suppkey, ps_supplycost from partsupp where ps_partkey in (select p_partkey from part +where p_retailprice between 901 and 910) and +ps_suppkey in (select s_suppkey from supplier +where s_nationkey in (select n_nationkey from nation +where n_name='PERU')); +ps_partkey ps_suppkey ps_supplycost +insert into partsupp select * from t; +select ps_partkey, ps_suppkey, ps_supplycost from partsupp where ps_partkey in (select p_partkey from part +where p_retailprice between 901 and 910) and +ps_suppkey in (select s_suppkey from supplier +where s_nationkey in (select n_nationkey from nation +where n_name='PERU')); +ps_partkey ps_suppkey ps_supplycost +1 8 357.84 +3 8 645.4 +4 1 444.37 +5 8 50.52 +6 1 642.13 +7 8 763.98 +8 1 957.34 +drop table t; +explain +select l_orderkey, l_linenumber, l_tax from lineitem where l_orderkey in (select o_orderkey from orders +where o_custkey in +(select c_custkey from customer +where c_nationkey in +(select n_nationkey from nation +where n_name='PERU')) +and +o_orderDATE between '1992-06-30' and '1992-12-31') +and +(l_partkey, l_suppkey) in +(select p_partkey, s_suppkey from part, supplier +where p_retailprice between 901 and 1000 and +s_nationkey in (select n_nationkey from nation +where n_name='PERU')); +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY nation ref PRIMARY,i_n_name i_n_name 26 const 1 Using index condition +1 PRIMARY nation ref PRIMARY,i_n_name i_n_name 26 const 1 Using index condition +1 PRIMARY supplier ref PRIMARY,i_s_nationkey i_s_nationkey 5 dbt3_s001.nation.n_nationkey 2 +1 PRIMARY lineitem ref PRIMARY,i_l_suppkey_partkey,i_l_partkey,i_l_suppkey,i_l_orderkey,i_l_orderkey_quantity i_l_suppkey 5 dbt3_s001.supplier.s_suppkey 100 Using where +1 PRIMARY orders eq_ref|filter PRIMARY,i_o_orderdate,i_o_custkey PRIMARY|i_o_orderdate 4|4 dbt3_s001.lineitem.l_orderkey 1 (7%) Using where; Using rowid filter +1 PRIMARY part eq_ref PRIMARY PRIMARY 4 dbt3_s001.lineitem.l_partkey 1 Using where +1 PRIMARY customer eq_ref PRIMARY,i_c_nationkey PRIMARY 4 dbt3_s001.orders.o_custkey 1 Using where +select l_orderkey, l_linenumber, l_tax from lineitem where l_orderkey in (select o_orderkey from orders +where o_custkey in +(select c_custkey from customer +where c_nationkey in +(select n_nationkey from nation +where n_name='PERU')) +and +o_orderDATE between '1992-06-30' and '1992-12-31') +and +(l_partkey, l_suppkey) in +(select p_partkey, s_suppkey from part, supplier +where p_retailprice between 901 and 1000 and +s_nationkey in (select n_nationkey from nation +where n_name='PERU')); +l_orderkey l_linenumber l_tax +2500 2 0.02 +2500 4 0.02 +4996 1 0.01 +933 1 0.04 +create table t as +select * from lineitem where l_orderkey in (select o_orderkey from orders +where o_custkey in +(select c_custkey from customer +where c_nationkey in +(select n_nationkey from nation +where n_name='PERU')) +and +o_orderDATE between '1992-06-30' and '1992-12-31') +and +(l_partkey, l_suppkey) in +(select p_partkey, s_suppkey from part, supplier +where p_retailprice between 901 and 1000 and +s_nationkey in (select n_nationkey from nation +where n_name='PERU')); +explain +delete from lineitem where l_orderkey in (select o_orderkey from orders +where o_custkey in +(select c_custkey from customer +where c_nationkey in +(select n_nationkey from nation +where n_name='PERU')) +and +o_orderDATE between '1992-06-30' and '1992-12-31') +and +(l_partkey, l_suppkey) in +(select p_partkey, s_suppkey from part, supplier +where p_retailprice between 901 and 1000 and +s_nationkey in (select n_nationkey from nation +where n_name='PERU')); +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY nation ref PRIMARY,i_n_name i_n_name 26 const 1 Using index condition +1 PRIMARY nation ref PRIMARY,i_n_name i_n_name 26 const 1 Using index condition +1 PRIMARY supplier ref PRIMARY,i_s_nationkey i_s_nationkey 5 dbt3_s001.nation.n_nationkey 2 +1 PRIMARY lineitem ref PRIMARY,i_l_suppkey_partkey,i_l_partkey,i_l_suppkey,i_l_orderkey,i_l_orderkey_quantity i_l_suppkey 5 dbt3_s001.supplier.s_suppkey 100 Using where +1 PRIMARY orders eq_ref|filter PRIMARY,i_o_orderdate,i_o_custkey PRIMARY|i_o_orderdate 4|4 dbt3_s001.lineitem.l_orderkey 1 (7%) Using where; Using rowid filter +1 PRIMARY part eq_ref PRIMARY PRIMARY 4 dbt3_s001.lineitem.l_partkey 1 Using where +1 PRIMARY customer eq_ref PRIMARY,i_c_nationkey PRIMARY 4 dbt3_s001.orders.o_custkey 1 Using where +delete from lineitem where l_orderkey in (select o_orderkey from orders +where o_custkey in +(select c_custkey from customer +where c_nationkey in +(select n_nationkey from nation +where n_name='PERU')) +and +o_orderDATE between '1992-06-30' and '1992-12-31') +and +(l_partkey, l_suppkey) in +(select p_partkey, s_suppkey from part, supplier +where p_retailprice between 901 and 1000 and +s_nationkey in (select n_nationkey from nation +where n_name='PERU')); +select l_orderkey, l_linenumber, l_tax from lineitem where l_orderkey in (select o_orderkey from orders +where o_custkey in +(select c_custkey from customer +where c_nationkey in +(select n_nationkey from nation +where n_name='PERU')) +and +o_orderDATE between '1992-06-30' and '1992-12-31') +and +(l_partkey, l_suppkey) in +(select p_partkey, s_suppkey from part, supplier +where p_retailprice between 901 and 1000 and +s_nationkey in (select n_nationkey from nation +where n_name='PERU')); +l_orderkey l_linenumber l_tax +insert into lineitem select * from t; +select l_orderkey, l_linenumber, l_tax from lineitem where l_orderkey in (select o_orderkey from orders +where o_custkey in +(select c_custkey from customer +where c_nationkey in +(select n_nationkey from nation +where n_name='PERU')) +and +o_orderDATE between '1992-06-30' and '1992-12-31') +and +(l_partkey, l_suppkey) in +(select p_partkey, s_suppkey from part, supplier +where p_retailprice between 901 and 1000 and +s_nationkey in (select n_nationkey from nation +where n_name='PERU')); +l_orderkey l_linenumber l_tax +2500 2 0.02 +2500 4 0.02 +4996 1 0.01 +933 1 0.04 +drop table t; +# FirstMatch +# ========== +explain +select c_name, c_acctbal from customer where c_nationkey in (select n_nationkey from nation +where n_regionkey in (1,2)) +and +c_custkey in (select o_custkey from orders +where o_orderDATE between '1992-10-09' and '1993-03-08'); +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY customer ALL PRIMARY,i_c_nationkey NULL NULL NULL 150 Using where +1 PRIMARY nation eq_ref|filter PRIMARY,i_n_regionkey PRIMARY|i_n_regionkey 4|5 dbt3_s001.customer.c_nationkey 1 (40%) Using where; Using rowid filter +1 PRIMARY orders ref|filter i_o_orderdate,i_o_custkey i_o_custkey|i_o_orderdate 5|4 dbt3_s001.customer.c_custkey 11 (6%) Using where; FirstMatch(nation); Using rowid filter +explain format=json +select c_name, c_acctbal from customer where c_nationkey in (select n_nationkey from nation +where n_regionkey in (1,2)) +and +c_custkey in (select o_custkey from orders +where o_orderDATE between '1992-10-09' and '1993-03-08'); +EXPLAIN +{ + "query_block": { + "select_id": 1, + "table": { + "table_name": "customer", + "access_type": "ALL", + "possible_keys": ["PRIMARY", "i_c_nationkey"], + "rows": 150, + "filtered": 100, + "attached_condition": "customer.c_nationkey is not null" + }, + "table": { + "table_name": "nation", + "access_type": "eq_ref", + "possible_keys": ["PRIMARY", "i_n_regionkey"], + "key": "PRIMARY", + "key_length": "4", + "used_key_parts": ["n_nationkey"], + "ref": ["dbt3_s001.customer.c_nationkey"], + "rowid_filter": { + "range": { + "key": "i_n_regionkey", + "used_key_parts": ["n_regionkey"] + }, + "rows": 10, + "selectivity_pct": 40 + }, + "rows": 1, + "filtered": 40, + "attached_condition": "nation.n_regionkey in (1,2)" + }, + "table": { + "table_name": "orders", + "access_type": "ref", + "possible_keys": ["i_o_orderdate", "i_o_custkey"], + "key": "i_o_custkey", + "key_length": "5", + "used_key_parts": ["o_custkey"], + "ref": ["dbt3_s001.customer.c_custkey"], + "rowid_filter": { + "range": { + "key": "i_o_orderdate", + "used_key_parts": ["o_orderDATE"] + }, + "rows": 89, + "selectivity_pct": 5.933333333 + }, + "rows": 11, + "filtered": 5.933333397, + "attached_condition": "orders.o_orderDATE between '1992-10-09' and '1993-03-08'", + "first_match": "nation" + } + } +} +select c_name, c_acctbal from customer where c_nationkey in (select n_nationkey from nation +where n_regionkey in (1,2)) +and +c_custkey in (select o_custkey from orders +where o_orderDATE between '1992-10-09' and '1993-03-08'); +c_name c_acctbal +Customer#000000007 9561.95 +Customer#000000019 8914.71 +Customer#000000022 591.98 +Customer#000000025 7133.7 +Customer#000000028 1007.18 +Customer#000000037 -917.75 +Customer#000000040 1335.3 +Customer#000000047 274.58 +Customer#000000059 3458.6 +Customer#000000061 1536.24 +Customer#000000064 -646.64 +Customer#000000067 8166.59 +Customer#000000082 9468.34 +Customer#000000091 4643.14 +Customer#000000094 5500.11 +Customer#000000097 2164.48 +Customer#000000101 7470.96 +Customer#000000103 2757.45 +Customer#000000106 3288.42 +Customer#000000115 7508.92 +Customer#000000121 6428.32 +Customer#000000122 7865.46 +Customer#000000127 9280.71 +Customer#000000130 5073.58 +Customer#000000133 2314.67 +Customer#000000139 7897.78 +create table t as +select * from customer where c_nationkey in (select n_nationkey from nation +where n_regionkey in (1,2)) +and +c_custkey in (select o_custkey from orders +where o_orderDATE between '1992-10-09' and '1993-03-08'); +explain +delete from customer where c_nationkey in (select n_nationkey from nation +where n_regionkey in (1,2)) +and +c_custkey in (select o_custkey from orders +where o_orderDATE between '1992-10-09' and '1993-03-08'); +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY customer ALL PRIMARY,i_c_nationkey NULL NULL NULL 150 Using where +1 PRIMARY nation eq_ref|filter PRIMARY,i_n_regionkey PRIMARY|i_n_regionkey 4|5 dbt3_s001.customer.c_nationkey 1 (40%) Using where; Using rowid filter +1 PRIMARY orders ref|filter i_o_orderdate,i_o_custkey i_o_custkey|i_o_orderdate 5|4 dbt3_s001.customer.c_custkey 11 (6%) Using where; FirstMatch(nation); Using rowid filter +explain format=json +delete from customer where c_nationkey in (select n_nationkey from nation +where n_regionkey in (1,2)) +and +c_custkey in (select o_custkey from orders +where o_orderDATE between '1992-10-09' and '1993-03-08'); +EXPLAIN +{ + "query_block": { + "select_id": 1, + "table": { + "table_name": "customer", + "access_type": "ALL", + "possible_keys": ["PRIMARY", "i_c_nationkey"], + "rows": 150, + "filtered": 100, + "attached_condition": "customer.c_nationkey is not null" + }, + "table": { + "table_name": "nation", + "access_type": "eq_ref", + "possible_keys": ["PRIMARY", "i_n_regionkey"], + "key": "PRIMARY", + "key_length": "4", + "used_key_parts": ["n_nationkey"], + "ref": ["dbt3_s001.customer.c_nationkey"], + "rowid_filter": { + "range": { + "key": "i_n_regionkey", + "used_key_parts": ["n_regionkey"] + }, + "rows": 10, + "selectivity_pct": 40 + }, + "rows": 1, + "filtered": 40, + "attached_condition": "nation.n_regionkey in (1,2)" + }, + "table": { + "table_name": "orders", + "access_type": "ref", + "possible_keys": ["i_o_orderdate", "i_o_custkey"], + "key": "i_o_custkey", + "key_length": "5", + "used_key_parts": ["o_custkey"], + "ref": ["dbt3_s001.customer.c_custkey"], + "rowid_filter": { + "range": { + "key": "i_o_orderdate", + "used_key_parts": ["o_orderDATE"] + }, + "rows": 89, + "selectivity_pct": 5.933333333 + }, + "rows": 11, + "filtered": 5.933333397, + "attached_condition": "orders.o_orderDATE between '1992-10-09' and '1993-03-08'", + "first_match": "nation" + } + } +} +delete from customer where c_nationkey in (select n_nationkey from nation +where n_regionkey in (1,2)) +and +c_custkey in (select o_custkey from orders +where o_orderDATE between '1992-10-09' and '1993-03-08'); +select c_name, c_acctbal from customer where c_nationkey in (select n_nationkey from nation +where n_regionkey in (1,2)) +and +c_custkey in (select o_custkey from orders +where o_orderDATE between '1992-10-09' and '1993-03-08'); +c_name c_acctbal +insert into customer select * from t; +select c_name, c_acctbal from customer where c_nationkey in (select n_nationkey from nation +where n_regionkey in (1,2)) +and +c_custkey in (select o_custkey from orders +where o_orderDATE between '1992-10-09' and '1993-03-08'); +c_name c_acctbal +Customer#000000007 9561.95 +Customer#000000019 8914.71 +Customer#000000022 591.98 +Customer#000000025 7133.7 +Customer#000000028 1007.18 +Customer#000000037 -917.75 +Customer#000000040 1335.3 +Customer#000000047 274.58 +Customer#000000059 3458.6 +Customer#000000061 1536.24 +Customer#000000064 -646.64 +Customer#000000067 8166.59 +Customer#000000082 9468.34 +Customer#000000091 4643.14 +Customer#000000094 5500.11 +Customer#000000097 2164.48 +Customer#000000101 7470.96 +Customer#000000103 2757.45 +Customer#000000106 3288.42 +Customer#000000115 7508.92 +Customer#000000121 6428.32 +Customer#000000122 7865.46 +Customer#000000127 9280.71 +Customer#000000130 5073.58 +Customer#000000133 2314.67 +Customer#000000139 7897.78 +drop table t; +explain +select c_name, c_acctbal from customer where c_nationkey in (select n_nationkey from nation where n_name='PERU') +and +c_custkey in (select o_custkey from orders +where o_orderDATE between "1992-01-09" and "1993-01-08"); +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY nation ref PRIMARY,i_n_name i_n_name 26 const 1 Using index condition +1 PRIMARY customer ref PRIMARY,i_c_nationkey i_c_nationkey 5 dbt3_s001.nation.n_nationkey 11 +1 PRIMARY orders ref|filter i_o_orderdate,i_o_custkey i_o_custkey|i_o_orderdate 5|4 dbt3_s001.customer.c_custkey 11 (14%) Using where; FirstMatch(customer); Using rowid filter +select c_name, c_acctbal from customer where c_nationkey in (select n_nationkey from nation where n_name='PERU') +and +c_custkey in (select o_custkey from orders +where o_orderDATE between "1992-01-09" and "1993-01-08"); +c_name c_acctbal +Customer#000000008 6819.74 +Customer#000000035 1228.24 +Customer#000000061 1536.24 +Customer#000000097 2164.48 +Customer#000000121 6428.32 +Customer#000000133 2314.67 +create table t as +select * from customer where c_nationkey in (select n_nationkey from nation where n_name='PERU') +and +c_custkey in (select o_custkey from orders +where o_orderDATE between "1992-01-09" and "1993-01-08"); +explain +delete from customer where c_nationkey in (select n_nationkey from nation where n_name='PERU') +and +c_custkey in (select o_custkey from orders +where o_orderDATE between "1992-01-09" and "1993-01-08"); +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY nation ref PRIMARY,i_n_name i_n_name 26 const 1 Using index condition +1 PRIMARY customer ref PRIMARY,i_c_nationkey i_c_nationkey 5 dbt3_s001.nation.n_nationkey 11 +1 PRIMARY orders ref|filter i_o_orderdate,i_o_custkey i_o_custkey|i_o_orderdate 5|4 dbt3_s001.customer.c_custkey 11 (14%) Using where; FirstMatch(customer); Using rowid filter +delete from customer where c_nationkey in (select n_nationkey from nation where n_name='PERU') +and +c_custkey in (select o_custkey from orders +where o_orderDATE between "1992-01-09" and "1993-01-08"); +select c_name, c_acctbal from customer where c_nationkey in (select n_nationkey from nation where n_name='PERU') +and +c_custkey in (select o_custkey from orders +where o_orderDATE between "1992-01-09" and "1993-01-08"); +c_name c_acctbal +insert into customer select * from t; +select c_name, c_acctbal from customer where c_nationkey in (select n_nationkey from nation where n_name='PERU') +and +c_custkey in (select o_custkey from orders +where o_orderDATE between "1992-01-09" and "1993-01-08"); +c_name c_acctbal +Customer#000000008 6819.74 +Customer#000000035 1228.24 +Customer#000000061 1536.24 +Customer#000000097 2164.48 +Customer#000000121 6428.32 +Customer#000000133 2314.67 +drop table t; +# Materialization +# =============== +explain +select c_name, c_acctbal from customer where c_custkey in (select o_custkey from orders +where o_orderDATE between '1992-01-09' and '1992-03-08'); +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY <subquery2> ALL distinct_key NULL NULL NULL 28 +1 PRIMARY customer eq_ref PRIMARY PRIMARY 4 dbt3_s001.orders.o_custkey 1 +2 MATERIALIZED orders range i_o_orderdate,i_o_custkey i_o_orderdate 4 NULL 28 Using index condition; Using where +explain format=json +select c_name, c_acctbal from customer where c_custkey in (select o_custkey from orders +where o_orderDATE between '1992-01-09' and '1992-03-08'); +EXPLAIN +{ + "query_block": { + "select_id": 1, + "table": { + "table_name": "<subquery2>", + "access_type": "ALL", + "possible_keys": ["distinct_key"], + "rows": 28, + "filtered": 100, + "materialized": { + "unique": 1, + "query_block": { + "select_id": 2, + "table": { + "table_name": "orders", + "access_type": "range", + "possible_keys": ["i_o_orderdate", "i_o_custkey"], + "key": "i_o_orderdate", + "key_length": "4", + "used_key_parts": ["o_orderDATE"], + "rows": 28, + "filtered": 100, + "index_condition": "orders.o_orderDATE between '1992-01-09' and '1992-03-08'", + "attached_condition": "orders.o_custkey is not null" + } + } + } + }, + "table": { + "table_name": "customer", + "access_type": "eq_ref", + "possible_keys": ["PRIMARY"], + "key": "PRIMARY", + "key_length": "4", + "used_key_parts": ["c_custkey"], + "ref": ["dbt3_s001.orders.o_custkey"], + "rows": 1, + "filtered": 100 + } + } +} +select c_name, c_acctbal from customer where c_custkey in (select o_custkey from orders +where o_orderDATE between '1992-01-09' and '1992-03-08'); +c_name c_acctbal +Customer#000000005 794.47 +Customer#000000013 3857.34 +Customer#000000016 4681.03 +Customer#000000017 6.34 +Customer#000000022 591.98 +Customer#000000023 3332.02 +Customer#000000025 7133.7 +Customer#000000032 3471.53 +Customer#000000035 1228.24 +Customer#000000037 -917.75 +Customer#000000038 6345.11 +Customer#000000040 1335.3 +Customer#000000052 5630.28 +Customer#000000056 6530.86 +Customer#000000065 8795.16 +Customer#000000076 5745.33 +Customer#000000091 4643.14 +Customer#000000098 -551.37 +Customer#000000109 -716.1 +Customer#000000115 7508.92 +Customer#000000116 8403.99 +Customer#000000118 3582.37 +Customer#000000136 -842.39 +Customer#000000140 9963.15 +create table t as +select * from customer where c_custkey in (select o_custkey from orders +where o_orderDATE between '1992-01-09' and '1992-03-08'); +explain +delete from customer where c_custkey in (select o_custkey from orders +where o_orderDATE between '1992-01-09' and '1992-03-08'); +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY <subquery2> ALL distinct_key NULL NULL NULL 28 +1 PRIMARY customer eq_ref PRIMARY PRIMARY 4 dbt3_s001.orders.o_custkey 1 +2 MATERIALIZED orders range i_o_orderdate,i_o_custkey i_o_orderdate 4 NULL 28 Using index condition; Using where +explain format=json +delete from customer where c_custkey in (select o_custkey from orders +where o_orderDATE between '1992-01-09' and '1992-03-08'); +EXPLAIN +{ + "query_block": { + "select_id": 1, + "table": { + "table_name": "<subquery2>", + "access_type": "ALL", + "possible_keys": ["distinct_key"], + "rows": 28, + "filtered": 100, + "materialized": { + "unique": 1, + "query_block": { + "select_id": 2, + "table": { + "table_name": "orders", + "access_type": "range", + "possible_keys": ["i_o_orderdate", "i_o_custkey"], + "key": "i_o_orderdate", + "key_length": "4", + "used_key_parts": ["o_orderDATE"], + "rows": 28, + "filtered": 100, + "index_condition": "orders.o_orderDATE between '1992-01-09' and '1992-03-08'", + "attached_condition": "orders.o_custkey is not null" + } + } + } + }, + "table": { + "table_name": "customer", + "access_type": "eq_ref", + "possible_keys": ["PRIMARY"], + "key": "PRIMARY", + "key_length": "4", + "used_key_parts": ["c_custkey"], + "ref": ["dbt3_s001.orders.o_custkey"], + "rows": 1, + "filtered": 100 + } + } +} +delete from customer where c_custkey in (select o_custkey from orders +where o_orderDATE between '1992-01-09' and '1992-03-08'); +select c_name, c_acctbal from customer where c_custkey in (select o_custkey from orders +where o_orderDATE between '1992-01-09' and '1992-03-08'); +c_name c_acctbal +insert into customer select * from t; +select c_name, c_acctbal from customer where c_custkey in (select o_custkey from orders +where o_orderDATE between '1992-01-09' and '1992-03-08'); +c_name c_acctbal +Customer#000000005 794.47 +Customer#000000013 3857.34 +Customer#000000016 4681.03 +Customer#000000017 6.34 +Customer#000000022 591.98 +Customer#000000023 3332.02 +Customer#000000025 7133.7 +Customer#000000032 3471.53 +Customer#000000035 1228.24 +Customer#000000037 -917.75 +Customer#000000038 6345.11 +Customer#000000040 1335.3 +Customer#000000052 5630.28 +Customer#000000056 6530.86 +Customer#000000065 8795.16 +Customer#000000076 5745.33 +Customer#000000091 4643.14 +Customer#000000098 -551.37 +Customer#000000109 -716.1 +Customer#000000115 7508.92 +Customer#000000116 8403.99 +Customer#000000118 3582.37 +Customer#000000136 -842.39 +Customer#000000140 9963.15 +drop table t; +explain +select c_name, c_acctbal from customer where c_custkey in (select o_custkey from orders +where o_orderDATE between '1992-06-09' and '1993-01-08'); +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY customer ALL PRIMARY NULL NULL NULL 150 +1 PRIMARY <subquery2> eq_ref distinct_key distinct_key 4 func 1 +2 MATERIALIZED orders range i_o_orderdate,i_o_custkey i_o_orderdate 4 NULL 114 Using index condition; Using where +select c_name, c_acctbal from customer where c_custkey in (select o_custkey from orders +where o_orderDATE between '1992-06-09' and '1993-01-08'); +c_name c_acctbal +Customer#000000001 711.56 +Customer#000000002 121.65 +Customer#000000007 9561.95 +Customer#000000008 6819.74 +Customer#000000010 2753.54 +Customer#000000011 -272.6 +Customer#000000016 4681.03 +Customer#000000017 6.34 +Customer#000000019 8914.71 +Customer#000000022 591.98 +Customer#000000023 3332.02 +Customer#000000025 7133.7 +Customer#000000028 1007.18 +Customer#000000029 7618.27 +Customer#000000031 5236.89 +Customer#000000034 8589.7 +Customer#000000037 -917.75 +Customer#000000040 1335.3 +Customer#000000043 9904.28 +Customer#000000044 7315.94 +Customer#000000046 5744.59 +Customer#000000047 274.58 +Customer#000000049 4573.94 +Customer#000000053 4113.64 +Customer#000000055 4572.11 +Customer#000000061 1536.24 +Customer#000000064 -646.64 +Customer#000000067 8166.59 +Customer#000000070 4867.52 +Customer#000000071 -611.19 +Customer#000000073 4288.5 +Customer#000000074 2764.43 +Customer#000000076 5745.33 +Customer#000000079 5121.28 +Customer#000000080 7383.53 +Customer#000000082 9468.34 +Customer#000000083 6463.51 +Customer#000000085 3386.64 +Customer#000000086 3306.32 +Customer#000000088 8031.44 +Customer#000000091 4643.14 +Customer#000000092 1182.91 +Customer#000000095 5327.38 +Customer#000000097 2164.48 +Customer#000000100 9889.89 +Customer#000000101 7470.96 +Customer#000000103 2757.45 +Customer#000000104 -588.38 +Customer#000000106 3288.42 +Customer#000000109 -716.1 +Customer#000000110 7462.99 +Customer#000000112 2953.35 +Customer#000000118 3582.37 +Customer#000000121 6428.32 +Customer#000000122 7865.46 +Customer#000000127 9280.71 +Customer#000000130 5073.58 +Customer#000000131 8595.53 +Customer#000000133 2314.67 +Customer#000000134 4608.9 +Customer#000000136 -842.39 +Customer#000000137 7838.3 +Customer#000000139 7897.78 +Customer#000000142 2209.81 +Customer#000000143 2186.5 +Customer#000000148 2135.6 +Customer#000000149 8959.65 +create table t as +select * from customer where c_custkey in (select o_custkey from orders +where o_orderDATE between '1992-06-09' and '1993-01-08'); +explain +delete from customer where c_custkey in (select o_custkey from orders +where o_orderDATE between '1992-06-09' and '1993-01-08'); +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY customer ALL PRIMARY NULL NULL NULL 150 +1 PRIMARY <subquery2> eq_ref distinct_key distinct_key 4 func 1 +2 MATERIALIZED orders range i_o_orderdate,i_o_custkey i_o_orderdate 4 NULL 114 Using index condition; Using where +delete from customer where c_custkey in (select o_custkey from orders +where o_orderDATE between '1992-06-09' and '1993-01-08'); +select c_name, c_acctbal from customer where c_custkey in (select o_custkey from orders +where o_orderDATE between '1992-06-09' and '1993-01-08'); +c_name c_acctbal +insert into customer select * from t; +select c_name, c_acctbal from customer where c_custkey in (select o_custkey from orders +where o_orderDATE between '1992-06-09' and '1993-01-08'); +c_name c_acctbal +Customer#000000001 711.56 +Customer#000000002 121.65 +Customer#000000007 9561.95 +Customer#000000008 6819.74 +Customer#000000010 2753.54 +Customer#000000011 -272.6 +Customer#000000016 4681.03 +Customer#000000017 6.34 +Customer#000000019 8914.71 +Customer#000000022 591.98 +Customer#000000023 3332.02 +Customer#000000025 7133.7 +Customer#000000028 1007.18 +Customer#000000029 7618.27 +Customer#000000031 5236.89 +Customer#000000034 8589.7 +Customer#000000037 -917.75 +Customer#000000040 1335.3 +Customer#000000043 9904.28 +Customer#000000044 7315.94 +Customer#000000046 5744.59 +Customer#000000047 274.58 +Customer#000000049 4573.94 +Customer#000000053 4113.64 +Customer#000000055 4572.11 +Customer#000000061 1536.24 +Customer#000000064 -646.64 +Customer#000000067 8166.59 +Customer#000000070 4867.52 +Customer#000000071 -611.19 +Customer#000000073 4288.5 +Customer#000000074 2764.43 +Customer#000000076 5745.33 +Customer#000000079 5121.28 +Customer#000000080 7383.53 +Customer#000000082 9468.34 +Customer#000000083 6463.51 +Customer#000000085 3386.64 +Customer#000000086 3306.32 +Customer#000000088 8031.44 +Customer#000000091 4643.14 +Customer#000000092 1182.91 +Customer#000000095 5327.38 +Customer#000000097 2164.48 +Customer#000000100 9889.89 +Customer#000000101 7470.96 +Customer#000000103 2757.45 +Customer#000000104 -588.38 +Customer#000000106 3288.42 +Customer#000000109 -716.1 +Customer#000000110 7462.99 +Customer#000000112 2953.35 +Customer#000000118 3582.37 +Customer#000000121 6428.32 +Customer#000000122 7865.46 +Customer#000000127 9280.71 +Customer#000000130 5073.58 +Customer#000000131 8595.53 +Customer#000000133 2314.67 +Customer#000000134 4608.9 +Customer#000000136 -842.39 +Customer#000000137 7838.3 +Customer#000000139 7897.78 +Customer#000000142 2209.81 +Customer#000000143 2186.5 +Customer#000000148 2135.6 +Customer#000000149 8959.65 +drop table t; +# Materialization SJM +# =================== +explain +select c_name, c_acctbal from customer where c_custkey in (select o_custkey from orders +where o_orderDATE between '1992-01-09' and '1992-03-08' + group by o_custkey having count(o_custkey) > 1); +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY <subquery2> ALL distinct_key NULL NULL NULL 28 +1 PRIMARY customer eq_ref PRIMARY PRIMARY 4 <subquery2>.o_custkey 1 +2 MATERIALIZED orders range i_o_orderdate i_o_orderdate 4 NULL 28 Using index condition; Using temporary +explain format=json +select c_name, c_acctbal from customer where c_custkey in (select o_custkey from orders +where o_orderDATE between '1992-01-09' and '1992-03-08' + group by o_custkey having count(o_custkey) > 1); +EXPLAIN +{ + "query_block": { + "select_id": 1, + "table": { + "table_name": "<subquery2>", + "access_type": "ALL", + "possible_keys": ["distinct_key"], + "rows": 28, + "filtered": 100, + "materialized": { + "unique": 1, + "query_block": { + "select_id": 2, + "having_condition": "count(orders.o_custkey) > 1", + "temporary_table": { + "table": { + "table_name": "orders", + "access_type": "range", + "possible_keys": ["i_o_orderdate"], + "key": "i_o_orderdate", + "key_length": "4", + "used_key_parts": ["o_orderDATE"], + "rows": 28, + "filtered": 100, + "index_condition": "orders.o_orderDATE between '1992-01-09' and '1992-03-08'" + } + } + } + } + }, + "table": { + "table_name": "customer", + "access_type": "eq_ref", + "possible_keys": ["PRIMARY"], + "key": "PRIMARY", + "key_length": "4", + "used_key_parts": ["c_custkey"], + "ref": ["<subquery2>.o_custkey"], + "rows": 1, + "filtered": 100 + } + } +} +select c_name, c_acctbal from customer where c_custkey in (select o_custkey from orders +where o_orderDATE between '1992-01-09' and '1992-03-08' + group by o_custkey having count(o_custkey) > 1); +c_name c_acctbal +Customer#000000013 3857.34 +Customer#000000032 3471.53 +Customer#000000037 -917.75 +Customer#000000056 6530.86 +Customer#000000118 3582.37 +create table t as +select * from customer where c_custkey in (select o_custkey from orders +where o_orderDATE between '1992-01-09' and '1992-03-08' + group by o_custkey having count(o_custkey) > 1); +explain +delete from customer where c_custkey in (select o_custkey from orders +where o_orderDATE between '1992-01-09' and '1992-03-08' + group by o_custkey having count(o_custkey) > 1); +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY <subquery2> ALL distinct_key NULL NULL NULL 28 +1 PRIMARY customer eq_ref PRIMARY PRIMARY 4 <subquery2>.o_custkey 1 +2 MATERIALIZED orders range i_o_orderdate i_o_orderdate 4 NULL 28 Using index condition; Using temporary +explain format=json +delete from customer where c_custkey in (select o_custkey from orders +where o_orderDATE between '1992-01-09' and '1992-03-08' + group by o_custkey having count(o_custkey) > 1); +EXPLAIN +{ + "query_block": { + "select_id": 1, + "table": { + "table_name": "<subquery2>", + "access_type": "ALL", + "possible_keys": ["distinct_key"], + "rows": 28, + "filtered": 100, + "materialized": { + "unique": 1, + "query_block": { + "select_id": 2, + "having_condition": "count(orders.o_custkey) > 1", + "temporary_table": { + "table": { + "table_name": "orders", + "access_type": "range", + "possible_keys": ["i_o_orderdate"], + "key": "i_o_orderdate", + "key_length": "4", + "used_key_parts": ["o_orderDATE"], + "rows": 28, + "filtered": 100, + "index_condition": "orders.o_orderDATE between '1992-01-09' and '1992-03-08'" + } + } + } + } + }, + "table": { + "table_name": "customer", + "access_type": "eq_ref", + "possible_keys": ["PRIMARY"], + "key": "PRIMARY", + "key_length": "4", + "used_key_parts": ["c_custkey"], + "ref": ["<subquery2>.o_custkey"], + "rows": 1, + "filtered": 100 + } + } +} +delete from customer where c_custkey in (select o_custkey from orders +where o_orderDATE between '1992-01-09' and '1992-03-08' + group by o_custkey having count(o_custkey) > 1); +select c_name, c_acctbal from customer where c_custkey in (select o_custkey from orders +where o_orderDATE between '1992-01-09' and '1992-03-08' + group by o_custkey having count(o_custkey) > 1); +c_name c_acctbal +insert into customer select * from t; +select c_name, c_acctbal from customer where c_custkey in (select o_custkey from orders +where o_orderDATE between '1992-01-09' and '1992-03-08' + group by o_custkey having count(o_custkey) > 1); +c_name c_acctbal +Customer#000000013 3857.34 +Customer#000000032 3471.53 +Customer#000000037 -917.75 +Customer#000000056 6530.86 +Customer#000000118 3582.37 +drop table t; +explain +select c_name, c_acctbal from customer where c_custkey in (select o_custkey from orders +where o_orderDATE between '1992-01-09' and '1993-03-08' + group by o_custkey having count(o_custkey) > 5); +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY customer ALL PRIMARY NULL NULL NULL 150 +1 PRIMARY <subquery2> eq_ref distinct_key distinct_key 4 dbt3_s001.customer.c_custkey 1 +2 MATERIALIZED orders range i_o_orderdate i_o_orderdate 4 NULL 242 Using index condition; Using temporary +select c_name, c_acctbal from customer where c_custkey in (select o_custkey from orders +where o_orderDATE between '1992-01-09' and '1993-03-08' + group by o_custkey having count(o_custkey) > 5); +c_name c_acctbal +Customer#000000007 9561.95 +Customer#000000016 4681.03 +Customer#000000037 -917.75 +Customer#000000046 5744.59 +Customer#000000091 4643.14 +Customer#000000103 2757.45 +Customer#000000118 3582.37 +Customer#000000133 2314.67 +Customer#000000134 4608.9 +create table t as +select * from customer where c_custkey in (select o_custkey from orders +where o_orderDATE between '1992-01-09' and '1993-03-08' + group by o_custkey having count(o_custkey) > 5); +explain +delete from customer where c_custkey in (select o_custkey from orders +where o_orderDATE between '1992-01-09' and '1993-03-08' + group by o_custkey having count(o_custkey) > 5); +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY customer ALL PRIMARY NULL NULL NULL 150 +1 PRIMARY <subquery2> eq_ref distinct_key distinct_key 4 dbt3_s001.customer.c_custkey 1 +2 MATERIALIZED orders range i_o_orderdate i_o_orderdate 4 NULL 242 Using index condition; Using temporary +delete from customer where c_custkey in (select o_custkey from orders +where o_orderDATE between '1992-01-09' and '1993-03-08' + group by o_custkey having count(o_custkey) > 5); +select c_name, c_acctbal from customer where c_custkey in (select o_custkey from orders +where o_orderDATE between '1992-01-09' and '1993-03-08' + group by o_custkey having count(o_custkey) > 5); +c_name c_acctbal +insert into customer select * from t; +select c_name, c_acctbal from customer where c_custkey in (select o_custkey from orders +where o_orderDATE between '1992-01-09' and '1993-03-08' + group by o_custkey having count(o_custkey) > 5); +c_name c_acctbal +Customer#000000007 9561.95 +Customer#000000016 4681.03 +Customer#000000037 -917.75 +Customer#000000046 5744.59 +Customer#000000091 4643.14 +Customer#000000103 2757.45 +Customer#000000118 3582.37 +Customer#000000133 2314.67 +Customer#000000134 4608.9 +drop table t; +# Pullout PS +# ========== +prepare stmt from " +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 (select n_nationkey from nation +where n_name='PERU')); +"; +select o_orderkey, o_totalprice 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 (select n_nationkey from nation +where n_name='PERU')); +o_orderkey o_totalprice +1729 12137.76 +2880 145761.99 +3142 16030.15 +5095 184583.99 +5121 150334.57 +5382 138423.03 +644 201268.06 +737 12984.85 +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 (select n_nationkey from nation +where n_name='PERU')); +execute stmt; +select o_orderkey, o_totalprice 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 (select n_nationkey from nation +where n_name='PERU')); +o_orderkey o_totalprice +insert into orders select * from t; +select o_orderkey, o_totalprice 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 (select n_nationkey from nation +where n_name='PERU')); +o_orderkey o_totalprice +1729 12137.76 +2880 145761.99 +3142 16030.15 +5095 184583.99 +5121 150334.57 +5382 138423.03 +644 201268.06 +737 12984.85 +create table r 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 (select n_nationkey from nation +where n_name='PERU')); +execute stmt; +select o_orderkey, o_totalprice 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 (select n_nationkey from nation +where n_name='PERU')); +o_orderkey o_totalprice +insert into orders select * from r; +select o_orderkey, o_totalprice 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 (select n_nationkey from nation +where n_name='PERU')); +o_orderkey o_totalprice +1729 12137.76 +2880 145761.99 +3142 16030.15 +5095 184583.99 +5121 150334.57 +5382 138423.03 +644 201268.06 +737 12984.85 +drop table t,r; +deallocate prepare stmt; +# FirstMatch PS +# ============= +prepare stmt from " +delete from customer where c_nationkey in (select n_nationkey from nation +where n_regionkey in (1,2)) +and +c_custkey in (select o_custkey from orders +where o_orderDATE between '1992-10-09' and '1993-03-08'); +"; +select c_name, c_acctbal from customer where c_nationkey in (select n_nationkey from nation +where n_regionkey in (1,2)) +and +c_custkey in (select o_custkey from orders +where o_orderDATE between '1992-10-09' and '1993-03-08'); +c_name c_acctbal +Customer#000000007 9561.95 +Customer#000000019 8914.71 +Customer#000000022 591.98 +Customer#000000025 7133.7 +Customer#000000028 1007.18 +Customer#000000037 -917.75 +Customer#000000040 1335.3 +Customer#000000047 274.58 +Customer#000000059 3458.6 +Customer#000000061 1536.24 +Customer#000000064 -646.64 +Customer#000000067 8166.59 +Customer#000000082 9468.34 +Customer#000000091 4643.14 +Customer#000000094 5500.11 +Customer#000000097 2164.48 +Customer#000000101 7470.96 +Customer#000000103 2757.45 +Customer#000000106 3288.42 +Customer#000000115 7508.92 +Customer#000000121 6428.32 +Customer#000000122 7865.46 +Customer#000000127 9280.71 +Customer#000000130 5073.58 +Customer#000000133 2314.67 +Customer#000000139 7897.78 +create table t as +select * from customer where c_nationkey in (select n_nationkey from nation +where n_regionkey in (1,2)) +and +c_custkey in (select o_custkey from orders +where o_orderDATE between '1992-10-09' and '1993-03-08'); +execute stmt; +select c_name, c_acctbal from customer where c_nationkey in (select n_nationkey from nation +where n_regionkey in (1,2)) +and +c_custkey in (select o_custkey from orders +where o_orderDATE between '1992-10-09' and '1993-03-08'); +c_name c_acctbal +insert into customer select * from t; +select c_name, c_acctbal from customer where c_nationkey in (select n_nationkey from nation +where n_regionkey in (1,2)) +and +c_custkey in (select o_custkey from orders +where o_orderDATE between '1992-10-09' and '1993-03-08'); +c_name c_acctbal +Customer#000000007 9561.95 +Customer#000000019 8914.71 +Customer#000000022 591.98 +Customer#000000025 7133.7 +Customer#000000028 1007.18 +Customer#000000037 -917.75 +Customer#000000040 1335.3 +Customer#000000047 274.58 +Customer#000000059 3458.6 +Customer#000000061 1536.24 +Customer#000000064 -646.64 +Customer#000000067 8166.59 +Customer#000000082 9468.34 +Customer#000000091 4643.14 +Customer#000000094 5500.11 +Customer#000000097 2164.48 +Customer#000000101 7470.96 +Customer#000000103 2757.45 +Customer#000000106 3288.42 +Customer#000000115 7508.92 +Customer#000000121 6428.32 +Customer#000000122 7865.46 +Customer#000000127 9280.71 +Customer#000000130 5073.58 +Customer#000000133 2314.67 +Customer#000000139 7897.78 +create table r as +select * from customer where c_nationkey in (select n_nationkey from nation +where n_regionkey in (1,2)) +and +c_custkey in (select o_custkey from orders +where o_orderDATE between '1992-10-09' and '1993-03-08'); +execute stmt; +select c_name, c_acctbal from customer where c_nationkey in (select n_nationkey from nation +where n_regionkey in (1,2)) +and +c_custkey in (select o_custkey from orders +where o_orderDATE between '1992-10-09' and '1993-03-08'); +c_name c_acctbal +insert into customer select * from r; +select c_name, c_acctbal from customer where c_nationkey in (select n_nationkey from nation +where n_regionkey in (1,2)) +and +c_custkey in (select o_custkey from orders +where o_orderDATE between '1992-10-09' and '1993-03-08'); +c_name c_acctbal +Customer#000000007 9561.95 +Customer#000000019 8914.71 +Customer#000000022 591.98 +Customer#000000025 7133.7 +Customer#000000028 1007.18 +Customer#000000037 -917.75 +Customer#000000040 1335.3 +Customer#000000047 274.58 +Customer#000000059 3458.6 +Customer#000000061 1536.24 +Customer#000000064 -646.64 +Customer#000000067 8166.59 +Customer#000000082 9468.34 +Customer#000000091 4643.14 +Customer#000000094 5500.11 +Customer#000000097 2164.48 +Customer#000000101 7470.96 +Customer#000000103 2757.45 +Customer#000000106 3288.42 +Customer#000000115 7508.92 +Customer#000000121 6428.32 +Customer#000000122 7865.46 +Customer#000000127 9280.71 +Customer#000000130 5073.58 +Customer#000000133 2314.67 +Customer#000000139 7897.78 +drop table t,r; +deallocate prepare stmt; +# Materialization PS +# ================== +prepare stmt from " +delete from customer where c_custkey in (select o_custkey from orders +where o_orderDATE between '1992-01-09' and '1992-03-08') and c_name like ?; +"; +select c_name, c_acctbal from customer where c_custkey in (select o_custkey from orders +where o_orderDATE between '1992-01-09' and '1992-03-08'); +c_name c_acctbal +Customer#000000005 794.47 +Customer#000000013 3857.34 +Customer#000000016 4681.03 +Customer#000000017 6.34 +Customer#000000022 591.98 +Customer#000000023 3332.02 +Customer#000000025 7133.7 +Customer#000000032 3471.53 +Customer#000000035 1228.24 +Customer#000000037 -917.75 +Customer#000000038 6345.11 +Customer#000000040 1335.3 +Customer#000000052 5630.28 +Customer#000000056 6530.86 +Customer#000000065 8795.16 +Customer#000000076 5745.33 +Customer#000000091 4643.14 +Customer#000000098 -551.37 +Customer#000000109 -716.1 +Customer#000000115 7508.92 +Customer#000000116 8403.99 +Customer#000000118 3582.37 +Customer#000000136 -842.39 +Customer#000000140 9963.15 +set @a1='Customer#%1_'; +create table t as +select * from customer where c_custkey in (select o_custkey from orders +where o_orderDATE between '1992-01-09' and '1992-03-08') and c_name like @a1; +execute stmt using @a1; +select c_name, c_acctbal from customer where c_custkey in (select o_custkey from orders +where o_orderDATE between '1992-01-09' and '1992-03-08'); +c_name c_acctbal +Customer#000000005 794.47 +Customer#000000022 591.98 +Customer#000000023 3332.02 +Customer#000000025 7133.7 +Customer#000000032 3471.53 +Customer#000000035 1228.24 +Customer#000000037 -917.75 +Customer#000000038 6345.11 +Customer#000000040 1335.3 +Customer#000000052 5630.28 +Customer#000000056 6530.86 +Customer#000000065 8795.16 +Customer#000000076 5745.33 +Customer#000000091 4643.14 +Customer#000000098 -551.37 +Customer#000000109 -716.1 +Customer#000000136 -842.39 +Customer#000000140 9963.15 +insert into customer select * from t; +select c_name, c_acctbal from customer where c_custkey in (select o_custkey from orders +where o_orderDATE between '1992-01-09' and '1992-03-08'); +c_name c_acctbal +Customer#000000005 794.47 +Customer#000000013 3857.34 +Customer#000000016 4681.03 +Customer#000000017 6.34 +Customer#000000022 591.98 +Customer#000000023 3332.02 +Customer#000000025 7133.7 +Customer#000000032 3471.53 +Customer#000000035 1228.24 +Customer#000000037 -917.75 +Customer#000000038 6345.11 +Customer#000000040 1335.3 +Customer#000000052 5630.28 +Customer#000000056 6530.86 +Customer#000000065 8795.16 +Customer#000000076 5745.33 +Customer#000000091 4643.14 +Customer#000000098 -551.37 +Customer#000000109 -716.1 +Customer#000000115 7508.92 +Customer#000000116 8403.99 +Customer#000000118 3582.37 +Customer#000000136 -842.39 +Customer#000000140 9963.15 +set @a2='Customer#%3_'; +create table r as +select * from customer where c_custkey in (select o_custkey from orders +where o_orderDATE between '1992-01-09' and '1992-03-08') and c_name like @a2; +execute stmt using @a2; +select c_name, c_acctbal from customer where c_custkey in (select o_custkey from orders +where o_orderDATE between '1992-01-09' and '1992-03-08'); +c_name c_acctbal +Customer#000000005 794.47 +Customer#000000013 3857.34 +Customer#000000016 4681.03 +Customer#000000017 6.34 +Customer#000000022 591.98 +Customer#000000023 3332.02 +Customer#000000025 7133.7 +Customer#000000040 1335.3 +Customer#000000052 5630.28 +Customer#000000056 6530.86 +Customer#000000065 8795.16 +Customer#000000076 5745.33 +Customer#000000091 4643.14 +Customer#000000098 -551.37 +Customer#000000109 -716.1 +Customer#000000115 7508.92 +Customer#000000116 8403.99 +Customer#000000118 3582.37 +Customer#000000140 9963.15 +insert into customer select * from r; +select c_name, c_acctbal from customer where c_custkey in (select o_custkey from orders +where o_orderDATE between '1992-01-09' and '1992-03-08'); +c_name c_acctbal +Customer#000000005 794.47 +Customer#000000013 3857.34 +Customer#000000016 4681.03 +Customer#000000017 6.34 +Customer#000000022 591.98 +Customer#000000023 3332.02 +Customer#000000025 7133.7 +Customer#000000032 3471.53 +Customer#000000035 1228.24 +Customer#000000037 -917.75 +Customer#000000038 6345.11 +Customer#000000040 1335.3 +Customer#000000052 5630.28 +Customer#000000056 6530.86 +Customer#000000065 8795.16 +Customer#000000076 5745.33 +Customer#000000091 4643.14 +Customer#000000098 -551.37 +Customer#000000109 -716.1 +Customer#000000115 7508.92 +Customer#000000116 8403.99 +Customer#000000118 3582.37 +Customer#000000136 -842.39 +Customer#000000140 9963.15 +drop table t,r; +deallocate prepare stmt; +# Materialization SJM PS +# ====================== +prepare stmt from " +delete from customer where c_custkey in (select o_custkey from orders +where o_orderDATE between '1992-01-09' and '1992-03-08') and c_acctbal between ? and ?; +"; +select c_name, c_acctbal from customer where c_custkey in (select o_custkey from orders +where o_orderDATE between '1992-01-09' and '1992-03-08' + group by o_custkey having count(o_custkey) > 1); +c_name c_acctbal +Customer#000000013 3857.34 +Customer#000000032 3471.53 +Customer#000000037 -917.75 +Customer#000000056 6530.86 +Customer#000000118 3582.37 +set @a1=3500; +set @a2=4000; +create table t as +select * from customer where c_custkey in (select o_custkey from orders +where o_orderDATE between '1992-01-09' and '1992-03-08' + group by o_custkey having count(o_custkey) > 1) and c_acctbal between @a1 and @a2; +execute stmt using @a1, @a2; +select c_name, c_acctbal from customer where c_custkey in (select o_custkey from orders +where o_orderDATE between '1992-01-09' and '1992-03-08' + group by o_custkey having count(o_custkey) > 1); +c_name c_acctbal +Customer#000000032 3471.53 +Customer#000000037 -917.75 +Customer#000000056 6530.86 +insert into customer select * from t; +select c_name, c_acctbal from customer where c_custkey in (select o_custkey from orders +where o_orderDATE between '1992-01-09' and '1992-03-08' + group by o_custkey having count(o_custkey) > 1); +c_name c_acctbal +Customer#000000013 3857.34 +Customer#000000032 3471.53 +Customer#000000037 -917.75 +Customer#000000056 6530.86 +Customer#000000118 3582.37 +set @a3=-1000; +set @a4=3500; +create table r as +select * from customer where c_custkey in (select o_custkey from orders +where o_orderDATE between '1992-01-09' and '1992-03-08' + group by o_custkey having count(o_custkey) > 1) and c_acctbal between @a3 and @a4; +execute stmt using @a3, @a4; +select c_name, c_acctbal from customer where c_custkey in (select o_custkey from orders +where o_orderDATE between '1992-01-09' and '1992-03-08' + group by o_custkey having count(o_custkey) > 1); +c_name c_acctbal +Customer#000000013 3857.34 +Customer#000000056 6530.86 +Customer#000000118 3582.37 +insert into customer select * from r; +select c_name, c_acctbal from customer where c_custkey in (select o_custkey from orders +where o_orderDATE between '1992-01-09' and '1992-03-08' + group by o_custkey having count(o_custkey) > 1); +c_name c_acctbal +Customer#000000013 3857.34 +Customer#000000032 3471.53 +Customer#000000037 -917.75 +Customer#000000056 6530.86 +Customer#000000118 3582.37 +drop table t,r; +deallocate prepare stmt; +# Pullout SP +# ========== +create procedure p(a1 int, a2 int) +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 (select n_nationkey from nation +where n_name='PERU')) and o_totalprice between a1 and a2; +select o_orderkey, o_totalprice 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 (select n_nationkey from nation +where n_name='PERU')); +o_orderkey o_totalprice +1729 12137.76 +2880 145761.99 +3142 16030.15 +5095 184583.99 +5121 150334.57 +644 201268.06 +737 12984.85 +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 (select n_nationkey from nation +where n_name='PERU')) and o_totalprice between 150000 and 200000; +call p(150000, 200000); +select o_orderkey, o_totalprice 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 (select n_nationkey from nation +where n_name='PERU')); +o_orderkey o_totalprice +1729 12137.76 +2880 145761.99 +3142 16030.15 +644 201268.06 +737 12984.85 +insert into orders select * from t; +select o_orderkey, o_totalprice 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 (select n_nationkey from nation +where n_name='PERU')); +o_orderkey o_totalprice +1729 12137.76 +2880 145761.99 +3142 16030.15 +5095 184583.99 +5121 150334.57 +644 201268.06 +737 12984.85 +create table r 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 (select n_nationkey from nation +where n_name='PERU')) and o_totalprice between 180000 and 210000; +call p(180000, 210000); +select o_orderkey, o_totalprice 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 (select n_nationkey from nation +where n_name='PERU')); +o_orderkey o_totalprice +1729 12137.76 +2880 145761.99 +3142 16030.15 +5121 150334.57 +737 12984.85 +insert into orders select * from r; +select o_orderkey, o_totalprice 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 (select n_nationkey from nation +where n_name='PERU')); +o_orderkey o_totalprice +1729 12137.76 +2880 145761.99 +3142 16030.15 +5095 184583.99 +5121 150334.57 +644 201268.06 +737 12984.85 +drop table t,r; +drop procedure p; +# FirstMatch SP +# ============= +create procedure p(a int) +delete from customer where c_nationkey in (select n_nationkey from nation +where n_regionkey in (1,2)) +and +c_custkey in (select o_custkey from orders +where o_orderDATE between '1992-10-09' and '1993-03-08') and c_acctbal > a; +select c_name, c_acctbal from customer where c_nationkey in (select n_nationkey from nation +where n_regionkey in (1,2)) +and +c_custkey in (select o_custkey from orders +where o_orderDATE between '1992-10-09' and '1993-03-08'); +c_name c_acctbal +Customer#000000007 9561.95 +Customer#000000019 8914.71 +Customer#000000025 7133.7 +Customer#000000028 1007.18 +Customer#000000037 -917.75 +Customer#000000047 274.58 +Customer#000000059 3458.6 +Customer#000000061 1536.24 +Customer#000000064 -646.64 +Customer#000000067 8166.59 +Customer#000000082 9468.34 +Customer#000000091 4643.14 +Customer#000000094 5500.11 +Customer#000000097 2164.48 +Customer#000000101 7470.96 +Customer#000000103 2757.45 +Customer#000000106 3288.42 +Customer#000000115 7508.92 +Customer#000000121 6428.32 +Customer#000000122 7865.46 +Customer#000000127 9280.71 +Customer#000000130 5073.58 +Customer#000000133 2314.67 +Customer#000000139 7897.78 +create table t as +select * from customer where c_nationkey in (select n_nationkey from nation +where n_regionkey in (1,2)) +and +c_custkey in (select o_custkey from orders +where o_orderDATE between '1992-10-09' and '1993-03-08') and c_acctbal > 4000; +call p(4000); +select c_name, c_acctbal from customer where c_nationkey in (select n_nationkey from nation +where n_regionkey in (1,2)) +and +c_custkey in (select o_custkey from orders +where o_orderDATE between '1992-10-09' and '1993-03-08'); +c_name c_acctbal +Customer#000000028 1007.18 +Customer#000000037 -917.75 +Customer#000000047 274.58 +Customer#000000059 3458.6 +Customer#000000061 1536.24 +Customer#000000064 -646.64 +Customer#000000097 2164.48 +Customer#000000103 2757.45 +Customer#000000106 3288.42 +Customer#000000133 2314.67 +insert into customer select * from t; +select c_name, c_acctbal from customer where c_nationkey in (select n_nationkey from nation +where n_regionkey in (1,2)) +and +c_custkey in (select o_custkey from orders +where o_orderDATE between '1992-10-09' and '1993-03-08'); +c_name c_acctbal +Customer#000000007 9561.95 +Customer#000000019 8914.71 +Customer#000000025 7133.7 +Customer#000000028 1007.18 +Customer#000000037 -917.75 +Customer#000000047 274.58 +Customer#000000059 3458.6 +Customer#000000061 1536.24 +Customer#000000064 -646.64 +Customer#000000067 8166.59 +Customer#000000082 9468.34 +Customer#000000091 4643.14 +Customer#000000094 5500.11 +Customer#000000097 2164.48 +Customer#000000101 7470.96 +Customer#000000103 2757.45 +Customer#000000106 3288.42 +Customer#000000115 7508.92 +Customer#000000121 6428.32 +Customer#000000122 7865.46 +Customer#000000127 9280.71 +Customer#000000130 5073.58 +Customer#000000133 2314.67 +Customer#000000139 7897.78 +create table r as +select * from customer where c_nationkey in (select n_nationkey from nation +where n_regionkey in (1,2)) +and +c_custkey in (select o_custkey from orders +where o_orderDATE between '1992-10-09' and '1993-03-08') and c_acctbal > 2000; +call p(2000); +select c_name, c_acctbal from customer where c_nationkey in (select n_nationkey from nation +where n_regionkey in (1,2)) +and +c_custkey in (select o_custkey from orders +where o_orderDATE between '1992-10-09' and '1993-03-08'); +c_name c_acctbal +Customer#000000028 1007.18 +Customer#000000037 -917.75 +Customer#000000047 274.58 +Customer#000000061 1536.24 +Customer#000000064 -646.64 +insert into customer select * from r; +select c_name, c_acctbal from customer where c_nationkey in (select n_nationkey from nation +where n_regionkey in (1,2)) +and +c_custkey in (select o_custkey from orders +where o_orderDATE between '1992-10-09' and '1993-03-08'); +c_name c_acctbal +Customer#000000007 9561.95 +Customer#000000019 8914.71 +Customer#000000025 7133.7 +Customer#000000028 1007.18 +Customer#000000037 -917.75 +Customer#000000047 274.58 +Customer#000000059 3458.6 +Customer#000000061 1536.24 +Customer#000000064 -646.64 +Customer#000000067 8166.59 +Customer#000000082 9468.34 +Customer#000000091 4643.14 +Customer#000000094 5500.11 +Customer#000000097 2164.48 +Customer#000000101 7470.96 +Customer#000000103 2757.45 +Customer#000000106 3288.42 +Customer#000000115 7508.92 +Customer#000000121 6428.32 +Customer#000000122 7865.46 +Customer#000000127 9280.71 +Customer#000000130 5073.58 +Customer#000000133 2314.67 +Customer#000000139 7897.78 +drop table t,r; +drop procedure p; +# Materialization SP +# ================== +create procedure p() +delete from customer where c_custkey in (select o_custkey from orders +where o_orderDATE between '1992-01-09' and '1992-03-08'); +select c_name, c_acctbal from customer where c_custkey in (select o_custkey from orders +where o_orderDATE between '1992-01-09' and '1992-03-08'); +c_name c_acctbal +Customer#000000013 3857.34 +Customer#000000016 4681.03 +Customer#000000025 7133.7 +Customer#000000032 3471.53 +Customer#000000037 -917.75 +Customer#000000038 6345.11 +Customer#000000052 5630.28 +Customer#000000056 6530.86 +Customer#000000065 8795.16 +Customer#000000076 5745.33 +Customer#000000091 4643.14 +Customer#000000115 7508.92 +Customer#000000116 8403.99 +Customer#000000118 3582.37 +Customer#000000140 9963.15 +create table t as +select * from customer where c_custkey in (select o_custkey from orders +where o_orderDATE between '1992-01-09' and '1992-03-08'); +call p(); +select c_name, c_acctbal from customer where c_custkey in (select o_custkey from orders +where o_orderDATE between '1992-01-09' and '1992-03-08'); +c_name c_acctbal +insert into customer select * from t; +select c_name, c_acctbal from customer where c_custkey in (select o_custkey from orders +where o_orderDATE between '1992-01-09' and '1992-03-08'); +c_name c_acctbal +Customer#000000013 3857.34 +Customer#000000016 4681.03 +Customer#000000025 7133.7 +Customer#000000032 3471.53 +Customer#000000037 -917.75 +Customer#000000038 6345.11 +Customer#000000052 5630.28 +Customer#000000056 6530.86 +Customer#000000065 8795.16 +Customer#000000076 5745.33 +Customer#000000091 4643.14 +Customer#000000115 7508.92 +Customer#000000116 8403.99 +Customer#000000118 3582.37 +Customer#000000140 9963.15 +create table r as +select * from customer where c_custkey in (select o_custkey from orders +where o_orderDATE between '1992-01-09' and '1992-03-08'); +call p(); +select c_name, c_acctbal from customer where c_custkey in (select o_custkey from orders +where o_orderDATE between '1992-01-09' and '1992-03-08'); +c_name c_acctbal +insert into customer select * from r; +select c_name, c_acctbal from customer where c_custkey in (select o_custkey from orders +where o_orderDATE between '1992-01-09' and '1992-03-08'); +c_name c_acctbal +Customer#000000013 3857.34 +Customer#000000016 4681.03 +Customer#000000025 7133.7 +Customer#000000032 3471.53 +Customer#000000037 -917.75 +Customer#000000038 6345.11 +Customer#000000052 5630.28 +Customer#000000056 6530.86 +Customer#000000065 8795.16 +Customer#000000076 5745.33 +Customer#000000091 4643.14 +Customer#000000115 7508.92 +Customer#000000116 8403.99 +Customer#000000118 3582.37 +Customer#000000140 9963.15 +drop table t,r; +drop procedure p; +# Materialization SJM SP +# ====================== +create procedure p() +delete from customer where c_custkey in (select o_custkey from orders +where o_orderDATE between '1992-01-09' and '1992-03-08' + group by o_custkey having count(o_custkey) > 1); +select c_name, c_acctbal from customer where c_custkey in (select o_custkey from orders +where o_orderDATE between '1992-01-09' and '1992-03-08' + group by o_custkey having count(o_custkey) > 1); +c_name c_acctbal +Customer#000000013 3857.34 +Customer#000000032 3471.53 +Customer#000000037 -917.75 +Customer#000000056 6530.86 +Customer#000000118 3582.37 +create table t as +select * from customer where c_custkey in (select o_custkey from orders +where o_orderDATE between '1992-01-09' and '1992-03-08' + group by o_custkey having count(o_custkey) > 1); +call p(); +select c_name, c_acctbal from customer where c_custkey in (select o_custkey from orders +where o_orderDATE between '1992-01-09' and '1992-03-08' + group by o_custkey having count(o_custkey) > 1); +c_name c_acctbal +insert into customer select * from t; +select c_name, c_acctbal from customer where c_custkey in (select o_custkey from orders +where o_orderDATE between '1992-01-09' and '1992-03-08' + group by o_custkey having count(o_custkey) > 1); +c_name c_acctbal +Customer#000000013 3857.34 +Customer#000000032 3471.53 +Customer#000000037 -917.75 +Customer#000000056 6530.86 +Customer#000000118 3582.37 +create table r as +select * from customer where c_custkey in (select o_custkey from orders +where o_orderDATE between '1992-01-09' and '1992-03-08' + group by o_custkey having count(o_custkey) > 1); +call p(); +select c_name, c_acctbal from customer where c_custkey in (select o_custkey from orders +where o_orderDATE between '1992-01-09' and '1992-03-08' + group by o_custkey having count(o_custkey) > 1); +c_name c_acctbal +insert into customer select * from r; +select c_name, c_acctbal from customer where c_custkey in (select o_custkey from orders +where o_orderDATE between '1992-01-09' and '1992-03-08' + group by o_custkey having count(o_custkey) > 1); +c_name c_acctbal +Customer#000000013 3857.34 +Customer#000000032 3471.53 +Customer#000000037 -917.75 +Customer#000000056 6530.86 +Customer#000000118 3582.37 +drop table t,r; +drop procedure p; +# Check for DELETE ... RETURNING with SJ subquery in WHERE +select c_name from customer where c_custkey in (select o_custkey from orders +where o_orderDATE between '1992-01-09' and '1992-03-08'); +c_name +Customer#000000013 +Customer#000000016 +Customer#000000025 +Customer#000000032 +Customer#000000037 +Customer#000000038 +Customer#000000052 +Customer#000000056 +Customer#000000065 +Customer#000000076 +Customer#000000091 +Customer#000000115 +Customer#000000116 +Customer#000000118 +Customer#000000140 +create table t as +select * from customer where c_custkey in (select o_custkey from orders +where o_orderDATE between '1992-01-09' and '1992-03-08'); +explain +delete from customer where c_custkey in (select o_custkey from orders +where o_orderDATE between '1992-01-09' and '1992-03-08') returning c_name; +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY customer ALL NULL NULL NULL NULL 141 Using where +2 DEPENDENT SUBQUERY orders index_subquery|filter i_o_orderdate,i_o_custkey i_o_custkey|i_o_orderdate 5|4 func 175 (2%) Using where; Using rowid filter +delete from customer where c_custkey in (select o_custkey from orders +where o_orderDATE between '1992-01-09' and '1992-03-08') returning c_name; +c_name +Customer#000000013 +Customer#000000016 +Customer#000000025 +Customer#000000032 +Customer#000000037 +Customer#000000038 +Customer#000000052 +Customer#000000056 +Customer#000000065 +Customer#000000076 +Customer#000000091 +Customer#000000115 +Customer#000000116 +Customer#000000118 +Customer#000000140 +select c_name from customer where c_custkey in (select o_custkey from orders +where o_orderDATE between '1992-01-09' and '1992-03-08'); +c_name +insert into customer select * from t; +select c_name from customer where c_custkey in (select o_custkey from orders +where o_orderDATE between '1992-01-09' and '1992-03-08'); +c_name +Customer#000000013 +Customer#000000016 +Customer#000000025 +Customer#000000032 +Customer#000000037 +Customer#000000038 +Customer#000000052 +Customer#000000056 +Customer#000000065 +Customer#000000076 +Customer#000000091 +Customer#000000115 +Customer#000000116 +Customer#000000118 +Customer#000000140 +drop table t; +select c_name from customer where c_custkey in (select o_custkey from orders +where o_orderDATE between '1992-01-09' and '1992-03-08' + group by o_custkey having count(o_custkey) > 1); +c_name +Customer#000000013 +Customer#000000032 +Customer#000000037 +Customer#000000056 +Customer#000000118 +create table t as +select * from customer where c_custkey in (select o_custkey from orders +where o_orderDATE between '1992-01-09' and '1992-03-08' + group by o_custkey having count(o_custkey) > 1); +explain +delete from customer where c_custkey in (select o_custkey from orders +where o_orderDATE between '1992-01-09' and '1992-03-08' + group by o_custkey having count(o_custkey) > 1) returning c_name; +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY customer ALL NULL NULL NULL NULL 141 Using where +2 DEPENDENT SUBQUERY orders range i_o_orderdate i_o_orderdate 4 NULL 28 Using index condition; Using temporary +delete from customer where c_custkey in (select o_custkey from orders +where o_orderDATE between '1992-01-09' and '1992-03-08' + group by o_custkey having count(o_custkey) > 1) returning c_name; +c_name +Customer#000000013 +Customer#000000032 +Customer#000000037 +Customer#000000056 +Customer#000000118 +select c_name from customer where c_custkey in (select o_custkey from orders +where o_orderDATE between '1992-01-09' and '1992-03-08' + group by o_custkey having count(o_custkey) > 1); +c_name +insert into customer select * from t; +select c_name from customer where c_custkey in (select o_custkey from orders +where o_orderDATE between '1992-01-09' and '1992-03-08' + group by o_custkey having count(o_custkey) > 1); +c_name +Customer#000000013 +Customer#000000032 +Customer#000000037 +Customer#000000056 +Customer#000000118 +drop table t; +DROP DATABASE dbt3_s001; diff --git a/mysql-test/main/delete_single_to_multi.test b/mysql-test/main/delete_single_to_multi.test new file mode 100644 index 0000000..825da74 --- /dev/null +++ b/mysql-test/main/delete_single_to_multi.test @@ -0,0 +1,723 @@ +--disable_warnings +DROP DATABASE IF EXISTS dbt3_s001; +--enable_warnings + +CREATE DATABASE dbt3_s001; + +use dbt3_s001; + +--disable_query_log +--disable_result_log +--disable_warnings +--source include/dbt3_s001.inc +--enable_warnings +--enable_result_log +--enable_query_log + +create index i_n_name on nation(n_name); +analyze table nation; + + +--echo # Pullout +--echo # ======= + +let $c1= + o_orderDATE between '1992-01-01' and '1992-06-30' and + o_custkey in (select c_custkey from customer + where c_nationkey in (select n_nationkey from nation + where n_name='PERU')); + +eval +explain +select o_orderkey, o_totalprice from orders where $c1; +eval +explain format=json +select o_orderkey, o_totalprice from orders where $c1; +--sorted_result +eval +select o_orderkey, o_totalprice from orders where $c1; +eval +create table t as +select * from orders where $c1; + +eval +explain +delete from orders where $c1; +eval +explain format=json +delete from orders where $c1; +eval +delete from orders where $c1; +eval +select o_orderkey, o_totalprice from orders where $c1; + + +insert into orders select * from t; +--sorted_result +eval +select o_orderkey, o_totalprice from orders where $c1; +drop table t; + + +let $c2= + (ps_partkey, ps_suppkey) in + (select p_partkey, s_suppkey from part, supplier + where p_retailprice between 901 and 910 and + s_nationkey in (select n_nationkey from nation + where n_name='PERU')); + +eval +explain +select ps_partkey, ps_suppkey, ps_supplycost from partsupp where $c2; +--sorted_result +eval +select ps_partkey, ps_suppkey, ps_supplycost from partsupp where $c2; +eval +create table t as +select * from partsupp where $c2; + +eval +explain +delete from partsupp where $c2; +eval +delete from partsupp where $c2; +eval +select ps_partkey, ps_suppkey, ps_supplycost from partsupp where $c2; + +insert into partsupp select * from t; +--sorted_result +eval +select ps_partkey, ps_suppkey, ps_supplycost from partsupp where $c2; +drop table t; + + +let $c3= + ps_partkey in (select p_partkey from part + where p_retailprice between 901 and 910) and + ps_suppkey in (select s_suppkey from supplier + where s_nationkey in (select n_nationkey from nation + where n_name='PERU')); +eval +explain +select ps_partkey, ps_suppkey, ps_supplycost from partsupp where $c3; +--sorted_result +eval +select ps_partkey, ps_suppkey, ps_supplycost from partsupp where $c3; +eval +create table t as +select * from partsupp where $c3; + +eval +explain +delete from partsupp where $c3; +eval +delete from partsupp where $c3; +eval +select ps_partkey, ps_suppkey, ps_supplycost from partsupp where $c3; + +insert into partsupp select * from t; +--sorted_result +eval +select ps_partkey, ps_suppkey, ps_supplycost from partsupp where $c3; +drop table t; + + +let $c4= + l_orderkey in (select o_orderkey from orders + where o_custkey in + (select c_custkey from customer + where c_nationkey in + (select n_nationkey from nation + where n_name='PERU')) + and + o_orderDATE between '1992-06-30' and '1992-12-31') + and + (l_partkey, l_suppkey) in + (select p_partkey, s_suppkey from part, supplier + where p_retailprice between 901 and 1000 and + s_nationkey in (select n_nationkey from nation + where n_name='PERU')); + +eval +explain +select l_orderkey, l_linenumber, l_tax from lineitem where $c4; +--sorted_result +eval +select l_orderkey, l_linenumber, l_tax from lineitem where $c4; +eval +create table t as +select * from lineitem where $c4; + +eval +explain +delete from lineitem where $c4; +eval +delete from lineitem where $c4; +eval +select l_orderkey, l_linenumber, l_tax from lineitem where $c4; + +insert into lineitem select * from t; +--sorted_result +eval +select l_orderkey, l_linenumber, l_tax from lineitem where $c4; +drop table t; + + +--echo # FirstMatch +--echo # ========== + +let $c5= + c_nationkey in (select n_nationkey from nation + where n_regionkey in (1,2)) + and + c_custkey in (select o_custkey from orders + where o_orderDATE between '1992-10-09' and '1993-03-08'); + +eval +explain +select c_name, c_acctbal from customer where $c5; +eval +explain format=json +select c_name, c_acctbal from customer where $c5; +--sorted_result +eval +select c_name, c_acctbal from customer where $c5; +eval +create table t as +select * from customer where $c5; + +eval +explain +delete from customer where $c5; +eval +explain format=json +delete from customer where $c5; +eval +delete from customer where $c5; +eval +select c_name, c_acctbal from customer where $c5; + +insert into customer select * from t; +--sorted_result +eval +select c_name, c_acctbal from customer where $c5; +drop table t; + + +let $c6= + c_nationkey in (select n_nationkey from nation where n_name='PERU') + and + c_custkey in (select o_custkey from orders + where o_orderDATE between "1992-01-09" and "1993-01-08"); + +eval +explain +select c_name, c_acctbal from customer where $c6; +--sorted_result +eval +select c_name, c_acctbal from customer where $c6; +eval +create table t as +select * from customer where $c6; + +eval +explain +delete from customer where $c6; +eval +delete from customer where $c6; +eval +select c_name, c_acctbal from customer where $c6; + +insert into customer select * from t; +--sorted_result +eval +select c_name, c_acctbal from customer where $c6; +drop table t; + + +--echo # Materialization +--echo # =============== + +let $c7= + c_custkey in (select o_custkey from orders + where o_orderDATE between '1992-01-09' and '1992-03-08'); + +eval +explain +select c_name, c_acctbal from customer where $c7; +eval +explain format=json +select c_name, c_acctbal from customer where $c7; +--sorted_result +eval +select c_name, c_acctbal from customer where $c7; +eval +create table t as +select * from customer where $c7; + +eval +explain +delete from customer where $c7; +eval +explain format=json +delete from customer where $c7; +eval +delete from customer where $c7; +eval +select c_name, c_acctbal from customer where $c7; + +insert into customer select * from t; +--sorted_result +eval +select c_name, c_acctbal from customer where $c7; +drop table t; + + +let $c8= + c_custkey in (select o_custkey from orders + where o_orderDATE between '1992-06-09' and '1993-01-08'); + +eval +explain +select c_name, c_acctbal from customer where $c8; +--sorted_result +eval +select c_name, c_acctbal from customer where $c8; +eval +create table t as +select * from customer where $c8; + +eval +explain +delete from customer where $c8; +eval +delete from customer where $c8; +eval +select c_name, c_acctbal from customer where $c8; + +insert into customer select * from t; +--sorted_result +eval +select c_name, c_acctbal from customer where $c8; +drop table t; + + +--echo # Materialization SJM +--echo # =================== + +let $c9= + c_custkey in (select o_custkey from orders + where o_orderDATE between '1992-01-09' and '1992-03-08' + group by o_custkey having count(o_custkey) > 1); + +eval +explain +select c_name, c_acctbal from customer where $c9; +eval +explain format=json +select c_name, c_acctbal from customer where $c9; +--sorted_result +eval +select c_name, c_acctbal from customer where $c9; +eval +create table t as +select * from customer where $c9; + +eval +explain +delete from customer where $c9; +eval +explain format=json +delete from customer where $c9; +eval +delete from customer where $c9; +eval +select c_name, c_acctbal from customer where $c9; + +insert into customer select * from t; +--sorted_result +eval +select c_name, c_acctbal from customer where $c9; +drop table t; + + +let $c10= + c_custkey in (select o_custkey from orders + where o_orderDATE between '1992-01-09' and '1993-03-08' + group by o_custkey having count(o_custkey) > 5); + +eval +explain +select c_name, c_acctbal from customer where $c10; +--sorted_result +eval +select c_name, c_acctbal from customer where $c10; +eval +create table t as +select * from customer where $c10; + +eval +explain +delete from customer where $c10; +eval +delete from customer where $c10; +eval +select c_name, c_acctbal from customer where $c10; + +insert into customer select * from t; +--sorted_result +eval +select c_name, c_acctbal from customer where $c10; +drop table t; + + +--echo # Pullout PS +--echo # ========== + +eval +prepare stmt from " +delete from orders where $c1; +"; + +--sorted_result +eval +select o_orderkey, o_totalprice from orders where $c1; +eval +create table t as +select * from orders where $c1; +execute stmt; +--sorted_result +eval +select o_orderkey, o_totalprice from orders where $c1; +insert into orders select * from t; +--sorted_result +eval +select o_orderkey, o_totalprice from orders where $c1; +eval +create table r as +select * from orders where $c1; +execute stmt; +--sorted_result +eval +select o_orderkey, o_totalprice from orders where $c1; +insert into orders select * from r; +--sorted_result +eval +select o_orderkey, o_totalprice from orders where $c1; +drop table t,r; + +deallocate prepare stmt; + + +--echo # FirstMatch PS +--echo # ============= + +eval +prepare stmt from " +delete from customer where $c5; +"; + +--sorted_result +eval +select c_name, c_acctbal from customer where $c5; +eval +create table t as +select * from customer where $c5; +execute stmt; +--sorted_result +eval +select c_name, c_acctbal from customer where $c5; +insert into customer select * from t; +--sorted_result +eval +select c_name, c_acctbal from customer where $c5; +eval +create table r as +select * from customer where $c5; +execute stmt; +--sorted_result +eval +select c_name, c_acctbal from customer where $c5; +insert into customer select * from r; +--sorted_result +eval +select c_name, c_acctbal from customer where $c5; +drop table t,r; + +deallocate prepare stmt; + + +--echo # Materialization PS +--echo # ================== + +eval +prepare stmt from " +delete from customer where $c7 and c_name like ?; +"; + +--sorted_result +eval +select c_name, c_acctbal from customer where $c7; +set @a1='Customer#%1_'; +eval +create table t as +select * from customer where $c7 and c_name like @a1; +execute stmt using @a1; +--sorted_result +eval +select c_name, c_acctbal from customer where $c7; +insert into customer select * from t; +--sorted_result +eval +select c_name, c_acctbal from customer where $c7; +set @a2='Customer#%3_'; +eval +create table r as +select * from customer where $c7 and c_name like @a2; +execute stmt using @a2; +--sorted_result +eval +select c_name, c_acctbal from customer where $c7; +insert into customer select * from r; +--sorted_result +eval +select c_name, c_acctbal from customer where $c7; +drop table t,r; + +deallocate prepare stmt; + + +--echo # Materialization SJM PS +--echo # ====================== + +eval +prepare stmt from " +delete from customer where $c7 and c_acctbal between ? and ?; +"; + +--sorted_result +eval +select c_name, c_acctbal from customer where $c9; +set @a1=3500; +set @a2=4000; +eval +create table t as +select * from customer where $c9 and c_acctbal between @a1 and @a2; +execute stmt using @a1, @a2; +--sorted_result +eval +select c_name, c_acctbal from customer where $c9; +insert into customer select * from t; +--sorted_result +eval +select c_name, c_acctbal from customer where $c9; +set @a3=-1000; +set @a4=3500; +eval +create table r as +select * from customer where $c9 and c_acctbal between @a3 and @a4; +execute stmt using @a3, @a4; +--sorted_result +eval +select c_name, c_acctbal from customer where $c9; +insert into customer select * from r; +--sorted_result +eval +select c_name, c_acctbal from customer where $c9; +drop table t,r; + +deallocate prepare stmt; + + +--echo # Pullout SP +--echo # ========== + +eval +create procedure p(a1 int, a2 int) +delete from orders where $c1 and o_totalprice between a1 and a2; + +--sorted_result +eval +select o_orderkey, o_totalprice from orders where $c1; +eval +create table t as +select * from orders where $c1 and o_totalprice between 150000 and 200000; +call p(150000, 200000); +--sorted_result +eval +select o_orderkey, o_totalprice from orders where $c1; +insert into orders select * from t; +--sorted_result +eval +select o_orderkey, o_totalprice from orders where $c1; +eval +create table r as +select * from orders where $c1 and o_totalprice between 180000 and 210000; +call p(180000, 210000); +--sorted_result +eval +select o_orderkey, o_totalprice from orders where $c1; +insert into orders select * from r; +--sorted_result +eval +select o_orderkey, o_totalprice from orders where $c1; +drop table t,r; + +drop procedure p; + + +--echo # FirstMatch SP +--echo # ============= + +eval +create procedure p(a int) +delete from customer where $c5 and c_acctbal > a; + +--sorted_result +eval +select c_name, c_acctbal from customer where $c5; +eval +create table t as +select * from customer where $c5 and c_acctbal > 4000; +call p(4000); +--sorted_result +eval +select c_name, c_acctbal from customer where $c5; +insert into customer select * from t; +--sorted_result +eval +select c_name, c_acctbal from customer where $c5; +eval +create table r as +select * from customer where $c5 and c_acctbal > 2000; +call p(2000); +--sorted_result +eval +select c_name, c_acctbal from customer where $c5; +insert into customer select * from r; +--sorted_result +eval +select c_name, c_acctbal from customer where $c5; +drop table t,r; + +drop procedure p; + + +--echo # Materialization SP +--echo # ================== + +eval +create procedure p() +delete from customer where $c7; + +--sorted_result +eval +select c_name, c_acctbal from customer where $c7; +eval +create table t as +select * from customer where $c7; +call p(); +--sorted_result +eval +select c_name, c_acctbal from customer where $c7; +insert into customer select * from t; +--sorted_result +eval +select c_name, c_acctbal from customer where $c7; +eval +create table r as +select * from customer where $c7; +call p(); +--sorted_result +eval +select c_name, c_acctbal from customer where $c7; +insert into customer select * from r; +--sorted_result +eval +select c_name, c_acctbal from customer where $c7; +drop table t,r; + +drop procedure p; + + +--echo # Materialization SJM SP +--echo # ====================== + +eval +create procedure p() +delete from customer where $c9; + +--sorted_result +eval +select c_name, c_acctbal from customer where $c9; +eval +create table t as +select * from customer where $c9; +call p(); +--sorted_result +eval +select c_name, c_acctbal from customer where $c9; +insert into customer select * from t; +--sorted_result +eval +select c_name, c_acctbal from customer where $c9; +eval +create table r as +select * from customer where $c9; +call p(); +--sorted_result +eval +select c_name, c_acctbal from customer where $c9; +insert into customer select * from r; +--sorted_result +eval +select c_name, c_acctbal from customer where $c9; +drop table t,r; + +drop procedure p; + +--echo # Check for DELETE ... RETURNING with SJ subquery in WHERE + +--sorted_result +eval +select c_name from customer where $c7; +eval +create table t as +select * from customer where $c7; +eval +explain +delete from customer where $c7 returning c_name; +--sorted_result +eval +delete from customer where $c7 returning c_name; +--sorted_result +eval +select c_name from customer where $c7; +insert into customer select * from t; +--sorted_result +eval +select c_name from customer where $c7; +drop table t; + +--sorted_result +eval +select c_name from customer where $c9; +eval +create table t as +select * from customer where $c9; +eval +explain +delete from customer where $c9 returning c_name; +--sorted_result +eval +delete from customer where $c9 returning c_name; +--sorted_result +eval +select c_name from customer where $c9; +insert into customer select * from t; +--sorted_result +eval +select c_name from customer where $c9; +drop table t; + +DROP DATABASE dbt3_s001; diff --git a/mysql-test/main/log_state.result b/mysql-test/main/log_state.result index 5e7aac8..1b1c737 100644 --- a/mysql-test/main/log_state.result +++ b/mysql-test/main/log_state.result @@ -243,7 +243,7 @@ rows_examined sql_text 4 UPDATE t1 SET a=a+sleep(.02) WHERE a>2 8 UPDATE t1 SET a=a+sleep(.02) ORDER BY a DESC 1 UPDATE t2 set b=b+sleep(.02) limit 1 -4 UPDATE t1 SET a=a+sleep(.02) WHERE a in (SELECT b from t2) +10 UPDATE t1 SET a=a+sleep(.02) WHERE a in (SELECT b from t2) 6 DELETE FROM t1 WHERE a=a+sleep(.02) ORDER BY a LIMIT 2 disconnect con2; connection default; diff --git a/mysql-test/main/myisam_explain_non_select_all.result b/mysql-test/main/myisam_explain_non_select_all.result index 36231c3..945c5ff 100644 --- a/mysql-test/main/myisam_explain_non_select_all.result +++ b/mysql-test/main/myisam_explain_non_select_all.result @@ -234,14 +234,16 @@ Warnings: Warning 1287 '<select expression> INTO <destination>;' is deprecated and will be removed in a future release. Please use 'SELECT <select list> INTO <destination> FROM...' instead EXPLAIN UPDATE t1 SET a = 10 WHERE 1 IN (SELECT 1 FROM t2 WHERE t2.b < 3); id select_type table type possible_keys key key_len ref rows Extra -1 PRIMARY t1 ALL NULL NULL NULL NULL 3 Using where -2 SUBQUERY t2 ALL NULL NULL NULL NULL 3 Using where +1 PRIMARY <subquery2> eq_ref distinct_key distinct_key 4 func 1 +1 PRIMARY t1 ALL NULL NULL NULL NULL 3 +2 MATERIALIZED t2 ALL NULL NULL NULL NULL 3 Using where FLUSH STATUS; FLUSH TABLES; EXPLAIN EXTENDED UPDATE t1 SET a = 10 WHERE 1 IN (SELECT 1 FROM t2 WHERE t2.b < 3); id select_type table type possible_keys key key_len ref rows filtered Extra -1 PRIMARY t1 ALL NULL NULL NULL NULL 3 100.00 Using where -2 SUBQUERY t2 ALL NULL NULL NULL NULL 3 100.00 Using where +1 PRIMARY <subquery2> eq_ref distinct_key distinct_key 4 func 1 100.00 +1 PRIMARY t1 ALL NULL NULL NULL NULL 3 100.00 +2 MATERIALIZED t2 ALL NULL NULL NULL NULL 3 100.00 Using where # Status of EXPLAIN EXTENDED query Variable_name Value FLUSH STATUS; @@ -264,7 +266,9 @@ Handler_read_key 5 Handler_read_rnd_next 8 # Status of testing query execution: Variable_name Value -Handler_read_rnd_next 5 +Handler_read_key 1 +Handler_read_rnd 3 +Handler_read_rnd_next 12 Handler_update 3 DROP TABLE t1, t2; @@ -282,13 +286,13 @@ Warning 1287 '<select expression> INTO <destination>;' is deprecated and will be EXPLAIN UPDATE t1 SET a = 10 WHERE a IN (SELECT b FROM t2 WHERE t1.a < 3); id select_type table type possible_keys key key_len ref rows Extra 1 PRIMARY t1 ALL NULL NULL NULL NULL 3 Using where -2 DEPENDENT SUBQUERY t2 ALL NULL NULL NULL NULL 3 Using where +1 PRIMARY t2 ALL NULL NULL NULL NULL 3 Using where; FirstMatch(t1) FLUSH STATUS; FLUSH TABLES; EXPLAIN EXTENDED UPDATE t1 SET a = 10 WHERE a IN (SELECT b FROM t2 WHERE t1.a < 3); id select_type table type possible_keys key key_len ref rows filtered Extra 1 PRIMARY t1 ALL NULL NULL NULL NULL 3 100.00 Using where -2 DEPENDENT SUBQUERY t2 ALL NULL NULL NULL NULL 3 100.00 Using where +1 PRIMARY t2 ALL NULL NULL NULL NULL 3 100.00 Using where; FirstMatch(t1) Warnings: Note 1276 Field or reference 'test.t1.a' of SELECT #2 was resolved in SELECT #1 # Status of EXPLAIN EXTENDED query @@ -967,14 +971,16 @@ Warnings: Warning 1287 '<select expression> INTO <destination>;' is deprecated and will be removed in a future release. Please use 'SELECT <select list> INTO <destination> FROM...' instead EXPLAIN UPDATE t1 SET a = 10 WHERE a IN (SELECT a FROM t2); id select_type table type possible_keys key key_len ref rows Extra -1 PRIMARY t1 ALL NULL NULL NULL NULL 3 Using where -2 DEPENDENT SUBQUERY t2 ALL NULL NULL NULL NULL 3 Using where +1 PRIMARY t1 ALL NULL NULL NULL NULL 3 +1 PRIMARY <subquery2> eq_ref distinct_key distinct_key 4 func 1 +2 MATERIALIZED t2 ALL NULL NULL NULL NULL 3 FLUSH STATUS; FLUSH TABLES; EXPLAIN EXTENDED UPDATE t1 SET a = 10 WHERE a IN (SELECT a FROM t2); id select_type table type possible_keys key key_len ref rows filtered Extra -1 PRIMARY t1 ALL NULL NULL NULL NULL 3 100.00 Using where -2 DEPENDENT SUBQUERY t2 ALL NULL NULL NULL NULL 3 100.00 Using where +1 PRIMARY t1 ALL NULL NULL NULL NULL 3 100.00 +1 PRIMARY <subquery2> eq_ref distinct_key distinct_key 4 func 1 100.00 +2 MATERIALIZED t2 ALL NULL NULL NULL NULL 3 100.00 # Status of EXPLAIN EXTENDED query Variable_name Value FLUSH STATUS; @@ -997,7 +1003,8 @@ Handler_read_key 7 Handler_read_rnd_next 8 # Status of testing query execution: Variable_name Value -Handler_read_rnd_next 10 +Handler_read_key 3 +Handler_read_rnd_next 8 Handler_update 3 DROP TABLE t1, t2; @@ -1060,14 +1067,14 @@ Warnings: Warning 1287 '<select expression> INTO <destination>;' is deprecated and will be removed in a future release. Please use 'SELECT <select list> INTO <destination> FROM...' instead EXPLAIN DELETE FROM t1 WHERE a1 IN (SELECT a2 FROM t2 WHERE a2 > 2); id select_type table type possible_keys key key_len ref rows Extra -1 PRIMARY t1 ALL NULL NULL NULL NULL 5 Using where -2 DEPENDENT SUBQUERY t2 ALL NULL NULL NULL NULL 5 Using where +1 PRIMARY t1 ALL NULL NULL NULL NULL 5 +1 PRIMARY t2 ALL NULL NULL NULL NULL 5 Using where; FirstMatch(t1) FLUSH STATUS; FLUSH TABLES; EXPLAIN EXTENDED DELETE FROM t1 WHERE a1 IN (SELECT a2 FROM t2 WHERE a2 > 2); id select_type table type possible_keys key key_len ref rows filtered Extra -1 PRIMARY t1 ALL NULL NULL NULL NULL 5 100.00 Using where -2 DEPENDENT SUBQUERY t2 ALL NULL NULL NULL NULL 5 100.00 Using where +1 PRIMARY t1 ALL NULL NULL NULL NULL 5 100.00 +1 PRIMARY t2 ALL NULL NULL NULL NULL 5 100.00 Using where; FirstMatch(t1) # Status of EXPLAIN EXTENDED query Variable_name Value Handler_read_key 4 @@ -3024,14 +3031,14 @@ Warning 1287 '<select expression> INTO <destination>;' is deprecated and will be EXPLAIN UPDATE t1 SET a = 10 WHERE a IN (SELECT * FROM (SELECT b FROM t2 ORDER BY b LIMIT 2,2) x); id select_type table type possible_keys key key_len ref rows Extra 1 PRIMARY t1 ALL NULL NULL NULL NULL 3 Using where -2 DEPENDENT SUBQUERY <derived3> index_subquery key0 key0 5 func 2 +1 PRIMARY <derived3> ref key0 key0 5 test.t1.a 2 FirstMatch(t1) 3 DERIVED t2 ALL NULL NULL NULL NULL 3 Using filesort FLUSH STATUS; FLUSH TABLES; EXPLAIN EXTENDED UPDATE t1 SET a = 10 WHERE a IN (SELECT * FROM (SELECT b FROM t2 ORDER BY b LIMIT 2,2) x); id select_type table type possible_keys key key_len ref rows filtered Extra 1 PRIMARY t1 ALL NULL NULL NULL NULL 3 100.00 Using where -2 DEPENDENT SUBQUERY <derived3> index_subquery key0 key0 5 func 2 100.00 +1 PRIMARY <derived3> ref key0 key0 5 test.t1.a 2 100.00 FirstMatch(t1) 3 DERIVED t2 ALL NULL NULL NULL NULL 3 100.00 Using filesort # Status of EXPLAIN EXTENDED query Variable_name Value diff --git a/mysql-test/main/opt_trace.result b/mysql-test/main/opt_trace.result index 1444320..8885860 100644 --- a/mysql-test/main/opt_trace.result +++ b/mysql-test/main/opt_trace.result @@ -3945,6 +3945,16 @@ explain delete t0,t1 from t0, t1 where t0.a=t1.a and t1.a<3 { } }, { + "table": "t0", + "rowid_filters": [ + { + "key": "a", + "build_cost": 0.174715752, + "rows": 3 + } + ] + }, + { "selectivity_for_indexes": [ { "index_name": "a", @@ -4010,6 +4020,16 @@ explain delete t0,t1 from t0, t1 where t0.a=t1.a and t1.a<3 { } }, { + "table": "t1", + "rowid_filters": [ + { + "key": "a", + "build_cost": 0.174715752, + "rows": 3 + } + ] + }, + { "selectivity_for_indexes": [ { "index_name": "a", diff --git a/mysql-test/main/update_single_to_multi.result b/mysql-test/main/update_single_to_multi.result new file mode 100644 index 0000000..66fdf3f --- /dev/null +++ b/mysql-test/main/update_single_to_multi.result @@ -0,0 +1,2137 @@ +DROP DATABASE IF EXISTS dbt3_s001; +CREATE DATABASE dbt3_s001; +use dbt3_s001; +create index i_n_name on nation(n_name); +analyze table nation; +Table Op Msg_type Msg_text +dbt3_s001.nation analyze status Engine-independent statistics collected +dbt3_s001.nation analyze status OK +# Pullout +# ======= +explain +select o_orderkey, o_totalprice 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 (select n_nationkey from nation +where n_name='PERU')); +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY nation ref PRIMARY,i_n_name i_n_name 26 const 1 Using index condition +1 PRIMARY customer ref PRIMARY,i_c_nationkey i_c_nationkey 5 dbt3_s001.nation.n_nationkey 11 +1 PRIMARY orders ref|filter i_o_orderdate,i_o_custkey i_o_custkey|i_o_orderdate 5|4 dbt3_s001.customer.c_custkey 11 (7%) Using where; Using rowid filter +explain format=json +select o_orderkey, o_totalprice 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 (select n_nationkey from nation +where n_name='PERU')); +EXPLAIN +{ + "query_block": { + "select_id": 1, + "table": { + "table_name": "nation", + "access_type": "ref", + "possible_keys": ["PRIMARY", "i_n_name"], + "key": "i_n_name", + "key_length": "26", + "used_key_parts": ["n_name"], + "ref": ["const"], + "rows": 1, + "filtered": 100, + "index_condition": "nation.n_name = 'PERU'" + }, + "table": { + "table_name": "customer", + "access_type": "ref", + "possible_keys": ["PRIMARY", "i_c_nationkey"], + "key": "i_c_nationkey", + "key_length": "5", + "used_key_parts": ["c_nationkey"], + "ref": ["dbt3_s001.nation.n_nationkey"], + "rows": 11, + "filtered": 100 + }, + "table": { + "table_name": "orders", + "access_type": "ref", + "possible_keys": ["i_o_orderdate", "i_o_custkey"], + "key": "i_o_custkey", + "key_length": "5", + "used_key_parts": ["o_custkey"], + "ref": ["dbt3_s001.customer.c_custkey"], + "rowid_filter": { + "range": { + "key": "i_o_orderdate", + "used_key_parts": ["o_orderDATE"] + }, + "rows": 108, + "selectivity_pct": 7.2 + }, + "rows": 11, + "filtered": 7.199999809, + "attached_condition": "orders.o_orderDATE between '1992-01-01' and '1992-06-30'" + } + } +} +select o_orderkey, o_totalprice 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 (select n_nationkey from nation +where n_name='PERU')); +o_orderkey o_totalprice +644 201268.06 +2880 145761.99 +3142 16030.15 +5382 138423.03 +5095 184583.99 +737 12984.85 +1729 12137.76 +5121 150334.57 +explain +update orders set o_totalprice = o_totalprice-50 where o_orderDATE between '1992-01-01' and '1992-06-30' and +o_custkey in (select c_custkey from customer +where c_nationkey in (select n_nationkey from nation +where n_name='PERU')); +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY nation ref PRIMARY,i_n_name i_n_name 26 const 1 Using index condition +1 PRIMARY customer ref PRIMARY,i_c_nationkey i_c_nationkey 5 dbt3_s001.nation.n_nationkey 11 +1 PRIMARY orders ref|filter i_o_orderdate,i_o_custkey i_o_custkey|i_o_orderdate 5|4 dbt3_s001.customer.c_custkey 11 (7%) Using where; Using rowid filter +explain format=json +update orders set o_totalprice = o_totalprice-50 where o_orderDATE between '1992-01-01' and '1992-06-30' and +o_custkey in (select c_custkey from customer +where c_nationkey in (select n_nationkey from nation +where n_name='PERU')); +EXPLAIN +{ + "query_block": { + "select_id": 1, + "table": { + "table_name": "nation", + "access_type": "ref", + "possible_keys": ["PRIMARY", "i_n_name"], + "key": "i_n_name", + "key_length": "26", + "used_key_parts": ["n_name"], + "ref": ["const"], + "rows": 1, + "filtered": 100, + "index_condition": "nation.n_name = 'PERU'" + }, + "table": { + "table_name": "customer", + "access_type": "ref", + "possible_keys": ["PRIMARY", "i_c_nationkey"], + "key": "i_c_nationkey", + "key_length": "5", + "used_key_parts": ["c_nationkey"], + "ref": ["dbt3_s001.nation.n_nationkey"], + "rows": 11, + "filtered": 100 + }, + "table": { + "table_name": "orders", + "access_type": "ref", + "possible_keys": ["i_o_orderdate", "i_o_custkey"], + "key": "i_o_custkey", + "key_length": "5", + "used_key_parts": ["o_custkey"], + "ref": ["dbt3_s001.customer.c_custkey"], + "rowid_filter": { + "range": { + "key": "i_o_orderdate", + "used_key_parts": ["o_orderDATE"] + }, + "rows": 108, + "selectivity_pct": 7.2 + }, + "rows": 11, + "filtered": 7.199999809, + "attached_condition": "orders.o_orderDATE between '1992-01-01' and '1992-06-30'" + } + } +} +update orders set o_totalprice = o_totalprice-50 where o_orderDATE between '1992-01-01' and '1992-06-30' and +o_custkey in (select c_custkey from customer +where c_nationkey in (select n_nationkey from nation +where n_name='PERU')); +select o_orderkey, o_totalprice 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 (select n_nationkey from nation +where n_name='PERU')); +o_orderkey o_totalprice +644 201218.06 +2880 145711.99 +3142 15980.15 +5382 138373.03 +5095 184533.99 +737 12934.85 +1729 12087.76 +5121 150284.57 +update orders set o_totalprice= o_totalprice+50 where o_orderDATE between '1992-01-01' and '1992-06-30' and +o_custkey in (select c_custkey from customer +where c_nationkey in (select n_nationkey from nation +where n_name='PERU')); +select o_orderkey, o_totalprice 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 (select n_nationkey from nation +where n_name='PERU')); +o_orderkey o_totalprice +644 201268.06 +2880 145761.99 +3142 16030.15 +5382 138423.03 +5095 184583.99 +737 12984.85 +1729 12137.76 +5121 150334.57 +explain +select ps_partkey, ps_suppkey, ps_supplycost from partsupp where (ps_partkey, ps_suppkey) in +(select p_partkey, s_suppkey from part, supplier +where p_retailprice between 901 and 910 and +s_nationkey in (select n_nationkey from nation +where n_name='PERU')); +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY nation ref PRIMARY,i_n_name i_n_name 26 const 1 Using index condition +1 PRIMARY supplier ref PRIMARY,i_s_nationkey i_s_nationkey 5 dbt3_s001.nation.n_nationkey 2 +1 PRIMARY partsupp ref PRIMARY,i_ps_partkey,i_ps_suppkey i_ps_suppkey 4 dbt3_s001.supplier.s_suppkey 16 +1 PRIMARY part eq_ref PRIMARY PRIMARY 4 dbt3_s001.partsupp.ps_partkey 1 Using where +select ps_partkey, ps_suppkey, ps_supplycost from partsupp where (ps_partkey, ps_suppkey) in +(select p_partkey, s_suppkey from part, supplier +where p_retailprice between 901 and 910 and +s_nationkey in (select n_nationkey from nation +where n_name='PERU')); +ps_partkey ps_suppkey ps_supplycost +4 1 444.37 +6 1 642.13 +8 1 957.34 +1 8 357.84 +3 8 645.4 +5 8 50.52 +7 8 763.98 +explain +update partsupp set ps_supplycost = ps_supplycost+2 where (ps_partkey, ps_suppkey) in +(select p_partkey, s_suppkey from part, supplier +where p_retailprice between 901 and 910 and +s_nationkey in (select n_nationkey from nation +where n_name='PERU')); +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY nation ref PRIMARY,i_n_name i_n_name 26 const 1 Using index condition +1 PRIMARY supplier ref PRIMARY,i_s_nationkey i_s_nationkey 5 dbt3_s001.nation.n_nationkey 2 +1 PRIMARY partsupp ref PRIMARY,i_ps_partkey,i_ps_suppkey i_ps_suppkey 4 dbt3_s001.supplier.s_suppkey 16 +1 PRIMARY part eq_ref PRIMARY PRIMARY 4 dbt3_s001.partsupp.ps_partkey 1 Using where +update partsupp set ps_supplycost = ps_supplycost+2 where (ps_partkey, ps_suppkey) in +(select p_partkey, s_suppkey from part, supplier +where p_retailprice between 901 and 910 and +s_nationkey in (select n_nationkey from nation +where n_name='PERU')); +select ps_partkey, ps_suppkey, ps_supplycost from partsupp where (ps_partkey, ps_suppkey) in +(select p_partkey, s_suppkey from part, supplier +where p_retailprice between 901 and 910 and +s_nationkey in (select n_nationkey from nation +where n_name='PERU')); +ps_partkey ps_suppkey ps_supplycost +4 1 446.37 +6 1 644.13 +8 1 959.34 +1 8 359.84 +3 8 647.4 +5 8 52.52 +7 8 765.98 +update partsupp set ps_supplycost = ps_supplycost-2 where (ps_partkey, ps_suppkey) in +(select p_partkey, s_suppkey from part, supplier +where p_retailprice between 901 and 910 and +s_nationkey in (select n_nationkey from nation +where n_name='PERU')); +select ps_partkey, ps_suppkey, ps_supplycost from partsupp where (ps_partkey, ps_suppkey) in +(select p_partkey, s_suppkey from part, supplier +where p_retailprice between 901 and 910 and +s_nationkey in (select n_nationkey from nation +where n_name='PERU')); +ps_partkey ps_suppkey ps_supplycost +4 1 444.37 +6 1 642.13 +8 1 957.34 +1 8 357.84 +3 8 645.4 +5 8 50.52 +7 8 763.98 +explain +select ps_partkey, ps_suppkey, ps_supplycost from partsupp where ps_partkey in (select p_partkey from part +where p_retailprice between 901 and 910) and +ps_suppkey in (select s_suppkey from supplier +where s_nationkey in (select n_nationkey from nation +where n_name='PERU')); +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY nation ref PRIMARY,i_n_name i_n_name 26 const 1 Using index condition +1 PRIMARY supplier ref PRIMARY,i_s_nationkey i_s_nationkey 5 dbt3_s001.nation.n_nationkey 2 +1 PRIMARY partsupp ref PRIMARY,i_ps_partkey,i_ps_suppkey i_ps_suppkey 4 dbt3_s001.supplier.s_suppkey 16 +1 PRIMARY part eq_ref PRIMARY PRIMARY 4 dbt3_s001.partsupp.ps_partkey 1 Using where +select ps_partkey, ps_suppkey, ps_supplycost from partsupp where ps_partkey in (select p_partkey from part +where p_retailprice between 901 and 910) and +ps_suppkey in (select s_suppkey from supplier +where s_nationkey in (select n_nationkey from nation +where n_name='PERU')); +ps_partkey ps_suppkey ps_supplycost +4 1 444.37 +6 1 642.13 +8 1 957.34 +1 8 357.84 +3 8 645.4 +5 8 50.52 +7 8 763.98 +explain +update partsupp set ps_supplycost = ps_supplycost+10 where ps_partkey in (select p_partkey from part +where p_retailprice between 901 and 910) and +ps_suppkey in (select s_suppkey from supplier +where s_nationkey in (select n_nationkey from nation +where n_name='PERU')); +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY nation ref PRIMARY,i_n_name i_n_name 26 const 1 Using index condition +1 PRIMARY supplier ref PRIMARY,i_s_nationkey i_s_nationkey 5 dbt3_s001.nation.n_nationkey 2 +1 PRIMARY partsupp ref PRIMARY,i_ps_partkey,i_ps_suppkey i_ps_suppkey 4 dbt3_s001.supplier.s_suppkey 16 +1 PRIMARY part eq_ref PRIMARY PRIMARY 4 dbt3_s001.partsupp.ps_partkey 1 Using where +update partsupp set ps_supplycost = ps_supplycost+10 where ps_partkey in (select p_partkey from part +where p_retailprice between 901 and 910) and +ps_suppkey in (select s_suppkey from supplier +where s_nationkey in (select n_nationkey from nation +where n_name='PERU')); +select ps_partkey, ps_suppkey, ps_supplycost from partsupp where ps_partkey in (select p_partkey from part +where p_retailprice between 901 and 910) and +ps_suppkey in (select s_suppkey from supplier +where s_nationkey in (select n_nationkey from nation +where n_name='PERU')); +ps_partkey ps_suppkey ps_supplycost +4 1 454.37 +6 1 652.13 +8 1 967.34 +1 8 367.84 +3 8 655.4 +5 8 60.52 +7 8 773.98 +update partsupp set ps_supplycost = ps_supplycost-10 where ps_partkey in (select p_partkey from part +where p_retailprice between 901 and 910) and +ps_suppkey in (select s_suppkey from supplier +where s_nationkey in (select n_nationkey from nation +where n_name='PERU')); +select ps_partkey, ps_suppkey, ps_supplycost from partsupp where ps_partkey in (select p_partkey from part +where p_retailprice between 901 and 910) and +ps_suppkey in (select s_suppkey from supplier +where s_nationkey in (select n_nationkey from nation +where n_name='PERU')); +ps_partkey ps_suppkey ps_supplycost +4 1 444.37 +6 1 642.13 +8 1 957.34 +1 8 357.84 +3 8 645.4 +5 8 50.52 +7 8 763.98 +explain +select l_orderkey, l_linenumber, l_tax from lineitem where l_orderkey in (select o_orderkey from orders +where o_custkey in +(select c_custkey from customer +where c_nationkey in +(select n_nationkey from nation +where n_name='PERU')) +and +o_orderDATE between '1992-06-30' and '1992-12-31') +and +(l_partkey, l_suppkey) in +(select p_partkey, s_suppkey from part, supplier +where p_retailprice between 901 and 1000 and +s_nationkey in (select n_nationkey from nation +where n_name='PERU')); +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY nation ref PRIMARY,i_n_name i_n_name 26 const 1 Using index condition +1 PRIMARY nation ref PRIMARY,i_n_name i_n_name 26 const 1 Using index condition +1 PRIMARY supplier ref PRIMARY,i_s_nationkey i_s_nationkey 5 dbt3_s001.nation.n_nationkey 2 +1 PRIMARY lineitem ref PRIMARY,i_l_suppkey_partkey,i_l_partkey,i_l_suppkey,i_l_orderkey,i_l_orderkey_quantity i_l_suppkey 5 dbt3_s001.supplier.s_suppkey 100 Using where +1 PRIMARY orders eq_ref|filter PRIMARY,i_o_orderdate,i_o_custkey PRIMARY|i_o_orderdate 4|4 dbt3_s001.lineitem.l_orderkey 1 (7%) Using where; Using rowid filter +1 PRIMARY part eq_ref PRIMARY PRIMARY 4 dbt3_s001.lineitem.l_partkey 1 Using where +1 PRIMARY customer eq_ref PRIMARY,i_c_nationkey PRIMARY 4 dbt3_s001.orders.o_custkey 1 Using where +select l_orderkey, l_linenumber, l_tax from lineitem where l_orderkey in (select o_orderkey from orders +where o_custkey in +(select c_custkey from customer +where c_nationkey in +(select n_nationkey from nation +where n_name='PERU')) +and +o_orderDATE between '1992-06-30' and '1992-12-31') +and +(l_partkey, l_suppkey) in +(select p_partkey, s_suppkey from part, supplier +where p_retailprice between 901 and 1000 and +s_nationkey in (select n_nationkey from nation +where n_name='PERU')); +l_orderkey l_linenumber l_tax +4996 1 0.01 +933 1 0.04 +2500 2 0.02 +2500 4 0.02 +explain +update lineitem set l_tax = (l_tax*100+1)/100 where l_orderkey in (select o_orderkey from orders +where o_custkey in +(select c_custkey from customer +where c_nationkey in +(select n_nationkey from nation +where n_name='PERU')) +and +o_orderDATE between '1992-06-30' and '1992-12-31') +and +(l_partkey, l_suppkey) in +(select p_partkey, s_suppkey from part, supplier +where p_retailprice between 901 and 1000 and +s_nationkey in (select n_nationkey from nation +where n_name='PERU')); +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY nation ref PRIMARY,i_n_name i_n_name 26 const 1 Using index condition +1 PRIMARY nation ref PRIMARY,i_n_name i_n_name 26 const 1 Using index condition +1 PRIMARY supplier ref PRIMARY,i_s_nationkey i_s_nationkey 5 dbt3_s001.nation.n_nationkey 2 +1 PRIMARY lineitem ref PRIMARY,i_l_suppkey_partkey,i_l_partkey,i_l_suppkey,i_l_orderkey,i_l_orderkey_quantity i_l_suppkey 5 dbt3_s001.supplier.s_suppkey 100 Using where +1 PRIMARY orders eq_ref|filter PRIMARY,i_o_orderdate,i_o_custkey PRIMARY|i_o_orderdate 4|4 dbt3_s001.lineitem.l_orderkey 1 (7%) Using where; Using rowid filter +1 PRIMARY part eq_ref PRIMARY PRIMARY 4 dbt3_s001.lineitem.l_partkey 1 Using where +1 PRIMARY customer eq_ref PRIMARY,i_c_nationkey PRIMARY 4 dbt3_s001.orders.o_custkey 1 Using where +update lineitem set l_tax = (l_tax*100+1)/100 where l_orderkey in (select o_orderkey from orders +where o_custkey in +(select c_custkey from customer +where c_nationkey in +(select n_nationkey from nation +where n_name='PERU')) +and +o_orderDATE between '1992-06-30' and '1992-12-31') +and +(l_partkey, l_suppkey) in +(select p_partkey, s_suppkey from part, supplier +where p_retailprice between 901 and 1000 and +s_nationkey in (select n_nationkey from nation +where n_name='PERU')); +select l_orderkey, l_linenumber, l_tax from lineitem where l_orderkey in (select o_orderkey from orders +where o_custkey in +(select c_custkey from customer +where c_nationkey in +(select n_nationkey from nation +where n_name='PERU')) +and +o_orderDATE between '1992-06-30' and '1992-12-31') +and +(l_partkey, l_suppkey) in +(select p_partkey, s_suppkey from part, supplier +where p_retailprice between 901 and 1000 and +s_nationkey in (select n_nationkey from nation +where n_name='PERU')); +l_orderkey l_linenumber l_tax +4996 1 0.02 +933 1 0.05 +2500 2 0.03 +2500 4 0.03 +update lineitem set l_tax = (l_tax*100-1)/100 where l_orderkey in (select o_orderkey from orders +where o_custkey in +(select c_custkey from customer +where c_nationkey in +(select n_nationkey from nation +where n_name='PERU')) +and +o_orderDATE between '1992-06-30' and '1992-12-31') +and +(l_partkey, l_suppkey) in +(select p_partkey, s_suppkey from part, supplier +where p_retailprice between 901 and 1000 and +s_nationkey in (select n_nationkey from nation +where n_name='PERU')); +select l_orderkey, l_linenumber, l_tax from lineitem where l_orderkey in (select o_orderkey from orders +where o_custkey in +(select c_custkey from customer +where c_nationkey in +(select n_nationkey from nation +where n_name='PERU')) +and +o_orderDATE between '1992-06-30' and '1992-12-31') +and +(l_partkey, l_suppkey) in +(select p_partkey, s_suppkey from part, supplier +where p_retailprice between 901 and 1000 and +s_nationkey in (select n_nationkey from nation +where n_name='PERU')); +l_orderkey l_linenumber l_tax +4996 1 0.01 +933 1 0.04 +2500 2 0.02 +2500 4 0.02 +# FirstMatch +# ========== +explain +select c_name, c_acctbal from customer where c_nationkey in (select n_nationkey from nation +where n_regionkey in (1,2)) +and +c_custkey in (select o_custkey from orders +where o_orderDATE between '1992-10-09' and '1993-03-08'); +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY customer ALL PRIMARY,i_c_nationkey NULL NULL NULL 150 Using where +1 PRIMARY nation eq_ref|filter PRIMARY,i_n_regionkey PRIMARY|i_n_regionkey 4|5 dbt3_s001.customer.c_nationkey 1 (40%) Using where; Using rowid filter +1 PRIMARY orders ref|filter i_o_orderdate,i_o_custkey i_o_custkey|i_o_orderdate 5|4 dbt3_s001.customer.c_custkey 11 (6%) Using where; FirstMatch(nation); Using rowid filter +explain format=json +select c_name, c_acctbal from customer where c_nationkey in (select n_nationkey from nation +where n_regionkey in (1,2)) +and +c_custkey in (select o_custkey from orders +where o_orderDATE between '1992-10-09' and '1993-03-08'); +EXPLAIN +{ + "query_block": { + "select_id": 1, + "table": { + "table_name": "customer", + "access_type": "ALL", + "possible_keys": ["PRIMARY", "i_c_nationkey"], + "rows": 150, + "filtered": 100, + "attached_condition": "customer.c_nationkey is not null" + }, + "table": { + "table_name": "nation", + "access_type": "eq_ref", + "possible_keys": ["PRIMARY", "i_n_regionkey"], + "key": "PRIMARY", + "key_length": "4", + "used_key_parts": ["n_nationkey"], + "ref": ["dbt3_s001.customer.c_nationkey"], + "rowid_filter": { + "range": { + "key": "i_n_regionkey", + "used_key_parts": ["n_regionkey"] + }, + "rows": 10, + "selectivity_pct": 40 + }, + "rows": 1, + "filtered": 40, + "attached_condition": "nation.n_regionkey in (1,2)" + }, + "table": { + "table_name": "orders", + "access_type": "ref", + "possible_keys": ["i_o_orderdate", "i_o_custkey"], + "key": "i_o_custkey", + "key_length": "5", + "used_key_parts": ["o_custkey"], + "ref": ["dbt3_s001.customer.c_custkey"], + "rowid_filter": { + "range": { + "key": "i_o_orderdate", + "used_key_parts": ["o_orderDATE"] + }, + "rows": 89, + "selectivity_pct": 5.933333333 + }, + "rows": 11, + "filtered": 5.933333397, + "attached_condition": "orders.o_orderDATE between '1992-10-09' and '1993-03-08'", + "first_match": "nation" + } + } +} +select c_name, c_acctbal from customer where c_nationkey in (select n_nationkey from nation +where n_regionkey in (1,2)) +and +c_custkey in (select o_custkey from orders +where o_orderDATE between '1992-10-09' and '1993-03-08'); +c_name c_acctbal +Customer#000000007 9561.95 +Customer#000000019 8914.71 +Customer#000000022 591.98 +Customer#000000025 7133.7 +Customer#000000028 1007.18 +Customer#000000037 -917.75 +Customer#000000040 1335.3 +Customer#000000047 274.58 +Customer#000000059 3458.6 +Customer#000000061 1536.24 +Customer#000000064 -646.64 +Customer#000000067 8166.59 +Customer#000000082 9468.34 +Customer#000000091 4643.14 +Customer#000000094 5500.11 +Customer#000000097 2164.48 +Customer#000000101 7470.96 +Customer#000000103 2757.45 +Customer#000000106 3288.42 +Customer#000000115 7508.92 +Customer#000000121 6428.32 +Customer#000000122 7865.46 +Customer#000000127 9280.71 +Customer#000000130 5073.58 +Customer#000000133 2314.67 +Customer#000000139 7897.78 +explain +update customer set c_acctbal = c_acctbal+10 where c_nationkey in (select n_nationkey from nation +where n_regionkey in (1,2)) +and +c_custkey in (select o_custkey from orders +where o_orderDATE between '1992-10-09' and '1993-03-08'); +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY customer ALL PRIMARY,i_c_nationkey NULL NULL NULL 150 Using where +1 PRIMARY nation eq_ref|filter PRIMARY,i_n_regionkey PRIMARY|i_n_regionkey 4|5 dbt3_s001.customer.c_nationkey 1 (40%) Using where; Using rowid filter +1 PRIMARY orders ref|filter i_o_orderdate,i_o_custkey i_o_custkey|i_o_orderdate 5|4 dbt3_s001.customer.c_custkey 11 (6%) Using where; FirstMatch(nation); Using rowid filter +explain format=json +update customer set c_acctbal = c_acctbal+10 where c_nationkey in (select n_nationkey from nation +where n_regionkey in (1,2)) +and +c_custkey in (select o_custkey from orders +where o_orderDATE between '1992-10-09' and '1993-03-08'); +EXPLAIN +{ + "query_block": { + "select_id": 1, + "table": { + "table_name": "customer", + "access_type": "ALL", + "possible_keys": ["PRIMARY", "i_c_nationkey"], + "rows": 150, + "filtered": 100, + "attached_condition": "customer.c_nationkey is not null" + }, + "table": { + "table_name": "nation", + "access_type": "eq_ref", + "possible_keys": ["PRIMARY", "i_n_regionkey"], + "key": "PRIMARY", + "key_length": "4", + "used_key_parts": ["n_nationkey"], + "ref": ["dbt3_s001.customer.c_nationkey"], + "rowid_filter": { + "range": { + "key": "i_n_regionkey", + "used_key_parts": ["n_regionkey"] + }, + "rows": 10, + "selectivity_pct": 40 + }, + "rows": 1, + "filtered": 40, + "attached_condition": "nation.n_regionkey in (1,2)" + }, + "table": { + "table_name": "orders", + "access_type": "ref", + "possible_keys": ["i_o_orderdate", "i_o_custkey"], + "key": "i_o_custkey", + "key_length": "5", + "used_key_parts": ["o_custkey"], + "ref": ["dbt3_s001.customer.c_custkey"], + "rowid_filter": { + "range": { + "key": "i_o_orderdate", + "used_key_parts": ["o_orderDATE"] + }, + "rows": 89, + "selectivity_pct": 5.933333333 + }, + "rows": 11, + "filtered": 5.933333397, + "attached_condition": "orders.o_orderDATE between '1992-10-09' and '1993-03-08'", + "first_match": "nation" + } + } +} +update customer set c_acctbal = c_acctbal+10 where c_nationkey in (select n_nationkey from nation +where n_regionkey in (1,2)) +and +c_custkey in (select o_custkey from orders +where o_orderDATE between '1992-10-09' and '1993-03-08'); +select c_name, c_acctbal from customer where c_nationkey in (select n_nationkey from nation +where n_regionkey in (1,2)) +and +c_custkey in (select o_custkey from orders +where o_orderDATE between '1992-10-09' and '1993-03-08'); +c_name c_acctbal +Customer#000000007 9571.95 +Customer#000000019 8924.71 +Customer#000000022 601.98 +Customer#000000025 7143.7 +Customer#000000028 1017.18 +Customer#000000037 -907.75 +Customer#000000040 1345.3 +Customer#000000047 284.58 +Customer#000000059 3468.6 +Customer#000000061 1546.24 +Customer#000000064 -636.64 +Customer#000000067 8176.59 +Customer#000000082 9478.34 +Customer#000000091 4653.14 +Customer#000000094 5510.11 +Customer#000000097 2174.48 +Customer#000000101 7480.96 +Customer#000000103 2767.45 +Customer#000000106 3298.42 +Customer#000000115 7518.92 +Customer#000000121 6438.32 +Customer#000000122 7875.46 +Customer#000000127 9290.71 +Customer#000000130 5083.58 +Customer#000000133 2324.67 +Customer#000000139 7907.78 +update customer set c_acctbal = c_acctbal-10 where c_nationkey in (select n_nationkey from nation +where n_regionkey in (1,2)) +and +c_custkey in (select o_custkey from orders +where o_orderDATE between '1992-10-09' and '1993-03-08'); +select c_name, c_acctbal from customer where c_nationkey in (select n_nationkey from nation +where n_regionkey in (1,2)) +and +c_custkey in (select o_custkey from orders +where o_orderDATE between '1992-10-09' and '1993-03-08'); +c_name c_acctbal +Customer#000000007 9561.95 +Customer#000000019 8914.71 +Customer#000000022 591.98 +Customer#000000025 7133.7 +Customer#000000028 1007.18 +Customer#000000037 -917.75 +Customer#000000040 1335.3 +Customer#000000047 274.58 +Customer#000000059 3458.6 +Customer#000000061 1536.24 +Customer#000000064 -646.64 +Customer#000000067 8166.59 +Customer#000000082 9468.34 +Customer#000000091 4643.14 +Customer#000000094 5500.11 +Customer#000000097 2164.48 +Customer#000000101 7470.96 +Customer#000000103 2757.45 +Customer#000000106 3288.42 +Customer#000000115 7508.92 +Customer#000000121 6428.32 +Customer#000000122 7865.46 +Customer#000000127 9280.71 +Customer#000000130 5073.58 +Customer#000000133 2314.67 +Customer#000000139 7897.78 +explain +select c_name, c_acctbal from customer where c_nationkey in (select n_nationkey from nation where n_name='PERU') +and +c_custkey in (select o_custkey from orders +where o_orderDATE between "1992-01-09" and "1993-01-08"); +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY nation ref PRIMARY,i_n_name i_n_name 26 const 1 Using index condition +1 PRIMARY customer ref PRIMARY,i_c_nationkey i_c_nationkey 5 dbt3_s001.nation.n_nationkey 11 +1 PRIMARY orders ref|filter i_o_orderdate,i_o_custkey i_o_custkey|i_o_orderdate 5|4 dbt3_s001.customer.c_custkey 11 (14%) Using where; FirstMatch(customer); Using rowid filter +select c_name, c_acctbal from customer where c_nationkey in (select n_nationkey from nation where n_name='PERU') +and +c_custkey in (select o_custkey from orders +where o_orderDATE between "1992-01-09" and "1993-01-08"); +c_name c_acctbal +Customer#000000008 6819.74 +Customer#000000035 1228.24 +Customer#000000061 1536.24 +Customer#000000097 2164.48 +Customer#000000121 6428.32 +Customer#000000133 2314.67 +explain +update customer set c_acctbal = c_acctbal+20 where c_nationkey in (select n_nationkey from nation where n_name='PERU') +and +c_custkey in (select o_custkey from orders +where o_orderDATE between "1992-01-09" and "1993-01-08"); +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY nation ref PRIMARY,i_n_name i_n_name 26 const 1 Using index condition +1 PRIMARY customer ref PRIMARY,i_c_nationkey i_c_nationkey 5 dbt3_s001.nation.n_nationkey 11 +1 PRIMARY orders ref|filter i_o_orderdate,i_o_custkey i_o_custkey|i_o_orderdate 5|4 dbt3_s001.customer.c_custkey 11 (14%) Using where; FirstMatch(customer); Using rowid filter +update customer set c_acctbal = c_acctbal+20 where c_nationkey in (select n_nationkey from nation where n_name='PERU') +and +c_custkey in (select o_custkey from orders +where o_orderDATE between "1992-01-09" and "1993-01-08"); +select c_name, c_acctbal from customer where c_nationkey in (select n_nationkey from nation where n_name='PERU') +and +c_custkey in (select o_custkey from orders +where o_orderDATE between "1992-01-09" and "1993-01-08"); +c_name c_acctbal +Customer#000000008 6839.74 +Customer#000000035 1248.24 +Customer#000000061 1556.24 +Customer#000000097 2184.48 +Customer#000000121 6448.32 +Customer#000000133 2334.67 +update customer set c_acctbal = c_acctbal-20 where c_nationkey in (select n_nationkey from nation where n_name='PERU') +and +c_custkey in (select o_custkey from orders +where o_orderDATE between "1992-01-09" and "1993-01-08"); +select c_name, c_acctbal from customer where c_nationkey in (select n_nationkey from nation where n_name='PERU') +and +c_custkey in (select o_custkey from orders +where o_orderDATE between "1992-01-09" and "1993-01-08"); +c_name c_acctbal +Customer#000000008 6819.74 +Customer#000000035 1228.24 +Customer#000000061 1536.24 +Customer#000000097 2164.48 +Customer#000000121 6428.32 +Customer#000000133 2314.67 +# Materialization +# =============== +explain +select c_name, c_acctbal from customer where c_custkey in (select o_custkey from orders +where o_orderDATE between '1992-01-09' and '1992-03-08'); +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY <subquery2> ALL distinct_key NULL NULL NULL 28 +1 PRIMARY customer eq_ref PRIMARY PRIMARY 4 dbt3_s001.orders.o_custkey 1 +2 MATERIALIZED orders range i_o_orderdate,i_o_custkey i_o_orderdate 4 NULL 28 Using index condition; Using where +explain format=json +select c_name, c_acctbal from customer where c_custkey in (select o_custkey from orders +where o_orderDATE between '1992-01-09' and '1992-03-08'); +EXPLAIN +{ + "query_block": { + "select_id": 1, + "table": { + "table_name": "<subquery2>", + "access_type": "ALL", + "possible_keys": ["distinct_key"], + "rows": 28, + "filtered": 100, + "materialized": { + "unique": 1, + "query_block": { + "select_id": 2, + "table": { + "table_name": "orders", + "access_type": "range", + "possible_keys": ["i_o_orderdate", "i_o_custkey"], + "key": "i_o_orderdate", + "key_length": "4", + "used_key_parts": ["o_orderDATE"], + "rows": 28, + "filtered": 100, + "index_condition": "orders.o_orderDATE between '1992-01-09' and '1992-03-08'", + "attached_condition": "orders.o_custkey is not null" + } + } + } + }, + "table": { + "table_name": "customer", + "access_type": "eq_ref", + "possible_keys": ["PRIMARY"], + "key": "PRIMARY", + "key_length": "4", + "used_key_parts": ["c_custkey"], + "ref": ["dbt3_s001.orders.o_custkey"], + "rows": 1, + "filtered": 100 + } + } +} +select c_name, c_acctbal from customer where c_custkey in (select o_custkey from orders +where o_orderDATE between '1992-01-09' and '1992-03-08'); +c_name c_acctbal +Customer#000000025 7133.7 +Customer#000000013 3857.34 +Customer#000000065 8795.16 +Customer#000000032 3471.53 +Customer#000000023 3332.02 +Customer#000000035 1228.24 +Customer#000000091 4643.14 +Customer#000000016 4681.03 +Customer#000000098 -551.37 +Customer#000000037 -917.75 +Customer#000000136 -842.39 +Customer#000000118 3582.37 +Customer#000000022 591.98 +Customer#000000005 794.47 +Customer#000000109 -716.1 +Customer#000000038 6345.11 +Customer#000000076 5745.33 +Customer#000000056 6530.86 +Customer#000000040 1335.3 +Customer#000000116 8403.99 +Customer#000000115 7508.92 +Customer#000000140 9963.15 +Customer#000000017 6.34 +Customer#000000052 5630.28 +explain +update customer set c_acctbal = c_acctbal+5 where c_custkey in (select o_custkey from orders +where o_orderDATE between '1992-01-09' and '1992-03-08'); +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY <subquery2> ALL distinct_key NULL NULL NULL 28 +1 PRIMARY customer eq_ref PRIMARY PRIMARY 4 dbt3_s001.orders.o_custkey 1 +2 MATERIALIZED orders range i_o_orderdate,i_o_custkey i_o_orderdate 4 NULL 28 Using index condition; Using where +explain format=json +update customer set c_acctbal = c_acctbal+5 where c_custkey in (select o_custkey from orders +where o_orderDATE between '1992-01-09' and '1992-03-08'); +EXPLAIN +{ + "query_block": { + "select_id": 1, + "table": { + "table_name": "<subquery2>", + "access_type": "ALL", + "possible_keys": ["distinct_key"], + "rows": 28, + "filtered": 100, + "materialized": { + "unique": 1, + "query_block": { + "select_id": 2, + "table": { + "table_name": "orders", + "access_type": "range", + "possible_keys": ["i_o_orderdate", "i_o_custkey"], + "key": "i_o_orderdate", + "key_length": "4", + "used_key_parts": ["o_orderDATE"], + "rows": 28, + "filtered": 100, + "index_condition": "orders.o_orderDATE between '1992-01-09' and '1992-03-08'", + "attached_condition": "orders.o_custkey is not null" + } + } + } + }, + "table": { + "table_name": "customer", + "access_type": "eq_ref", + "possible_keys": ["PRIMARY"], + "key": "PRIMARY", + "key_length": "4", + "used_key_parts": ["c_custkey"], + "ref": ["dbt3_s001.orders.o_custkey"], + "rows": 1, + "filtered": 100 + } + } +} +update customer set c_acctbal = c_acctbal+5 where c_custkey in (select o_custkey from orders +where o_orderDATE between '1992-01-09' and '1992-03-08'); +select c_name, c_acctbal from customer where c_custkey in (select o_custkey from orders +where o_orderDATE between '1992-01-09' and '1992-03-08'); +c_name c_acctbal +Customer#000000025 7138.7 +Customer#000000013 3862.34 +Customer#000000065 8800.16 +Customer#000000032 3476.53 +Customer#000000023 3337.02 +Customer#000000035 1233.24 +Customer#000000091 4648.14 +Customer#000000016 4686.03 +Customer#000000098 -546.37 +Customer#000000037 -912.75 +Customer#000000136 -837.39 +Customer#000000118 3587.37 +Customer#000000022 596.98 +Customer#000000005 799.47 +Customer#000000109 -711.1 +Customer#000000038 6350.11 +Customer#000000076 5750.33 +Customer#000000056 6535.86 +Customer#000000040 1340.3 +Customer#000000116 8408.99 +Customer#000000115 7513.92 +Customer#000000140 9968.15 +Customer#000000017 11.34 +Customer#000000052 5635.28 +update customer set c_acctbal = c_acctbal-5 where c_custkey in (select o_custkey from orders +where o_orderDATE between '1992-01-09' and '1992-03-08'); +select c_name, c_acctbal from customer where c_custkey in (select o_custkey from orders +where o_orderDATE between '1992-01-09' and '1992-03-08'); +c_name c_acctbal +Customer#000000025 7133.7 +Customer#000000013 3857.34 +Customer#000000065 8795.16 +Customer#000000032 3471.53 +Customer#000000023 3332.02 +Customer#000000035 1228.24 +Customer#000000091 4643.14 +Customer#000000016 4681.03 +Customer#000000098 -551.37 +Customer#000000037 -917.75 +Customer#000000136 -842.39 +Customer#000000118 3582.37 +Customer#000000022 591.98 +Customer#000000005 794.47 +Customer#000000109 -716.1 +Customer#000000038 6345.11 +Customer#000000076 5745.33 +Customer#000000056 6530.86 +Customer#000000040 1335.3 +Customer#000000116 8403.99 +Customer#000000115 7508.92 +Customer#000000140 9963.15 +Customer#000000017 6.34 +Customer#000000052 5630.28 +explain +select c_name, c_acctbal from customer where c_custkey in (select o_custkey from orders +where o_orderDATE between '1992-06-09' and '1993-01-08'); +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY customer ALL PRIMARY NULL NULL NULL 150 +1 PRIMARY <subquery2> eq_ref distinct_key distinct_key 4 func 1 +2 MATERIALIZED orders range i_o_orderdate,i_o_custkey i_o_orderdate 4 NULL 114 Using index condition; Using where +select c_name, c_acctbal from customer where c_custkey in (select o_custkey from orders +where o_orderDATE between '1992-06-09' and '1993-01-08'); +c_name c_acctbal +Customer#000000001 711.56 +Customer#000000002 121.65 +Customer#000000007 9561.95 +Customer#000000008 6819.74 +Customer#000000010 2753.54 +Customer#000000011 -272.6 +Customer#000000016 4681.03 +Customer#000000017 6.34 +Customer#000000019 8914.71 +Customer#000000022 591.98 +Customer#000000023 3332.02 +Customer#000000025 7133.7 +Customer#000000028 1007.18 +Customer#000000029 7618.27 +Customer#000000031 5236.89 +Customer#000000034 8589.7 +Customer#000000037 -917.75 +Customer#000000040 1335.3 +Customer#000000043 9904.28 +Customer#000000044 7315.94 +Customer#000000046 5744.59 +Customer#000000047 274.58 +Customer#000000049 4573.94 +Customer#000000053 4113.64 +Customer#000000055 4572.11 +Customer#000000061 1536.24 +Customer#000000064 -646.64 +Customer#000000067 8166.59 +Customer#000000070 4867.52 +Customer#000000071 -611.19 +Customer#000000073 4288.5 +Customer#000000074 2764.43 +Customer#000000076 5745.33 +Customer#000000079 5121.28 +Customer#000000080 7383.53 +Customer#000000082 9468.34 +Customer#000000083 6463.51 +Customer#000000085 3386.64 +Customer#000000086 3306.32 +Customer#000000088 8031.44 +Customer#000000091 4643.14 +Customer#000000092 1182.91 +Customer#000000095 5327.38 +Customer#000000097 2164.48 +Customer#000000100 9889.89 +Customer#000000101 7470.96 +Customer#000000103 2757.45 +Customer#000000104 -588.38 +Customer#000000106 3288.42 +Customer#000000109 -716.1 +Customer#000000110 7462.99 +Customer#000000112 2953.35 +Customer#000000118 3582.37 +Customer#000000121 6428.32 +Customer#000000122 7865.46 +Customer#000000127 9280.71 +Customer#000000130 5073.58 +Customer#000000131 8595.53 +Customer#000000133 2314.67 +Customer#000000134 4608.9 +Customer#000000136 -842.39 +Customer#000000137 7838.3 +Customer#000000139 7897.78 +Customer#000000142 2209.81 +Customer#000000143 2186.5 +Customer#000000148 2135.6 +Customer#000000149 8959.65 +explain +update customer set c_acctbal = c_acctbal+1 where c_custkey in (select o_custkey from orders +where o_orderDATE between '1992-06-09' and '1993-01-08'); +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY customer ALL PRIMARY NULL NULL NULL 150 +1 PRIMARY <subquery2> eq_ref distinct_key distinct_key 4 func 1 +2 MATERIALIZED orders range i_o_orderdate,i_o_custkey i_o_orderdate 4 NULL 114 Using index condition; Using where +update customer set c_acctbal = c_acctbal+1 where c_custkey in (select o_custkey from orders +where o_orderDATE between '1992-06-09' and '1993-01-08'); +select c_name, c_acctbal from customer where c_custkey in (select o_custkey from orders +where o_orderDATE between '1992-06-09' and '1993-01-08'); +c_name c_acctbal +Customer#000000001 712.56 +Customer#000000002 122.65 +Customer#000000007 9562.95 +Customer#000000008 6820.74 +Customer#000000010 2754.54 +Customer#000000011 -271.6 +Customer#000000016 4682.03 +Customer#000000017 7.34 +Customer#000000019 8915.71 +Customer#000000022 592.98 +Customer#000000023 3333.02 +Customer#000000025 7134.7 +Customer#000000028 1008.18 +Customer#000000029 7619.27 +Customer#000000031 5237.89 +Customer#000000034 8590.7 +Customer#000000037 -916.75 +Customer#000000040 1336.3 +Customer#000000043 9905.28 +Customer#000000044 7316.94 +Customer#000000046 5745.59 +Customer#000000047 275.58 +Customer#000000049 4574.94 +Customer#000000053 4114.64 +Customer#000000055 4573.11 +Customer#000000061 1537.24 +Customer#000000064 -645.64 +Customer#000000067 8167.59 +Customer#000000070 4868.52 +Customer#000000071 -610.19 +Customer#000000073 4289.5 +Customer#000000074 2765.43 +Customer#000000076 5746.33 +Customer#000000079 5122.28 +Customer#000000080 7384.53 +Customer#000000082 9469.34 +Customer#000000083 6464.51 +Customer#000000085 3387.64 +Customer#000000086 3307.32 +Customer#000000088 8032.44 +Customer#000000091 4644.14 +Customer#000000092 1183.91 +Customer#000000095 5328.38 +Customer#000000097 2165.48 +Customer#000000100 9890.89 +Customer#000000101 7471.96 +Customer#000000103 2758.45 +Customer#000000104 -587.38 +Customer#000000106 3289.42 +Customer#000000109 -715.1 +Customer#000000110 7463.99 +Customer#000000112 2954.35 +Customer#000000118 3583.37 +Customer#000000121 6429.32 +Customer#000000122 7866.46 +Customer#000000127 9281.71 +Customer#000000130 5074.58 +Customer#000000131 8596.53 +Customer#000000133 2315.67 +Customer#000000134 4609.9 +Customer#000000136 -841.39 +Customer#000000137 7839.3 +Customer#000000139 7898.78 +Customer#000000142 2210.81 +Customer#000000143 2187.5 +Customer#000000148 2136.6 +Customer#000000149 8960.65 +update customer set c_acctbal = c_acctbal-1 where c_custkey in (select o_custkey from orders +where o_orderDATE between '1992-06-09' and '1993-01-08'); +select c_name, c_acctbal from customer where c_custkey in (select o_custkey from orders +where o_orderDATE between '1992-06-09' and '1993-01-08'); +c_name c_acctbal +Customer#000000001 711.56 +Customer#000000002 121.65 +Customer#000000007 9561.95 +Customer#000000008 6819.74 +Customer#000000010 2753.54 +Customer#000000011 -272.6 +Customer#000000016 4681.03 +Customer#000000017 6.34 +Customer#000000019 8914.71 +Customer#000000022 591.98 +Customer#000000023 3332.02 +Customer#000000025 7133.7 +Customer#000000028 1007.18 +Customer#000000029 7618.27 +Customer#000000031 5236.89 +Customer#000000034 8589.7 +Customer#000000037 -917.75 +Customer#000000040 1335.3 +Customer#000000043 9904.28 +Customer#000000044 7315.94 +Customer#000000046 5744.59 +Customer#000000047 274.58 +Customer#000000049 4573.94 +Customer#000000053 4113.64 +Customer#000000055 4572.11 +Customer#000000061 1536.24 +Customer#000000064 -646.64 +Customer#000000067 8166.59 +Customer#000000070 4867.52 +Customer#000000071 -611.19 +Customer#000000073 4288.5 +Customer#000000074 2764.43 +Customer#000000076 5745.33 +Customer#000000079 5121.28 +Customer#000000080 7383.53 +Customer#000000082 9468.34 +Customer#000000083 6463.51 +Customer#000000085 3386.64 +Customer#000000086 3306.32 +Customer#000000088 8031.44 +Customer#000000091 4643.14 +Customer#000000092 1182.91 +Customer#000000095 5327.38 +Customer#000000097 2164.48 +Customer#000000100 9889.89 +Customer#000000101 7470.96 +Customer#000000103 2757.45 +Customer#000000104 -588.38 +Customer#000000106 3288.42 +Customer#000000109 -716.1 +Customer#000000110 7462.99 +Customer#000000112 2953.35 +Customer#000000118 3582.37 +Customer#000000121 6428.32 +Customer#000000122 7865.46 +Customer#000000127 9280.71 +Customer#000000130 5073.58 +Customer#000000131 8595.53 +Customer#000000133 2314.67 +Customer#000000134 4608.9 +Customer#000000136 -842.39 +Customer#000000137 7838.3 +Customer#000000139 7897.78 +Customer#000000142 2209.81 +Customer#000000143 2186.5 +Customer#000000148 2135.6 +Customer#000000149 8959.65 +# Materialization SJM +# =================== +explain +select c_name, c_acctbal from customer where c_custkey in (select o_custkey from orders +where o_orderDATE between '1992-01-09' and '1992-03-08' + group by o_custkey having count(o_custkey) > 1); +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY <subquery2> ALL distinct_key NULL NULL NULL 28 +1 PRIMARY customer eq_ref PRIMARY PRIMARY 4 <subquery2>.o_custkey 1 +2 MATERIALIZED orders range i_o_orderdate i_o_orderdate 4 NULL 28 Using index condition; Using temporary +explain format=json +select c_name, c_acctbal from customer where c_custkey in (select o_custkey from orders +where o_orderDATE between '1992-01-09' and '1992-03-08' + group by o_custkey having count(o_custkey) > 1); +EXPLAIN +{ + "query_block": { + "select_id": 1, + "table": { + "table_name": "<subquery2>", + "access_type": "ALL", + "possible_keys": ["distinct_key"], + "rows": 28, + "filtered": 100, + "materialized": { + "unique": 1, + "query_block": { + "select_id": 2, + "having_condition": "count(orders.o_custkey) > 1", + "temporary_table": { + "table": { + "table_name": "orders", + "access_type": "range", + "possible_keys": ["i_o_orderdate"], + "key": "i_o_orderdate", + "key_length": "4", + "used_key_parts": ["o_orderDATE"], + "rows": 28, + "filtered": 100, + "index_condition": "orders.o_orderDATE between '1992-01-09' and '1992-03-08'" + } + } + } + } + }, + "table": { + "table_name": "customer", + "access_type": "eq_ref", + "possible_keys": ["PRIMARY"], + "key": "PRIMARY", + "key_length": "4", + "used_key_parts": ["c_custkey"], + "ref": ["<subquery2>.o_custkey"], + "rows": 1, + "filtered": 100 + } + } +} +select c_name, c_acctbal from customer where c_custkey in (select o_custkey from orders +where o_orderDATE between '1992-01-09' and '1992-03-08' + group by o_custkey having count(o_custkey) > 1); +c_name c_acctbal +Customer#000000013 3857.34 +Customer#000000032 3471.53 +Customer#000000037 -917.75 +Customer#000000118 3582.37 +Customer#000000056 6530.86 +explain +update customer set c_acctbal = c_acctbal-5 where c_custkey in (select o_custkey from orders +where o_orderDATE between '1992-01-09' and '1992-03-08' + group by o_custkey having count(o_custkey) > 1); +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY <subquery2> ALL distinct_key NULL NULL NULL 28 +1 PRIMARY customer eq_ref PRIMARY PRIMARY 4 <subquery2>.o_custkey 1 +2 MATERIALIZED orders range i_o_orderdate i_o_orderdate 4 NULL 28 Using index condition; Using temporary +explain format=json +update customer set c_acctbal = c_acctbal-5 where c_custkey in (select o_custkey from orders +where o_orderDATE between '1992-01-09' and '1992-03-08' + group by o_custkey having count(o_custkey) > 1); +EXPLAIN +{ + "query_block": { + "select_id": 1, + "table": { + "table_name": "<subquery2>", + "access_type": "ALL", + "possible_keys": ["distinct_key"], + "rows": 28, + "filtered": 100, + "materialized": { + "unique": 1, + "query_block": { + "select_id": 2, + "having_condition": "count(orders.o_custkey) > 1", + "temporary_table": { + "table": { + "table_name": "orders", + "access_type": "range", + "possible_keys": ["i_o_orderdate"], + "key": "i_o_orderdate", + "key_length": "4", + "used_key_parts": ["o_orderDATE"], + "rows": 28, + "filtered": 100, + "index_condition": "orders.o_orderDATE between '1992-01-09' and '1992-03-08'" + } + } + } + } + }, + "table": { + "table_name": "customer", + "access_type": "eq_ref", + "possible_keys": ["PRIMARY"], + "key": "PRIMARY", + "key_length": "4", + "used_key_parts": ["c_custkey"], + "ref": ["<subquery2>.o_custkey"], + "rows": 1, + "filtered": 100 + } + } +} +update customer set c_acctbal = c_acctbal-5 where c_custkey in (select o_custkey from orders +where o_orderDATE between '1992-01-09' and '1992-03-08' + group by o_custkey having count(o_custkey) > 1); +select c_name, c_acctbal from customer where c_custkey in (select o_custkey from orders +where o_orderDATE between '1992-01-09' and '1992-03-08' + group by o_custkey having count(o_custkey) > 1); +c_name c_acctbal +Customer#000000013 3852.34 +Customer#000000032 3466.53 +Customer#000000037 -922.75 +Customer#000000118 3577.37 +Customer#000000056 6525.86 +update customer set c_acctbal = c_acctbal+5 where c_custkey in (select o_custkey from orders +where o_orderDATE between '1992-01-09' and '1992-03-08' + group by o_custkey having count(o_custkey) > 1); +select c_name, c_acctbal from customer where c_custkey in (select o_custkey from orders +where o_orderDATE between '1992-01-09' and '1992-03-08' + group by o_custkey having count(o_custkey) > 1); +c_name c_acctbal +Customer#000000013 3857.34 +Customer#000000032 3471.53 +Customer#000000037 -917.75 +Customer#000000118 3582.37 +Customer#000000056 6530.86 +explain +select c_name, c_acctbal from customer where c_custkey in (select o_custkey from orders +where o_orderDATE between '1992-01-09' and '1993-03-08' + group by o_custkey having count(o_custkey) > 5); +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY customer ALL PRIMARY NULL NULL NULL 150 +1 PRIMARY <subquery2> eq_ref distinct_key distinct_key 4 dbt3_s001.customer.c_custkey 1 +2 MATERIALIZED orders range i_o_orderdate i_o_orderdate 4 NULL 242 Using index condition; Using temporary +select c_name, c_acctbal from customer where c_custkey in (select o_custkey from orders +where o_orderDATE between '1992-01-09' and '1993-03-08' + group by o_custkey having count(o_custkey) > 5); +c_name c_acctbal +Customer#000000007 9561.95 +Customer#000000016 4681.03 +Customer#000000037 -917.75 +Customer#000000046 5744.59 +Customer#000000091 4643.14 +Customer#000000103 2757.45 +Customer#000000118 3582.37 +Customer#000000133 2314.67 +Customer#000000134 4608.9 +explain +update customer set c_acctbal = c_acctbal-1 where c_custkey in (select o_custkey from orders +where o_orderDATE between '1992-01-09' and '1993-03-08' + group by o_custkey having count(o_custkey) > 5); +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY customer ALL PRIMARY NULL NULL NULL 150 +1 PRIMARY <subquery2> eq_ref distinct_key distinct_key 4 dbt3_s001.customer.c_custkey 1 +2 MATERIALIZED orders range i_o_orderdate i_o_orderdate 4 NULL 242 Using index condition; Using temporary +update customer set c_acctbal = c_acctbal-1 where c_custkey in (select o_custkey from orders +where o_orderDATE between '1992-01-09' and '1993-03-08' + group by o_custkey having count(o_custkey) > 5); +select c_name, c_acctbal from customer where c_custkey in (select o_custkey from orders +where o_orderDATE between '1992-01-09' and '1993-03-08' + group by o_custkey having count(o_custkey) > 5); +c_name c_acctbal +Customer#000000007 9560.95 +Customer#000000016 4680.03 +Customer#000000037 -918.75 +Customer#000000046 5743.59 +Customer#000000091 4642.14 +Customer#000000103 2756.45 +Customer#000000118 3581.37 +Customer#000000133 2313.67 +Customer#000000134 4607.9 +update customer set c_acctbal = c_acctbal+1 where c_custkey in (select o_custkey from orders +where o_orderDATE between '1992-01-09' and '1993-03-08' + group by o_custkey having count(o_custkey) > 5); +select c_name, c_acctbal from customer where c_custkey in (select o_custkey from orders +where o_orderDATE between '1992-01-09' and '1993-03-08' + group by o_custkey having count(o_custkey) > 5); +c_name c_acctbal +Customer#000000007 9561.95 +Customer#000000016 4681.03 +Customer#000000037 -917.75 +Customer#000000046 5744.59 +Customer#000000091 4643.14 +Customer#000000103 2757.45 +Customer#000000118 3582.37 +Customer#000000133 2314.67 +Customer#000000134 4608.9 +# Pullout PS +# ========== +prepare stmt from " +update orders set o_totalprice = o_totalprice+? where o_orderDATE between '1992-01-01' and '1992-06-30' and +o_custkey in (select c_custkey from customer +where c_nationkey in (select n_nationkey from nation +where n_name='PERU')); +"; +select o_orderkey, o_totalprice 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 (select n_nationkey from nation +where n_name='PERU')); +o_orderkey o_totalprice +644 201268.06 +2880 145761.99 +3142 16030.15 +5382 138423.03 +5095 184583.99 +737 12984.85 +1729 12137.76 +5121 150334.57 +set @a1=-20; +execute stmt using @a1; +select o_orderkey, o_totalprice 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 (select n_nationkey from nation +where n_name='PERU')); +o_orderkey o_totalprice +644 201248.06 +2880 145741.99 +3142 16010.15 +5382 138403.03 +5095 184563.99 +737 12964.85 +1729 12117.76 +5121 150314.57 +set @a2=-10; +execute stmt using @a2; +select o_orderkey, o_totalprice 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 (select n_nationkey from nation +where n_name='PERU')); +o_orderkey o_totalprice +644 201238.06 +2880 145731.99 +3142 16000.15 +5382 138393.03 +5095 184553.99 +737 12954.85 +1729 12107.76 +5121 150304.57 +execute stmt using -(@a1+@a2); +select o_orderkey, o_totalprice 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 (select n_nationkey from nation +where n_name='PERU')); +o_orderkey o_totalprice +644 201268.06 +2880 145761.99 +3142 16030.15 +5382 138423.03 +5095 184583.99 +737 12984.85 +1729 12137.76 +5121 150334.57 +deallocate prepare stmt; +# FirstMatch PS +# ============= +prepare stmt from " +update customer set c_acctbal = c_acctbal+? where c_nationkey in (select n_nationkey from nation +where n_regionkey in (1,2)) +and +c_custkey in (select o_custkey from orders +where o_orderDATE between '1992-10-09' and '1993-03-08'); +"; +select c_name, c_acctbal from customer where c_nationkey in (select n_nationkey from nation +where n_regionkey in (1,2)) +and +c_custkey in (select o_custkey from orders +where o_orderDATE between '1992-10-09' and '1993-03-08'); +c_name c_acctbal +Customer#000000007 9561.95 +Customer#000000019 8914.71 +Customer#000000022 591.98 +Customer#000000025 7133.7 +Customer#000000028 1007.18 +Customer#000000037 -917.75 +Customer#000000040 1335.3 +Customer#000000047 274.58 +Customer#000000059 3458.6 +Customer#000000061 1536.24 +Customer#000000064 -646.64 +Customer#000000067 8166.59 +Customer#000000082 9468.34 +Customer#000000091 4643.14 +Customer#000000094 5500.11 +Customer#000000097 2164.48 +Customer#000000101 7470.96 +Customer#000000103 2757.45 +Customer#000000106 3288.42 +Customer#000000115 7508.92 +Customer#000000121 6428.32 +Customer#000000122 7865.46 +Customer#000000127 9280.71 +Customer#000000130 5073.58 +Customer#000000133 2314.67 +Customer#000000139 7897.78 +set @a1=15; +execute stmt using @a1; +select c_name, c_acctbal from customer where c_nationkey in (select n_nationkey from nation +where n_regionkey in (1,2)) +and +c_custkey in (select o_custkey from orders +where o_orderDATE between '1992-10-09' and '1993-03-08'); +c_name c_acctbal +Customer#000000007 9576.95 +Customer#000000019 8929.71 +Customer#000000022 606.98 +Customer#000000025 7148.7 +Customer#000000028 1022.18 +Customer#000000037 -902.75 +Customer#000000040 1350.3 +Customer#000000047 289.58 +Customer#000000059 3473.6 +Customer#000000061 1551.24 +Customer#000000064 -631.64 +Customer#000000067 8181.59 +Customer#000000082 9483.34 +Customer#000000091 4658.14 +Customer#000000094 5515.11 +Customer#000000097 2179.48 +Customer#000000101 7485.96 +Customer#000000103 2772.45 +Customer#000000106 3303.42 +Customer#000000115 7523.92 +Customer#000000121 6443.32 +Customer#000000122 7880.46 +Customer#000000127 9295.71 +Customer#000000130 5088.58 +Customer#000000133 2329.67 +Customer#000000139 7912.78 +set @a2=5; +execute stmt using @a2; +select c_name, c_acctbal from customer where c_nationkey in (select n_nationkey from nation +where n_regionkey in (1,2)) +and +c_custkey in (select o_custkey from orders +where o_orderDATE between '1992-10-09' and '1993-03-08'); +c_name c_acctbal +Customer#000000007 9581.95 +Customer#000000019 8934.71 +Customer#000000022 611.98 +Customer#000000025 7153.7 +Customer#000000028 1027.1799999999998 +Customer#000000037 -897.75 +Customer#000000040 1355.3 +Customer#000000047 294.58 +Customer#000000059 3478.6 +Customer#000000061 1556.24 +Customer#000000064 -626.64 +Customer#000000067 8186.59 +Customer#000000082 9488.34 +Customer#000000091 4663.14 +Customer#000000094 5520.11 +Customer#000000097 2184.48 +Customer#000000101 7490.96 +Customer#000000103 2777.45 +Customer#000000106 3308.42 +Customer#000000115 7528.92 +Customer#000000121 6448.32 +Customer#000000122 7885.46 +Customer#000000127 9300.71 +Customer#000000130 5093.58 +Customer#000000133 2334.67 +Customer#000000139 7917.78 +execute stmt using -(@a1+@a2); +select c_name, c_acctbal from customer where c_nationkey in (select n_nationkey from nation +where n_regionkey in (1,2)) +and +c_custkey in (select o_custkey from orders +where o_orderDATE between '1992-10-09' and '1993-03-08'); +c_name c_acctbal +Customer#000000007 9561.95 +Customer#000000019 8914.71 +Customer#000000022 591.98 +Customer#000000025 7133.7 +Customer#000000028 1007.1799999999998 +Customer#000000037 -917.75 +Customer#000000040 1335.3 +Customer#000000047 274.58 +Customer#000000059 3458.6 +Customer#000000061 1536.24 +Customer#000000064 -646.64 +Customer#000000067 8166.59 +Customer#000000082 9468.34 +Customer#000000091 4643.14 +Customer#000000094 5500.11 +Customer#000000097 2164.48 +Customer#000000101 7470.96 +Customer#000000103 2757.45 +Customer#000000106 3288.42 +Customer#000000115 7508.92 +Customer#000000121 6428.32 +Customer#000000122 7865.46 +Customer#000000127 9280.71 +Customer#000000130 5073.58 +Customer#000000133 2314.67 +Customer#000000139 7897.78 +deallocate prepare stmt; +# Materialization PS +# ================== +prepare stmt from " +update customer set c_acctbal = c_acctbal+? where c_custkey in (select o_custkey from orders +where o_orderDATE between '1992-01-09' and '1992-03-08'); +"; +select c_name, c_acctbal from customer where c_custkey in (select o_custkey from orders +where o_orderDATE between '1992-01-09' and '1992-03-08'); +c_name c_acctbal +Customer#000000025 7133.7 +Customer#000000013 3857.34 +Customer#000000065 8795.16 +Customer#000000032 3471.53 +Customer#000000023 3332.02 +Customer#000000035 1228.24 +Customer#000000091 4643.14 +Customer#000000016 4681.03 +Customer#000000098 -551.37 +Customer#000000037 -917.75 +Customer#000000136 -842.39 +Customer#000000118 3582.37 +Customer#000000022 591.98 +Customer#000000005 794.47 +Customer#000000109 -716.1 +Customer#000000038 6345.11 +Customer#000000076 5745.33 +Customer#000000056 6530.86 +Customer#000000040 1335.3 +Customer#000000116 8403.99 +Customer#000000115 7508.92 +Customer#000000140 9963.15 +Customer#000000017 6.34 +Customer#000000052 5630.28 +set @a1=7; +execute stmt using @a1; +select c_name, c_acctbal from customer where c_custkey in (select o_custkey from orders +where o_orderDATE between '1992-01-09' and '1992-03-08'); +c_name c_acctbal +Customer#000000025 7140.7 +Customer#000000013 3864.34 +Customer#000000065 8802.16 +Customer#000000032 3478.53 +Customer#000000023 3339.02 +Customer#000000035 1235.24 +Customer#000000091 4650.14 +Customer#000000016 4688.03 +Customer#000000098 -544.37 +Customer#000000037 -910.75 +Customer#000000136 -835.39 +Customer#000000118 3589.37 +Customer#000000022 598.98 +Customer#000000005 801.47 +Customer#000000109 -709.1 +Customer#000000038 6352.11 +Customer#000000076 5752.33 +Customer#000000056 6537.86 +Customer#000000040 1342.3 +Customer#000000116 8410.99 +Customer#000000115 7515.92 +Customer#000000140 9970.15 +Customer#000000017 13.34 +Customer#000000052 5637.28 +set @a2=3; +execute stmt using @a2; +select c_name, c_acctbal from customer where c_custkey in (select o_custkey from orders +where o_orderDATE between '1992-01-09' and '1992-03-08'); +c_name c_acctbal +Customer#000000025 7143.7 +Customer#000000013 3867.34 +Customer#000000065 8805.16 +Customer#000000032 3481.53 +Customer#000000023 3342.02 +Customer#000000035 1238.24 +Customer#000000091 4653.14 +Customer#000000016 4691.03 +Customer#000000098 -541.37 +Customer#000000037 -907.75 +Customer#000000136 -832.39 +Customer#000000118 3592.37 +Customer#000000022 601.98 +Customer#000000005 804.47 +Customer#000000109 -706.1 +Customer#000000038 6355.11 +Customer#000000076 5755.33 +Customer#000000056 6540.86 +Customer#000000040 1345.3 +Customer#000000116 8413.99 +Customer#000000115 7518.92 +Customer#000000140 9973.15 +Customer#000000017 16.34 +Customer#000000052 5640.28 +execute stmt using -(@a1+@a2); +select c_name, c_acctbal from customer where c_custkey in (select o_custkey from orders +where o_orderDATE between '1992-01-09' and '1992-03-08'); +c_name c_acctbal +Customer#000000025 7133.7 +Customer#000000013 3857.34 +Customer#000000065 8795.16 +Customer#000000032 3471.53 +Customer#000000023 3332.02 +Customer#000000035 1228.24 +Customer#000000091 4643.14 +Customer#000000016 4681.03 +Customer#000000098 -551.37 +Customer#000000037 -917.75 +Customer#000000136 -842.39 +Customer#000000118 3582.37 +Customer#000000022 591.98 +Customer#000000005 794.47 +Customer#000000109 -716.1 +Customer#000000038 6345.11 +Customer#000000076 5745.33 +Customer#000000056 6530.86 +Customer#000000040 1335.3 +Customer#000000116 8403.99 +Customer#000000115 7508.92 +Customer#000000140 9963.15 +Customer#000000017 6.34 +Customer#000000052 5630.28 +deallocate prepare stmt; +# Materialization SJM PS +# ====================== +prepare stmt from " +update customer set c_acctbal = c_acctbal+? where c_custkey in (select o_custkey from orders +where o_orderDATE between '1992-01-09' and '1992-03-08' + group by o_custkey having count(o_custkey) > 1); +"; +select c_name, c_acctbal from customer where c_custkey in (select o_custkey from orders +where o_orderDATE between '1992-01-09' and '1992-03-08' + group by o_custkey having count(o_custkey) > 1); +c_name c_acctbal +Customer#000000013 3857.34 +Customer#000000032 3471.53 +Customer#000000037 -917.75 +Customer#000000118 3582.37 +Customer#000000056 6530.86 +set @a1=-2; +execute stmt using @a1; +select c_name, c_acctbal from customer where c_custkey in (select o_custkey from orders +where o_orderDATE between '1992-01-09' and '1992-03-08' + group by o_custkey having count(o_custkey) > 1); +c_name c_acctbal +Customer#000000013 3855.34 +Customer#000000032 3469.53 +Customer#000000037 -919.75 +Customer#000000118 3580.37 +Customer#000000056 6528.86 +set @a2=-1; +execute stmt using @a2; +select c_name, c_acctbal from customer where c_custkey in (select o_custkey from orders +where o_orderDATE between '1992-01-09' and '1992-03-08' + group by o_custkey having count(o_custkey) > 1); +c_name c_acctbal +Customer#000000013 3854.34 +Customer#000000032 3468.53 +Customer#000000037 -920.75 +Customer#000000118 3579.37 +Customer#000000056 6527.86 +execute stmt using -(@a1+@a2); +select c_name, c_acctbal from customer where c_custkey in (select o_custkey from orders +where o_orderDATE between '1992-01-09' and '1992-03-08' + group by o_custkey having count(o_custkey) > 1); +c_name c_acctbal +Customer#000000013 3857.34 +Customer#000000032 3471.53 +Customer#000000037 -917.75 +Customer#000000118 3582.37 +Customer#000000056 6530.86 +deallocate prepare stmt; +# Pullout SP +# ========== +create procedure p(d int) +update orders set o_totalprice = o_totalprice+d where o_orderDATE between '1992-01-01' and '1992-06-30' and +o_custkey in (select c_custkey from customer +where c_nationkey in (select n_nationkey from nation +where n_name='PERU')); +select o_orderkey, o_totalprice 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 (select n_nationkey from nation +where n_name='PERU')); +o_orderkey o_totalprice +644 201268.06 +2880 145761.99 +3142 16030.15 +5382 138423.03 +5095 184583.99 +737 12984.85 +1729 12137.76 +5121 150334.57 +call p(-10); +select o_orderkey, o_totalprice 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 (select n_nationkey from nation +where n_name='PERU')); +o_orderkey o_totalprice +644 201258.06 +2880 145751.99 +3142 16020.15 +5382 138413.03 +5095 184573.99 +737 12974.85 +1729 12127.76 +5121 150324.57 +call p(-20); +select o_orderkey, o_totalprice 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 (select n_nationkey from nation +where n_name='PERU')); +o_orderkey o_totalprice +644 201238.06 +2880 145731.99 +3142 16000.15 +5382 138393.03 +5095 184553.99 +737 12954.85 +1729 12107.76 +5121 150304.57 +call p(10+20); +select o_orderkey, o_totalprice 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 (select n_nationkey from nation +where n_name='PERU')); +o_orderkey o_totalprice +644 201268.06 +2880 145761.99 +3142 16030.15 +5382 138423.03 +5095 184583.99 +737 12984.85 +1729 12137.76 +5121 150334.57 +drop procedure p; +# FirstMatch SP +# ============= +create procedure p(d int) +update customer set c_acctbal = c_acctbal+d where c_nationkey in (select n_nationkey from nation +where n_regionkey in (1,2)) +and +c_custkey in (select o_custkey from orders +where o_orderDATE between '1992-10-09' and '1993-03-08'); +select c_name, c_acctbal from customer where c_nationkey in (select n_nationkey from nation +where n_regionkey in (1,2)) +and +c_custkey in (select o_custkey from orders +where o_orderDATE between '1992-10-09' and '1993-03-08'); +c_name c_acctbal +Customer#000000007 9561.95 +Customer#000000019 8914.71 +Customer#000000022 591.98 +Customer#000000025 7133.7 +Customer#000000028 1007.1799999999998 +Customer#000000037 -917.75 +Customer#000000040 1335.3 +Customer#000000047 274.58 +Customer#000000059 3458.6 +Customer#000000061 1536.24 +Customer#000000064 -646.64 +Customer#000000067 8166.59 +Customer#000000082 9468.34 +Customer#000000091 4643.14 +Customer#000000094 5500.11 +Customer#000000097 2164.48 +Customer#000000101 7470.96 +Customer#000000103 2757.45 +Customer#000000106 3288.42 +Customer#000000115 7508.92 +Customer#000000121 6428.32 +Customer#000000122 7865.46 +Customer#000000127 9280.71 +Customer#000000130 5073.58 +Customer#000000133 2314.67 +Customer#000000139 7897.78 +call p(5); +select c_name, c_acctbal from customer where c_nationkey in (select n_nationkey from nation +where n_regionkey in (1,2)) +and +c_custkey in (select o_custkey from orders +where o_orderDATE between '1992-10-09' and '1993-03-08'); +c_name c_acctbal +Customer#000000007 9566.95 +Customer#000000019 8919.71 +Customer#000000022 596.98 +Customer#000000025 7138.7 +Customer#000000028 1012.1799999999998 +Customer#000000037 -912.75 +Customer#000000040 1340.3 +Customer#000000047 279.58 +Customer#000000059 3463.6 +Customer#000000061 1541.24 +Customer#000000064 -641.64 +Customer#000000067 8171.59 +Customer#000000082 9473.34 +Customer#000000091 4648.14 +Customer#000000094 5505.11 +Customer#000000097 2169.48 +Customer#000000101 7475.96 +Customer#000000103 2762.45 +Customer#000000106 3293.42 +Customer#000000115 7513.92 +Customer#000000121 6433.32 +Customer#000000122 7870.46 +Customer#000000127 9285.71 +Customer#000000130 5078.58 +Customer#000000133 2319.67 +Customer#000000139 7902.78 +call p(15); +select c_name, c_acctbal from customer where c_nationkey in (select n_nationkey from nation +where n_regionkey in (1,2)) +and +c_custkey in (select o_custkey from orders +where o_orderDATE between '1992-10-09' and '1993-03-08'); +c_name c_acctbal +Customer#000000007 9581.95 +Customer#000000019 8934.71 +Customer#000000022 611.98 +Customer#000000025 7153.7 +Customer#000000028 1027.1799999999998 +Customer#000000037 -897.75 +Customer#000000040 1355.3 +Customer#000000047 294.58 +Customer#000000059 3478.6 +Customer#000000061 1556.24 +Customer#000000064 -626.64 +Customer#000000067 8186.59 +Customer#000000082 9488.34 +Customer#000000091 4663.14 +Customer#000000094 5520.11 +Customer#000000097 2184.48 +Customer#000000101 7490.96 +Customer#000000103 2777.45 +Customer#000000106 3308.42 +Customer#000000115 7528.92 +Customer#000000121 6448.32 +Customer#000000122 7885.46 +Customer#000000127 9300.71 +Customer#000000130 5093.58 +Customer#000000133 2334.67 +Customer#000000139 7917.78 +call p(-(5+15)); +select c_name, c_acctbal from customer where c_nationkey in (select n_nationkey from nation +where n_regionkey in (1,2)) +and +c_custkey in (select o_custkey from orders +where o_orderDATE between '1992-10-09' and '1993-03-08'); +c_name c_acctbal +Customer#000000007 9561.95 +Customer#000000019 8914.71 +Customer#000000022 591.98 +Customer#000000025 7133.7 +Customer#000000028 1007.1799999999998 +Customer#000000037 -917.75 +Customer#000000040 1335.3 +Customer#000000047 274.58 +Customer#000000059 3458.6 +Customer#000000061 1536.24 +Customer#000000064 -646.64 +Customer#000000067 8166.59 +Customer#000000082 9468.34 +Customer#000000091 4643.14 +Customer#000000094 5500.11 +Customer#000000097 2164.48 +Customer#000000101 7470.96 +Customer#000000103 2757.45 +Customer#000000106 3288.42 +Customer#000000115 7508.92 +Customer#000000121 6428.32 +Customer#000000122 7865.46 +Customer#000000127 9280.71 +Customer#000000130 5073.58 +Customer#000000133 2314.67 +Customer#000000139 7897.78 +drop procedure p; +# Materialization SP +# ================== +create procedure p(d int) +update customer set c_acctbal = c_acctbal+d where c_custkey in (select o_custkey from orders +where o_orderDATE between '1992-01-09' and '1992-03-08'); +select c_name, c_acctbal from customer where c_custkey in (select o_custkey from orders +where o_orderDATE between '1992-01-09' and '1992-03-08'); +c_name c_acctbal +Customer#000000025 7133.7 +Customer#000000013 3857.34 +Customer#000000065 8795.16 +Customer#000000032 3471.53 +Customer#000000023 3332.02 +Customer#000000035 1228.24 +Customer#000000091 4643.14 +Customer#000000016 4681.03 +Customer#000000098 -551.37 +Customer#000000037 -917.75 +Customer#000000136 -842.39 +Customer#000000118 3582.37 +Customer#000000022 591.98 +Customer#000000005 794.47 +Customer#000000109 -716.1 +Customer#000000038 6345.11 +Customer#000000076 5745.33 +Customer#000000056 6530.86 +Customer#000000040 1335.3 +Customer#000000116 8403.99 +Customer#000000115 7508.92 +Customer#000000140 9963.15 +Customer#000000017 6.34 +Customer#000000052 5630.28 +call p(3); +select c_name, c_acctbal from customer where c_custkey in (select o_custkey from orders +where o_orderDATE between '1992-01-09' and '1992-03-08'); +c_name c_acctbal +Customer#000000025 7136.7 +Customer#000000013 3860.34 +Customer#000000065 8798.16 +Customer#000000032 3474.53 +Customer#000000023 3335.02 +Customer#000000035 1231.24 +Customer#000000091 4646.14 +Customer#000000016 4684.03 +Customer#000000098 -548.37 +Customer#000000037 -914.75 +Customer#000000136 -839.39 +Customer#000000118 3585.37 +Customer#000000022 594.98 +Customer#000000005 797.47 +Customer#000000109 -713.1 +Customer#000000038 6348.11 +Customer#000000076 5748.33 +Customer#000000056 6533.86 +Customer#000000040 1338.3 +Customer#000000116 8406.99 +Customer#000000115 7511.92 +Customer#000000140 9966.15 +Customer#000000017 9.34 +Customer#000000052 5633.28 +call p(7); +select c_name, c_acctbal from customer where c_custkey in (select o_custkey from orders +where o_orderDATE between '1992-01-09' and '1992-03-08'); +c_name c_acctbal +Customer#000000025 7143.7 +Customer#000000013 3867.34 +Customer#000000065 8805.16 +Customer#000000032 3481.53 +Customer#000000023 3342.02 +Customer#000000035 1238.24 +Customer#000000091 4653.14 +Customer#000000016 4691.03 +Customer#000000098 -541.37 +Customer#000000037 -907.75 +Customer#000000136 -832.39 +Customer#000000118 3592.37 +Customer#000000022 601.98 +Customer#000000005 804.47 +Customer#000000109 -706.1 +Customer#000000038 6355.11 +Customer#000000076 5755.33 +Customer#000000056 6540.86 +Customer#000000040 1345.3 +Customer#000000116 8413.99 +Customer#000000115 7518.92 +Customer#000000140 9973.15 +Customer#000000017 16.34 +Customer#000000052 5640.28 +call p(-(3+7)); +select c_name, c_acctbal from customer where c_custkey in (select o_custkey from orders +where o_orderDATE between '1992-01-09' and '1992-03-08'); +c_name c_acctbal +Customer#000000025 7133.7 +Customer#000000013 3857.34 +Customer#000000065 8795.16 +Customer#000000032 3471.53 +Customer#000000023 3332.02 +Customer#000000035 1228.24 +Customer#000000091 4643.14 +Customer#000000016 4681.03 +Customer#000000098 -551.37 +Customer#000000037 -917.75 +Customer#000000136 -842.39 +Customer#000000118 3582.37 +Customer#000000022 591.98 +Customer#000000005 794.47 +Customer#000000109 -716.1 +Customer#000000038 6345.11 +Customer#000000076 5745.33 +Customer#000000056 6530.86 +Customer#000000040 1335.3 +Customer#000000116 8403.99 +Customer#000000115 7508.92 +Customer#000000140 9963.15 +Customer#000000017 6.34 +Customer#000000052 5630.28 +drop procedure p; +# Materialization SJM SP +# ====================== +create procedure p(d int) +update customer set c_acctbal = c_acctbal+d where c_custkey in (select o_custkey from orders +where o_orderDATE between '1992-01-09' and '1992-03-08' + group by o_custkey having count(o_custkey) > 1); +select c_name, c_acctbal from customer where c_custkey in (select o_custkey from orders +where o_orderDATE between '1992-01-09' and '1992-03-08' + group by o_custkey having count(o_custkey) > 1); +c_name c_acctbal +Customer#000000013 3857.34 +Customer#000000032 3471.53 +Customer#000000037 -917.75 +Customer#000000118 3582.37 +Customer#000000056 6530.86 +call p(-1); +select c_name, c_acctbal from customer where c_custkey in (select o_custkey from orders +where o_orderDATE between '1992-01-09' and '1992-03-08' + group by o_custkey having count(o_custkey) > 1); +c_name c_acctbal +Customer#000000013 3856.34 +Customer#000000032 3470.53 +Customer#000000037 -918.75 +Customer#000000118 3581.37 +Customer#000000056 6529.86 +call p(-2); +select c_name, c_acctbal from customer where c_custkey in (select o_custkey from orders +where o_orderDATE between '1992-01-09' and '1992-03-08' + group by o_custkey having count(o_custkey) > 1); +c_name c_acctbal +Customer#000000013 3854.34 +Customer#000000032 3468.53 +Customer#000000037 -920.75 +Customer#000000118 3579.37 +Customer#000000056 6527.86 +call p(1+2); +select c_name, c_acctbal from customer where c_custkey in (select o_custkey from orders +where o_orderDATE between '1992-01-09' and '1992-03-08' + group by o_custkey having count(o_custkey) > 1); +c_name c_acctbal +Customer#000000013 3857.34 +Customer#000000032 3471.53 +Customer#000000037 -917.75 +Customer#000000118 3582.37 +Customer#000000056 6530.86 +drop procedure p; +DROP DATABASE dbt3_s001; diff --git a/mysql-test/main/update_single_to_multi.test b/mysql-test/main/update_single_to_multi.test new file mode 100644 index 0000000..f018365 --- /dev/null +++ b/mysql-test/main/update_single_to_multi.test @@ -0,0 +1,511 @@ +--disable_warnings +DROP DATABASE IF EXISTS dbt3_s001; +--enable_warnings + +CREATE DATABASE dbt3_s001; + +use dbt3_s001; + +--disable_query_log +--disable_result_log +--disable_warnings +--source include/dbt3_s001.inc +--enable_warnings +--enable_result_log +--enable_query_log + +create index i_n_name on nation(n_name); +analyze table nation; + + +--echo # Pullout +--echo # ======= + +let $c1= + o_orderDATE between '1992-01-01' and '1992-06-30' and + o_custkey in (select c_custkey from customer + where c_nationkey in (select n_nationkey from nation + where n_name='PERU')); + +eval +explain +select o_orderkey, o_totalprice from orders where $c1; +eval +explain format=json +select o_orderkey, o_totalprice from orders where $c1; +eval +select o_orderkey, o_totalprice from orders where $c1; + +eval +explain +update orders set o_totalprice = o_totalprice-50 where $c1; +eval +explain format=json +update orders set o_totalprice = o_totalprice-50 where $c1; +eval +update orders set o_totalprice = o_totalprice-50 where $c1; +eval +select o_orderkey, o_totalprice from orders where $c1; + +eval +update orders set o_totalprice= o_totalprice+50 where $c1; +eval +select o_orderkey, o_totalprice from orders where $c1; + + +let $c2= + (ps_partkey, ps_suppkey) in + (select p_partkey, s_suppkey from part, supplier + where p_retailprice between 901 and 910 and + s_nationkey in (select n_nationkey from nation + where n_name='PERU')); + +eval +explain +select ps_partkey, ps_suppkey, ps_supplycost from partsupp where $c2; +eval +select ps_partkey, ps_suppkey, ps_supplycost from partsupp where $c2; + +eval +explain +update partsupp set ps_supplycost = ps_supplycost+2 where $c2; +eval +update partsupp set ps_supplycost = ps_supplycost+2 where $c2; +eval +select ps_partkey, ps_suppkey, ps_supplycost from partsupp where $c2; + +eval +update partsupp set ps_supplycost = ps_supplycost-2 where $c2; +eval +select ps_partkey, ps_suppkey, ps_supplycost from partsupp where $c2; + + +let $c3= + ps_partkey in (select p_partkey from part + where p_retailprice between 901 and 910) and + ps_suppkey in (select s_suppkey from supplier + where s_nationkey in (select n_nationkey from nation + where n_name='PERU')); +eval +explain +select ps_partkey, ps_suppkey, ps_supplycost from partsupp where $c3; +eval +select ps_partkey, ps_suppkey, ps_supplycost from partsupp where $c3; + +eval +explain +update partsupp set ps_supplycost = ps_supplycost+10 where $c3; +eval +update partsupp set ps_supplycost = ps_supplycost+10 where $c3; +eval +select ps_partkey, ps_suppkey, ps_supplycost from partsupp where $c3; + +eval +update partsupp set ps_supplycost = ps_supplycost-10 where $c3; +eval +select ps_partkey, ps_suppkey, ps_supplycost from partsupp where $c3; + + +let $c4= + l_orderkey in (select o_orderkey from orders + where o_custkey in + (select c_custkey from customer + where c_nationkey in + (select n_nationkey from nation + where n_name='PERU')) + and + o_orderDATE between '1992-06-30' and '1992-12-31') + and + (l_partkey, l_suppkey) in + (select p_partkey, s_suppkey from part, supplier + where p_retailprice between 901 and 1000 and + s_nationkey in (select n_nationkey from nation + where n_name='PERU')); + +eval +explain +select l_orderkey, l_linenumber, l_tax from lineitem where $c4; +eval +select l_orderkey, l_linenumber, l_tax from lineitem where $c4; + +eval +explain +update lineitem set l_tax = (l_tax*100+1)/100 where $c4; +eval +update lineitem set l_tax = (l_tax*100+1)/100 where $c4; +eval +select l_orderkey, l_linenumber, l_tax from lineitem where $c4; + +eval +update lineitem set l_tax = (l_tax*100-1)/100 where $c4; +eval +select l_orderkey, l_linenumber, l_tax from lineitem where $c4; + + +--echo # FirstMatch +--echo # ========== + +let $c5= + c_nationkey in (select n_nationkey from nation + where n_regionkey in (1,2)) + and + c_custkey in (select o_custkey from orders + where o_orderDATE between '1992-10-09' and '1993-03-08'); + +eval +explain +select c_name, c_acctbal from customer where $c5; +eval +explain format=json +select c_name, c_acctbal from customer where $c5; +eval +select c_name, c_acctbal from customer where $c5; + +eval +explain +update customer set c_acctbal = c_acctbal+10 where $c5; +eval +explain format=json +update customer set c_acctbal = c_acctbal+10 where $c5; +eval +update customer set c_acctbal = c_acctbal+10 where $c5; +eval +select c_name, c_acctbal from customer where $c5; + +eval +update customer set c_acctbal = c_acctbal-10 where $c5; +eval +select c_name, c_acctbal from customer where $c5; + + +let $c6= + c_nationkey in (select n_nationkey from nation where n_name='PERU') + and + c_custkey in (select o_custkey from orders + where o_orderDATE between "1992-01-09" and "1993-01-08"); + +eval +explain +select c_name, c_acctbal from customer where $c6; +eval +select c_name, c_acctbal from customer where $c6; + +eval +explain +update customer set c_acctbal = c_acctbal+20 where $c6; +eval +update customer set c_acctbal = c_acctbal+20 where $c6; +eval +select c_name, c_acctbal from customer where $c6; + +eval +update customer set c_acctbal = c_acctbal-20 where $c6; +eval +select c_name, c_acctbal from customer where $c6; + + +--echo # Materialization +--echo # =============== + +let $c7= + c_custkey in (select o_custkey from orders + where o_orderDATE between '1992-01-09' and '1992-03-08'); + +eval +explain +select c_name, c_acctbal from customer where $c7; +eval +explain format=json +select c_name, c_acctbal from customer where $c7; +eval +select c_name, c_acctbal from customer where $c7; + +eval +explain +update customer set c_acctbal = c_acctbal+5 where $c7; +eval +explain format=json +update customer set c_acctbal = c_acctbal+5 where $c7; +eval +update customer set c_acctbal = c_acctbal+5 where $c7; +eval +select c_name, c_acctbal from customer where $c7; + +eval +update customer set c_acctbal = c_acctbal-5 where $c7; +eval +select c_name, c_acctbal from customer where $c7; + + +let $c8= + c_custkey in (select o_custkey from orders + where o_orderDATE between '1992-06-09' and '1993-01-08'); + +eval +explain +select c_name, c_acctbal from customer where $c8; +eval +select c_name, c_acctbal from customer where $c8; + +eval +explain +update customer set c_acctbal = c_acctbal+1 where $c8; +eval +update customer set c_acctbal = c_acctbal+1 where $c8; +eval +select c_name, c_acctbal from customer where $c8; + +eval +update customer set c_acctbal = c_acctbal-1 where $c8; +eval +select c_name, c_acctbal from customer where $c8; + + +--echo # Materialization SJM +--echo # =================== + +let $c9= + c_custkey in (select o_custkey from orders + where o_orderDATE between '1992-01-09' and '1992-03-08' + group by o_custkey having count(o_custkey) > 1); + +eval +explain +select c_name, c_acctbal from customer where $c9; +eval +explain format=json +select c_name, c_acctbal from customer where $c9; +eval +select c_name, c_acctbal from customer where $c9; + +eval +explain +update customer set c_acctbal = c_acctbal-5 where $c9; +eval +explain format=json +update customer set c_acctbal = c_acctbal-5 where $c9; +eval +update customer set c_acctbal = c_acctbal-5 where $c9; +eval +select c_name, c_acctbal from customer where $c9; + +eval +update customer set c_acctbal = c_acctbal+5 where $c9; +eval +select c_name, c_acctbal from customer where $c9; + + +let $c10= + c_custkey in (select o_custkey from orders + where o_orderDATE between '1992-01-09' and '1993-03-08' + group by o_custkey having count(o_custkey) > 5); + +eval +explain +select c_name, c_acctbal from customer where $c10; +eval +select c_name, c_acctbal from customer where $c10; + +eval +explain +update customer set c_acctbal = c_acctbal-1 where $c10; +eval +update customer set c_acctbal = c_acctbal-1 where $c10; +eval +select c_name, c_acctbal from customer where $c10; + +eval +update customer set c_acctbal = c_acctbal+1 where $c10; +eval +select c_name, c_acctbal from customer where $c10; + + +--echo # Pullout PS +--echo # ========== + +eval +prepare stmt from " +update orders set o_totalprice = o_totalprice+? where $c1; +"; + +eval +select o_orderkey, o_totalprice from orders where $c1; +set @a1=-20; +execute stmt using @a1; +eval +select o_orderkey, o_totalprice from orders where $c1; +set @a2=-10; +execute stmt using @a2; +eval +select o_orderkey, o_totalprice from orders where $c1; +execute stmt using -(@a1+@a2); +eval +select o_orderkey, o_totalprice from orders where $c1; + +deallocate prepare stmt; + + +--echo # FirstMatch PS +--echo # ============= + +eval +prepare stmt from " +update customer set c_acctbal = c_acctbal+? where $c5; +"; + +eval +select c_name, c_acctbal from customer where $c5; +set @a1=15; +execute stmt using @a1; +eval +select c_name, c_acctbal from customer where $c5; +set @a2=5; +execute stmt using @a2; +eval +select c_name, c_acctbal from customer where $c5; +execute stmt using -(@a1+@a2); +eval +select c_name, c_acctbal from customer where $c5; + +deallocate prepare stmt; + + +--echo # Materialization PS +--echo # ================== + +eval +prepare stmt from " +update customer set c_acctbal = c_acctbal+? where $c7; +"; + +eval +select c_name, c_acctbal from customer where $c7; +set @a1=7; +execute stmt using @a1; +eval +select c_name, c_acctbal from customer where $c7; +set @a2=3; +execute stmt using @a2; +eval +select c_name, c_acctbal from customer where $c7; +execute stmt using -(@a1+@a2); +eval +select c_name, c_acctbal from customer where $c7; + +deallocate prepare stmt; + + +--echo # Materialization SJM PS +--echo # ====================== + +eval +prepare stmt from " +update customer set c_acctbal = c_acctbal+? where $c9; +"; + +eval +select c_name, c_acctbal from customer where $c9; +set @a1=-2; +execute stmt using @a1; +eval +select c_name, c_acctbal from customer where $c9; +set @a2=-1; +execute stmt using @a2; +eval +select c_name, c_acctbal from customer where $c9; +execute stmt using -(@a1+@a2); +eval +select c_name, c_acctbal from customer where $c9; + +deallocate prepare stmt; + + +--echo # Pullout SP +--echo # ========== + +eval +create procedure p(d int) +update orders set o_totalprice = o_totalprice+d where $c1; + +eval +select o_orderkey, o_totalprice from orders where $c1; +call p(-10); +eval +select o_orderkey, o_totalprice from orders where $c1; +call p(-20); +eval +select o_orderkey, o_totalprice from orders where $c1; +call p(10+20); +eval +select o_orderkey, o_totalprice from orders where $c1; + +drop procedure p; + + +--echo # FirstMatch SP +--echo # ============= + +eval +create procedure p(d int) +update customer set c_acctbal = c_acctbal+d where $c5; + +eval +select c_name, c_acctbal from customer where $c5; +call p(5); +eval +select c_name, c_acctbal from customer where $c5; +call p(15); +eval +select c_name, c_acctbal from customer where $c5; +call p(-(5+15)); +eval +select c_name, c_acctbal from customer where $c5; + +drop procedure p; + + +--echo # Materialization SP +--echo # ================== + +eval +create procedure p(d int) +update customer set c_acctbal = c_acctbal+d where $c7; + +eval +select c_name, c_acctbal from customer where $c7; +call p(3); +eval +select c_name, c_acctbal from customer where $c7; +call p(7); +eval +select c_name, c_acctbal from customer where $c7; +call p(-(3+7)); +eval +select c_name, c_acctbal from customer where $c7; + +drop procedure p; + + +--echo # Materialization SJM SP +--echo # ====================== + +eval +create procedure p(d int) +update customer set c_acctbal = c_acctbal+d where $c9; + +eval +select c_name, c_acctbal from customer where $c9; +call p(-1); +eval +select c_name, c_acctbal from customer where $c9; +call p(-2); +eval +select c_name, c_acctbal from customer where $c9; +call p(1+2); +eval +select c_name, c_acctbal from customer where $c9; + +drop procedure p; + + +DROP DATABASE dbt3_s001; diff --git a/sql/opt_subselect.cc b/sql/opt_subselect.cc index 58d8687..df8f808 100644 --- a/sql/opt_subselect.cc +++ b/sql/opt_subselect.cc @@ -675,9 +675,8 @@ int check_and_do_in_subquery_rewrites(JOIN *join) 3. Subquery does not have GROUP BY or ORDER BY 4. Subquery does not use aggregate functions or HAVING 5. Subquery predicate is at the AND-top-level of ON/WHERE clause - 6. We are not in a subquery of a single table UPDATE/DELETE that - doesn't have a JOIN (TODO: We should handle this at some - point by switching to multi-table UPDATE/DELETE) + 6. We are not in a top level subquery of a single table DELETE + with RETURNING 7. We're not in a table-less subquery like "SELECT 1" 8. No execution method was already chosen (by a prepared statement) 9. Parent select is not a table-less select @@ -692,9 +691,9 @@ int check_and_do_in_subquery_rewrites(JOIN *join) !select_lex->group_list.elements && !join->order && // 3 !join->having && !select_lex->with_sum_func && // 4 in_subs->emb_on_expr_nest && // 5 - select_lex->outer_select()->join && // 6 - (!thd->lex->m_sql_cmd || - thd->lex->m_sql_cmd->sql_command_code() == SQLCOM_UPDATE_MULTI) && + !(thd->lex->sql_command == SQLCOM_DELETE && // 6 + thd->lex->has_returning() && // 6 + !select_lex->outer_select()->outer_select()) && // 6 parent_unit->first_select()->leaf_tables.elements && // 7 !in_subs->has_strategy() && // 8 select_lex->outer_select()->table_list.first && // 9 @@ -7192,3 +7191,23 @@ bool TABLE_LIST::is_sjm_scan_table() { return is_active_sjm() && sj_mat_info->is_sj_scan; } + + +bool SELECT_LEX::is_sj_subselect_lifted_to_top() +{ + st_select_lex *sl= this; + st_select_lex *outer_sl= outer_select(); + for ( ; outer_sl; sl= outer_sl, outer_sl= outer_sl->outer_select()) + { + List_iterator_fast<Item_in_subselect> it(outer_sl->sj_subselects); + Item_in_subselect *in_subs; + while ((in_subs= it++)) + { + if (in_subs->unit->first_select() == sl) + break; + } + if (!in_subs) + return false; + } + return true; +} diff --git a/sql/sql_class.h b/sql/sql_class.h index 5d1f97a..f0160eb 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -6931,6 +6931,7 @@ class multi_delete :public select_result_interceptor // Methods used by ColumnStore uint get_num_of_tables() const { return num_of_tables; } TABLE_LIST* get_tables() const { return delete_tables; } + void set_delete_tables (TABLE_LIST *tbl) { delete_tables= tbl; } public: multi_delete(THD *thd_arg, TABLE_LIST *dt, uint num_of_tables); ~multi_delete(); diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc index 7ac4797..611ce48 100644 --- a/sql/sql_delete.cc +++ b/sql/sql_delete.cc @@ -1511,6 +1511,47 @@ bool Sql_cmd_delete::prepare_inner(THD *thd) table_list->delete_while_scanning= false; } + + { + if (thd->lex->describe) + select_options|= SELECT_DESCRIBE; + + /* + When in EXPLAIN, delay deleting the joins so that they are still + available when we're producing EXPLAIN EXTENDED warning text. + */ + if (select_options & SELECT_DESCRIBE) + free_join= 0; + select_options|= + SELECT_NO_JOIN_CACHE | SELECT_NO_UNLOCK | OPTION_SETUP_TABLES_DONE; + + if (!(join= new (thd->mem_root) JOIN(thd, empty_list, + select_options, result))) + DBUG_RETURN(TRUE); + THD_STAGE_INFO(thd, stage_init); + select_lex->join= join; + thd->lex->used_tables=0; + if ((err= join->prepare(table_list, select_lex->where, + select_lex->order_list.elements, + select_lex->order_list.first, + false, NULL, NULL, NULL, + select_lex, &lex->unit))) + + { + goto err; + } + + if (!multitable && + select_lex->sj_subselects.elements && + !select_lex->order_list.elements && + select_lex->master_unit()->lim.get_select_limit() == HA_POS_ERROR && + !thd->lex->has_returning()) + multitable= true; + + if (!multitable) + ((multi_delete *)result)->set_delete_tables(0); + } + if (multitable) { /* @@ -1552,7 +1593,8 @@ bool Sql_cmd_delete::prepare_inner(THD *thd) { TABLE_LIST *duplicate; if ((duplicate= unique_table(thd, target_tbl->correspondent_table, - lex->query_tables, 0))) + lex->query_tables, 0)) && + !duplicate->select_lex->is_sj_subselect_lifted_to_top()) { update_non_unique_table_error(target_tbl->correspondent_table, "DELETE", duplicate); @@ -1567,38 +1609,6 @@ bool Sql_cmd_delete::prepare_inner(THD *thd) lex->first_select_lex()->exclude_from_table_unique_test= FALSE; } - { - if (thd->lex->describe) - select_options|= SELECT_DESCRIBE; - - /* - When in EXPLAIN, delay deleting the joins so that they are still - available when we're producing EXPLAIN EXTENDED warning text. - */ - if (select_options & SELECT_DESCRIBE) - free_join= 0; - select_options|= - SELECT_NO_JOIN_CACHE | SELECT_NO_UNLOCK | OPTION_SETUP_TABLES_DONE; - - if (!(join= new (thd->mem_root) JOIN(thd, empty_list, - select_options, result))) - DBUG_RETURN(TRUE); - THD_STAGE_INFO(thd, stage_init); - select_lex->join= join; - thd->lex->used_tables=0; - if ((err= join->prepare(table_list, select_lex->where, - select_lex->order_list.elements, - select_lex->order_list.first, - false, NULL, NULL, NULL, - select_lex, &lex->unit))) - - { - goto err; - } - - } - - if (setup_returning_fields(thd, table_list) || setup_ftfuncs(select_lex)) goto err; diff --git a/sql/sql_lex.h b/sql/sql_lex.h index 5436bfd..f760ea1 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -1642,6 +1642,7 @@ class st_select_lex: public st_select_lex_node void lex_start(LEX *plex); bool is_unit_nest() { return (nest_flags & UNIT_NEST_FL); } void mark_as_unit_nest() { nest_flags= UNIT_NEST_FL; } + bool is_sj_subselect_lifted_to_top(); }; typedef class st_select_lex SELECT_LEX; diff --git a/sql/sql_select.cc b/sql/sql_select.cc index ffd2efe..8360e36 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -5800,8 +5800,7 @@ make_join_statistics(JOIN *join, List<TABLE_LIST> &tables_list, s->needed_reg=select->needed_reg; select->quick=0; impossible_range= records == 0 && s->table->reginfo.impossible_range; - if (join->thd->lex->sql_command == SQLCOM_SELECT && - optimizer_flag(join->thd, OPTIMIZER_SWITCH_USE_ROWID_FILTER)) + if (optimizer_flag(join->thd, OPTIMIZER_SWITCH_USE_ROWID_FILTER)) s->table->init_cost_info_for_usable_range_rowid_filters(join->thd); } if (!impossible_range) diff --git a/sql/sql_update.cc b/sql/sql_update.cc index 0ed25c3..7c48f8e 100644 --- a/sql/sql_update.cc +++ b/sql/sql_update.cc @@ -2671,6 +2671,8 @@ int multi_update::do_updates() table = cur_table->table; if (table == table_to_update) continue; // Already updated + if (table->file->pushed_rowid_filter) + table->file->disable_pushed_rowid_filter(); org_updated= updated; tmp_table= tmp_tables[cur_table->shared]; tmp_table->file->extra(HA_EXTRA_CACHE); // Change to read cache @@ -2865,7 +2867,8 @@ int multi_update::do_updates() check_opt_it.rewind(); while (TABLE *tbl= check_opt_it++) tbl->file->ha_rnd_end(); - + if (table->file->save_pushed_rowid_filter) + table->file->enable_pushed_rowid_filter(); } DBUG_RETURN(0); @@ -2876,6 +2879,8 @@ int multi_update::do_updates() } err2: + if (table->file->save_pushed_rowid_filter) + table->file->enable_pushed_rowid_filter(); if (table->file->inited) (void) table->file->ha_rnd_end(); if (tmp_table->file->inited) @@ -3126,6 +3131,11 @@ bool Sql_cmd_update::prepare_inner(THD *thd) goto err; } + if (!multitable && + select_lex->sj_subselects.elements && + !select_lex->order_list.elements && + select_lex->master_unit()->lim.get_select_limit() == HA_POS_ERROR) + multitable= true; } free_join= false; diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 4dfbfb4..3953136 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -13347,8 +13347,21 @@ delete_single_table: YYPS->m_lock_type, YYPS->m_mdl_type, NULL, + 0))) + MYSQL_YYABORT; + Select->table_list.save_and_clear(&Lex->auxiliary_table_list); + Lex->table_count= 1; + Lex->query_tables= 0; + Lex->query_tables_last= &Lex->query_tables; + if (unlikely(!Select-> + add_table_to_list(thd, $2, NULL, TL_OPTION_UPDATING, + YYPS->m_lock_type, + YYPS->m_mdl_type, + NULL, $3))) MYSQL_YYABORT; + Lex->auxiliary_table_list.first->correspondent_table= + Lex->query_tables; YYPS->m_lock_type= TL_READ_DEFAULT; YYPS->m_mdl_type= MDL_SHARED_READ; }
1 0
0 0
[Commits] 6e7c6fc: MDEV-28448 Assertion failure for SELECT with subquery using ON expression
by IgorBabaev 30 Apr '22

30 Apr '22
revision-id: 6e7c6fcfd1f1ac131c423c1ba084d61abad10e8b (mariadb-10.4.23-82-g6e7c6fc) parent(s): c8228369f6dad5cfd17c5a9d9ea1c5c3ecd30fe7 author: Igor Babaev committer: Igor Babaev timestamp: 2022-04-30 13:25:34 -0700 message: MDEV-28448 Assertion failure for SELECT with subquery using ON expression This patch corrects the fix for MDEV-26412. Note that when parsing an ON expression the pointer to the current select is always in select_stack[select_stack_top - 1]. So the pointer to the outer select (if any) is in select_stack[select_stack_top - 2]. The query manifesting this bug is added to the test case of MDEV-26412. --- mysql-test/main/insert.result | 4 ++++ mysql-test/main/insert.test | 5 +++++ sql/sql_lex.h | 2 +- 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/mysql-test/main/insert.result b/mysql-test/main/insert.result index 1062ddd..af7dcbe 100644 --- a/mysql-test/main/insert.result +++ b/mysql-test/main/insert.result @@ -766,4 +766,8 @@ replace t4 select * from t1 left join t2 on (select t1.i from t3); ERROR 42S22: Unknown column 't1.i' in 'field list' drop table t1,t2,t3,t4; +create table t (a int); +select 1 in (select count(*) from t t1 join (t t2 join t t3 on (t1.a != 0))); +ERROR 42S22: Unknown column 't1.a' in 'on clause' +drop table t; # End of 10.4 tests diff --git a/mysql-test/main/insert.test b/mysql-test/main/insert.test index 9fd0933..27d4491 100644 --- a/mysql-test/main/insert.test +++ b/mysql-test/main/insert.test @@ -633,4 +633,9 @@ replace t4 drop table t1,t2,t3,t4; +create table t (a int); +--error ER_BAD_FIELD_ERROR +select 1 in (select count(*) from t t1 join (t t2 join t t3 on (t1.a != 0))); +drop table t; + --echo # End of 10.4 tests diff --git a/sql/sql_lex.h b/sql/sql_lex.h index 3e35d16..6a3a01f 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -3700,7 +3700,7 @@ struct LEX: public Query_tables_list SELECT_LEX *parser_current_outer_select() { return select_stack_top - 1 == select_stack_outer_barrier ? - 0 : select_stack[select_stack_top - 1]; + 0 : select_stack[select_stack_top - 2]; } Name_resolution_context *current_context()
1 0
0 0
[Commits] c985c93: MDEV-28080 Crash when using HAVING with NOT EXIST predicate in an equality
by IgorBabaev 29 Apr '22

29 Apr '22
revision-id: c985c93b6e10c247865b4710c1b9e6623bd6cceb (mariadb-10.4.23-75-gc985c93) parent(s): 39feab3cd31b5414aa9b428eaba915c251ac34a2 author: Igor Babaev committer: Igor Babaev timestamp: 2022-04-29 10:37:01 -0700 message: MDEV-28080 Crash when using HAVING with NOT EXIST predicate in an equality MDEV-28082 Crash when using HAVING with IS NULL predicate in an equality These bugs have been fixed by the patch for MDEV-26402. Only test cases are added. --- mysql-test/main/having_cond_pushdown.result | 31 +++++++++++++++++++++++++++ mysql-test/main/having_cond_pushdown.test | 33 +++++++++++++++++++++++++++++ 2 files changed, 64 insertions(+) diff --git a/mysql-test/main/having_cond_pushdown.result b/mysql-test/main/having_cond_pushdown.result index 9a9ed2b..ad8befb 100644 --- a/mysql-test/main/having_cond_pushdown.result +++ b/mysql-test/main/having_cond_pushdown.result @@ -4939,3 +4939,34 @@ i Warnings: Warning 1292 Truncated incorrect DOUBLE value: 'x' DROP TABLE t1; +# +# MDEV-28080: HAVING with NOT EXIST predicate in an equality +# (fixed by the patch for MDEV-26402) +# +CREATE TABLE t1 (a int); +CREATE TABLE t2 (b int); +INSERT INTO t1 VALUES (0), (1), (1), (0); +INSERT INTO t2 VALUES (3), (7); +SELECT a FROM t1 +GROUP BY a HAVING a= (NOT EXISTS (SELECT b FROM t2 WHERE b = 1)); +a +1 +SELECT a FROM t1 +GROUP BY a HAVING a= (NOT EXISTS (SELECT b FROM t2 WHERE b = 7)); +a +0 +DROP TABLE t1, t2; +# +# MDEV-28082: HAVING with IS NULL predicate in an equality +# (fixed by the patch for MDEV-26402) +# +CREATE TABLE t1 (a int, b int NOT NULL) ; +INSERT INTO t1 VALUES (1,10), (0,11), (0,11), (1,10); +SELECT a,b FROM t1 GROUP BY a HAVING a = (b IS NULL); +a b +0 11 +SELECT a,b FROM t1 GROUP BY a,b HAVING a = (b IS NULL); +a b +0 11 +DROP TABLE t1; +End of 10.4 tests diff --git a/mysql-test/main/having_cond_pushdown.test b/mysql-test/main/having_cond_pushdown.test index 270eac2..aee5a77 100644 --- a/mysql-test/main/having_cond_pushdown.test +++ b/mysql-test/main/having_cond_pushdown.test @@ -1450,3 +1450,36 @@ SELECT * FROM t1 GROUP BY i HAVING i IN ( i IS NULL); SELECT * FROM t1 GROUP BY i HAVING i IN ( i IS NULL AND 'x' = 0); SELECT * FROM t1 GROUP BY i HAVING i='1' IN ( i IS NULL AND 'x' = 0); DROP TABLE t1; + +--echo # +--echo # MDEV-28080: HAVING with NOT EXIST predicate in an equality +--echo # (fixed by the patch for MDEV-26402) +--echo # + +CREATE TABLE t1 (a int); +CREATE TABLE t2 (b int); +INSERT INTO t1 VALUES (0), (1), (1), (0); +INSERT INTO t2 VALUES (3), (7); + +SELECT a FROM t1 + GROUP BY a HAVING a= (NOT EXISTS (SELECT b FROM t2 WHERE b = 1)); +SELECT a FROM t1 + GROUP BY a HAVING a= (NOT EXISTS (SELECT b FROM t2 WHERE b = 7)); + +DROP TABLE t1, t2; + +--echo # +--echo # MDEV-28082: HAVING with IS NULL predicate in an equality +--echo # (fixed by the patch for MDEV-26402) +--echo # + +CREATE TABLE t1 (a int, b int NOT NULL) ; +INSERT INTO t1 VALUES (1,10), (0,11), (0,11), (1,10); + +SELECT a,b FROM t1 GROUP BY a HAVING a = (b IS NULL); + +SELECT a,b FROM t1 GROUP BY a,b HAVING a = (b IS NULL); + +DROP TABLE t1; + +--echo End of 10.4 tests
1 0
0 0
[Commits] 1f0333d: MDEV-27159 Re-design the upper level of handling DML commands
by IgorBabaev 29 Apr '22

29 Apr '22
revision-id: 1f0333db90fb942b2a8435733184460ca3c56288 (mariadb-10.6.1-309-g1f0333d) parent(s): 31c01a5b28fc59479755cab5098b3d13e55f732b author: Igor Babaev committer: Igor Babaev timestamp: 2022-04-28 17:15:26 -0700 message: MDEV-27159 Re-design the upper level of handling DML commands This is the second commit for the task. This patch allows to execute only single-table and multi-table DELETE statements using the method Sql_cmd_dml::execute(). The code that handles INSERT statements has not been touched. This patch still does not have the final changes to handle UPDATE/DELETE statements. All tests from the main suite passed. With --ps-protocol one test from opt_trace_security returns not the same result. This will be fixed soon. --- extra/wolfssl/wolfssl | 2 +- mysql-test/main/opt_trace.result | 14 +- mysql-test/main/opt_trace_security.result | 1 - sql/opt_range.cc | 2 +- sql/sql_base.cc | 7 +- sql/sql_cmd.h | 9 +- sql/sql_delete.cc | 577 ++++++++++++++++-------------- sql/sql_delete.h | 9 + sql/sql_lex.cc | 30 +- sql/sql_parse.cc | 138 +------ sql/sql_parse.h | 1 + sql/sql_prepare.cc | 102 +----- sql/sql_select.cc | 1 - sql/sql_update.cc | 7 +- sql/sql_yacc.yy | 63 ++-- sql/table.h | 1 + 16 files changed, 379 insertions(+), 585 deletions(-) diff --git a/extra/wolfssl/wolfssl b/extra/wolfssl/wolfssl index c3513bf..9c87f97 160000 --- a/extra/wolfssl/wolfssl +++ b/extra/wolfssl/wolfssl @@ -1 +1 @@ -Subproject commit c3513bf2573c30f6d2df815de216120e92142020 +Subproject commit 9c87f979a7f1d3a6d786b260653d566c1d31a1c4 diff --git a/mysql-test/main/opt_trace.result b/mysql-test/main/opt_trace.result index 044db82..1444320 100644 --- a/mysql-test/main/opt_trace.result +++ b/mysql-test/main/opt_trace.result @@ -3742,6 +3742,16 @@ QUERY TRACE MISSING_BYTES_BEYOND_MAX_MEM_SIZE INSUFFICIENT_PRIVILEGES explain delete from t0 where t0.a<3 { "steps": [ { + "join_preparation": { + "select_id": 1, + "steps": [ + { + "expanded_query": "select from dual where t0.a < 3" + } + ] + } + }, + { "table": "t0", "range_analysis": { "table_scan": { @@ -3773,7 +3783,7 @@ explain delete from t0 where t0.a<3 { }, "group_index_range": { "chosen": false, - "cause": "no join" + "cause": "no group by or distinct" }, "chosen_range_access_summary": { "range_access_plan": { @@ -3816,7 +3826,7 @@ explain delete t0,t1 from t0, t1 where t0.a=t1.a and t1.a<3 { "select_id": 1, "steps": [ { - "expanded_query": "select NULL AS `NULL` from t0 join t1 where t0.a = t1.a and t1.a < 3" + "expanded_query": "select from t0 join t1 where t0.a = t1.a and t1.a < 3" } ] } diff --git a/mysql-test/main/opt_trace_security.result b/mysql-test/main/opt_trace_security.result index e1937e7..d960dd2 100644 --- a/mysql-test/main/opt_trace_security.result +++ b/mysql-test/main/opt_trace_security.result @@ -16,7 +16,6 @@ select * from db1.t1; ERROR 42000: SELECT command denied to user 'foo'@'localhost' for table 't1' select * from information_schema.OPTIMIZER_TRACE; QUERY TRACE MISSING_BYTES_BEYOND_MAX_MEM_SIZE INSUFFICIENT_PRIVILEGES - 0 1 set optimizer_trace="enabled=off"; grant select(a) on db1.t1 to 'foo'@'%'; set optimizer_trace="enabled=on"; diff --git a/sql/opt_range.cc b/sql/opt_range.cc index 7909f5b..13457d7 100644 --- a/sql/opt_range.cc +++ b/sql/opt_range.cc @@ -11589,7 +11589,7 @@ ha_rows check_quick_select(PARAM *param, uint idx, bool index_only, Skip materialized derived table/view result table from MRR check as they aren't contain any data yet. */ - if (param->table->pos_in_table_list->is_non_derived()) + if (!param->table->pos_in_table_list->is_materialized_derived()) rows= file->multi_range_read_info_const(keynr, &seq_if, (void*)&seq, 0, bufsize, mrr_flags, cost); param->quick_rows[keynr]= rows; diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 6883fb1..51b619d 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -1074,7 +1074,9 @@ TABLE_LIST* find_dup_table(THD *thd, TABLE_LIST *table, TABLE_LIST *table_list, */ if (table->table && thd->lex->sql_command != SQLCOM_UPDATE && - thd->lex->sql_command != SQLCOM_UPDATE_MULTI) + thd->lex->sql_command != SQLCOM_UPDATE_MULTI && + thd->lex->sql_command != SQLCOM_DELETE && + thd->lex->sql_command != SQLCOM_DELETE_MULTI) { /* All MyISAMMRG children are plain MyISAM tables. */ DBUG_ASSERT(table->table->file->ht->db_type != DB_TYPE_MRG_MYISAM); @@ -7570,6 +7572,9 @@ int setup_wild(THD *thd, TABLE_LIST *tables, List<Item> &fields, if (!select_lex->with_wild) DBUG_RETURN(0); + if (!fields.elements) + DBUG_RETURN(0); + /* Don't use arena if we are not in prepared statements or stored procedures For PS/SP we have to use arena to remember the changes diff --git a/sql/sql_cmd.h b/sql/sql_cmd.h index c62fe83..2c069cf 100644 --- a/sql/sql_cmd.h +++ b/sql/sql_cmd.h @@ -262,6 +262,7 @@ class LEX; class select_result; class Prelocking_strategy; class DML_prelocking_strategy; +class Protocol; class Sql_cmd_dml : public Sql_cmd { @@ -289,7 +290,9 @@ class Sql_cmd_dml : public Sql_cmd protected: Sql_cmd_dml() - : Sql_cmd(), lex(nullptr), result(nullptr), m_empty_query(false) {} + : Sql_cmd(), lex(nullptr), result(nullptr), + m_empty_query(false), save_protocol(NULL) + {} /// @return true if query is guaranteed to return no data /** @@ -347,12 +350,14 @@ class Sql_cmd_dml : public Sql_cmd virtual DML_prelocking_strategy *get_dml_prelocking_strategy() = 0; - uint table_count; + uint table_count; protected: LEX *lex; ///< Pointer to LEX for this statement select_result *result; ///< Pointer to object for handling of the result bool m_empty_query; ///< True if query will produce no rows + List<Item> empty_list; + Protocol *save_protocol; }; diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc index 95adf17..7ac4797 100644 --- a/sql/sql_delete.cc +++ b/sql/sql_delete.cc @@ -103,7 +103,7 @@ bool Update_plan::save_explain_data_intern(MEM_ROOT *mem_root, bool is_analyze) { explain->select_type= "SIMPLE"; - explain->table_name.append(&table->pos_in_table_list->alias); + explain->table_name.append(table->alias); explain->impossible_where= false; explain->no_partitions= false; @@ -294,124 +294,79 @@ int TABLE::delete_row() } -/** - Implement DELETE SQL word. - - @note Like implementations of other DDL/DML in MySQL, this function - relies on the caller to close the thread tables. This is done in the - end of dispatch_command(). -*/ - -bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, - SQL_I_List<ORDER> *order_list, ha_rows limit, - ulonglong options, select_result *result) +bool Sql_cmd_delete::delete_from_single_table(THD *thd) { - bool will_batch= FALSE; - int error, loc_error; - TABLE *table; - SQL_SELECT *select=0; - SORT_INFO *file_sort= 0; - READ_RECORD info; - bool using_limit=limit != HA_POS_ERROR; - bool transactional_table, safe_update, const_cond; - bool const_cond_result; - bool return_error= 0; - ha_rows deleted= 0; - bool reverse= FALSE; - bool has_triggers= false; - ORDER *order= (ORDER *) ((order_list && order_list->elements) ? - order_list->first : NULL); - SELECT_LEX *select_lex= thd->lex->first_select_lex(); - SELECT_LEX *returning= thd->lex->has_returning() ? thd->lex->returning() : 0; + int error; + int loc_error; + bool transactional_table; + bool const_cond; + bool safe_update; + bool const_cond_result; + bool return_error= 0; + TABLE *table; + SQL_SELECT *select= 0; + SORT_INFO *file_sort= 0; + READ_RECORD info; + ha_rows deleted= 0; + bool reverse= FALSE; + bool binlog_is_row; killed_state killed_status= NOT_KILLED; THD::enum_binlog_query_type query_type= THD::ROW_QUERY_TYPE; - bool binlog_is_row; - Explain_delete *explain; + bool will_batch= FALSE; + + bool has_triggers= false; + SELECT_LEX_UNIT *unit = &lex->unit; + SELECT_LEX *select_lex= unit->first_select(); + SELECT_LEX *returning= thd->lex->has_returning() ? thd->lex->returning() : 0; + TABLE_LIST *const table_list = select_lex->get_table_list(); + ulonglong options= select_lex->options; + ORDER *order= select_lex->order_list.first; + COND *conds= select_lex->join->conds; + ha_rows limit= unit->lim.get_select_limit(); + bool using_limit= limit != HA_POS_ERROR; + Delete_plan query_plan(thd->mem_root); + Explain_delete *explain; Unique * deltempfile= NULL; bool delete_record= false; - bool delete_while_scanning; + bool delete_while_scanning= table_list->delete_while_scanning; bool portion_of_time_through_update; - DBUG_ENTER("mysql_delete"); + + DBUG_ENTER("Sql_cmd_delete::delete_single_table"); query_plan.index= MAX_KEY; query_plan.using_filesort= FALSE; - create_explain_query(thd->lex, thd->mem_root); - if (open_and_lock_tables(thd, table_list, TRUE, 0)) - DBUG_RETURN(TRUE); - THD_STAGE_INFO(thd, stage_init_update); + create_explain_query(thd->lex, thd->mem_root); const bool delete_history= table_list->vers_conditions.delete_history; DBUG_ASSERT(!(delete_history && table_list->period_conditions.is_set())); - if (thd->lex->handle_list_of_derived(table_list, DT_MERGE_FOR_INSERT)) - DBUG_RETURN(TRUE); - if (thd->lex->handle_list_of_derived(table_list, DT_PREPARE)) - DBUG_RETURN(TRUE); + if (table_list->handle_derived(thd->lex, DT_MERGE_FOR_INSERT)) + DBUG_RETURN(1); + if (table_list->handle_derived(thd->lex, DT_PREPARE)) + DBUG_RETURN(1); + + table= table_list->table; if (!table_list->single_table_updatable()) { my_error(ER_NON_UPDATABLE_TABLE, MYF(0), table_list->alias.str, "DELETE"); DBUG_RETURN(TRUE); } - if (!(table= table_list->table) || !table->is_created()) + + if (!table || !table->is_created()) { my_error(ER_VIEW_DELETE_MERGE_VIEW, MYF(0), table_list->view_db.str, table_list->view_name.str); DBUG_RETURN(TRUE); } - table->map=1; + query_plan.select_lex= thd->lex->first_select_lex(); query_plan.table= table; - thd->lex->promote_select_describe_flag_if_needed(); - if (mysql_prepare_delete(thd, table_list, &conds, &delete_while_scanning)) - DBUG_RETURN(TRUE); - - if (table_list->has_period()) - { - if (!table_list->period_conditions.start.item->const_item() - || !table_list->period_conditions.end.item->const_item()) - { - my_error(ER_NOT_CONSTANT_EXPRESSION, MYF(0), "FOR PORTION OF"); - DBUG_RETURN(true); - } - } - - if (delete_history) - table->vers_write= false; - - if (returning) - (void) result->prepare(returning->item_list, NULL); - - if (thd->lex->current_select->first_cond_optimization) - { - thd->lex->current_select->save_leaf_tables(thd); - thd->lex->current_select->first_cond_optimization= 0; - } - /* check ORDER BY even if it can be ignored */ - if (order) - { - TABLE_LIST tables; - List<Item> fields; - List<Item> all_fields; - - bzero((char*) &tables,sizeof(tables)); - tables.table = table; - tables.alias = table_list->alias; - - if (select_lex->setup_ref_array(thd, order_list->elements) || - setup_order(thd, select_lex->ref_pointer_array, &tables, - fields, all_fields, order)) - { - free_underlaid_joins(thd, thd->lex->first_select_lex()); - DBUG_RETURN(TRUE); - } - } - /* Apply the IN=>EXISTS transformation to all subqueries and optimize them. */ if (select_lex->optimize_unflattened_subqueries(false)) DBUG_RETURN(TRUE); @@ -519,7 +474,7 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, table->covering_keys.clear_all(); table->opt_range_keys.clear_all(); - select=make_select(table, 0, 0, conds, (SORT_INFO*) 0, 0, &error); + select= make_select(table, 0, 0, conds, (SORT_INFO*) 0, 0, &error); if (unlikely(error)) DBUG_RETURN(TRUE); if ((select && select->check_quick(thd, safe_update, limit)) || !limit) @@ -953,7 +908,7 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, } } DBUG_ASSERT(transactional_table || !deleted || thd->transaction->stmt.modified_non_trans_table); - + if (likely(error < 0) || (thd->lex->ignore && !thd->is_error() && !thd->is_fatal_error)) { @@ -1003,90 +958,6 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, } -/* - Prepare items in DELETE statement - - SYNOPSIS - mysql_prepare_delete() - thd - thread handler - table_list - global/local table list - conds - conditions - - RETURN VALUE - FALSE OK - TRUE error -*/ -int mysql_prepare_delete(THD *thd, TABLE_LIST *table_list, Item **conds, - bool *delete_while_scanning) -{ - Item *fake_conds= 0; - SELECT_LEX *select_lex= thd->lex->first_select_lex(); - DBUG_ENTER("mysql_prepare_delete"); - List<Item> all_fields; - - *delete_while_scanning= true; - thd->lex->allow_sum_func.clear_all(); - if (setup_tables_and_check_access(thd, &select_lex->context, - &select_lex->top_join_list, table_list, - select_lex->leaf_tables, FALSE, - DELETE_ACL, SELECT_ACL, TRUE)) - DBUG_RETURN(TRUE); - - 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); - DBUG_RETURN(true); - } - - if (table_list->has_period()) - { - if (table_list->is_view_or_derived()) - { - my_error(ER_IT_IS_A_VIEW, MYF(0), table_list->table_name.str); - DBUG_RETURN(true); - } - - if (select_lex->period_setup_conds(thd, table_list)) - DBUG_RETURN(true); - } - - DBUG_ASSERT(table_list->table); - // conds could be cached from previous SP call - DBUG_ASSERT(!table_list->vers_conditions.need_setup() || - !*conds || thd->stmt_arena->is_stmt_execute()); - if (select_lex->vers_setup_conds(thd, table_list)) - DBUG_RETURN(TRUE); - - *conds= select_lex->where; - - if (setup_returning_fields(thd, table_list) || - setup_conds(thd, table_list, select_lex->leaf_tables, conds) || - setup_ftfuncs(select_lex)) - DBUG_RETURN(TRUE); - if (!table_list->single_table_updatable() || - check_key_in_view(thd, table_list)) - { - my_error(ER_NON_UPDATABLE_TABLE, MYF(0), table_list->alias.str, "DELETE"); - DBUG_RETURN(TRUE); - } - - /* - Application-time periods: if FOR PORTION OF ... syntax used, DELETE - statement could issue delete_row's mixed with write_row's. This causes - problems for myisam and corrupts table, if deleting while scanning. - */ - if (table_list->has_period() - || unique_table(thd, table_list, table_list->next_global, 0)) - *delete_while_scanning= false; - - if (select_lex->inner_refs_list.elements && - fix_inner_refs(thd, all_fields, select_lex, select_lex->ref_pointer_array)) - DBUG_RETURN(TRUE); - - select_lex->fix_prepare_information(thd, conds, &fake_conds); - DBUG_RETURN(FALSE); -} - /*************************************************************************** Delete multiple tables from join @@ -1099,106 +970,6 @@ extern "C" int refpos_order_cmp(void* arg, const void *a,const void *b) return file->cmp_ref((const uchar*)a, (const uchar*)b); } -/* - make delete specific preparation and checks after opening tables - - SYNOPSIS - mysql_multi_delete_prepare() - thd thread handler - - RETURN - FALSE OK - TRUE Error -*/ - -int mysql_multi_delete_prepare(THD *thd) -{ - LEX *lex= thd->lex; - TABLE_LIST *aux_tables= lex->auxiliary_table_list.first; - TABLE_LIST *target_tbl; - DBUG_ENTER("mysql_multi_delete_prepare"); - - if (mysql_handle_derived(lex, DT_INIT)) - DBUG_RETURN(TRUE); - if (mysql_handle_derived(lex, DT_MERGE_FOR_INSERT)) - DBUG_RETURN(TRUE); - if (mysql_handle_derived(lex, DT_PREPARE)) - DBUG_RETURN(TRUE); - /* - setup_tables() need for VIEWs. JOIN::prepare() will not do it second - time. - - lex->query_tables also point on local list of DELETE SELECT_LEX - */ - if (setup_tables_and_check_access(thd, - &thd->lex->first_select_lex()->context, - &thd->lex->first_select_lex()-> - top_join_list, - lex->query_tables, - lex->first_select_lex()->leaf_tables, - FALSE, DELETE_ACL, SELECT_ACL, FALSE)) - DBUG_RETURN(TRUE); - - /* - Multi-delete can't be constructed over-union => we always have - single SELECT on top and have to check underlying SELECTs of it - */ - lex->first_select_lex()->set_unique_exclude(); - /* Fix tables-to-be-deleted-from list to point at opened tables */ - for (target_tbl= (TABLE_LIST*) aux_tables; - target_tbl; - target_tbl= target_tbl->next_local) - { - - target_tbl->table= target_tbl->correspondent_table->table; - if (target_tbl->correspondent_table->is_multitable()) - { - my_error(ER_VIEW_DELETE_MERGE_VIEW, MYF(0), - target_tbl->correspondent_table->view_db.str, - target_tbl->correspondent_table->view_name.str); - DBUG_RETURN(TRUE); - } - - if (!target_tbl->correspondent_table->single_table_updatable() || - check_key_in_view(thd, target_tbl->correspondent_table)) - { - my_error(ER_NON_UPDATABLE_TABLE, MYF(0), - target_tbl->table_name.str, "DELETE"); - DBUG_RETURN(TRUE); - } - } - - for (target_tbl= (TABLE_LIST*) aux_tables; - target_tbl; - target_tbl= target_tbl->next_local) - { - /* - Check that table from which we delete is not used somewhere - inside subqueries/view. - */ - { - TABLE_LIST *duplicate; - if ((duplicate= unique_table(thd, target_tbl->correspondent_table, - lex->query_tables, 0))) - { - update_non_unique_table_error(target_tbl->correspondent_table, - "DELETE", duplicate); - DBUG_RETURN(TRUE); - } - } - } - /* - Reset the exclude flag to false so it doesn't interfare - with further calls to unique_table - */ - lex->first_select_lex()->exclude_from_table_unique_test= FALSE; - - if (lex->save_prep_leaf_tables()) - DBUG_RETURN(TRUE); - - DBUG_RETURN(FALSE); -} - 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), @@ -1647,3 +1418,261 @@ bool multi_delete::send_eof() } return 0; } + + +bool Sql_cmd_delete::precheck(THD *thd) +{ + if (!multitable) + { + if (delete_precheck(thd, lex->query_tables)) + return true; + } + else + { + if (multi_delete_precheck(thd, lex->query_tables)) + return true; + } + return false; +} + + +bool Sql_cmd_delete::prepare_inner(THD *thd) +{ + int err= 0; + TABLE_LIST *target_tbl; + JOIN *join; + SELECT_LEX *const select_lex = thd->lex->first_select_lex(); + TABLE_LIST *const table_list = select_lex->get_table_list(); + TABLE_LIST *aux_tables= thd->lex->auxiliary_table_list.first; + ulonglong select_options= select_lex->options; + bool free_join= 1; + SELECT_LEX *returning= thd->lex->has_returning() ? thd->lex->returning() : 0; + + DBUG_ENTER("Sql_cmd_delete::prepare_inner"); + + (void) read_statistics_for_tables_if_needed(thd, table_list); + + { + if (mysql_handle_derived(lex, DT_INIT)) + DBUG_RETURN(TRUE); + if (mysql_handle_derived(lex, DT_MERGE_FOR_INSERT)) + DBUG_RETURN(TRUE); + if (mysql_handle_derived(lex, DT_PREPARE)) + DBUG_RETURN(TRUE); + } + + if (!(result= new (thd->mem_root) multi_delete(thd, aux_tables, + lex->table_count))) + { + DBUG_RETURN(TRUE); + } + + table_list->delete_while_scanning= true; + + if (setup_tables_and_check_access(thd, &select_lex->context, + &select_lex->top_join_list, + table_list, select_lex->leaf_tables, + false, DELETE_ACL, SELECT_ACL, true)) + DBUG_RETURN(TRUE); + + if (setup_tables(thd, &select_lex->context, &select_lex->top_join_list, + table_list, select_lex->leaf_tables, false, false)) + DBUG_RETURN(TRUE); + + if (!multitable) + { + 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); + DBUG_RETURN(true); + } + + if (table_list->has_period()) + { + if (table_list->is_view_or_derived()) + { + my_error(ER_IT_IS_A_VIEW, MYF(0), table_list->table_name.str); + DBUG_RETURN(true); + } + + if (select_lex->period_setup_conds(thd, table_list)) + DBUG_RETURN(true); + } + + if (select_lex->vers_setup_conds(thd, table_list)) + DBUG_RETURN(TRUE); + /* + Application-time periods: if FOR PORTION OF ... syntax used, DELETE + statement could issue delete_row's mixed with write_row's. This causes + problems for myisam and corrupts table, if deleting while scanning. + */ + if (table_list->has_period() + || unique_table(thd, table_list, table_list->next_global, 0)) + table_list->delete_while_scanning= false; + } + + if (multitable) + { + /* + Multi-delete can't be constructed over-union => we always have + single SELECT on top and have to check underlying SELECTs of it + */ + lex->first_select_lex()->set_unique_exclude(); + /* Fix tables-to-be-deleted-from list to point at opened tables */ + for (target_tbl= (TABLE_LIST*) aux_tables; + target_tbl; + target_tbl= target_tbl->next_local) + { + target_tbl->table= target_tbl->correspondent_table->table; + if (target_tbl->correspondent_table->is_multitable()) + { + my_error(ER_VIEW_DELETE_MERGE_VIEW, MYF(0), + target_tbl->correspondent_table->view_db.str, + target_tbl->correspondent_table->view_name.str); + DBUG_RETURN(TRUE); + } + + if (!target_tbl->correspondent_table->single_table_updatable() || + check_key_in_view(thd, target_tbl->correspondent_table)) + { + my_error(ER_NON_UPDATABLE_TABLE, MYF(0), + target_tbl->table_name.str, "DELETE"); + DBUG_RETURN(TRUE); + } + } + + for (target_tbl= (TABLE_LIST*) aux_tables; + target_tbl; + target_tbl= target_tbl->next_local) + { + /* + Check that table from which we delete is not used somewhere + inside subqueries/view. + */ + { + TABLE_LIST *duplicate; + if ((duplicate= unique_table(thd, target_tbl->correspondent_table, + lex->query_tables, 0))) + { + update_non_unique_table_error(target_tbl->correspondent_table, + "DELETE", duplicate); + DBUG_RETURN(TRUE); + } + } + } + /* + Reset the exclude flag to false so it doesn't interfare + with further calls to unique_table + */ + lex->first_select_lex()->exclude_from_table_unique_test= FALSE; + } + + { + if (thd->lex->describe) + select_options|= SELECT_DESCRIBE; + + /* + When in EXPLAIN, delay deleting the joins so that they are still + available when we're producing EXPLAIN EXTENDED warning text. + */ + if (select_options & SELECT_DESCRIBE) + free_join= 0; + select_options|= + SELECT_NO_JOIN_CACHE | SELECT_NO_UNLOCK | OPTION_SETUP_TABLES_DONE; + + if (!(join= new (thd->mem_root) JOIN(thd, empty_list, + select_options, result))) + DBUG_RETURN(TRUE); + THD_STAGE_INFO(thd, stage_init); + select_lex->join= join; + thd->lex->used_tables=0; + if ((err= join->prepare(table_list, select_lex->where, + select_lex->order_list.elements, + select_lex->order_list.first, + false, NULL, NULL, NULL, + select_lex, &lex->unit))) + + { + goto err; + } + + } + + + if (setup_returning_fields(thd, table_list) || + setup_ftfuncs(select_lex)) + goto err; + + free_join= false; + + if (returning) + (void) result->prepare(returning->item_list, NULL); + +err: + + if (free_join) + { + THD_STAGE_INFO(thd, stage_end); + err|= (int)(select_lex->cleanup()); + DBUG_RETURN(err || thd->is_error()); + } + DBUG_RETURN(err); + +} + +bool Sql_cmd_delete::execute_inner(THD *thd) +{ + if (!multitable) + { + if (lex->has_returning()) + { + select_result *sel_result= NULL; + delete result; + /* This is DELETE ... RETURNING. It will return output to the client */ + if (thd->lex->analyze_stmt) + { + /* + Actually, it is ANALYZE .. DELETE .. RETURNING. We need to produce + output and then discard it. + */ + sel_result= new (thd->mem_root) select_send_analyze(thd); + save_protocol= thd->protocol; + thd->protocol= new Protocol_discard(thd); + } + else + { + if (!lex->result && !(sel_result= new (thd->mem_root) select_send(thd))) + return true; + } + result= lex->result ? lex->result : sel_result; + } + } + + bool res= multitable ? Sql_cmd_dml::execute_inner(thd) + : delete_from_single_table(thd); + + res|= thd->is_error(); + + if (save_protocol) + { + delete thd->protocol; + thd->protocol= save_protocol; + } + { + if (unlikely(res)) + result->abort_result_set(); + else + { + if (thd->lex->describe || thd->lex->analyze_stmt) + res= thd->lex->explain->send_explain(thd); + } + } + + if (result) + { + res= false; + delete result; + } + + return res; +} diff --git a/sql/sql_delete.h b/sql/sql_delete.h index dabcafb..4aee510 100644 --- a/sql/sql_delete.h +++ b/sql/sql_delete.h @@ -17,6 +17,9 @@ #define SQL_DELETE_INCLUDED #include "my_base.h" /* ha_rows */ +#include "sql_class.h" /* enum_duplicates */ +#include "sql_cmd.h" // Sql_cmd_dml +#include "sql_base.h" class THD; struct TABLE_LIST; @@ -43,6 +46,11 @@ class Sql_cmd_delete final : public Sql_cmd_dml return multitable ? SQLCOM_DELETE_MULTI : SQLCOM_DELETE; } + DML_prelocking_strategy *get_dml_prelocking_strategy() + { + return &dml_prelocking_strategy; + } + protected: bool precheck(THD *thd) override; @@ -55,5 +63,6 @@ class Sql_cmd_delete final : public Sql_cmd_dml bool multitable; + DML_prelocking_strategy dml_prelocking_strategy; }; #endif /* SQL_DELETE_INCLUDED */ diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index ee5b2a8..aca34e9 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -3328,34 +3328,6 @@ void st_select_lex_unit::exclude_level() } -#if 0 -/* - Exclude subtree of current unit from tree of SELECTs - - SYNOPSYS - st_select_lex_unit::exclude_tree() -*/ -void st_select_lex_unit::exclude_tree() -{ - for (SELECT_LEX *sl= first_select(); sl; sl= sl->next_select()) - { - // unlink current level from global SELECTs list - if (sl->link_prev && (*sl->link_prev= sl->link_next)) - sl->link_next->link_prev= sl->link_prev; - - // unlink underlay levels - for (SELECT_LEX_UNIT *u= sl->first_inner_unit(); u; u= u->next_unit()) - { - u->exclude_level(); - } - } - // exclude currect unit from list of nodes - (*prev)= next; - if (next) - next->prev= prev; -} -#endif - /* st_select_lex_node::mark_as_dependent mark all st_select_lex struct from @@ -3577,7 +3549,7 @@ bool st_select_lex::setup_ref_array(THD *thd, uint order_group_num) select_n_where_fields + order_group_num + hidden_bit_fields + - fields_in_window_functions) * (size_t) 5; + fields_in_window_functions + 1) * (size_t) 5; DBUG_ASSERT(n_elems % 5 == 0); if (!ref_pointer_array.is_null()) { diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 72ac199..4a32736 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -4395,10 +4395,15 @@ mysql_execute_command(THD *thd, bool is_called_from_prepared_stmt) } case SQLCOM_UPDATE: case SQLCOM_UPDATE_MULTI: + case SQLCOM_DELETE: + case SQLCOM_DELETE_MULTI: { DBUG_ASSERT(first_table == all_tables && first_table != 0); DBUG_ASSERT(lex->m_sql_cmd != NULL); - thd->abort_on_warning= !thd->lex->ignore && thd->is_strict_mode(); + + if (lex->sql_command == SQLCOM_UPDATE || + lex->sql_command == SQLCOM_UPDATE_MULTI) + thd->abort_on_warning= !thd->lex->ignore && thd->is_strict_mode(); res = lex->m_sql_cmd->execute(thd); thd->abort_on_warning= 0; break; @@ -4662,129 +4667,6 @@ mysql_execute_command(THD *thd, bool is_called_from_prepared_stmt) break; } - case SQLCOM_DELETE: - { - WSREP_SYNC_WAIT(thd, WSREP_SYNC_WAIT_BEFORE_UPDATE_DELETE); - select_result *sel_result= NULL; - DBUG_ASSERT(first_table == all_tables && first_table != 0); - WSREP_SYNC_WAIT(thd, WSREP_SYNC_WAIT_BEFORE_UPDATE_DELETE); - - if ((res= delete_precheck(thd, all_tables))) - break; - DBUG_ASSERT(select_lex->limit_params.offset_limit == 0); - unit->set_limit(select_lex); - - MYSQL_DELETE_START(thd->query()); - Protocol *save_protocol= NULL; - - if (lex->has_returning()) - { - /* This is DELETE ... RETURNING. It will return output to the client */ - if (thd->lex->analyze_stmt) - { - /* - Actually, it is ANALYZE .. DELETE .. RETURNING. We need to produce - output and then discard it. - */ - sel_result= new (thd->mem_root) select_send_analyze(thd); - save_protocol= thd->protocol; - thd->protocol= new Protocol_discard(thd); - } - else - { - if (!lex->result && !(sel_result= new (thd->mem_root) select_send(thd))) - goto error; - } - } - - res = mysql_delete(thd, all_tables, - select_lex->where, &select_lex->order_list, - unit->lim.get_select_limit(), select_lex->options, - lex->result ? lex->result : sel_result); - - if (save_protocol) - { - delete thd->protocol; - thd->protocol= save_protocol; - } - - if (thd->lex->analyze_stmt || thd->lex->describe) - { - if (!res) - res= thd->lex->explain->send_explain(thd); - } - - delete sel_result; - MYSQL_DELETE_DONE(res, (ulong) thd->get_row_count_func()); - break; - } - case SQLCOM_DELETE_MULTI: - { - WSREP_SYNC_WAIT(thd, WSREP_SYNC_WAIT_BEFORE_UPDATE_DELETE); - DBUG_ASSERT(first_table == all_tables && first_table != 0); - TABLE_LIST *aux_tables= thd->lex->auxiliary_table_list.first; - multi_delete *result; - WSREP_SYNC_WAIT(thd, WSREP_SYNC_WAIT_BEFORE_UPDATE_DELETE); - - if ((res= multi_delete_precheck(thd, all_tables))) - break; - - /* condition will be TRUE on SP re-excuting */ - if (select_lex->item_list.elements != 0) - select_lex->item_list.empty(); - if (add_item_to_list(thd, new (thd->mem_root) Item_null(thd))) - goto error; - - THD_STAGE_INFO(thd, stage_init); - if ((res= open_and_lock_tables(thd, all_tables, TRUE, 0))) - break; - - MYSQL_MULTI_DELETE_START(thd->query()); - if (unlikely(res= mysql_multi_delete_prepare(thd))) - { - MYSQL_MULTI_DELETE_DONE(1, 0); - goto error; - } - - if (likely(!thd->is_fatal_error)) - { - result= new (thd->mem_root) multi_delete(thd, aux_tables, - lex->table_count); - if (likely(result)) - { - if (unlikely(select_lex->vers_setup_conds(thd, aux_tables))) - goto multi_delete_error; - res= mysql_select(thd, - select_lex->get_table_list(), - select_lex->item_list, - select_lex->where, - 0, (ORDER *)NULL, (ORDER *)NULL, (Item *)NULL, - (ORDER *)NULL, - (select_lex->options | thd->variables.option_bits | - SELECT_NO_JOIN_CACHE | SELECT_NO_UNLOCK | - OPTION_SETUP_TABLES_DONE) & ~OPTION_BUFFER_RESULT, - result, unit, select_lex); - res|= (int)(thd->is_error()); - - MYSQL_MULTI_DELETE_DONE(res, result->num_deleted()); - if (res) - result->abort_result_set(); /* for both DELETE and EXPLAIN DELETE */ - else - { - if (lex->describe || lex->analyze_stmt) - res= thd->lex->explain->send_explain(thd); - } - multi_delete_error: - delete result; - } - } - else - { - res= TRUE; // Error - MYSQL_MULTI_DELETE_DONE(1, 0); - } - break; - } case SQLCOM_DROP_SEQUENCE: case SQLCOM_DROP_TABLE: { @@ -7659,12 +7541,16 @@ void create_select_for_variable(THD *thd, LEX_CSTRING *var_name) } -void mysql_init_multi_delete(LEX *lex) +void mysql_init_delete(LEX *lex) { - lex->sql_command= SQLCOM_DELETE_MULTI; mysql_init_select(lex); lex->first_select_lex()->limit_params.clear(); lex->unit.lim.clear(); +} + +void mysql_init_multi_delete(LEX *lex) +{ + lex->sql_command= SQLCOM_DELETE_MULTI; lex->first_select_lex()->table_list. save_and_clear(&lex->auxiliary_table_list); lex->query_tables= 0; diff --git a/sql/sql_parse.h b/sql/sql_parse.h index ebe3fe9..45cd15c 100644 --- a/sql/sql_parse.h +++ b/sql/sql_parse.h @@ -95,6 +95,7 @@ void mysql_parse(THD *thd, char *rawbuf, uint length, bool mysql_new_select(LEX *lex, bool move_down, SELECT_LEX *sel); void create_select_for_variable(THD *thd, LEX_CSTRING *var_name); void create_table_set_open_action_and_adjust_tables(LEX *lex); +void mysql_init_delete(LEX *lex); void mysql_init_multi_delete(LEX *lex); bool multi_delete_set_locks_and_link_aux_tables(LEX *lex); void create_table_set_open_action_and_adjust_tables(LEX *lex); diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc index 3859a13..84feda3 100644 --- a/sql/sql_prepare.cc +++ b/sql/sql_prepare.cc @@ -95,7 +95,6 @@ When one supplies long data for a placeholder: #include "sql_base.h" // open_normal_and_derived_tables #include "sql_cache.h" // query_cache_* #include "sql_view.h" // create_view_precheck -#include "sql_delete.h" // mysql_prepare_delete #include "sql_select.h" // for JOIN #include "sql_insert.h" // upgrade_lock_type_for_insert, mysql_prepare_insert #include "sql_db.h" // mysql_opt_change_db, mysql_change_db @@ -1398,56 +1397,6 @@ static bool mysql_test_insert(Prepared_statement *stmt, } -/** - Validate DELETE statement. - - @param stmt prepared statement - @param tables list of tables used in this query - - @retval - FALSE success - @retval - TRUE error, error message is set in THD -*/ - -static bool mysql_test_delete(Prepared_statement *stmt, - TABLE_LIST *table_list) -{ - uint table_count= 0; - THD *thd= stmt->thd; - LEX *lex= stmt->lex; - bool delete_while_scanning; - DBUG_ENTER("mysql_test_delete"); - - if (delete_precheck(thd, table_list) || - open_tables(thd, &table_list, &table_count, MYSQL_OPEN_FORCE_SHARED_MDL)) - goto error; - - if (mysql_handle_derived(thd->lex, DT_INIT)) - goto error; - if (mysql_handle_derived(thd->lex, DT_MERGE_FOR_INSERT)) - goto error; - if (mysql_handle_derived(thd->lex, DT_PREPARE)) - goto error; - - if (!table_list->single_table_updatable()) - { - my_error(ER_NON_UPDATABLE_TABLE, MYF(0), table_list->alias.str, "DELETE"); - goto error; - } - if (!table_list->table || !table_list->table->is_created()) - { - my_error(ER_VIEW_DELETE_MERGE_VIEW, MYF(0), - table_list->view_db.str, table_list->view_name.str); - goto error; - } - - DBUG_RETURN(mysql_prepare_delete(thd, table_list, - &lex->first_select_lex()->where, - &delete_while_scanning)); -error: - DBUG_RETURN(TRUE); -} /** @@ -2031,48 +1980,6 @@ static bool mysql_test_create_view(Prepared_statement *stmt) /** - Validate and prepare for execution a multi delete statement. - - @param stmt prepared statement - @param tables list of tables used in this query - - @retval - FALSE success - @retval - TRUE error, error message in THD is set. -*/ - -static bool mysql_test_multidelete(Prepared_statement *stmt, - TABLE_LIST *tables) -{ - THD *thd= stmt->thd; - - thd->lex->current_select= thd->lex->first_select_lex(); - if (add_item_to_list(thd, new (thd->mem_root) - Item_null(thd))) - { - my_error(ER_OUTOFMEMORY, MYF(ME_FATAL), 0); - goto error; - } - - if (multi_delete_precheck(thd, tables) || - select_like_stmt_test_with_open(stmt, tables, - &mysql_multi_delete_prepare, - OPTION_SETUP_TABLES_DONE)) - goto error; - if (!tables->table) - { - my_error(ER_VIEW_DELETE_MERGE_VIEW, MYF(0), - tables->view_db.str, tables->view_name.str); - goto error; - } - return FALSE; -error: - return TRUE; -} - - -/** Wrapper for mysql_insert_select_prepare, to make change of local tables after open_normal_and_derived_tables() call. @@ -2354,14 +2261,13 @@ static bool check_prepared_statement(Prepared_statement *stmt) case SQLCOM_UPDATE: case SQLCOM_UPDATE_MULTI: + case SQLCOM_DELETE: + case SQLCOM_DELETE_MULTI: res = lex->m_sql_cmd->prepare(thd); if (!res) lex->m_sql_cmd->unprepare(thd); break; - case SQLCOM_DELETE: - res= mysql_test_delete(stmt, tables); - break; /* The following allow WHERE clause, so they must be tested like SELECT */ case SQLCOM_SHOW_DATABASES: case SQLCOM_SHOW_TABLES: @@ -2498,10 +2404,6 @@ static bool check_prepared_statement(Prepared_statement *stmt) res= mysql_test_set_fields(stmt, tables, &lex->var_list); break; - case SQLCOM_DELETE_MULTI: - res= mysql_test_multidelete(stmt, tables); - break; - case SQLCOM_INSERT_SELECT: case SQLCOM_REPLACE_SELECT: res= mysql_test_insert_select(stmt, tables); diff --git a/sql/sql_select.cc b/sql/sql_select.cc index edd9aed..ffd2efe 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -30345,7 +30345,6 @@ bool Sql_cmd_dml::execute(THD *thd) THD_STAGE_INFO(thd, stage_init); - DBUG_ASSERT(!lex->is_query_tables_locked()); /* Locking of tables is done after preparation but before optimization. This allows to do better partition pruning and avoid locking unused diff --git a/sql/sql_update.cc b/sql/sql_update.cc index 3f04db0..0ed25c3 100644 --- a/sql/sql_update.cc +++ b/sql/sql_update.cc @@ -3029,12 +3029,10 @@ bool Sql_cmd_update::prepare_inner(THD *thd) { JOIN *join; int err= 0; - // uint table_cnt= 0; SELECT_LEX *const select_lex = thd->lex->first_select_lex(); TABLE_LIST *const table_list = select_lex->get_table_list(); ulonglong select_options= select_lex->options; bool free_join= 1; - // bool orig_multitable= multitable; DBUG_ENTER("Sql_cmd_update::prepare_inner"); if (!multitable) @@ -3088,8 +3086,8 @@ bool Sql_cmd_update::prepare_inner(THD *thd) DBUG_RETURN(TRUE); } - if (((multi_update *)result)->init(thd)) - DBUG_RETURN(TRUE); + if (((multi_update *)result)->init(thd)) + DBUG_RETURN(TRUE); if (setup_tables(thd, &select_lex->context, &select_lex->top_join_list, table_list, select_lex->leaf_tables, false, false)) @@ -3147,6 +3145,7 @@ bool Sql_cmd_update::prepare_inner(THD *thd) bool Sql_cmd_update::execute_inner(THD *thd) { + thd->get_stmt_da()->reset_current_row_for_warning(1); bool res= multitable ? Sql_cmd_dml::execute_inner(thd) : update_single_table(thd); diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 30eda7c..4dfbfb4 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -70,6 +70,7 @@ #include "sql_type_json.h" #include "json_table.h" #include "sql_update.h" +#include "sql_delete.h" /* this is to get the bison compilation windows warnings out */ #ifdef _MSC_VER @@ -1675,7 +1676,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize); opt_mi_check_type opt_to mi_check_types table_to_table_list table_to_table opt_table_list opt_as handler_rkey_function handler_read_or_scan - single_multi table_wild_list table_wild_one opt_wild + single_multi opt_wild opt_and select_var_list select_var_list_init help opt_extended_describe shutdown @@ -13303,12 +13304,11 @@ delete: DELETE_SYM { LEX *lex= Lex; - lex->sql_command= SQLCOM_DELETE; YYPS->m_lock_type= TL_WRITE_DEFAULT; YYPS->m_mdl_type= MDL_SHARED_WRITE; if (Lex->main_select_push()) MYSQL_YYABORT; - mysql_init_select(lex); + mysql_init_delete(lex); lex->ignore= 0; lex->first_select_lex()->order_list.empty(); } @@ -13369,12 +13369,22 @@ single_multi: delete_limit_clause opt_returning { + LEX *lex= Lex; if ($3) Select->order_list= *($3); - Lex->pop_select(); //main select + lex->pop_select(); //main select + lex->sql_command= SQLCOM_DELETE; + if (!(lex->m_sql_cmd= + new (thd->mem_root) Sql_cmd_delete(false))) + MYSQL_YYABORT; } - | table_wild_list + | table_alias_ref_list { + LEX *lex= Lex; + lex->sql_command= SQLCOM_DELETE_MULTI; + if (!(lex->m_sql_cmd= + new (thd->mem_root) Sql_cmd_delete(true))) + MYSQL_YYABORT; mysql_init_multi_delete(Lex); YYPS->m_lock_type= TL_READ_DEFAULT; YYPS->m_mdl_type= MDL_SHARED_READ; @@ -13386,6 +13396,11 @@ single_multi: } stmt_end {} | FROM table_alias_ref_list { + LEX *lex= Lex; + lex->sql_command= SQLCOM_DELETE_MULTI; + if (!(lex->m_sql_cmd= + new (thd->mem_root) Sql_cmd_delete(true))) + MYSQL_YYABORT; mysql_init_multi_delete(Lex); YYPS->m_lock_type= TL_READ_DEFAULT; YYPS->m_mdl_type= MDL_SHARED_READ; @@ -13421,44 +13436,6 @@ opt_returning: } ; -table_wild_list: - table_wild_one - | table_wild_list ',' table_wild_one - ; - -table_wild_one: - ident opt_wild - { - Table_ident *ti= new (thd->mem_root) Table_ident(&$1); - if (unlikely(ti == NULL)) - MYSQL_YYABORT; - if (unlikely(!Select-> - add_table_to_list(thd, - ti, - NULL, - (TL_OPTION_UPDATING | - TL_OPTION_ALIAS), - YYPS->m_lock_type, - YYPS->m_mdl_type))) - MYSQL_YYABORT; - } - | ident '.' ident opt_wild - { - Table_ident *ti= new (thd->mem_root) Table_ident(thd, &$1, &$3, 0); - if (unlikely(ti == NULL)) - MYSQL_YYABORT; - if (unlikely(!Select-> - add_table_to_list(thd, - ti, - NULL, - (TL_OPTION_UPDATING | - TL_OPTION_ALIAS), - YYPS->m_lock_type, - YYPS->m_mdl_type))) - MYSQL_YYABORT; - } - ; - opt_wild: /* empty */ {} | '.' '*' {} diff --git a/sql/table.h b/sql/table.h index 8d609fb..358e1a9 100644 --- a/sql/table.h +++ b/sql/table.h @@ -2321,6 +2321,7 @@ struct TABLE_LIST */ select_unit *derived_result; /* Stub used for materialized derived tables. */ + bool delete_while_scanning; table_map map; /* ID bit of table (1,2,4,8,16...) */ table_map get_map() {
1 0
0 0
[Commits] f04bf35: MDEV-26412 Server crash in Item_field::fix_outer_field for INSERT SELECT
by IgorBabaev 26 Apr '22

26 Apr '22
revision-id: f04bf3529cbe472c6d73158e9bc73b1a5e8c1841 (mariadb-10.4.23-34-gf04bf35) parent(s): c519aa3d7aea1afb7754ccf70aa5b276ff5fbf87 author: Igor Babaev committer: Igor Babaev timestamp: 2022-04-25 18:08:57 -0700 message: MDEV-26412 Server crash in Item_field::fix_outer_field for INSERT SELECT IF an INSERT/REPLACE SELECT statement contained an ON expression in the top level select and this expression used a subquery with a column reference that could not be resolved then an attempt to resolve this reference as an outer reference caused a crash of the server. This happened because the outer context field in the Name_resolution_context structure was not set to NULL for such references. Rather it pointed to the first element in the select_stack. Note that starting from 10.4 we cannot use the SELECT_LEX::outer_select() method when parsing a SELECT construct. Approved by Oleksandr Byelkin <sanja(a)mariadb.com> --- mysql-test/main/insert.result | 16 ++++++++++++++++ mysql-test/main/insert.test | 22 ++++++++++++++++++++++ sql/sql_lex.cc | 1 + sql/sql_lex.h | 17 +++++++++++++++++ sql/sql_parse.cc | 4 +--- sql/sql_yacc.yy | 2 ++ 6 files changed, 59 insertions(+), 3 deletions(-) diff --git a/mysql-test/main/insert.result b/mysql-test/main/insert.result index 4aea812..1062ddd 100644 --- a/mysql-test/main/insert.result +++ b/mysql-test/main/insert.result @@ -751,3 +751,19 @@ REPLACE INTO v1 SET f = NULL; ERROR 22007: Truncated incorrect DOUBLE value: 'foo' DROP VIEW v1; DROP TABLE t1; +# End of 10.0 tests +# +# MDEV-26412: INSERT CREATE with subquery in ON expression +# +create table t1 (a int); +create table t2 (b int); +create table t3 (c int); +create table t4 (d1 int, d2 int); +insert into t4 +select * from t1 left join t2 on (select t1.i from t3); +ERROR 42S22: Unknown column 't1.i' in 'field list' +replace t4 +select * from t1 left join t2 on (select t1.i from t3); +ERROR 42S22: Unknown column 't1.i' in 'field list' +drop table t1,t2,t3,t4; +# End of 10.4 tests diff --git a/mysql-test/main/insert.test b/mysql-test/main/insert.test index e00c9cd..9fd0933 100644 --- a/mysql-test/main/insert.test +++ b/mysql-test/main/insert.test @@ -612,3 +612,25 @@ CREATE VIEW v1 AS SELECT * FROM t1 WHERE f <=> 'foo' WITH CHECK OPTION; REPLACE INTO v1 SET f = NULL; DROP VIEW v1; DROP TABLE t1; + +--echo # End of 10.0 tests + +--echo # +--echo # MDEV-26412: INSERT CREATE with subquery in ON expression +--echo # + +create table t1 (a int); +create table t2 (b int); +create table t3 (c int); +create table t4 (d1 int, d2 int); + +--error ER_BAD_FIELD_ERROR +insert into t4 + select * from t1 left join t2 on (select t1.i from t3); +--error ER_BAD_FIELD_ERROR +replace t4 + select * from t1 left join t2 on (select t1.i from t3); + +drop table t1,t2,t3,t4; + +--echo # End of 10.4 tests diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index a93652c..b8f009f 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -701,6 +701,7 @@ void LEX::start(THD *thd_arg) context_stack.empty(); //empty select_stack select_stack_top= 0; + select_stack_outer_barrier= 0; unit.init_query(); current_select_number= 0; curr_with_clause= 0; diff --git a/sql/sql_lex.h b/sql/sql_lex.h index 19d4eb0..f274695 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -3246,6 +3246,12 @@ struct LEX: public Query_tables_list List<Name_resolution_context> context_stack; SELECT_LEX *select_stack[MAX_SELECT_NESTING + 1]; uint select_stack_top; + /* + Usually this is set to 0, but for INSERT/REPLACE SELECT it is set to 1. + When parsing such statements the pointer to the most outer select is placed + into the second element of select_stack rather than into the first. + */ + uint select_stack_outer_barrier; SQL_I_List<ORDER> proc_list; SQL_I_List<TABLE_LIST> auxiliary_table_list, save_list; @@ -3686,6 +3692,17 @@ struct LEX: public Query_tables_list bool copy_db_to(LEX_CSTRING *to); + void inc_select_stack_outer_barrier() + { + select_stack_outer_barrier++; + } + + SELECT_LEX *parser_current_outer_select() + { + return select_stack_top - 1 == select_stack_outer_barrier ? + 0 : select_stack[select_stack_top - 1]; + } + Name_resolution_context *current_context() { return context_stack.head(); diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 35310d2..723f072 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -9015,9 +9015,7 @@ push_new_name_resolution_context(THD *thd, right_op->last_leaf_for_name_resolution(); LEX *lex= thd->lex; on_context->select_lex = lex->current_select; - st_select_lex *curr_select= lex->pop_select(); - st_select_lex *outer_sel= lex->select_stack_head(); - lex->push_select(curr_select); + st_select_lex *outer_sel= lex->parser_current_outer_select(); on_context->outer_context = outer_sel ? &outer_sel->context : 0; return lex->push_context(on_context); } diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index fed704e..ddb3604 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -13579,6 +13579,7 @@ insert: if (Lex->main_select_push()) MYSQL_YYABORT; mysql_init_select(lex); + lex->inc_select_stack_outer_barrier(); lex->current_select->parsing_place= BEFORE_OPT_LIST; } insert_lock_option @@ -13605,6 +13606,7 @@ replace: if (Lex->main_select_push()) MYSQL_YYABORT; mysql_init_select(lex); + lex->inc_select_stack_outer_barrier(); lex->current_select->parsing_place= BEFORE_OPT_LIST; } replace_lock_option insert2
1 0
0 0
  • ← Newer
  • 1
  • ...
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • ...
  • 1461
  • Older →

HyperKitty Powered by HyperKitty version 1.3.12.