Commit 0936c138 authored by Marko Mäkelä's avatar Marko Mäkelä

MDEV-33993 Possible server hang on DROP INDEX or RENAME INDEX

commit_try_norebuild(): Add the parameter statistics_exist,
similar to commit_try_rebuild(). If the InnoDB statistics tables
did not exist, we will not attempt to update statistics later on
during the transaction.

Thanks to Matthias Leich for originally reproducing this scenario.
parent 8c8b7da0
......@@ -108,11 +108,11 @@ DROP TABLE t;
# MDEV-26772 InnoDB DDL fails with DUPLICATE KEY error
#
create table t1(f1 int not null primary key,
f2 int not null, index idx(f2))engine=innodb;
f2 int not null, index idx(f2), index i(f2,f1))engine=innodb;
insert into t1 values(1, 1);
connect con1,localhost,root,,,;
SET DEBUG_SYNC='before_delete_table_stats SIGNAL blocked WAIT_FOR go';
SET innodb_lock_wait_timeout=0;
SET STATEMENT innodb_lock_wait_timeout=0 FOR
ALTER TABLE t1 FORCE, ALGORITHM=COPY;
connection default;
SET DEBUG_SYNC='now WAIT_FOR blocked';
......@@ -124,6 +124,17 @@ connection con1;
connection default;
COMMIT;
SET DEBUG_SYNC=RESET;
RENAME TABLE mysql.innodb_table_stats TO mysql.innodb_table_stats_hidden;
connection con1;
SET DEBUG_SYNC='innodb_commit_inplace_before_lock SIGNAL blocked WAIT_FOR go';
ALTER TABLE t1 DROP INDEX i;
connection default;
SET DEBUG_SYNC='now WAIT_FOR blocked';
RENAME TABLE mysql.innodb_table_stats_hidden TO mysql.innodb_table_stats;
SET DEBUG_SYNC='now SIGNAL go';
connection con1;
connection default;
SET DEBUG_SYNC=RESET;
connection con1;
ALTER TABLE t1 RENAME KEY idx TO idx1, ALGORITHM=COPY;
disconnect con1;
......
......@@ -144,15 +144,14 @@ DROP TABLE t;
--echo #
create table t1(f1 int not null primary key,
f2 int not null, index idx(f2))engine=innodb;
f2 int not null, index idx(f2), index i(f2,f1))engine=innodb;
insert into t1 values(1, 1);
connect(con1,localhost,root,,,);
SET DEBUG_SYNC='before_delete_table_stats SIGNAL blocked WAIT_FOR go';
SET innodb_lock_wait_timeout=0;
send ALTER TABLE t1 FORCE, ALGORITHM=COPY;
send SET STATEMENT innodb_lock_wait_timeout=0 FOR
ALTER TABLE t1 FORCE, ALGORITHM=COPY;
connection default;
SET DEBUG_SYNC='now WAIT_FOR blocked';
......@@ -167,6 +166,21 @@ connection default;
COMMIT;
SET DEBUG_SYNC=RESET;
RENAME TABLE mysql.innodb_table_stats TO mysql.innodb_table_stats_hidden;
connection con1;
SET DEBUG_SYNC='innodb_commit_inplace_before_lock SIGNAL blocked WAIT_FOR go';
send ALTER TABLE t1 DROP INDEX i;
connection default;
SET DEBUG_SYNC='now WAIT_FOR blocked';
RENAME TABLE mysql.innodb_table_stats_hidden TO mysql.innodb_table_stats;
SET DEBUG_SYNC='now SIGNAL go';
connection con1;
reap;
connection default;
SET DEBUG_SYNC=RESET;
connection con1;
ALTER TABLE t1 RENAME KEY idx TO idx1, ALGORITHM=COPY;
disconnect con1;
......
......@@ -10228,6 +10228,7 @@ when rebuilding the table.
@param ctx In-place ALTER TABLE context
@param altered_table MySQL table that is being altered
@param old_table MySQL table as it is before the ALTER operation
@param statistics_exist whether to update InnoDB persistent statistics
@param trx Data dictionary transaction
@param table_name Table name in MySQL
@retval true Failure
......@@ -10501,6 +10502,7 @@ when not rebuilding the table.
@param ha_alter_info Data used during in-place alter
@param ctx In-place ALTER TABLE context
@param old_table MySQL table as it is before the ALTER operation
@param statistics_exist whether to update InnoDB persistent statistics
@param trx Data dictionary transaction
@param table_name Table name in MySQL
@retval true Failure
......@@ -10514,6 +10516,7 @@ commit_try_norebuild(
ha_innobase_inplace_ctx*ctx,
TABLE* altered_table,
const TABLE* old_table,
bool statistics_exist,
trx_t* trx,
const char* table_name)
{
......@@ -10628,6 +10631,10 @@ commit_try_norebuild(
goto handle_error;
}
if (!statistics_exist) {
continue;
}
error = dict_stats_delete_from_index_stats(db, table,
index->name, trx);
switch (error) {
......@@ -10639,7 +10646,8 @@ commit_try_norebuild(
}
}
if (const size_t size = ha_alter_info->rename_keys.size()) {
if (!statistics_exist) {
} else if (const size_t size = ha_alter_info->rename_keys.size()) {
char tmp_name[5];
char db[MAX_DB_UTF8_LEN], table[MAX_TABLE_UTF8_LEN];
......@@ -11386,6 +11394,8 @@ ha_innobase::commit_inplace_alter_table(
}
}
DEBUG_SYNC(m_user_thd, "innodb_commit_inplace_before_lock");
DBUG_EXECUTE_IF("stats_lock_fail",
error = DB_LOCK_WAIT_TIMEOUT;
trx_rollback_for_mysql(trx););
......@@ -11469,7 +11479,9 @@ ha_innobase::commit_inplace_alter_table(
goto fail;
}
} else if (commit_try_norebuild(ha_alter_info, ctx,
altered_table, table, trx,
altered_table, table,
table_stats && index_stats,
trx,
table_share->table_name.str)) {
goto fail;
}
......
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