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