Skip to content

Commit

Permalink
MDEV-20938 Double free of dict_foreign_t during instant ALTER TABLE
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
Thirunarayanan authored and dr-m committed Nov 1, 2019
1 parent 6dce6ae commit 162f475
Show file tree
Hide file tree
Showing 3 changed files with 58 additions and 26 deletions.
15 changes: 15 additions & 0 deletions mysql-test/suite/innodb/r/instant_alter_bugs.result
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,10 @@ HANDLER h READ `PRIMARY` PREV WHERE 0;
pk f1 f2 f3 f4 f5 f6 f7 f8 filler
HANDLER h CLOSE;
DROP TABLE t1;
#
# MDEV-19630 ALTER TABLE ... ADD COLUMN damages foreign keys
# which are pointed to the table being altered
#
CREATE TABLE t1(f1 int not null, primary key(f1))engine=innodb;
CREATE TABLE t2(f1 INT AUTO_INCREMENT NOT NULL, f2 INT NOT NULL,
status ENUM ('a', 'b', 'c'), INDEX idx1(f2),
Expand All @@ -154,3 +158,14 @@ t2 CREATE TABLE `t2` (
) ENGINE=InnoDB DEFAULT CHARSET=latin1
ALTER TABLE t2 CHANGE status status VARCHAR(20) DEFAULT NULL;
DROP TABLE t2, t1;
#
# MDEV-20938 Double free of dict_foreign_t during instant ALTER TABLE
#
CREATE TABLE t1 (id INT UNSIGNED PRIMARY KEY) ENGINE=InnoDB;
CREATE TABLE t2 (a INT UNSIGNED PRIMARY KEY, b INT UNSIGNED UNIQUE,
FOREIGN KEY fk1 (b) REFERENCES t1 (id)) ENGINE=InnoDB;
ALTER TABLE t2
DROP FOREIGN KEY fk1,
CHANGE b d INT UNSIGNED,
ADD c INT;
DROP TABLE t2, t1;
19 changes: 17 additions & 2 deletions mysql-test/suite/innodb/t/instant_alter_bugs.test
Original file line number Diff line number Diff line change
Expand Up @@ -135,8 +135,10 @@ HANDLER h READ `PRIMARY` PREV WHERE 0;
HANDLER h CLOSE;
DROP TABLE t1;

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

--let $datadir= `select @@datadir`
--remove_file $datadir/test/load.data

--echo #
--echo # MDEV-20938 Double free of dict_foreign_t during instant ALTER TABLE
--echo #

CREATE TABLE t1 (id INT UNSIGNED PRIMARY KEY) ENGINE=InnoDB;
CREATE TABLE t2 (a INT UNSIGNED PRIMARY KEY, b INT UNSIGNED UNIQUE,
FOREIGN KEY fk1 (b) REFERENCES t1 (id)) ENGINE=InnoDB;
ALTER TABLE t2
DROP FOREIGN KEY fk1,
CHANGE b d INT UNSIGNED,
ADD c INT;
DROP TABLE t2, t1;
50 changes: 26 additions & 24 deletions storage/innobase/handler/handler0alter.cc
Original file line number Diff line number Diff line change
Expand Up @@ -7428,27 +7428,23 @@ innobase_drop_foreign_try(
}

/** Rename a column in the data dictionary tables.
@param[in] user_table InnoDB table that was being altered
@param[in] trx Data dictionary transaction
@param[in] ctx ALTER TABLE context
@param[in,out] trx Data dictionary transaction
@param[in] table_name Table name in MySQL
@param[in] nth_col 0-based index of the column
@param[in] from old column name
@param[in] to new column name
@param[in] new_clustered whether the table has been rebuilt
@param[in] evict_fk_cache Evict the fk info from cache
@retval true Failure
@retval false Success */
static MY_ATTRIBUTE((nonnull, warn_unused_result))
bool
innobase_rename_column_try(
const dict_table_t* user_table,
trx_t* trx,
const char* table_name,
ulint nth_col,
const char* from,
const char* to,
bool new_clustered,
bool evict_fk_cache)
const ha_innobase_inplace_ctx& ctx,
trx_t* trx,
const char* table_name,
ulint nth_col,
const char* from,
const char* to)
{
pars_info_t* info;
dberr_t error;
Expand All @@ -7460,13 +7456,13 @@ innobase_rename_column_try(
ut_ad(mutex_own(&dict_sys->mutex));
ut_ad(rw_lock_own(&dict_operation_lock, RW_LOCK_X));

if (new_clustered) {
if (ctx.need_rebuild()) {
goto rename_foreign;
}

info = pars_info_create();

pars_info_add_ull_literal(info, "tableid", user_table->id);
pars_info_add_ull_literal(info, "tableid", ctx.old_table->id);
pars_info_add_int4_literal(info, "nth", nth_col);
pars_info_add_str_literal(info, "new", to);

Expand Down Expand Up @@ -7496,7 +7492,7 @@ innobase_rename_column_try(
trx->op_info = "renaming column in SYS_FIELDS";

for (const dict_index_t* index = dict_table_get_first_index(
user_table);
ctx.old_table);
index != NULL;
index = dict_table_get_next_index(index)) {

Expand Down Expand Up @@ -7549,8 +7545,8 @@ innobase_rename_column_try(
std::set<dict_foreign_t*> fk_evict;
bool foreign_modified;

for (dict_foreign_set::const_iterator it = user_table->foreign_set.begin();
it != user_table->foreign_set.end();
for (dict_foreign_set::const_iterator it = ctx.old_table->foreign_set.begin();
it != ctx.old_table->foreign_set.end();
++it) {

dict_foreign_t* foreign = *it;
Expand All @@ -7563,6 +7559,14 @@ innobase_rename_column_try(
continue;
}

/* Ignore the foreign key rename if fk info
is being dropped. */
if (innobase_dropping_foreign(
foreign, ctx.drop_fk,
ctx.num_to_drop_fk)) {
continue;
}

info = pars_info_create();

pars_info_add_str_literal(info, "id", foreign->id);
Expand Down Expand Up @@ -7591,8 +7595,8 @@ innobase_rename_column_try(
}

for (dict_foreign_set::const_iterator it
= user_table->referenced_set.begin();
it != user_table->referenced_set.end();
= ctx.old_table->referenced_set.begin();
it != ctx.old_table->referenced_set.end();
++it) {

foreign_modified = false;
Expand Down Expand Up @@ -7633,7 +7637,7 @@ innobase_rename_column_try(
}

/* Reload the foreign key info for instant table too. */
if (new_clustered || evict_fk_cache) {
if (ctx.need_rebuild() || ctx.is_instant()) {
std::for_each(fk_evict.begin(), fk_evict.end(),
dict_foreign_remove_from_cache);
}
Expand Down Expand Up @@ -7684,12 +7688,10 @@ innobase_rename_columns_try(
: i - num_v;

if (innobase_rename_column_try(
ctx->old_table, trx, table_name,
*ctx, trx, table_name,
col_n,
cf->field->field_name.str,
cf->field_name.str,
ctx->need_rebuild(),
ctx->is_instant())) {
cf->field_name.str)) {
return(true);
}
goto processed_field;
Expand Down

0 comments on commit 162f475

Please sign in to comment.