diff --git a/mysql-test/main/derived_view.result b/mysql-test/main/derived_view.result index 3f3f68154882c..747cfbaf55632 100644 --- a/mysql-test/main/derived_view.result +++ b/mysql-test/main/derived_view.result @@ -4430,3 +4430,97 @@ deallocate prepare stmt; drop view v1,v2; drop table t1,t2; # End of 10.6 tests +# +# Beginning of 10.11 tests +# +# +# MDEV-37020: derived table not merged in multi-table DELETE/UPDATE +# while equivalent SELECT and view-based form do merge +# +create table t1 (a int, b int); +insert into t1 select seq, seq from seq_1_to_10; +create table t2 (a int, b int); +insert into t2 select seq, seq from seq_1_to_10; +create table t3 (a int, b int); +insert into t3 select seq, seq from seq_1_to_10; +# Baseline: equivalent SELECT merges the derived table. +explain select * from t1, (select t2.b as b1 from t2, t3 where t3.a=t2.a and t2.a<5) dt +where t1.b+1=1+dt.b1; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 10 +1 SIMPLE t2 ALL NULL NULL NULL NULL 10 Using where; Using join buffer (flat, BNL join) +1 SIMPLE t3 ALL NULL NULL NULL NULL 10 Using where; Using join buffer (incremental, BNL join) +# Multitable DELETE must merge the derived table. +explain delete t1.* from t1, (select t2.b as b1 from t2, t3 where t3.a=t2.a) dt +where t1.b+1=1+dt.b1; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 10 +1 SIMPLE t2 ALL NULL NULL NULL NULL 10 Using where +1 SIMPLE t3 ALL NULL NULL NULL NULL 10 Using where +# Multitable UPDATE must merge the derived table. +explain update t1, (select t2.b as b1 from t2, t3 where t3.a=t2.a) dt +set t1.a=t1.a+1 where t1.b+1=1+dt.b1; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 10 +1 SIMPLE t2 ALL NULL NULL NULL NULL 10 Using where +1 SIMPLE t3 ALL NULL NULL NULL NULL 10 Using where +# VIEW form already merged the derived table before this fix, verify. +create view v1 as select t2.b as b1 from t2, t3 where t3.a=t2.a; +select t1.* from t1, v1 where t1.b+1=1+v1.b1; +a b +1 1 +2 2 +3 3 +4 4 +5 5 +6 6 +7 7 +8 8 +9 9 +10 10 +explain select t1.* from t1, v1 where t1.b+1=1+v1.b1; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 10 +1 SIMPLE t2 ALL NULL NULL NULL NULL 10 Using where; Using join buffer (flat, BNL join) +1 SIMPLE t3 ALL NULL NULL NULL NULL 10 Using where; Using join buffer (incremental, BNL join) +explain delete t1.* from t1, v1 where t1.b+1=1+v1.b1; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 10 +1 SIMPLE t2 ALL NULL NULL NULL NULL 10 Using where +1 SIMPLE t3 ALL NULL NULL NULL NULL 10 Using where +explain update t1, v1 set t1.a=t1.a+1 where t1.b+1=1+v1.b1; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 10 +1 SIMPLE t2 ALL NULL NULL NULL NULL 10 Using where +1 SIMPLE t3 ALL NULL NULL NULL NULL 10 Using where +# Confirm queries still produce the correct results. +select t1.* from t1, (select t2.b as b1 from t2, t3 where t3.a=t2.a and t2.a<5) dt +where t1.b+1=1+dt.b1; +a b +1 1 +2 2 +3 3 +4 4 +delete t1.* from t1, (select t2.b as b1 from t2, t3 where t3.a=t2.a and t2.a<5) dt2 +where t1.b=dt2.b1; +select * from t1 order by a; +a b +5 5 +6 6 +7 7 +8 8 +9 9 +10 10 +update t1, (select t2.b as b1 from t2, t3 where t3.a=t2.a and t2.a>7) dt2 +set t1.a=t1.a*100 where t1.b=dt2.b1; +select * from t1 order by b; +a b +5 5 +6 6 +7 7 +800 8 +900 9 +1000 10 +drop view v1; +drop table t1, t2, t3; +# End of 10.11 tests diff --git a/mysql-test/main/derived_view.test b/mysql-test/main/derived_view.test index 4c02d0fa906b4..5d4a3681ee229 100644 --- a/mysql-test/main/derived_view.test +++ b/mysql-test/main/derived_view.test @@ -2940,3 +2940,54 @@ drop view v1,v2; drop table t1,t2; --echo # End of 10.6 tests + + +--echo # +--echo # Beginning of 10.11 tests +--echo # + +--echo # +--echo # MDEV-37020: derived table not merged in multi-table DELETE/UPDATE +--echo # while equivalent SELECT and view-based form do merge +--echo # + +create table t1 (a int, b int); +insert into t1 select seq, seq from seq_1_to_10; +create table t2 (a int, b int); +insert into t2 select seq, seq from seq_1_to_10; +create table t3 (a int, b int); +insert into t3 select seq, seq from seq_1_to_10; + +--echo # Baseline: equivalent SELECT merges the derived table. +explain select * from t1, (select t2.b as b1 from t2, t3 where t3.a=t2.a and t2.a<5) dt + where t1.b+1=1+dt.b1; + +--echo # Multitable DELETE must merge the derived table. +explain delete t1.* from t1, (select t2.b as b1 from t2, t3 where t3.a=t2.a) dt + where t1.b+1=1+dt.b1; + +--echo # Multitable UPDATE must merge the derived table. +explain update t1, (select t2.b as b1 from t2, t3 where t3.a=t2.a) dt + set t1.a=t1.a+1 where t1.b+1=1+dt.b1; + +--echo # VIEW form already merged the derived table before this fix, verify. +create view v1 as select t2.b as b1 from t2, t3 where t3.a=t2.a; +select t1.* from t1, v1 where t1.b+1=1+v1.b1; +explain select t1.* from t1, v1 where t1.b+1=1+v1.b1; +explain delete t1.* from t1, v1 where t1.b+1=1+v1.b1; +explain update t1, v1 set t1.a=t1.a+1 where t1.b+1=1+v1.b1; + +--echo # Confirm queries still produce the correct results. +select t1.* from t1, (select t2.b as b1 from t2, t3 where t3.a=t2.a and t2.a<5) dt + where t1.b+1=1+dt.b1; +delete t1.* from t1, (select t2.b as b1 from t2, t3 where t3.a=t2.a and t2.a<5) dt2 + where t1.b=dt2.b1; +select * from t1 order by a; +update t1, (select t2.b as b1 from t2, t3 where t3.a=t2.a and t2.a>7) dt2 + set t1.a=t1.a*100 where t1.b=dt2.b1; +select * from t1 order by b; + +drop view v1; +drop table t1, t2, t3; + +--echo # End of 10.11 tests diff --git a/mysql-test/main/multi_update.result b/mysql-test/main/multi_update.result index d94adfca24290..7cd17580ac44e 100644 --- a/mysql-test/main/multi_update.result +++ b/mysql-test/main/multi_update.result @@ -585,9 +585,6 @@ INSERT INTO t3 VALUES (1), (2); UPDATE IGNORE ( SELECT ( SELECT COUNT(*) FROM t1 GROUP BY a, @v ) a FROM t2 ) x, t3 SET t3.a = 0; -Warnings: -Warning 1242 Subquery returns more than 1 row -Warning 1242 Subquery returns more than 1 row DROP TABLE t1, t2, t3; SET SESSION sql_safe_updates = DEFAULT; # diff --git a/mysql-test/main/myisam_explain_non_select_all.result b/mysql-test/main/myisam_explain_non_select_all.result index bc6152b8f0aa0..cd8395b6ce3c3 100644 --- a/mysql-test/main/myisam_explain_non_select_all.result +++ b/mysql-test/main/myisam_explain_non_select_all.result @@ -198,18 +198,16 @@ Warnings: Warning 1287 ' INTO FROM...' instead EXPLAIN UPDATE t1 t11, (SELECT * FROM t2) t12 SET t11.a = 10 WHERE t11.a = 1; id select_type table type possible_keys key key_len ref rows Extra -1 PRIMARY t11 ALL NULL NULL NULL NULL 3 Using where -1 PRIMARY ALL NULL NULL NULL NULL 3 -2 DERIVED t2 ALL NULL NULL NULL NULL 3 +1 SIMPLE t11 ALL NULL NULL NULL NULL 3 Using where +1 SIMPLE t2 ALL NULL NULL NULL NULL 3 FLUSH STATUS; FLUSH TABLES; EXPLAIN EXTENDED UPDATE t1 t11, (SELECT * FROM t2) t12 SET t11.a = 10 WHERE t11.a = 1; id select_type table type possible_keys key key_len ref rows filtered Extra -1 PRIMARY t11 ALL NULL NULL NULL NULL 3 100.00 Using where -1 PRIMARY ALL NULL NULL NULL NULL 3 100.00 -2 DERIVED t2 ALL NULL NULL NULL NULL 3 100.00 +1 SIMPLE t11 ALL NULL NULL NULL NULL 3 100.00 Using where +1 SIMPLE t2 ALL NULL NULL NULL NULL 3 100.00 Warnings: -Note 1003 /* select#1 */ update `test`.`t1` `t11` join (/* select#2 */ select `test`.`t2`.`b` AS `b` from `test`.`t2`) `t12` set `test`.`t11`.`a` = 10 where `test`.`t11`.`a` = 1 +Note 1003 update `test`.`t1` `t11` join `test`.`t2` set `test`.`t11`.`a` = 10 where `test`.`t11`.`a` = 1 # Status of EXPLAIN EXTENDED query Variable_name Value Handler_read_key 4 @@ -233,7 +231,7 @@ Handler_read_rnd_next 8 # Status of testing query execution: Variable_name Value Handler_read_key 4 -Handler_read_rnd_next 12 +Handler_read_rnd_next 8 Handler_update 1 DROP TABLE t1, t2; @@ -410,18 +408,16 @@ Warnings: Warning 1287 ' INTO FROM...' instead EXPLAIN UPDATE t1 t11, (SELECT * FROM t2) t12 SET t11.a = t11.a + 10; id select_type table type possible_keys key key_len ref rows Extra -1 PRIMARY t11 ALL NULL NULL NULL NULL 3 -1 PRIMARY ALL NULL NULL NULL NULL 3 -2 DERIVED t2 ALL NULL NULL NULL NULL 3 +1 SIMPLE t11 ALL NULL NULL NULL NULL 3 +1 SIMPLE t2 ALL NULL NULL NULL NULL 3 FLUSH STATUS; FLUSH TABLES; EXPLAIN EXTENDED UPDATE t1 t11, (SELECT * FROM t2) t12 SET t11.a = t11.a + 10; id select_type table type possible_keys key key_len ref rows filtered Extra -1 PRIMARY t11 ALL NULL NULL NULL NULL 3 100.00 -1 PRIMARY ALL NULL NULL NULL NULL 3 100.00 -2 DERIVED t2 ALL NULL NULL NULL NULL 3 100.00 +1 SIMPLE t11 ALL NULL NULL NULL NULL 3 100.00 +1 SIMPLE t2 ALL NULL NULL NULL NULL 3 100.00 Warnings: -Note 1003 /* select#1 */ update `test`.`t1` `t11` join (/* select#2 */ select `test`.`t2`.`b` AS `b` from `test`.`t2`) `t12` set `test`.`t11`.`a` = `test`.`t11`.`a` + 10 +Note 1003 update `test`.`t1` `t11` join `test`.`t2` set `test`.`t11`.`a` = `test`.`t11`.`a` + 10 # Status of EXPLAIN EXTENDED query Variable_name Value Handler_read_key 4 @@ -447,7 +443,7 @@ Variable_name Value Handler_read_key 4 Handler_read_rnd 3 Handler_read_rnd_deleted 1 -Handler_read_rnd_next 24 +Handler_read_rnd_next 20 Handler_update 3 DROP TABLE t1, t2; @@ -520,18 +516,16 @@ Warnings: Warning 1287 ' INTO FROM...' instead EXPLAIN UPDATE t1 t11, (SELECT * FROM t2) t12 SET t11.a = 10 WHERE t11.a > 1; id select_type table type possible_keys key key_len ref rows Extra -1 PRIMARY t11 ALL NULL NULL NULL NULL 3 Using where -1 PRIMARY ALL NULL NULL NULL NULL 3 -2 DERIVED t2 ALL NULL NULL NULL NULL 3 +1 SIMPLE t11 ALL NULL NULL NULL NULL 3 Using where +1 SIMPLE t2 ALL NULL NULL NULL NULL 3 FLUSH STATUS; FLUSH TABLES; EXPLAIN EXTENDED UPDATE t1 t11, (SELECT * FROM t2) t12 SET t11.a = 10 WHERE t11.a > 1; id select_type table type possible_keys key key_len ref rows filtered Extra -1 PRIMARY t11 ALL NULL NULL NULL NULL 3 100.00 Using where -1 PRIMARY ALL NULL NULL NULL NULL 3 100.00 -2 DERIVED t2 ALL NULL NULL NULL NULL 3 100.00 +1 SIMPLE t11 ALL NULL NULL NULL NULL 3 100.00 Using where +1 SIMPLE t2 ALL NULL NULL NULL NULL 3 100.00 Warnings: -Note 1003 /* select#1 */ update `test`.`t1` `t11` join (/* select#2 */ select `test`.`t2`.`b` AS `b` from `test`.`t2`) `t12` set `test`.`t11`.`a` = 10 where `test`.`t11`.`a` > 1 +Note 1003 update `test`.`t1` `t11` join `test`.`t2` set `test`.`t11`.`a` = 10 where `test`.`t11`.`a` > 1 # Status of EXPLAIN EXTENDED query Variable_name Value Handler_read_key 4 @@ -555,7 +549,7 @@ Handler_read_rnd_next 8 # Status of testing query execution: Variable_name Value Handler_read_key 4 -Handler_read_rnd_next 16 +Handler_read_rnd_next 12 Handler_update 2 DROP TABLE t1, t2; @@ -3409,20 +3403,18 @@ EXPLAIN UPDATE t1, (SELECT * FROM t2) y SET a = 10 WHERE a IN (SELECT * F id select_type table type possible_keys key key_len ref rows Extra 1 PRIMARY t1 ALL NULL NULL NULL NULL 3 Using where 1 PRIMARY ref key0 key0 5 test.t1.a 2 FirstMatch(t1) -1 PRIMARY ALL NULL NULL NULL NULL 3 +1 PRIMARY t2 ALL NULL NULL NULL NULL 3 4 DERIVED t2 ALL NULL NULL NULL NULL 3 Using filesort -2 DERIVED t2 ALL NULL NULL NULL NULL 3 FLUSH STATUS; FLUSH TABLES; EXPLAIN EXTENDED UPDATE t1, (SELECT * FROM t2) y 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 1 PRIMARY ref key0 key0 5 test.t1.a 2 100.00 FirstMatch(t1) -1 PRIMARY ALL NULL NULL NULL NULL 3 100.00 +1 PRIMARY t2 ALL NULL NULL NULL NULL 3 100.00 4 DERIVED t2 ALL NULL NULL NULL NULL 3 100.00 Using filesort -2 DERIVED t2 ALL NULL NULL NULL NULL 3 100.00 Warnings: -Note 1003 /* select#1 */ update `test`.`t1` semi join ((/* select#4 */ select `test`.`t2`.`b` AS `b` from `test`.`t2` order by `test`.`t2`.`b` limit 2,2) `x`) join (/* select#2 */ select `test`.`t2`.`b` AS `b` from `test`.`t2`) `y` set `test`.`t1`.`a` = 10 where `x`.`b` = `test`.`t1`.`a` +Note 1003 /* select#1 */ update `test`.`t1` semi join ((/* select#4 */ select `test`.`t2`.`b` AS `b` from `test`.`t2` order by `test`.`t2`.`b` limit 2,2) `x`) join `test`.`t2` set `test`.`t1`.`a` = 10 where `x`.`b` = `test`.`t1`.`a` # Status of EXPLAIN EXTENDED query Variable_name Value Handler_read_key 4 diff --git a/sql/sql_update.cc b/sql/sql_update.cc index 7a18cc640ee5c..cf2c383aa72e1 100644 --- a/sql/sql_update.cc +++ b/sql/sql_update.cc @@ -1733,6 +1733,19 @@ static bool multi_update_check_table_access(THD *thd, TABLE_LIST *table, } else { + /* + A merged derived table that is not a VIEW has no TABLE* so we + can't check privileges on it here. Besides, the privileges for + the underlying base tables are already checked by + multi_update_precheck. + + Without this guard, the main.lock_multi_bug38499 test will crash + just below when running a prepared statement that is made from an + UPDATE. + */ + if (table->is_merged_derived()) + return false; + /* Must be a base or derived table. */ const bool updated= table->table->map & tables_for_update; if (check_table_access(thd, updated ? UPDATE_ACL : SELECT_ACL, table, diff --git a/sql/table.cc b/sql/table.cc index 2b283fd93e1b8..9a6a0659df03c 100644 --- a/sql/table.cc +++ b/sql/table.cc @@ -9903,8 +9903,14 @@ bool TABLE_LIST::init_derived(THD *thd, bool init_view) (is_view() || optimizer_flag(thd, OPTIMIZER_SWITCH_DERIVED_MERGE)) && !thd->lex->can_not_use_merged() && + /* + Allow merged derived optimization for multitable DELETE and + UPDATE, but with one exception: if this derived table is + itself within a VIEW, then don't allow it to be merged. + */ !((thd->lex->sql_command == SQLCOM_UPDATE_MULTI || - thd->lex->sql_command == SQLCOM_DELETE_MULTI) && !is_view()) && + thd->lex->sql_command == SQLCOM_DELETE_MULTI) && + !is_view() && belong_to_view) && !is_recursive_with_table()) set_merged_derived(); else