From c233d6e120e20ea39dc7b9eac4cef264befc3cd0 Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 11 Feb 2015 01:26:50 +0100 Subject: [PATCH] MDEV-7260: Crash in get_best_combination when executing multi-table UPDATE with nested views Do not use merge_for_insert for commands which use SELECT because optimizer can't work with such tables. Fixes which makes multi-delete working with normally merged views. --- mysql-test/r/view.result | 13 +++++++++++++ mysql-test/t/view.test | 22 ++++++++++++++++++++++ sql/sql_base.cc | 3 +++ sql/sql_delete.cc | 5 +++-- sql/sql_derived.cc | 3 +++ sql/table.h | 18 ++++++++++++++++++ 6 files changed, 62 insertions(+), 2 deletions(-) diff --git a/mysql-test/r/view.result b/mysql-test/r/view.result index cb40fbbcbca..b3977648d41 100644 --- a/mysql-test/r/view.result +++ b/mysql-test/r/view.result @@ -5398,6 +5398,19 @@ DROP VIEW v1; DROP TABLE t1, t2; create view v1 as select 1; drop view v1; +# +# MDEV-7260: Crash in get_best_combination when executing multi-table +# UPDATE with nested views +# +CREATE TABLE `t1` (`id` bigint(20)); +INSERT INTO `t1` VALUES (1),(2); +CREATE TABLE `t2` (`id` bigint(20)); +CREATE TABLE `t3` (`id` bigint(20), `flag` tinyint(4)); +create view v1 as select id from t1; +create view v2 as select t2.* from (t2 left join v1 using (id)); +update t3 left join v2 using (id) set flag=flag+1; +drop view v2, v1; +drop table t1, t2, t3; # ----------------------------------------------------------------- # -- End of 5.5 tests. # ----------------------------------------------------------------- diff --git a/mysql-test/t/view.test b/mysql-test/t/view.test index 8056836fe9a..b96799215fe 100644 --- a/mysql-test/t/view.test +++ b/mysql-test/t/view.test @@ -5345,6 +5345,28 @@ create view v1 as select 1; drop view v1; +--echo # +--echo # MDEV-7260: Crash in get_best_combination when executing multi-table +--echo # UPDATE with nested views +--echo # + +CREATE TABLE `t1` (`id` bigint(20)); + +INSERT INTO `t1` VALUES (1),(2); + +CREATE TABLE `t2` (`id` bigint(20)); + +CREATE TABLE `t3` (`id` bigint(20), `flag` tinyint(4)); + +create view v1 as select id from t1; + +create view v2 as select t2.* from (t2 left join v1 using (id)); + +update t3 left join v2 using (id) set flag=flag+1; + +drop view v2, v1; +drop table t1, t2, t3; + --echo # ----------------------------------------------------------------- --echo # -- End of 5.5 tests. --echo # ----------------------------------------------------------------- diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 90a12eb366d..3c36c2b514a 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -2028,6 +2028,9 @@ unique_table(THD *thd, TABLE_LIST *table, TABLE_LIST *table_list, bool check_alias) { TABLE_LIST *dup; + + table= table->find_table_for_update(); + if (table->table && table->table->file->ht->db_type == DB_TYPE_MRG_MYISAM) { TABLE_LIST *child; diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc index 97d3d10c21c..055b4858598 100644 --- a/sql/sql_delete.cc +++ b/sql/sql_delete.cc @@ -657,9 +657,10 @@ multi_delete::initialize_tables(JOIN *join) delete_while_scanning= 1; for (walk= delete_tables; walk; walk= walk->next_local) { - tables_to_delete_from|= walk->table->map; + TABLE_LIST *tbl= walk->correspondent_table->find_table_for_update(); + tables_to_delete_from|= tbl->table->map; if (delete_while_scanning && - unique_table(thd, walk, join->tables_list, false)) + unique_table(thd, tbl, join->tables_list, false)) { /* If the table we are going to delete from appears diff --git a/sql/sql_derived.cc b/sql/sql_derived.cc index 004cccb41a9..56748fa110d 100644 --- a/sql/sql_derived.cc +++ b/sql/sql_derived.cc @@ -507,6 +507,9 @@ bool mysql_derived_merge_for_insert(THD *thd, LEX *lex, TABLE_LIST *derived) DBUG_RETURN(FALSE); if (derived->is_materialized_derived()) DBUG_RETURN(mysql_derived_prepare(thd, lex, derived)); + if ((thd->lex->sql_command == SQLCOM_UPDATE_MULTI || + thd->lex->sql_command == SQLCOM_DELETE_MULTI)) + DBUG_RETURN(FALSE); if (!derived->is_multitable()) { if (!derived->single_table_updatable()) diff --git a/sql/table.h b/sql/table.h index 8e8c2811b71..7a1e380f14c 100644 --- a/sql/table.h +++ b/sql/table.h @@ -1984,6 +1984,24 @@ struct TABLE_LIST TABLE_LIST *find_underlying_table(TABLE *table); TABLE_LIST *first_leaf_for_name_resolution(); TABLE_LIST *last_leaf_for_name_resolution(); + /** + @brief + Find the bottom in the chain of embedded table VIEWs. + + @detail + This is used for single-table UPDATE/DELETE when they are modifying a + single-table VIEW. + */ + TABLE_LIST *find_table_for_update() + { + TABLE_LIST *tbl= this; + while(!tbl->is_multitable() && tbl->single_table_updatable() && + tbl->merge_underlying_list) + { + tbl= tbl->merge_underlying_list; + } + return tbl; + } TABLE *get_real_join_table(); bool is_leaf_for_name_resolution(); inline TABLE_LIST *top_table()