diff --git a/handler/ha_innodb.cc b/handler/ha_innodb.cc index b5abe667455..31795c8e644 100644 --- a/handler/ha_innodb.cc +++ b/handler/ha_innodb.cc @@ -8513,10 +8513,11 @@ error_handling: table as a old table and drop the old table. */ if (new_primary) { - char* old_name = mem_heap_strdup( - heap, innodb_table->name); - char* tmp_table_name = innobase_create_temporary_tablename( - heap, '2', innodb_table->name); + const char* old_name + = innodb_table->name; + const char* tmp_name + = innobase_create_temporary_tablename(heap, '2', + old_name); trx_start_if_not_started(trx); @@ -8527,7 +8528,7 @@ error_handling: /* Write entry for UNDO */ error = row_undo_report_rename_table_dict_operation( - trx, old_name, indexed_table->name, tmp_table_name); + trx, old_name, indexed_table->name, tmp_name); if (error != DB_SUCCESS) { @@ -8538,15 +8539,10 @@ error_handling: /* Set the commit flag to FALSE, we will commit the transaction ourselves, required for UNDO */ - error = innobase_rename_table(trx, innodb_table->name, - tmp_table_name, FALSE); - if (error != DB_SUCCESS) { - goto func_exit; - } + error = row_merge_rename_tables(innodb_table, indexed_table, + tmp_name, trx); - error = innobase_rename_table(trx, indexed_table->name, - old_name, FALSE); if (error != DB_SUCCESS) { goto func_exit; diff --git a/include/row0merge.h b/include/row0merge.h index aa6ee4c1018..3c2a2b32dd2 100644 --- a/include/row0merge.h +++ b/include/row0merge.h @@ -63,6 +63,20 @@ row_merge_drop_indexes( dict_index_t** index, /* in: indexes to drop */ ulint num_created); /* in: number of elements in index[] */ +/************************************************************************* +Rename the tables in the data dictionary. */ + +ulint +row_merge_rename_tables( +/*====================*/ + /* out: error code or DB_SUCCESS */ + dict_table_t* old_table, /* in/out: old table, renamed to + tmp_name */ + dict_table_t* new_table, /* in/out: new table, renamed to + old_table->name */ + const char* tmp_name, /* in: new name for old_table */ + trx_t* trx); /* in: transaction handle */ + /************************************************************************* Create a temporary table for creating a primary key, using the definition of an existing table. */ diff --git a/mysql-test/innodb-index.result b/mysql-test/innodb-index.result index f3088c7ad14..1e126949d88 100644 --- a/mysql-test/innodb-index.result +++ b/mysql-test/innodb-index.result @@ -469,8 +469,9 @@ create table t1(a int not null, b int not null, c int, primary key (a), key (b)) create table t3(a int not null, c int not null, d int, primary key (a), key (c)) engine = innodb; create table t4(a int not null, d int not null, e int, primary key (a), key (d)) engine = innodb; create table t2(a int not null, b int not null, c int not null, d int not null, e int, -primary key (a), foreign key (b) references t1(b), foreign key (c) references t3(c), -foreign key (d) references t4(d)) engine = innodb; +foreign key (b) references t1(b) on delete cascade, +foreign key (c) references t3(c), foreign key (d) references t4(d)) +engine = innodb; alter table t1 drop index b; ERROR HY000: Cannot drop index 'b': needed in a foreign key constraint alter table t3 drop index c; @@ -481,7 +482,8 @@ alter table t2 drop index b; ERROR HY000: Cannot drop index 'b': needed in a foreign key constraint alter table t2 drop index b, drop index c, drop index d; ERROR HY000: Cannot drop index 'b': needed in a foreign key constraint -set foreign_key_checks=0; +create unique index dc on t2 (d,c); +alter table t2 add primary key (a); insert into t1 values (1,1,1); insert into t3 values (1,1,1); insert into t4 values (1,1,1); @@ -497,17 +499,17 @@ t2 CREATE TABLE `t2` ( `d` int(11) NOT NULL, `e` int(11) DEFAULT NULL, PRIMARY KEY (`a`), + UNIQUE KEY `dc` (`d`,`c`), KEY `c` (`c`), - KEY `d` (`d`), KEY `b` (`b`), - CONSTRAINT `t2_ibfk_1` FOREIGN KEY (`b`) REFERENCES `t1` (`b`), + CONSTRAINT `t2_ibfk_1` FOREIGN KEY (`b`) REFERENCES `t1` (`b`) ON DELETE CASCADE, CONSTRAINT `t2_ibfk_2` FOREIGN KEY (`c`) REFERENCES `t3` (`c`), CONSTRAINT `t2_ibfk_3` FOREIGN KEY (`d`) REFERENCES `t4` (`d`) ) ENGINE=InnoDB DEFAULT CHARSET=latin1 -set foreign_key_checks=1; -set foreign_key_checks=0; -drop table if exists t1,t2,t3,t4; -set foreign_key_checks=1; +delete from t1; +select * from t2; +a b c d e +drop table if exists t2,t1,t3,t4; create table t1(a int not null, b int, c char(10), d varchar(20), primary key (a)) engine = innodb default charset=utf8; insert into t1 values (1,1,'ab','ab'),(2,2,'ac','ac'),(3,2,'ad','ad'),(4,4,'afe','afe'); diff --git a/mysql-test/innodb-index.test b/mysql-test/innodb-index.test index 254e05f0c3d..12df24748b3 100644 --- a/mysql-test/innodb-index.test +++ b/mysql-test/innodb-index.test @@ -111,8 +111,9 @@ create table t1(a int not null, b int not null, c int, primary key (a), key (b)) create table t3(a int not null, c int not null, d int, primary key (a), key (c)) engine = innodb; create table t4(a int not null, d int not null, e int, primary key (a), key (d)) engine = innodb; create table t2(a int not null, b int not null, c int not null, d int not null, e int, -primary key (a), foreign key (b) references t1(b), foreign key (c) references t3(c), -foreign key (d) references t4(d)) engine = innodb; +foreign key (b) references t1(b) on delete cascade, +foreign key (c) references t3(c), foreign key (d) references t4(d)) +engine = innodb; --error ER_DROP_INDEX_FK alter table t1 drop index b; --error ER_DROP_INDEX_FK @@ -123,7 +124,10 @@ alter table t4 drop index d; alter table t2 drop index b; --error ER_DROP_INDEX_FK alter table t2 drop index b, drop index c, drop index d; -set foreign_key_checks=0; +-- Apparently, the following makes mysql_alter_table() drop index d. +create unique index dc on t2 (d,c); +-- This should preserve the foreign key constraints. +alter table t2 add primary key (a); insert into t1 values (1,1,1); insert into t3 values (1,1,1); insert into t4 values (1,1,1); @@ -131,13 +135,10 @@ insert into t2 values (1,1,1,1,1); commit; alter table t2 drop index b, add index (b); show create table t2; -set foreign_key_checks=1; +delete from t1; +select * from t2; -set foreign_key_checks=0; ---disable_warnings -drop table if exists t1,t2,t3,t4; ---enable_warnings -set foreign_key_checks=1; +drop table if exists t2,t1,t3,t4; create table t1(a int not null, b int, c char(10), d varchar(20), primary key (a)) engine = innodb default charset=utf8; diff --git a/row/row0merge.c b/row/row0merge.c index e6c2f7d93f4..0862cbbfb8d 100644 --- a/row/row0merge.c +++ b/row/row0merge.c @@ -1657,6 +1657,77 @@ row_merge_rename_index( return(err); } +/************************************************************************* +Rename the tables in the data dictionary. */ + +ulint +row_merge_rename_tables( +/*====================*/ + /* out: error code or DB_SUCCESS */ + dict_table_t* old_table, /* in/out: old table, renamed to + tmp_name */ + dict_table_t* new_table, /* in/out: new table, renamed to + old_table->name */ + const char* tmp_name, /* in: new name for old_table */ + trx_t* trx) /* in: transaction handle */ +{ + ulint err = DB_ERROR; + pars_info_t* info; + const char* old_name= old_table->name; + + ut_ad(trx->mysql_thread_id == os_thread_get_curr_id()); + ut_ad(old_table != new_table); + + trx->op_info = "renaming tables"; + trx_start_if_not_started(trx); + + /* We use the private SQL parser of Innobase to generate the query + graphs needed in updating the dictionary data in system tables. */ + + info = pars_info_create(); + + pars_info_add_str_literal(info, "new_name", new_table->name); + pars_info_add_str_literal(info, "old_name", old_name); + pars_info_add_str_literal(info, "tmp_name", tmp_name); + + err = que_eval_sql(info, + "PROCEDURE RENAME_TABLES () IS\n" + "BEGIN\n" + "UPDATE SYS_TABLES SET NAME = :tmp_name\n" + " WHERE NAME = :old_name;\n" + "UPDATE SYS_TABLES SET NAME = :old_name\n" + " WHERE NAME = :new_name;\n" + "END;\n", FALSE, trx); + + if (err != DB_SUCCESS) { + + goto err_exit; + } + + /* The following calls will also rename the .ibd data files if + the tables are stored in a single-table tablespace */ + + if (!dict_table_rename_in_cache(old_table, tmp_name, FALSE) + || !dict_table_rename_in_cache(new_table, old_name, FALSE)) { + + err = DB_ERROR; + goto err_exit; + } + + err = dict_load_foreigns(old_name, TRUE); + + if (err != DB_SUCCESS) { +err_exit: + trx->error_state = DB_SUCCESS; + trx_general_rollback_for_mysql(trx, FALSE, NULL); + trx->error_state = DB_SUCCESS; + } + + trx->op_info = ""; + + return(err); +} + /************************************************************************* Create the index and load in to the dictionary. */