summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMarko Mäkelä <marko.makela@mariadb.com>2021-10-19 19:54:29 +0300
committerMarko Mäkelä <marko.makela@mariadb.com>2021-10-19 19:54:29 +0300
commit6e390a62baa9dfd92d2776d28c97fd9525422295 (patch)
tree3a0d11abc341712995db9b0eda964d09bf412e49
parentf7684f0ca5f6f53d63e93afe89194b43bb5431d2 (diff)
downloadmariadb-git-6e390a62baa9dfd92d2776d28c97fd9525422295.tar.gz
MDEV-26772 InnoDB DDL fails with DUPLICATE KEY error
ha_innobase::delete_table(): When the table that is being dropped has a name starting with #sql, temporarily set innodb_lock_wait_timeout=0 while attempting to lock the persistent statistics tables. If the statistics tables cannot be locked, pretend that statistics did not exist and carry on with dropping the table. The SQL layer is not really prepared for failures of this operation. This is what fixes the test case. ha_innobase::rename_table(): When renaming a table from a name that starts with #sql, try to lock the statistics tables with an immediate timeout, and ignore the statistics if the locks were not available. In fact, during any rename from a #sql name, dict_stats_rename_table() should have no effect, because already when an earlier rename to a #sql name took place we should have deleted the statistics for the table using the non-#sql name. This change is just analogous to the ha_innobase::delete_table().
-rw-r--r--mysql-test/suite/innodb/r/innodb-alter-debug.result26
-rw-r--r--mysql-test/suite/innodb/t/innodb-alter-debug.test37
-rw-r--r--storage/innobase/handler/ha_innodb.cc51
3 files changed, 110 insertions, 4 deletions
diff --git a/mysql-test/suite/innodb/r/innodb-alter-debug.result b/mysql-test/suite/innodb/r/innodb-alter-debug.result
index 4644c124a45..519283536d5 100644
--- a/mysql-test/suite/innodb/r/innodb-alter-debug.result
+++ b/mysql-test/suite/innodb/r/innodb-alter-debug.result
@@ -107,3 +107,29 @@ ALTER TABLE t RENAME INDEX i2 to x, ALGORITHM=INPLACE;
ERROR 40001: Deadlock found when trying to get lock; try restarting transaction
SET DEBUG_DBUG = @saved_debug_dbug;
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;
+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;
+ALTER TABLE t1 FORCE, ALGORITHM=COPY;
+connection default;
+SET DEBUG_SYNC='now WAIT_FOR blocked';
+BEGIN;
+SELECT * FROM mysql.innodb_table_stats FOR UPDATE;
+database_name table_name last_update n_rows clustered_index_size sum_of_other_index_sizes
+SET DEBUG_SYNC='now SIGNAL go';
+connection con1;
+connection default;
+COMMIT;
+SET DEBUG_SYNC=RESET;
+connection con1;
+ALTER TABLE t1 RENAME KEY idx TO idx1, ALGORITHM=COPY;
+disconnect con1;
+connection default;
+DROP TABLE t1;
+# End of 10.6 tests
diff --git a/mysql-test/suite/innodb/t/innodb-alter-debug.test b/mysql-test/suite/innodb/t/innodb-alter-debug.test
index 7fbbb3159ee..7af8e882724 100644
--- a/mysql-test/suite/innodb/t/innodb-alter-debug.test
+++ b/mysql-test/suite/innodb/t/innodb-alter-debug.test
@@ -142,5 +142,42 @@ SET DEBUG_DBUG = @saved_debug_dbug;
DROP TABLE t;
+--echo #
+--echo # MDEV-26772 InnoDB DDL fails with DUPLICATE KEY error
+--echo #
+
+create table t1(f1 int not null primary key,
+
+ f2 int not null, index idx(f2))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;
+
+connection default;
+SET DEBUG_SYNC='now WAIT_FOR blocked';
+BEGIN;
+SELECT * FROM mysql.innodb_table_stats FOR UPDATE;
+SET DEBUG_SYNC='now SIGNAL go';
+
+connection con1;
+reap;
+
+connection default;
+COMMIT;
+SET DEBUG_SYNC=RESET;
+
+connection con1;
+ALTER TABLE t1 RENAME KEY idx TO idx1, ALGORITHM=COPY;
+disconnect con1;
+
+connection default;
+DROP TABLE t1;
+
+--echo # End of 10.6 tests
+
# Wait till all disconnects are completed
--source include/wait_until_count_sessions.inc
diff --git a/storage/innobase/handler/ha_innodb.cc b/storage/innobase/handler/ha_innodb.cc
index b4690a6e8c5..bcc160d5f0e 100644
--- a/storage/innobase/handler/ha_innodb.cc
+++ b/storage/innobase/handler/ha_innodb.cc
@@ -13473,6 +13473,8 @@ int ha_innobase::delete_table(const char *name)
}
#endif
+ DEBUG_SYNC(thd, "before_delete_table_stats");
+
if (err == DB_SUCCESS && dict_stats_is_persistent_enabled(table) &&
!table->is_stats_table())
{
@@ -13496,11 +13498,29 @@ int ha_innobase::delete_table(const char *name)
dict_sys.unfreeze();
}
+ auto &timeout= THDVAR(thd, lock_wait_timeout);
+ const auto save_timeout= timeout;
+ if (table->name.is_temporary())
+ timeout= 0;
+
if (table_stats && index_stats &&
!strcmp(table_stats->name.m_name, TABLE_STATS_NAME) &&
!strcmp(index_stats->name.m_name, INDEX_STATS_NAME) &&
!(err= lock_table_for_trx(table_stats, trx, LOCK_X)))
err= lock_table_for_trx(index_stats, trx, LOCK_X);
+
+ if (err != DB_SUCCESS && !timeout)
+ {
+ /* We may skip deleting statistics if we cannot lock the tables,
+ when the table carries a temporary name. */
+ err= DB_SUCCESS;
+ dict_table_close(table_stats, false, thd, mdl_table);
+ dict_table_close(index_stats, false, thd, mdl_index);
+ table_stats= nullptr;
+ index_stats= nullptr;
+ }
+
+ timeout= save_timeout;
}
if (err == DB_SUCCESS)
@@ -13959,8 +13979,9 @@ ha_innobase::rename_table(
normalize_table_name(norm_to, to);
dberr_t error = DB_SUCCESS;
+ const bool from_temp = dict_table_t::is_temporary_name(norm_from);
- if (dict_table_t::is_temporary_name(norm_from)) {
+ if (from_temp) {
/* There is no need to lock any FOREIGN KEY child tables. */
} else if (dict_table_t *table = dict_table_open_on_name(
norm_from, false, DICT_ERR_IGNORE_FK_NOKEY)) {
@@ -14003,9 +14024,31 @@ ha_innobase::rename_table(
if (error == DB_SUCCESS && table_stats && index_stats
&& !strcmp(table_stats->name.m_name, TABLE_STATS_NAME)
- && !strcmp(index_stats->name.m_name, INDEX_STATS_NAME) &&
- !(error = lock_table_for_trx(table_stats, trx, LOCK_X))) {
- error = lock_table_for_trx(index_stats, trx, LOCK_X);
+ && !strcmp(index_stats->name.m_name, INDEX_STATS_NAME)) {
+ auto &timeout = THDVAR(thd, lock_wait_timeout);
+ const auto save_timeout = timeout;
+ if (from_temp) {
+ timeout = 0;
+ }
+ error = lock_table_for_trx(table_stats, trx, LOCK_X);
+ if (error == DB_SUCCESS) {
+ error = lock_table_for_trx(index_stats, trx,
+ LOCK_X);
+ }
+ if (error != DB_SUCCESS && from_temp) {
+ error = DB_SUCCESS;
+ /* We may skip renaming statistics if
+ we cannot lock the tables, when the
+ table is being renamed from from a
+ temporary name. */
+ dict_table_close(table_stats, false, thd,
+ mdl_table);
+ dict_table_close(index_stats, false, thd,
+ mdl_index);
+ table_stats = nullptr;
+ index_stats = nullptr;
+ }
+ timeout = save_timeout;
}
}