From ae49d9710b791d1eaf7f552a44067c5108bd8c85 Mon Sep 17 00:00:00 2001 From: Georgi Kodinov Date: Wed, 17 Mar 2010 16:18:46 +0200 Subject: [PATCH] Bug #49838: DROP INDEX and ADD UNIQUE INDEX for same index may corrupt definition at engine If a single ALTER TABLE contains both DROP INDEX and ADD INDEX using the same index name (a.k.a. index modification) we need to disable in-place alter table because we can't ask the storage engine to have two copies of the index with the same name even temporarily (if we first do the ADD INDEX and then DROP INDEX) and we can't modify indexes that are needed by e.g. foreign keys if we first do DROP INDEX and then ADD INDEX. Fixed the problem by disabling in-place ALTER TABLE for these cases. --- mysql-test/r/innodb_mysql.result | 33 +++++++++++++++++++++++ mysql-test/t/innodb_mysql.test | 13 +++++++++ sql/sql_table.cc | 46 +++++++++++++++++++++++++++++++- 3 files changed, 91 insertions(+), 1 deletion(-) diff --git a/mysql-test/r/innodb_mysql.result b/mysql-test/r/innodb_mysql.result index 586cd5477a7..0e691611f02 100644 --- a/mysql-test/r/innodb_mysql.result +++ b/mysql-test/r/innodb_mysql.result @@ -2317,4 +2317,37 @@ ref NULL rows 10 Extra Using index DROP TABLE t1; +# +# Bug #49838: DROP INDEX and ADD UNIQUE INDEX for same index may +# corrupt definition at engine +# +CREATE TABLE t1 (a INT NOT NULL, b INT NOT NULL, KEY k (a,b)) +ENGINE=InnoDB; +ALTER TABLE t1 DROP INDEX k, ADD UNIQUE INDEX k (a,b); +SHOW INDEXES FROM t1;; +Table t1 +Non_unique 0 +Key_name k +Seq_in_index 1 +Column_name a +Collation A +Cardinality 0 +Sub_part NULL +Packed NULL +Null +Index_type BTREE +Comment +Table t1 +Non_unique 0 +Key_name k +Seq_in_index 2 +Column_name b +Collation A +Cardinality 0 +Sub_part NULL +Packed NULL +Null +Index_type BTREE +Comment +DROP TABLE t1; End of 5.1 tests diff --git a/mysql-test/t/innodb_mysql.test b/mysql-test/t/innodb_mysql.test index 75fff9656e2..88ca8d42e3d 100644 --- a/mysql-test/t/innodb_mysql.test +++ b/mysql-test/t/innodb_mysql.test @@ -575,5 +575,18 @@ INSERT INTO t1 VALUES (1,1,1,1,1,1), (2,2,2,2,2,2), (3,3,3,3,3,3), DROP TABLE t1; +--echo # +--echo # Bug #49838: DROP INDEX and ADD UNIQUE INDEX for same index may +--echo # corrupt definition at engine +--echo # + +CREATE TABLE t1 (a INT NOT NULL, b INT NOT NULL, KEY k (a,b)) + ENGINE=InnoDB; + +ALTER TABLE t1 DROP INDEX k, ADD UNIQUE INDEX k (a,b); + +--query_vertical SHOW INDEXES FROM t1; + +DROP TABLE t1; --echo End of 5.1 tests diff --git a/sql/sql_table.cc b/sql/sql_table.cc index eb88b1e70a5..ad72cab664e 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -5566,6 +5566,45 @@ err: DBUG_RETURN(-1); } +/** + @brief Check if both DROP and CREATE are present for an index in ALTER TABLE + + @details Checks if any index is being modified (present as both DROP INDEX + and ADD INDEX) in the current ALTER TABLE statement. Needed for disabling + online ALTER TABLE. + + @param table The table being altered + @param alter_info The ALTER TABLE structure + @return presence of index being altered + @retval FALSE No such index + @retval TRUE Have at least 1 index modified +*/ + +static bool +is_index_maintenance_unique (TABLE *table, Alter_info *alter_info) +{ + List_iterator key_it(alter_info->key_list); + List_iterator drop_it(alter_info->drop_list); + Key *key; + + while ((key= key_it++)) + { + if (key->name) + { + Alter_drop *drop; + + drop_it.rewind(); + while ((drop= drop_it++)) + { + if (drop->type == Alter_drop::KEY && + !my_strcasecmp(system_charset_info, key->name, drop->name)) + return TRUE; + } + } + } + return FALSE; +} + /* SYNOPSIS @@ -5654,6 +5693,7 @@ compare_tables(TABLE *table, */ Alter_info tmp_alter_info(*alter_info, thd->mem_root); uint db_options= 0; /* not used */ + /* Create the prepared information. */ if (mysql_prepare_create_table(thd, create_info, &tmp_alter_info, @@ -6851,10 +6891,14 @@ view_err: */ new_db_type= create_info->db_type; + if (is_index_maintenance_unique (table, alter_info)) + need_copy_table= ALTER_TABLE_DATA_CHANGED; + if (mysql_prepare_alter_table(thd, table, create_info, alter_info)) goto err; - need_copy_table= alter_info->change_level; + if (need_copy_table == ALTER_TABLE_METADATA_ONLY) + need_copy_table= alter_info->change_level; set_table_default_charset(thd, create_info, db);