summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMonty <monty@mariadb.org>2021-03-02 14:08:38 +0200
committerMonty <monty@mariadb.org>2021-03-02 15:23:56 +0200
commit676987c4a14069d415c56d0f259aaaa7ca742c23 (patch)
tree87770cf27c7447b37f04c4a06923934bebf4df98
parentfc77431624b8d4dc966cd3855c16bee856b05645 (diff)
downloadmariadb-git-676987c4a14069d415c56d0f259aaaa7ca742c23.tar.gz
MDEV-24532 Table corruption ER_NO_SUCH_TABLE_IN_ENGINE .. on table with foreign key
When doing a truncate on an Innodb under lock tables, InnoDB would rename the old table to #sql-... and recreate a new 't1' table. The table lock would still be on the #sql-table. When doing ALTER TABLE, Innodb would do the changes on the #sql table (which would disappear on close). When the SQL layer, as part of inline alter table, would close the original t1 table (#sql in InnoDB) and then reopen the t1 table, Innodb would notice that this does not match it's own (old) t1 table and generate an error. Fixed by adding code in truncate table that if we are under lock tables and truncating an InnoDB table, we would close, reopen and lock the table after truncate. This will remove the #sql table and ensure that lock tables is using the new empty table. Reviewer: Marko Mäkelä
-rw-r--r--mysql-test/suite/innodb/r/truncate_foreign.result11
-rw-r--r--mysql-test/suite/innodb/t/truncate_foreign.test13
-rw-r--r--sql/handler.h6
-rw-r--r--sql/sql_truncate.cc9
-rw-r--r--storage/innobase/handler/ha_innodb.cc3
5 files changed, 41 insertions, 1 deletions
diff --git a/mysql-test/suite/innodb/r/truncate_foreign.result b/mysql-test/suite/innodb/r/truncate_foreign.result
index fc09b74d62f..12a41860708 100644
--- a/mysql-test/suite/innodb/r/truncate_foreign.result
+++ b/mysql-test/suite/innodb/r/truncate_foreign.result
@@ -57,3 +57,14 @@ disconnect dml;
connection default;
SET DEBUG_SYNC = RESET;
DROP TABLE child, parent;
+#
+# MDEV-24532 Table corruption ER_NO_SUCH_TABLE_IN_ENGINE or
+# ER_CRASHED_ON_USAGE after ALTER on table with foreign key
+#
+CREATE TABLE t1 (a INT, b INT, PRIMARY KEY (a)) ENGINE=InnoDB;
+ALTER TABLE t1 ADD FOREIGN KEY (b) REFERENCES t1 (a) ON UPDATE CASCADE;
+LOCK TABLE t1 WRITE;
+TRUNCATE TABLE t1;
+ALTER TABLE t1 ADD c INT;
+UNLOCK TABLES;
+DROP TABLE t1;
diff --git a/mysql-test/suite/innodb/t/truncate_foreign.test b/mysql-test/suite/innodb/t/truncate_foreign.test
index d9d647e69f0..1c150e5db40 100644
--- a/mysql-test/suite/innodb/t/truncate_foreign.test
+++ b/mysql-test/suite/innodb/t/truncate_foreign.test
@@ -67,3 +67,16 @@ connection default;
SET DEBUG_SYNC = RESET;
DROP TABLE child, parent;
+
+--echo #
+--echo # MDEV-24532 Table corruption ER_NO_SUCH_TABLE_IN_ENGINE or
+--echo # ER_CRASHED_ON_USAGE after ALTER on table with foreign key
+--echo #
+
+CREATE TABLE t1 (a INT, b INT, PRIMARY KEY (a)) ENGINE=InnoDB;
+ALTER TABLE t1 ADD FOREIGN KEY (b) REFERENCES t1 (a) ON UPDATE CASCADE;
+LOCK TABLE t1 WRITE;
+TRUNCATE TABLE t1;
+ALTER TABLE t1 ADD c INT;
+UNLOCK TABLES;
+DROP TABLE t1;
diff --git a/sql/handler.h b/sql/handler.h
index 6113b748696..e0e0604176d 100644
--- a/sql/handler.h
+++ b/sql/handler.h
@@ -1419,6 +1419,12 @@ handlerton *ha_default_tmp_handlerton(THD *thd);
// MySQL compatibility. Unused.
#define HTON_SUPPORTS_FOREIGN_KEYS (1 << 0) //Foreign key constraint supported.
+/*
+ Table requires and close and reopen after truncate
+ If the handler has HTON_CAN_RECREATE, this flag is not used
+*/
+#define HTON_REQUIRES_CLOSE_AFTER_TRUNCATE (1 << 18)
+
class Ha_trx_info;
struct THD_TRANS
diff --git a/sql/sql_truncate.cc b/sql/sql_truncate.cc
index ea4d7399ea3..7d1f630b88c 100644
--- a/sql/sql_truncate.cc
+++ b/sql/sql_truncate.cc
@@ -439,6 +439,15 @@ bool Sql_cmd_truncate_table::truncate_table(THD *thd, TABLE_LIST *table_ref)
*/
error= handler_truncate(thd, table_ref, FALSE);
+ if (error == TRUNCATE_OK && thd->locked_tables_mode &&
+ (table_ref->table->file->ht->flags &
+ HTON_REQUIRES_CLOSE_AFTER_TRUNCATE))
+ {
+ thd->locked_tables_list.mark_table_for_reopen(thd, table_ref->table);
+ if (unlikely(thd->locked_tables_list.reopen_tables(thd, true)))
+ thd->locked_tables_list.unlink_all_closed_tables(thd, NULL, 0);
+ }
+
/*
All effects of a TRUNCATE TABLE operation are committed even if
truncation fails in the case of non transactional tables. Thus, the
diff --git a/storage/innobase/handler/ha_innodb.cc b/storage/innobase/handler/ha_innodb.cc
index 2c51f0a9fdf..c67bf6bd607 100644
--- a/storage/innobase/handler/ha_innodb.cc
+++ b/storage/innobase/handler/ha_innodb.cc
@@ -3702,7 +3702,8 @@ innobase_init(
innobase_hton->flush_logs = innobase_flush_logs;
innobase_hton->show_status = innobase_show_status;
innobase_hton->flags =
- HTON_SUPPORTS_EXTENDED_KEYS | HTON_SUPPORTS_FOREIGN_KEYS;
+ HTON_SUPPORTS_EXTENDED_KEYS | HTON_SUPPORTS_FOREIGN_KEYS |
+ HTON_REQUIRES_CLOSE_AFTER_TRUNCATE;
#ifdef WITH_WSREP
innobase_hton->abort_transaction=wsrep_abort_transaction;