summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIgor Babaev <igor@askmonty.org>2021-12-02 21:51:14 -0800
committerIgor Babaev <igor@askmonty.org>2022-04-27 10:24:40 -0700
commit31c01a5b28fc59479755cab5098b3d13e55f732b (patch)
tree668447324774afe1d27107761bc83e65da4fe5c4
parent638afc4acf86b32b74b3b37314f2dbd048062814 (diff)
downloadmariadb-git-31c01a5b28fc59479755cab5098b3d13e55f732b.tar.gz
MDEV-27159 Re-design the upper level of handling DML commands
This is the first commit for the task. This patch allows to execute only single-table and multi-table UPDATE statements using the method Sql_cmd_dml::execute(). The code that handles DELETE and INSERT statements has not been touched. Moreover, these are not the final changes to handle UPDATE 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.
-rw-r--r--mysql-test/main/analyze_stmt_privileges2.result9
-rw-r--r--mysql-test/main/analyze_stmt_privileges2.test15
-rw-r--r--mysql-test/main/myisam_explain_non_select_all.result86
-rw-r--r--mysql-test/main/mysqlbinlog_row_minimal.test1
-rw-r--r--mysql-test/main/order_by.result8
-rw-r--r--mysql-test/main/partition_explicit_prune.result1
-rw-r--r--mysql-test/main/sp.result2
-rw-r--r--mysql-test/main/update.result2
-rw-r--r--mysql-test/main/update_use_source.result7
-rw-r--r--mysql-test/main/update_use_source.test2
-rw-r--r--mysql-test/main/view_grant.result1
-rw-r--r--mysql-test/main/view_grant.test1
-rw-r--r--sql/opt_subselect.cc2
-rw-r--r--sql/opt_trace.cc3
-rw-r--r--sql/sql_base.cc26
-rw-r--r--sql/sql_base.h15
-rw-r--r--sql/sql_class.h1
-rw-r--r--sql/sql_cmd.h151
-rw-r--r--sql/sql_delete.h24
-rw-r--r--sql/sql_lex.cc9
-rw-r--r--sql/sql_lex.h26
-rw-r--r--sql/sql_parse.cc128
-rw-r--r--sql/sql_prepare.cc143
-rw-r--r--sql/sql_select.cc138
-rw-r--r--sql/sql_update.cc352
-rw-r--r--sql/sql_update.h40
-rw-r--r--sql/sql_yacc.yy9
27 files changed, 689 insertions, 513 deletions
diff --git a/mysql-test/main/analyze_stmt_privileges2.result b/mysql-test/main/analyze_stmt_privileges2.result
index f269aaf540b..d40dd636818 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 a0f1f4908b6..8b011c2b592 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 2ff966fdfd3..36231c3878c 100644
--- a/mysql-test/main/myisam_explain_non_select_all.result
+++ b/mysql-test/main/myisam_explain_non_select_all.result
@@ -19,7 +19,6 @@ 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;
@@ -38,7 +37,6 @@ 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
@@ -152,7 +150,6 @@ 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;
@@ -172,7 +169,6 @@ 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
@@ -202,7 +198,6 @@ 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;
@@ -222,7 +217,6 @@ 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
@@ -240,18 +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
+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);
@@ -272,7 +264,6 @@ 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
@@ -302,7 +293,6 @@ 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);
@@ -323,7 +313,6 @@ 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
@@ -355,7 +344,6 @@ 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);
@@ -377,7 +365,7 @@ Handler_read_key 7
Handler_read_rnd_next 12
# Status of testing query execution:
Variable_name Value
-Handler_read_key 7
+Handler_read_key 3
Handler_read_rnd_next 16
Handler_update 2
@@ -407,7 +395,6 @@ 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;
@@ -427,7 +414,6 @@ 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
@@ -459,7 +445,6 @@ 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;
@@ -482,7 +467,6 @@ 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
@@ -513,7 +497,6 @@ 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;
@@ -533,7 +516,6 @@ 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
@@ -995,7 +977,6 @@ 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);
@@ -1016,7 +997,6 @@ 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
@@ -1134,7 +1114,6 @@ 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;
@@ -1153,7 +1132,6 @@ 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
@@ -1917,7 +1895,6 @@ 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;
@@ -1936,7 +1913,7 @@ Handler_read_key 5
Handler_read_next 4
# Status of testing query execution:
Variable_name Value
-Handler_read_key 5
+Handler_read_key 1
Handler_read_next 4
Handler_read_rnd 5
Handler_update 5
@@ -1965,7 +1942,6 @@ 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;
@@ -1987,7 +1963,6 @@ 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
@@ -2019,7 +1994,6 @@ 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;
@@ -2041,7 +2015,6 @@ 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
@@ -2074,7 +2047,6 @@ 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;
@@ -2095,7 +2067,6 @@ 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
@@ -2124,7 +2095,6 @@ 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;
@@ -2146,7 +2116,6 @@ 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
@@ -2178,7 +2147,6 @@ 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;
@@ -2201,7 +2169,6 @@ 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
@@ -2233,7 +2200,6 @@ 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;
@@ -2255,7 +2221,7 @@ Sort_range 1
Sort_rows 4
# Status of testing query execution:
Variable_name Value
-Handler_read_key 8
+Handler_read_key 2
Handler_read_next 7
Handler_read_rnd 8
Handler_update 4
@@ -2286,7 +2252,6 @@ 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;
@@ -2305,7 +2270,7 @@ Handler_read_key 5
Handler_read_prev 4
# Status of testing query execution:
Variable_name Value
-Handler_read_key 5
+Handler_read_key 1
Handler_read_prev 4
Handler_read_rnd 5
Handler_update 5
@@ -2334,7 +2299,6 @@ 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;
@@ -2356,7 +2320,6 @@ 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
@@ -2389,7 +2352,6 @@ 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;
@@ -2409,7 +2371,6 @@ 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
@@ -2441,7 +2402,6 @@ 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;
@@ -2463,7 +2423,7 @@ Sort_range 1
Sort_rows 2
# Status of testing query execution:
Variable_name Value
-Handler_read_key 7
+Handler_read_key 1
Handler_read_next 2
Handler_read_rnd 2
Handler_update 2
@@ -2536,7 +2496,6 @@ 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;
@@ -2555,7 +2514,7 @@ Handler_read_key 4
Handler_read_next 2
# Status of testing query execution:
Variable_name Value
-Handler_read_key 4
+Handler_read_key 1
Handler_read_next 2
Handler_read_rnd 2
Handler_update 2
@@ -2583,7 +2542,6 @@ 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;
@@ -2605,7 +2563,6 @@ 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
#
@@ -2626,7 +2583,6 @@ 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;
@@ -2648,7 +2604,6 @@ 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;
@@ -2678,7 +2633,6 @@ 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;
@@ -2699,7 +2653,6 @@ 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
@@ -2723,9 +2676,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);
@@ -2749,7 +2702,6 @@ 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;
@@ -2769,7 +2721,6 @@ 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
@@ -2794,7 +2745,6 @@ 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;
@@ -2815,7 +2765,6 @@ 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
@@ -3086,7 +3035,6 @@ 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);
@@ -3110,7 +3058,7 @@ Sort_rows 3
Sort_scan 1
# Status of testing query execution:
Variable_name Value
-Handler_read_key 7
+Handler_read_key 3
Handler_read_rnd_next 8
Handler_update 1
Sort_priority_queue_sorts 1
@@ -3139,7 +3087,6 @@ 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);
@@ -3164,7 +3111,7 @@ Sort_rows 3
Sort_scan 1
# Status of testing query execution:
Variable_name Value
-Handler_read_key 7
+Handler_read_key 3
Handler_read_rnd_next 8
Sort_priority_queue_sorts 1
Sort_rows 3
@@ -3194,7 +3141,6 @@ 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);
@@ -3219,7 +3165,7 @@ Sort_rows 3
Sort_scan 1
# Status of testing query execution:
Variable_name Value
-Handler_read_key 7
+Handler_read_key 3
Handler_read_rnd_next 8
Sort_priority_queue_sorts 1
Sort_rows 3
@@ -3271,7 +3217,6 @@ 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;
@@ -3289,7 +3234,7 @@ Variable_name Value
Handler_read_key 4
# Status of testing query execution:
Variable_name Value
-Handler_read_key 4
+Handler_read_key 1
# used key is modified & Using filesort
#
@@ -3308,7 +3253,6 @@ 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;
@@ -3327,7 +3271,7 @@ Handler_read_key 4
Sort_range 1
# Status of testing query execution:
Variable_name Value
-Handler_read_key 4
+Handler_read_key 1
Sort_range 1
DROP TABLE t1;
diff --git a/mysql-test/main/mysqlbinlog_row_minimal.test b/mysql-test/main/mysqlbinlog_row_minimal.test
index 67fa7b94e6d..0f64bbaf396 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/order_by.result b/mysql-test/main/order_by.result
index 1df737f2c37..d05cdd104da 100644
--- a/mysql-test/main/order_by.result
+++ b/mysql-test/main/order_by.result
@@ -982,13 +982,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/partition_explicit_prune.result b/mysql-test/main/partition_explicit_prune.result
index 5b3049c146f..a0b7db8c815 100644
--- a/mysql-test/main/partition_explicit_prune.result
+++ b/mysql-test/main/partition_explicit_prune.result
@@ -777,7 +777,6 @@ 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 bddb41c0730..7b0b25d4cf8 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 9
+Handler_read_key 2
Handler_read_last 0
Handler_read_next 4097
Handler_read_prev 0
diff --git a/mysql-test/main/update.result b/mysql-test/main/update.result
index f5edf1c6be3..15efd7e4362 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 2774e7ee92d..320f5b6f540 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 d446bd512dd..d13ee78983f 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 c31ba882e76..6167c1ff26d 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 83bbeb3be77..538342c08cb 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/sql/opt_subselect.cc b/sql/opt_subselect.cc
index a74ec6ae5fe..58d86876388 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 4bc493940fb..33209ff0a8c 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 bc16af59d8d..6883fb1af13 100644
--- a/sql/sql_base.cc
+++ b/sql/sql_base.cc
@@ -1072,7 +1072,9 @@ 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)
{
/* All MyISAMMRG children are plain MyISAM tables. */
DBUG_ASSERT(table->table->file->ht->db_type != DB_TYPE_MRG_MYISAM);
@@ -5403,6 +5405,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.
diff --git a/sql/sql_base.h b/sql/sql_base.h
index 5b449fdddac..b21d26f0938 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;
@@ -287,6 +288,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);
@@ -429,6 +433,17 @@ public:
};
+
+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 9592eafde8e..5d1f97a78bf 100644
--- a/sql/sql_class.h
+++ b/sql/sql_class.h
@@ -6989,6 +6989,7 @@ public:
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);
diff --git a/sql/sql_cmd.h b/sql/sql_cmd.h
index 430afadb491..c62fe83adc6 100644
--- a/sql/sql_cmd.h
+++ b/sql/sql_cmd.h
@@ -117,6 +117,7 @@ enum enum_sql_command {
SQLCOM_END
};
+class TABLE_LIST;
class Storage_engine_name
{
@@ -143,6 +144,8 @@ public:
};
+class Prepared_statement;
+
/**
@class Sql_cmd - Representation of an SQL command.
@@ -178,6 +181,25 @@ public:
*/
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.
@@ -191,8 +213,28 @@ public:
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()
@@ -205,8 +247,115 @@ protected:
*/
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 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; }
+
+protected:
+ Sql_cmd_dml()
+ : Sql_cmd(), lex(nullptr), result(nullptr), m_empty_query(false) {}
+
+ /// @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
};
+
class Sql_cmd_show_slave_status: public Sql_cmd
{
protected:
diff --git a/sql/sql_delete.h b/sql/sql_delete.h
index 520524c72cc..dabcafb778c 100644
--- a/sql/sql_delete.h
+++ b/sql/sql_delete.h
@@ -32,4 +32,28 @@ 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;
+ }
+
+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;
+
+};
#endif /* SQL_DELETE_INCLUDED */
diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc
index a28a423d20a..ee5b2a8f7d0 100644
--- a/sql/sql_lex.cc
+++ b/sql/sql_lex.cc
@@ -1328,6 +1328,8 @@ void LEX::start(THD *thd_arg)
wild= 0;
exchange= 0;
+ table_count= 0;
+
DBUG_VOID_RETURN;
}
@@ -3055,6 +3057,7 @@ void st_select_lex::init_select()
curr_tvc_name= 0;
versioned_tables= 0;
nest_flags= 0;
+ item_list_usage= MARK_COLUMNS_READ;
}
/*
@@ -4120,6 +4123,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 b692f33bc66..5436bfd0a59 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 @@ public:
{
}
+ 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 @@ public:
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 @@ public:
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 @@ public:
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 @@ public:
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
@@ -3389,6 +3414,7 @@ public:
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 b097f84132b..72ac1999989 100644
--- a/sql/sql_parse.cc
+++ b/sql/sql_parse.cc
@@ -3458,7 +3458,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();
@@ -3470,7 +3469,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
@@ -3592,7 +3590,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;
@@ -4397,130 +4394,13 @@ 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:
{
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);
-
-#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);
- }
- }
+ 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;
}
case SQLCOM_REPLACE:
diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc
index 83c064c63c6..3859a1322da 100644
--- a/sql/sql_prepare.cc
+++ b/sql/sql_prepare.cc
@@ -98,7 +98,6 @@ When one supplies long data for a placeholder:
#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
@@ -1400,110 +1399,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
@@ -2135,32 +2030,6 @@ err:
}
-/*
- 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.
@@ -2484,13 +2353,10 @@ 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);
+ res = lex->m_sql_cmd->prepare(thd);
+ if (!res)
+ lex->m_sql_cmd->unprepare(thd);
break;
case SQLCOM_DELETE:
@@ -4373,6 +4239,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 97cac8f0cb8..edd9aed2e29 100644
--- a/sql/sql_select.cc
+++ b/sql/sql_select.cc
@@ -1396,7 +1396,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;
@@ -1686,6 +1686,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;
@@ -30278,6 +30280,140 @@ static bool process_direct_rownum_comparison(THD *thd, SELECT_LEX_UNIT *unit,
}
+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;
+
+ 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;
+ if (open_tables_for_query(thd, lex->query_tables, &table_count, 0,
+ get_dml_prelocking_strategy()))
+ goto err;
+ }
+
+ 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
+ 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);
+
+ return res;
+
+err:
+ DBUG_ASSERT(thd->is_error() || thd->killed);
+ 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 843966f13c8..3f04db058ca 100644
--- a/sql/sql_update.cc
+++ b/sql/sql_update.cc
@@ -339,36 +339,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 +363,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);
- }
+ thd->table_map_for_update= 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);
- 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_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 +404,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;
@@ -770,7 +657,7 @@ 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->direct_update_rows_init(fields))
{
do_direct_update= TRUE;
@@ -1020,7 +907,7 @@ update_begin:
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 */
@@ -1353,9 +1240,6 @@ update_end:
thd->lex->current_select->save_leaf_tables(thd);
thd->lex->current_select->first_cond_optimization= 0;
}
- *found_return= found;
- *updated_return= updated;
-
if (unlikely(thd->lex->analyze_stmt))
goto emit_explain_and_leave;
@@ -1683,15 +1567,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;
@@ -1928,7 +1803,7 @@ int mysql_multi_update_prepare(THD *thd)
if (lex->save_prep_leaf_tables())
DBUG_RETURN(TRUE);
-
+
DBUG_RETURN (FALSE);
}
@@ -2022,6 +1897,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
*/
@@ -2095,7 +1983,8 @@ int multi_update::prepare(List<Item> &not_used_values,
{
table->read_set= &table->def_read_set;
bitmap_union(table->read_set, &table->tmp_set);
- table->file->prepare_for_insert(1);
+ if (!(thd->lex->context_analysis_only & CONTEXT_ANALYSIS_ONLY_PREPARE))
+ table->file->prepare_for_insert(1);
}
}
if (unlikely(error))
@@ -3118,3 +3007,166 @@ 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;
+ }
+ return false;
+}
+
+
+bool Sql_cmd_update::prepare_inner(THD *thd)
+{
+ JOIN *join;
+ int err= 0;
+ // uint table_cnt= 0;
+ SELECT_LEX *const select_lex = thd->lex->first_select_lex();
+ TABLE_LIST *const table_list = select_lex->get_table_list();
+ ulonglong select_options= select_lex->options;
+ bool free_join= 1;
+ // bool orig_multitable= multitable;
+ DBUG_ENTER("Sql_cmd_update::prepare_inner");
+
+ if (!multitable)
+ {
+ 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 (((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;
+ }
+
+ }
+
+ 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= multitable ? Sql_cmd_dml::execute_inner(thd)
+ : update_single_table(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 65e44d112a4..e52d3cda641 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;
@@ -41,4 +43,42 @@ bool mysql_multi_update(THD *thd, TABLE_LIST *table_list,
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 116c2b7fe1d..30eda7c94de 100644
--- a/sql/sql_yacc.yy
+++ b/sql/sql_yacc.yy
@@ -69,6 +69,7 @@
#include "my_base.h"
#include "sql_type_json.h"
#include "json_table.h"
+#include "sql_update.h"
/* this is to get the bison compilation windows warnings out */
#ifdef _MSC_VER
@@ -13224,9 +13225,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 */
@@ -13234,6 +13240,9 @@ 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