Skip to content

Commit 162f475

Browse files
Thirunarayanandr-m
authored andcommitted
MDEV-20938 Double free of dict_foreign_t during instant ALTER TABLE
innobase_drop_foreign_try(): Don't evict and reload the dict_foreign_t during instant ALTER TABLE if the FOREIGN KEY constraint is being dropped. The MDEV-19630 fix (commit 07b1a26) was incomplete, because it did not cover a case where the FOREIGN KEY constraint is being dropped.
1 parent 6dce6ae commit 162f475

File tree

3 files changed

+58
-26
lines changed

3 files changed

+58
-26
lines changed

mysql-test/suite/innodb/r/instant_alter_bugs.result

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,10 @@ HANDLER h READ `PRIMARY` PREV WHERE 0;
128128
pk f1 f2 f3 f4 f5 f6 f7 f8 filler
129129
HANDLER h CLOSE;
130130
DROP TABLE t1;
131+
#
132+
# MDEV-19630 ALTER TABLE ... ADD COLUMN damages foreign keys
133+
# which are pointed to the table being altered
134+
#
131135
CREATE TABLE t1(f1 int not null, primary key(f1))engine=innodb;
132136
CREATE TABLE t2(f1 INT AUTO_INCREMENT NOT NULL, f2 INT NOT NULL,
133137
status ENUM ('a', 'b', 'c'), INDEX idx1(f2),
@@ -154,3 +158,14 @@ t2 CREATE TABLE `t2` (
154158
) ENGINE=InnoDB DEFAULT CHARSET=latin1
155159
ALTER TABLE t2 CHANGE status status VARCHAR(20) DEFAULT NULL;
156160
DROP TABLE t2, t1;
161+
#
162+
# MDEV-20938 Double free of dict_foreign_t during instant ALTER TABLE
163+
#
164+
CREATE TABLE t1 (id INT UNSIGNED PRIMARY KEY) ENGINE=InnoDB;
165+
CREATE TABLE t2 (a INT UNSIGNED PRIMARY KEY, b INT UNSIGNED UNIQUE,
166+
FOREIGN KEY fk1 (b) REFERENCES t1 (id)) ENGINE=InnoDB;
167+
ALTER TABLE t2
168+
DROP FOREIGN KEY fk1,
169+
CHANGE b d INT UNSIGNED,
170+
ADD c INT;
171+
DROP TABLE t2, t1;

mysql-test/suite/innodb/t/instant_alter_bugs.test

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -135,8 +135,10 @@ HANDLER h READ `PRIMARY` PREV WHERE 0;
135135
HANDLER h CLOSE;
136136
DROP TABLE t1;
137137

138-
# MDEV-19630 ALTER TABLE ... ADD COLUMN damages foreign keys which are pointed
139-
# to the table being altered
138+
--echo #
139+
--echo # MDEV-19630 ALTER TABLE ... ADD COLUMN damages foreign keys
140+
--echo # which are pointed to the table being altered
141+
--echo #
140142
CREATE TABLE t1(f1 int not null, primary key(f1))engine=innodb;
141143
CREATE TABLE t2(f1 INT AUTO_INCREMENT NOT NULL, f2 INT NOT NULL,
142144
status ENUM ('a', 'b', 'c'), INDEX idx1(f2),
@@ -154,3 +156,16 @@ DROP TABLE t2, t1;
154156

155157
--let $datadir= `select @@datadir`
156158
--remove_file $datadir/test/load.data
159+
160+
--echo #
161+
--echo # MDEV-20938 Double free of dict_foreign_t during instant ALTER TABLE
162+
--echo #
163+
164+
CREATE TABLE t1 (id INT UNSIGNED PRIMARY KEY) ENGINE=InnoDB;
165+
CREATE TABLE t2 (a INT UNSIGNED PRIMARY KEY, b INT UNSIGNED UNIQUE,
166+
FOREIGN KEY fk1 (b) REFERENCES t1 (id)) ENGINE=InnoDB;
167+
ALTER TABLE t2
168+
DROP FOREIGN KEY fk1,
169+
CHANGE b d INT UNSIGNED,
170+
ADD c INT;
171+
DROP TABLE t2, t1;

storage/innobase/handler/handler0alter.cc

Lines changed: 26 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -7428,27 +7428,23 @@ innobase_drop_foreign_try(
74287428
}
74297429

74307430
/** Rename a column in the data dictionary tables.
7431-
@param[in] user_table InnoDB table that was being altered
7432-
@param[in] trx Data dictionary transaction
7431+
@param[in] ctx ALTER TABLE context
7432+
@param[in,out] trx Data dictionary transaction
74337433
@param[in] table_name Table name in MySQL
74347434
@param[in] nth_col 0-based index of the column
74357435
@param[in] from old column name
74367436
@param[in] to new column name
7437-
@param[in] new_clustered whether the table has been rebuilt
7438-
@param[in] evict_fk_cache Evict the fk info from cache
74397437
@retval true Failure
74407438
@retval false Success */
74417439
static MY_ATTRIBUTE((nonnull, warn_unused_result))
74427440
bool
74437441
innobase_rename_column_try(
7444-
const dict_table_t* user_table,
7445-
trx_t* trx,
7446-
const char* table_name,
7447-
ulint nth_col,
7448-
const char* from,
7449-
const char* to,
7450-
bool new_clustered,
7451-
bool evict_fk_cache)
7442+
const ha_innobase_inplace_ctx& ctx,
7443+
trx_t* trx,
7444+
const char* table_name,
7445+
ulint nth_col,
7446+
const char* from,
7447+
const char* to)
74527448
{
74537449
pars_info_t* info;
74547450
dberr_t error;
@@ -7460,13 +7456,13 @@ innobase_rename_column_try(
74607456
ut_ad(mutex_own(&dict_sys->mutex));
74617457
ut_ad(rw_lock_own(&dict_operation_lock, RW_LOCK_X));
74627458

7463-
if (new_clustered) {
7459+
if (ctx.need_rebuild()) {
74647460
goto rename_foreign;
74657461
}
74667462

74677463
info = pars_info_create();
74687464

7469-
pars_info_add_ull_literal(info, "tableid", user_table->id);
7465+
pars_info_add_ull_literal(info, "tableid", ctx.old_table->id);
74707466
pars_info_add_int4_literal(info, "nth", nth_col);
74717467
pars_info_add_str_literal(info, "new", to);
74727468

@@ -7496,7 +7492,7 @@ innobase_rename_column_try(
74967492
trx->op_info = "renaming column in SYS_FIELDS";
74977493

74987494
for (const dict_index_t* index = dict_table_get_first_index(
7499-
user_table);
7495+
ctx.old_table);
75007496
index != NULL;
75017497
index = dict_table_get_next_index(index)) {
75027498

@@ -7549,8 +7545,8 @@ innobase_rename_column_try(
75497545
std::set<dict_foreign_t*> fk_evict;
75507546
bool foreign_modified;
75517547

7552-
for (dict_foreign_set::const_iterator it = user_table->foreign_set.begin();
7553-
it != user_table->foreign_set.end();
7548+
for (dict_foreign_set::const_iterator it = ctx.old_table->foreign_set.begin();
7549+
it != ctx.old_table->foreign_set.end();
75547550
++it) {
75557551

75567552
dict_foreign_t* foreign = *it;
@@ -7563,6 +7559,14 @@ innobase_rename_column_try(
75637559
continue;
75647560
}
75657561

7562+
/* Ignore the foreign key rename if fk info
7563+
is being dropped. */
7564+
if (innobase_dropping_foreign(
7565+
foreign, ctx.drop_fk,
7566+
ctx.num_to_drop_fk)) {
7567+
continue;
7568+
}
7569+
75667570
info = pars_info_create();
75677571

75687572
pars_info_add_str_literal(info, "id", foreign->id);
@@ -7591,8 +7595,8 @@ innobase_rename_column_try(
75917595
}
75927596

75937597
for (dict_foreign_set::const_iterator it
7594-
= user_table->referenced_set.begin();
7595-
it != user_table->referenced_set.end();
7598+
= ctx.old_table->referenced_set.begin();
7599+
it != ctx.old_table->referenced_set.end();
75967600
++it) {
75977601

75987602
foreign_modified = false;
@@ -7633,7 +7637,7 @@ innobase_rename_column_try(
76337637
}
76347638

76357639
/* Reload the foreign key info for instant table too. */
7636-
if (new_clustered || evict_fk_cache) {
7640+
if (ctx.need_rebuild() || ctx.is_instant()) {
76377641
std::for_each(fk_evict.begin(), fk_evict.end(),
76387642
dict_foreign_remove_from_cache);
76397643
}
@@ -7684,12 +7688,10 @@ innobase_rename_columns_try(
76847688
: i - num_v;
76857689

76867690
if (innobase_rename_column_try(
7687-
ctx->old_table, trx, table_name,
7691+
*ctx, trx, table_name,
76887692
col_n,
76897693
cf->field->field_name.str,
7690-
cf->field_name.str,
7691-
ctx->need_rebuild(),
7692-
ctx->is_instant())) {
7694+
cf->field_name.str)) {
76937695
return(true);
76947696
}
76957697
goto processed_field;

0 commit comments

Comments
 (0)