[Commits] ab67d5c: MDEV-27159 Re-design the upper level of handling DML commands
by IgorBabaev 18 Jun '22
by IgorBabaev 18 Jun '22
18 Jun '22
revision-id: ab67d5c0db5645e6cfee10700a0e84fda35606b0 (mariadb-10.9.1-18-gab67d5c)
parent(s): 9fe784ff7e56b7b5c182b29819ccc4da5f7b2af7
author: Igor Babaev
committer: Igor Babaev
timestamp: 2022-06-17 21:20:03 -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/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 | 206 ++++++-
sql/sql_delete.cc | 630 +++++++++++--------
sql/sql_delete.h | 69 ++-
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 | 267 +++++++-
sql/sql_update.cc | 669 +++++++++------------
sql/sql_update.h | 81 ++-
sql/sql_yacc.yy | 85 ++-
sql/table.h | 1 +
.../mysql-test/spider/r/error_row_number.result | 2 +-
38 files changed, 1453 insertions(+), 1286 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/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..8e104a5 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
+# 'order by 1' should be considered as in 'select * from v1 order 1'
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..9852886 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 global context the processed statement
+ @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 global context the processed statement
+ @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 global context of the processed statement
+
+ @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,152 @@ 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 - derivative abstract class used for DML statements
+
+ This class is a class derived from Sql_cmd used when processing such
+ data manipulation commands as SELECT, INSERT, UPDATE, DELETE and others
+ that operate over some tables.
+ After the parser phase all these commands are supposed to be processed
+ by the same schema:
+ - precheck of the access rights is performed for the used tables
+ - the used tables are opened
+ - context analysis phase is performed for the statement
+ - the used tables are locked
+ - the statement is optimized and executed
+ - clean-up is performed for the statement.
+ This schema is reflected in the function Sql_cmd_dml::execute() that
+ uses Sql_cmd_dml::prepare is the statement has not been prepared yet.
+ Precheck of the access right, context analysis are specific for statements
+ of a certain type. That's why the methods implementing this operations are
+ declared as abstract in this class.
+
+ @note
+ Currently this class is used only for UPDATE and DELETE commands.
+*/
+class Sql_cmd_dml : public Sql_cmd
+{
+public:
+
+ /**
+ @brief Check whether the statement changes the contents of used tables
+ @return true if this is data change statement, false otherwise
+ */
+ virtual bool is_data_change_stmt() const { return true; }
+
+ /**
+ @brief Perform context analysis of the statement
+ @param thd global context the processed statement
+ @returns false on success, true on error
+ */
+ virtual bool prepare(THD *thd);
+
+ /**
+ Execute the processed statement once
+ @param thd global context the processed statement
+ @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)
+ {}
+
+ /**
+ @brief Check whether query is guaranteed to return no data
+ @return true if query is guaranteed to return no data, false otherwise
+
+ @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;
}
+
+ /**
+ @brief Set statement as returning no data
+ */
+ void set_empty_query() { m_empty_query = true; }
+
+ /**
+ @brief Perform precheck of table privileges for the specific command
+ @param thd global context the processed statement
+ @returns false if success, true if false
+
+ @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.
+ */
+ virtual bool precheck(THD *thd) = 0;
+
+ /**
+ @brief Perform the command-specific actions of the context analysis
+ @param thd global context the processed statement
+ @returns false if success, true if error
+
+ @note
+ This function is called from prepare()
+ */
+ virtual bool prepare_inner(THD *thd) = 0;
+
+ /**
+ @brief Perform the command-specific actions of optimization and excution
+ @param thd global context the processed statement
+ @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 */
};
+
class Sql_cmd_show_slave_status: public Sql_cmd
{
protected:
diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc
index 65a3a76..0e7a2ea 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;
@@ -295,123 +295,86 @@ int TABLE::delete_row()
/**
- Implement DELETE SQL word.
+ @brief Special handling of single-table deletes after prepare phase
- @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().
+ @param thd global context the processed statement
+ @returns false on success, true on error
*/
-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 +974,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 +986,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 +1438,320 @@ 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;
+}
+
+
+/**
+ @brief Perform context analysis for delete statements
+
+ @param thd global context the processed statement
+ @returns false on success, true on error
+
+ @note
+ The main bulk of the context analysis actions for a delete statement
+ is performed by a call of JOIN::prepare().
+*/
+
+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);
+
+}
+
+
+/**
+ @brief Perform optimization and execution actions needed for deletes
+
+ @param thd global context the processed statement
+ @returns false on success, true on error
+*/
+
+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..e1d5044 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,66 @@ 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 - class used for any DELETE statements
+ This class is derived from Sql_cmd_dml and contains implementations
+ for abstract virtual function of the latter such as precheck() and
+ prepare_inner(). It also overrides the implementation of execute_inner()
+ providing a special handling for single-table delete statements that
+ are not converted to multi-table delete.
+ The class provides an object of the DML_prelocking_strategy class
+ for the virtual function get_dml_prelocking_strategy().
+*/
+class Sql_cmd_delete final : public Sql_cmd_dml
+{
+public:
+ Sql_cmd_delete(bool multitable_arg)
+ : multitable(multitable_arg), save_protocol(NULL) {}
+
+ 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:
+ /**
+ @brief Perform precheck of table privileges for delete statements
+ */
+ bool precheck(THD *thd) override;
+
+ /**
+ @brief Perform context analysis for delete statements
+ */
+ bool prepare_inner(THD *thd) override;
+
+ /**
+ @brief Perform optimization and execution actions needed for deletes
+ */
+ bool execute_inner(THD *thd) override;
+
+ private:
+ /**
+ @biefSpecial handling of single-table deletes after prepare phase
+ */
+ bool delete_from_single_table(THD *thd);
+
+ /*
+ True if the statement is a multitable delete or converted to such.
+ For a single-table delete this flag is set to true if the statement
+ is supposed to be converted to multi-table delete.
+ */
+ bool multitable;
+
+ /* The prelocking strategy used when opening the used tables */
+ DML_prelocking_strategy dml_prelocking_strategy;
+
+ List<Item> empty_list; /**< auxiliary empty list used by prepare_inner() */
+ Protocol *save_protocol; /**< needed for ANALYZE .. DELETE .. RETURNING */
+};
#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..bf49d69 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,266 @@ 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);
+ }
+}
+
+/*
+ @brief Perform actions needed before locking tables for a DML statement
+
+ @param thd global context the processed statement
+ @returns false if success, true if error
+
+ @details
+ This function calls the precheck() procedure fo the processed statement,
+ then is opens tables used in the statement and finally it calls the function
+ prepare_inner() that is specific for the type of the statement.
+
+ @note
+ The function are used when processing:
+ - a DML statement
+ - PREPARE stmt FROM <DML "statement>"
+ - EXECUTE stmt when stmt is prepared from a DML statement.
+*/
+
+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;
+}
+
+
+/**
+ @brief Execute a DML statement
+
+ @param thd global context the processed statement
+ @returns false if success, true if error
+
+ @details
+ The function assumes that each type of a DML statement has its own
+ implementation of the virtunal functions precheck(). It is also
+ assumed that that the virtual function execute execute_inner() is to be
+ overridden by the implementations for specific commands.
+
+ @note
+ Currently only UPDATE and DELETE statement are executed using this function.
+*/
+
+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())
+ {
+ /*
+ This is called when processing
+ - a DML statement
+ - PREPARE stmt FROM <DML "statement>"
+ - EXECUTE stmt when stmt is prepared from a DML statement.
+ The call will invoke open_tables_for_query()
+ */
+ if (prepare(thd))
+ goto err;
+ }
+ else // This branch currently is never used for DML commands
+ {
+ 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();
+}
+
+/**
+ @brief Generic implemention of optimization and execution phases
+ @param thd global context the processed statement
+ @returns false if success, true if error
+
+ @note
+ This implementation assumes that the processed DML statement is represented
+ as a SELECT_LEX or SELECT_LEX_UNIT tree with attached corresponding
+ JOIN structures. Any JOIN structure is constructed at the prepare phase.
+ When created at the top level join it is provided with an object of a class
+ derived from select_result_sink. The pointer to the object is saved in
+ the this->result field. For different types of DML statements different
+ derived classes are used for this object. The class of this object determines
+ additional specific actions performed at the phases of context analysis,
+ optimization and execution.
+*/
+
+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..6b14c4f 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,25 @@ 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
+/**
+ @brief Special handling of single-table updates after prepare phase
- RETURN
- 0 - OK
- 2 - privilege check and openning table passed, but we need to convert to
- multi-update because of view substitution
- 1 - error
+ @param thd global context the processed statement
+ @returns false on success, true on 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 +358,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");
+ thd->table_map_for_update= 0;
- 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_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 +399,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 +658,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 +909,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 +1242,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 +1276,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 +1503,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 +1532,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 +1549,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 +1661,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 +1696,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 +1782,8 @@ int multi_update::prepare(List<Item> ¬_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 +2804,237 @@ bool multi_update::send_eof()
}
DBUG_RETURN(FALSE);
}
+
+
+/**
+ @brief Perform precheck of table privileges for update statements
+
+ @param thd global context the processed statement
+ @returns false on success, true on error
+*/
+
+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;
+}
+
+
+/**
+ @brief Perform context analysis for update statements
+
+ @param thd global context the processed statement
+ @returns false on success, true on error
+
+ @note
+ The main bulk of the context analysis actions for and update statement
+ is performed by a call of JOIN::prepare().
+*/
+
+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);
+
+}
+
+
+/**
+ @brief Perform optimization and execution actions needed for updates
+
+ @param thd global context the processed statement
+ @returns false on success, true on error
+*/
+
+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..d0fc7cb 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,75 @@ 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 - class used for any UPDATE statements
+
+ This class is derived from Sql_cmd_dml and contains implementations
+ for abstract virtual function of the latter such as precheck() and
+ prepare_inner(). It also overrides the implementation of execute_inner()
+ providing a special handling for single-table update statements that
+ are not converted to multi-table updates.
+ The class provides an object of the Multiupdate_prelocking_strategy class
+ for the virtual function get_dml_prelocking_strategy().
+*/
+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:
+ /**
+ @brief Perform precheck of table privileges for update statements
+ */
+ bool precheck(THD *thd) override;
+
+ /**
+ @brief Perform context analysis for update statements
+ */
+ bool prepare_inner(THD *thd) override;
+
+ /**
+ @brief Perform optimization and execution actions needed for updates
+ */
+ bool execute_inner(THD *thd) override;
+
+private:
+
+ /**
+ @brief Special handling of single-table updates after prepare phase
+ */
+ bool update_single_table(THD *thd);
+
+ /*
+ True if the statement is a multi-table update or converted to such.
+ For a single-table update this flag is set to true if the statement
+ is supposed to be converted to multi-table update.
+ */
+ bool multitable;
+
+ /* The prelocking strategy used when opening the used tables */
+ Multiupdate_prelocking_strategy multiupdate_prelocking_strategy;
+
+ public:
+ /* The list of the updating expressions used in the set clause */
+ 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
[Commits] a2c0e27: MDEV-27159 Re-design the upper level of handling DML commands
by IgorBabaev 17 Jun '22
by IgorBabaev 17 Jun '22
17 Jun '22
revision-id: a2c0e27863e8a875b8fd9f33ca83cc43d24edbf7 (mariadb-10.9.1-18-ga2c0e27)
parent(s): 9fe784ff7e56b7b5c182b29819ccc4da5f7b2af7
author: Igor Babaev
committer: Igor Babaev
timestamp: 2022-06-16 23:45:38 -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/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 | 206 ++++++-
sql/sql_delete.cc | 612 +++++++++++---------
sql/sql_delete.h | 39 +-
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 +-
38 files changed, 1285 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/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..8e104a5 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
+# 'order by 1' should be considered as in 'select * from v1 order 1'
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..679d1c7 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 global context the processed statement
+ @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 global context the processed statement
+ @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 global context of the processed statement
+
+ @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,152 @@ 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 - a derivative abstract class used for DML statements
+
+ This class is a class derived from Sql_cmd used when processing such
+ data manipulation commands as SELECT, INSERT, UPDATE, DELETE and others
+ that operate over some tables.
+ After the parser phase all these commands are supposed to be processed
+ by the same schema:
+ - precheck of the access rights is performed for the used tables
+ - the used tables are opened
+ - context analysis phase is performed for the statement
+ - the used tables are locked
+ - the statement is optimized and executed
+ - clean up is performed for the statement.
+ This schema is reflected in the function Sql_cmd_dml::execute() that
+ uses Sql_cmd_dml::prepare is the statement has not been prepared yet.
+ Precheck of the access right, context analysis are specific for statements
+ of a certain type. That's why the methods implementing this operations are
+ declared as abstract in this class.
+
+ @note
+ Currently this class is used only for UPDATE and DELETE command.
+*/
+class Sql_cmd_dml : public Sql_cmd
+{
+public:
+
+ /**
+ @brief Check whether the statement changes the contents of used tables
+ @return true if this is data change statement, false otherwise
+ */
+ virtual bool is_data_change_stmt() const { return true; }
+
+ /**
+ @brief Perform context analysis of the statement
+ @param thd global context the processed statement
+ @returns false on success, true on error
+ */
+ virtual bool prepare(THD *thd);
+
+ /**
+ Execute the processed statement once
+ @param thd global context the processed statement
+ @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)
+ {}
+
+ /**
+ @brief Check whether query is guaranteed to return no data
+ @return true if query is guaranteed to return no data, false otherwise
+
+ @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;
}
+
+ /**
+ @brief Set statement as returning no data
+ */
+ void set_empty_query() { m_empty_query = true; }
+
+ /**
+ @brief Perform precheck of table privileges for the specific command
+ @param thd global context the processed statement
+ @returns false if success, true if false
+
+ @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.
+ */
+ virtual bool precheck(THD *thd) = 0;
+
+ /**
+ @brief Perform the command-specific actions of the context analysis
+ @param thd global context the processed statement
+ @returns false if success, true if error
+
+ @note
+ This function is called from prepare()
+ */
+ virtual bool prepare_inner(THD *thd) = 0;
+
+ /**
+ @brief Perform the command-specific actions of optimization and excution
+ @param thd global context the processed statement
+ @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 */
};
+
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..55f7147 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,36 @@ 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), save_protocol(NULL) {}
+ 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;
+ List<Item> empty_list;
+ Protocol *save_protocol;
+};
#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..ccccd69 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)) // this will call open_tables_for_query()
+ 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..1161e03 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> ¬_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
[Commits] a5ea3c1: MDEV-27159 Re-design the upper level of handling DML commands
by IgorBabaev 16 Jun '22
by IgorBabaev 16 Jun '22
16 Jun '22
revision-id: a5ea3c103e544773da559f2f07ebbbb7284b7310 (mariadb-10.9.1-17-ga5ea3c1)
parent(s): f929fa45b66e4a98c815bac3a2778efb97989b98
author: Igor Babaev
committer: Igor Babaev
timestamp: 2022-06-15 23:31:03 -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 | 204 ++++++-
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, 1281 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..d023fed 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,150 @@ 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 - a derivative abstract class used for DML statements
+
+ This class is a class derived from Sql_cmd used when processing such
+ data manipulation commands as SELECT, INSERT, UPDATE, DELETE and others
+ that operate over some tables.
+ After the parser phase all these commands are supposed to be processed
+ by the same schema:
+ - precheck of the access rights is performed for the used tables
+ - the used tables are opened
+ - context analysis phase is performed for the statement
+ - the used tables are locked
+ - the statement is optimized and executed
+ - clean up is performed for the statement.
+ This schema is reflected in the function Sql_cmd_dml::execute() that
+ uses Sql_cmd_dml::prepare is the statement has not been prepared yet.
+ Precheck of the access right, context analysis and optimization/execution
+ phase are specific for statements of a certain type. That's why the methods
+ implementing this operations are declared as abstract in this class.
+
+ @note
+ Currently this class is used only for UPDATE and DELETE command.
+*/
+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; }
+
+ /**
+ @brief Perform a precheck of table privileges for the specific operation
+
+ @param thd thread handler
+ @returns false if success, true if false
+
+ @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.
+ */
+ virtual bool precheck(THD *thd) = 0;
+
+ /**
+ @brief Perform the command-specific actions of the context analysis phase
+ 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..1161e03 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> ¬_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
[Commits] be59442: MDEV-27159 Re-design the upper level of handling DML commands
by IgorBabaev 15 Jun '22
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> ¬_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
[Commits] d478191: MDEV-27159 Re-design the upper level of handling DML commands
by IgorBabaev 14 Jun '22
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> ¬_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
[Commits] 67585ab: MDEV-27159 Re-design the upper level of handling DML commands
by IgorBabaev 14 Jun '22
by IgorBabaev 14 Jun '22
14 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
[Commits] 0a975cc: MDEV-27159 Re-design the upper level of handling DML commands
by IgorBabaev 13 Jun '22
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
[Commits] bb9579a: MDEV-27159 Re-design the upper level of handling DML commands
by IgorBabaev 13 Jun '22
by IgorBabaev 13 Jun '22
13 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
[Commits] d5d8695: MDEV-7487 Semi-join optimization for single-table UPDATE/DELETEs
by IgorBabaev 04 Jun '22
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
[Commits] 6e7c6fc: MDEV-28448 Assertion failure for SELECT with subquery using ON expression
by IgorBabaev 30 Apr '22
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