Commit 162f475c authored by Thirunarayanan Balathandayuthapani's avatar Thirunarayanan Balathandayuthapani Committed by Marko Mäkelä

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 07b1a26c)
was incomplete, because it did not cover a case where the
FOREIGN KEY constraint is being dropped.
parent 6dce6aec
...@@ -128,6 +128,10 @@ HANDLER h READ `PRIMARY` PREV WHERE 0; ...@@ -128,6 +128,10 @@ HANDLER h READ `PRIMARY` PREV WHERE 0;
pk f1 f2 f3 f4 f5 f6 f7 f8 filler pk f1 f2 f3 f4 f5 f6 f7 f8 filler
HANDLER h CLOSE; HANDLER h CLOSE;
DROP TABLE t1; 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 t1(f1 int not null, primary key(f1))engine=innodb;
CREATE TABLE t2(f1 INT AUTO_INCREMENT NOT NULL, f2 INT NOT NULL, CREATE TABLE t2(f1 INT AUTO_INCREMENT NOT NULL, f2 INT NOT NULL,
status ENUM ('a', 'b', 'c'), INDEX idx1(f2), status ENUM ('a', 'b', 'c'), INDEX idx1(f2),
...@@ -154,3 +158,14 @@ t2 CREATE TABLE `t2` ( ...@@ -154,3 +158,14 @@ t2 CREATE TABLE `t2` (
) ENGINE=InnoDB DEFAULT CHARSET=latin1 ) ENGINE=InnoDB DEFAULT CHARSET=latin1
ALTER TABLE t2 CHANGE status status VARCHAR(20) DEFAULT NULL; ALTER TABLE t2 CHANGE status status VARCHAR(20) DEFAULT NULL;
DROP TABLE t2, t1; 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;
...@@ -135,8 +135,10 @@ HANDLER h READ `PRIMARY` PREV WHERE 0; ...@@ -135,8 +135,10 @@ HANDLER h READ `PRIMARY` PREV WHERE 0;
HANDLER h CLOSE; HANDLER h CLOSE;
DROP TABLE t1; DROP TABLE t1;
# MDEV-19630 ALTER TABLE ... ADD COLUMN damages foreign keys which are pointed --echo #
# to the table being altered --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 t1(f1 int not null, primary key(f1))engine=innodb;
CREATE TABLE t2(f1 INT AUTO_INCREMENT NOT NULL, f2 INT NOT NULL, CREATE TABLE t2(f1 INT AUTO_INCREMENT NOT NULL, f2 INT NOT NULL,
status ENUM ('a', 'b', 'c'), INDEX idx1(f2), status ENUM ('a', 'b', 'c'), INDEX idx1(f2),
...@@ -154,3 +156,16 @@ DROP TABLE t2, t1; ...@@ -154,3 +156,16 @@ DROP TABLE t2, t1;
--let $datadir= `select @@datadir` --let $datadir= `select @@datadir`
--remove_file $datadir/test/load.data --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;
...@@ -7428,27 +7428,23 @@ innobase_drop_foreign_try( ...@@ -7428,27 +7428,23 @@ innobase_drop_foreign_try(
} }
/** Rename a column in the data dictionary tables. /** Rename a column in the data dictionary tables.
@param[in] user_table InnoDB table that was being altered @param[in] ctx ALTER TABLE context
@param[in] trx Data dictionary transaction @param[in,out] trx Data dictionary transaction
@param[in] table_name Table name in MySQL @param[in] table_name Table name in MySQL
@param[in] nth_col 0-based index of the column @param[in] nth_col 0-based index of the column
@param[in] from old column name @param[in] from old column name
@param[in] to new 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 true Failure
@retval false Success */ @retval false Success */
static MY_ATTRIBUTE((nonnull, warn_unused_result)) static MY_ATTRIBUTE((nonnull, warn_unused_result))
bool bool
innobase_rename_column_try( innobase_rename_column_try(
const dict_table_t* user_table, const ha_innobase_inplace_ctx& ctx,
trx_t* trx, trx_t* trx,
const char* table_name, const char* table_name,
ulint nth_col, ulint nth_col,
const char* from, const char* from,
const char* to, const char* to)
bool new_clustered,
bool evict_fk_cache)
{ {
pars_info_t* info; pars_info_t* info;
dberr_t error; dberr_t error;
...@@ -7460,13 +7456,13 @@ innobase_rename_column_try( ...@@ -7460,13 +7456,13 @@ innobase_rename_column_try(
ut_ad(mutex_own(&dict_sys->mutex)); ut_ad(mutex_own(&dict_sys->mutex));
ut_ad(rw_lock_own(&dict_operation_lock, RW_LOCK_X)); ut_ad(rw_lock_own(&dict_operation_lock, RW_LOCK_X));
if (new_clustered) { if (ctx.need_rebuild()) {
goto rename_foreign; goto rename_foreign;
} }
info = pars_info_create(); 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_int4_literal(info, "nth", nth_col);
pars_info_add_str_literal(info, "new", to); pars_info_add_str_literal(info, "new", to);
...@@ -7496,7 +7492,7 @@ innobase_rename_column_try( ...@@ -7496,7 +7492,7 @@ innobase_rename_column_try(
trx->op_info = "renaming column in SYS_FIELDS"; trx->op_info = "renaming column in SYS_FIELDS";
for (const dict_index_t* index = dict_table_get_first_index( for (const dict_index_t* index = dict_table_get_first_index(
user_table); ctx.old_table);
index != NULL; index != NULL;
index = dict_table_get_next_index(index)) { index = dict_table_get_next_index(index)) {
...@@ -7549,8 +7545,8 @@ innobase_rename_column_try( ...@@ -7549,8 +7545,8 @@ innobase_rename_column_try(
std::set<dict_foreign_t*> fk_evict; std::set<dict_foreign_t*> fk_evict;
bool foreign_modified; bool foreign_modified;
for (dict_foreign_set::const_iterator it = user_table->foreign_set.begin(); for (dict_foreign_set::const_iterator it = ctx.old_table->foreign_set.begin();
it != user_table->foreign_set.end(); it != ctx.old_table->foreign_set.end();
++it) { ++it) {
dict_foreign_t* foreign = *it; dict_foreign_t* foreign = *it;
...@@ -7563,6 +7559,14 @@ innobase_rename_column_try( ...@@ -7563,6 +7559,14 @@ innobase_rename_column_try(
continue; 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(); info = pars_info_create();
pars_info_add_str_literal(info, "id", foreign->id); pars_info_add_str_literal(info, "id", foreign->id);
...@@ -7591,8 +7595,8 @@ innobase_rename_column_try( ...@@ -7591,8 +7595,8 @@ innobase_rename_column_try(
} }
for (dict_foreign_set::const_iterator it for (dict_foreign_set::const_iterator it
= user_table->referenced_set.begin(); = ctx.old_table->referenced_set.begin();
it != user_table->referenced_set.end(); it != ctx.old_table->referenced_set.end();
++it) { ++it) {
foreign_modified = false; foreign_modified = false;
...@@ -7633,7 +7637,7 @@ innobase_rename_column_try( ...@@ -7633,7 +7637,7 @@ innobase_rename_column_try(
} }
/* Reload the foreign key info for instant table too. */ /* 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(), std::for_each(fk_evict.begin(), fk_evict.end(),
dict_foreign_remove_from_cache); dict_foreign_remove_from_cache);
} }
...@@ -7684,12 +7688,10 @@ innobase_rename_columns_try( ...@@ -7684,12 +7688,10 @@ innobase_rename_columns_try(
: i - num_v; : i - num_v;
if (innobase_rename_column_try( if (innobase_rename_column_try(
ctx->old_table, trx, table_name, *ctx, trx, table_name,
col_n, col_n,
cf->field->field_name.str, cf->field->field_name.str,
cf->field_name.str, cf->field_name.str)) {
ctx->need_rebuild(),
ctx->is_instant())) {
return(true); return(true);
} }
goto processed_field; goto processed_field;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment