Skip to content

Commit 03acdd6

Browse files
LukeYL026midenok
authored andcommitted
MDEV-35732 Failed ALTER TABLE leaves cached table metadata corrupted
A failed ALTER TABLE ... RENAME INDEX ... ALGORITHM=INSTANT changed the outcome of a subsequent, unrelated ALTER TABLE ... ADD FOREIGN KEY. A failed statement must have no side effects, but here the second statement wrongly succeeded where on a fresh table it correctly fails with ER_DUP_KEYNAME. Root cause: while rebuilding the key list, mysql_prepare_alter_table() handled a RENAME INDEX request by clearing HA_GENERATED_KEY in place on key_info->flags. key_info points into the (possibly cached) TABLE object that is reused across statements. The ALGORITHM=INSTANT incompatibility is only detected later, after mysql_prepare_alter_table() has returned, so the statement fails with ER_ALTER_OPERATION_NOT_SUPPORTED with the cleared flag never restored. The cached generated FK-support index (fk1) was thus left permanently marked as user-defined. That corrupted flag flips the de-duplication tie-break in the next ALTER: adding FOREIGN KEY ind1 (b) creates a generated support index on column b that prefix-matches fk1(b). Normally fk1 (generated) is dropped and the new ind1 survives, colliding by name with the existing user index ind1(a) and raising ER_DUP_KEYNAME. With fk1 no longer marked generated, the new ind1 is dropped instead, so no name collision is reached and the ADD FOREIGN KEY silently succeeds. Fix: do not mutate the cached key_info->flags. Track the "renamed => no longer generated" decision in a per-key local variable (generated_key), initialised from the flag, set to false on rename, and passed to the Key constructor. This preserves the in-statement behaviour while leaving the cached TABLE metadata untouched, so a failed ALTER has no lingering effect. All new code of the whole pull request, including one or several files that are either new files or modified ones, are contributed under the BSD-new license. I am contributing on behalf of my employer Amazon Web Services, Inc.
1 parent 4da5458 commit 03acdd6

3 files changed

Lines changed: 56 additions & 2 deletions

File tree

mysql-test/main/alter_table.result

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3225,5 +3225,28 @@ Tp4 CREATE TABLE `Tp4`-ok (
32253225
drop table Another.Tp4;
32263226
drop database Another;
32273227
#
3228+
# MDEV-35732: Failed ALTER TABLE causes inconsistency, changes behavior of the next statement
3229+
#
3230+
# It should be impossible for a failed rename index to affect
3231+
# a subsequent duplicate name check for a foreign key.
3232+
#
3233+
create table t1 (f1 int, f2 int, key(f1), key(f2)) engine=innodb;
3234+
create table t2 (pk int primary key, a int, b int, key ind1(a),
3235+
foreign key fk1 (b) references t1 (f1)) engine=innodb;
3236+
alter table t2 rename index fk1 to fk, algorithm=instant, order by a;
3237+
ERROR 0A000: ALGORITHM=INSTANT is not supported for this operation. Try ALGORITHM=COPY
3238+
alter table t2 add foreign key ind1 (b) references t1 (f2);
3239+
ERROR 42000: Duplicate key name 'ind1'
3240+
drop table t2, t1;
3241+
#
3242+
# It should be impossible to add a foreign key with a duplicate name.
3243+
#
3244+
create table t1 (f1 int, f2 int, key(f1), key(f2)) engine=innodb;
3245+
create table t2 (pk int primary key, a int, b int, key ind1(a),
3246+
foreign key fk1 (b) references t1 (f1)) engine=innodb;
3247+
alter table t2 add foreign key ind1 (b) references t1 (f2);
3248+
ERROR 42000: Duplicate key name 'ind1'
3249+
drop table t2, t1;
3250+
#
32283251
# End of 11.4 tests
32293252
#

mysql-test/main/alter_table.test

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2520,6 +2520,36 @@ show create table Another.Tp4;
25202520
drop table Another.Tp4;
25212521
drop database Another;
25222522

2523+
--echo #
2524+
--echo # MDEV-35732: Failed ALTER TABLE causes inconsistency, changes behavior of the next statement
2525+
--echo #
2526+
--echo # It should be impossible for a failed rename index to affect
2527+
--echo # a subsequent duplicate name check for a foreign key.
2528+
--echo #
2529+
create table t1 (f1 int, f2 int, key(f1), key(f2)) engine=innodb;
2530+
create table t2 (pk int primary key, a int, b int, key ind1(a),
2531+
foreign key fk1 (b) references t1 (f1)) engine=innodb;
2532+
2533+
--error ER_ALTER_OPERATION_NOT_SUPPORTED
2534+
alter table t2 rename index fk1 to fk, algorithm=instant, order by a;
2535+
2536+
--error ER_DUP_KEYNAME
2537+
alter table t2 add foreign key ind1 (b) references t1 (f2);
2538+
2539+
drop table t2, t1;
2540+
2541+
--echo #
2542+
--echo # It should be impossible to add a foreign key with a duplicate name.
2543+
--echo #
2544+
create table t1 (f1 int, f2 int, key(f1), key(f2)) engine=innodb;
2545+
create table t2 (pk int primary key, a int, b int, key ind1(a),
2546+
foreign key fk1 (b) references t1 (f1)) engine=innodb;
2547+
2548+
--error ER_DUP_KEYNAME
2549+
alter table t2 add foreign key ind1 (b) references t1 (f2);
2550+
2551+
drop table t2, t1;
2552+
25232553
--echo #
25242554
--echo # End of 11.4 tests
25252555
--echo #

sql/sql_table.cc

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9024,6 +9024,7 @@ mysql_prepare_alter_table(THD *thd, TABLE *table,
90249024
bool long_hash_key= false;
90259025
if (key_info->flags & HA_INVISIBLE_KEY)
90269026
continue;
9027+
bool generated_key= key_info->flags & HA_GENERATED_KEY;
90279028
const char *key_name= key_info->name.str;
90289029
const bool primary_key= table->s->primary_key == i;
90299030
const bool explicit_pk= primary_key &&
@@ -9112,7 +9113,7 @@ mysql_prepare_alter_table(THD *thd, TABLE *table,
91129113
dropped by mysql_prepare_create_table() and this will confuse
91139114
code in fill_alter_inplace_info().
91149115
*/
9115-
key_info->flags&= ~HA_GENERATED_KEY;
9116+
generated_key= false;
91169117
break;
91179118
}
91189119
}
@@ -9312,7 +9313,7 @@ mysql_prepare_alter_table(THD *thd, TABLE *table,
93129313
tmp_name.length= strlen(key_name);
93139314
/* We dont need LONG_UNIQUE_HASH_FIELD flag because it will be autogenerated */
93149315
key= new (thd->mem_root) Key(key_type, &tmp_name, &key_create_info,
9315-
key_info->flags & HA_GENERATED_KEY,
9316+
generated_key,
93169317
&key_parts, key_info->option_list, DDL_options());
93179318
key->without_overlaps= key_info->without_overlaps;
93189319
key->period= table->s->period.name;

0 commit comments

Comments
 (0)