diff options
author | Marko Mäkelä <marko.makela@mariadb.com> | 2022-12-09 10:42:19 +0200 |
---|---|---|
committer | Marko Mäkelä <marko.makela@mariadb.com> | 2022-12-09 10:42:19 +0200 |
commit | 782b2a750067a12be07b9c305ede4d2c28f173e0 (patch) | |
tree | 228deca0a3082f87e4b5b4fc0b86c0b7d0927e5c | |
parent | 8f3631d0096ceecc21f2879a9558fd9242f09f8c (diff) | |
download | mariadb-git-782b2a750067a12be07b9c305ede4d2c28f173e0.tar.gz |
MDEV-29144 ER_TABLE_SCHEMA_MISMATCH or crash on DISCARD/IMPORT
mysql_discard_or_import_tablespace(): On successful
ALTER TABLE...DISCARD TABLESPACE, evict the table handle from the
table definition cache, so that ha_innobase::close() will be invoked,
like InnoDB expects to be the case. This will avoid an assertion failure
ut_a(table->get_ref_count() == 0) during IMPORT TABLESPACE.
ha_innobase::open(): Do not issue any ER_TABLESPACE_DISCARDED warning.
Member functions for DML will do that.
ha_innobase::truncate(), ha_innobase::check_if_supported_inplace_alter():
Issue ER_TABLESPACE_DISCARDED warnings, to compensate for the removal of
the warning in ha_innobase::open().
row_quiesce_write_indexes(): Only write information about committed
indexes. The ALTER TABLE t NOWAIT ADD INDEX(c) in the nondeterministic
test case will most of the time fail due to a metadata lock (MDL) timeout
and leave behind an uncommitted index.
Reviewed by: Sergei Golubchik
-rw-r--r-- | mysql-test/suite/encryption/r/innodb-bad-key-change2.result | 1 | ||||
-rw-r--r-- | mysql-test/suite/innodb/r/import_tablespace_race.result | 26 | ||||
-rw-r--r-- | mysql-test/suite/innodb/t/import_tablespace_race.test | 54 | ||||
-rw-r--r-- | sql/sql_table.cc | 4 | ||||
-rw-r--r-- | storage/innobase/handler/ha_innodb.cc | 11 | ||||
-rw-r--r-- | storage/innobase/handler/handler0alter.cc | 6 | ||||
-rw-r--r-- | storage/innobase/row/row0quiesce.cc | 15 |
7 files changed, 110 insertions, 7 deletions
diff --git a/mysql-test/suite/encryption/r/innodb-bad-key-change2.result b/mysql-test/suite/encryption/r/innodb-bad-key-change2.result index 5c28203ecf8..50da3dc7b2b 100644 --- a/mysql-test/suite/encryption/r/innodb-bad-key-change2.result +++ b/mysql-test/suite/encryption/r/innodb-bad-key-change2.result @@ -44,7 +44,6 @@ ALTER TABLE t1 DISCARD TABLESPACE; restore: t1 .ibd and .cfg files ALTER TABLE t1 DISCARD TABLESPACE; Warnings: -Warning 1814 Tablespace has been discarded for table `t1` Warning 1812 Tablespace is missing for table 'test/t1' restore: t1 .ibd and .cfg files ALTER TABLE t1 IMPORT TABLESPACE; diff --git a/mysql-test/suite/innodb/r/import_tablespace_race.result b/mysql-test/suite/innodb/r/import_tablespace_race.result new file mode 100644 index 00000000000..786b8cbd261 --- /dev/null +++ b/mysql-test/suite/innodb/r/import_tablespace_race.result @@ -0,0 +1,26 @@ +# +# MDEV-29144 ER_TABLE_SCHEMA_MISMATCH or crash on DISCARD/IMPORT +# +CREATE TABLE t (pk int PRIMARY KEY, c varchar(1024)) +ENGINE=InnoDB CHARSET latin1; +INSERT INTO t SELECT seq, 'x' FROM seq_1_to_100; +connect con1,localhost,root,,test; +BEGIN NOT ATOMIC +DECLARE a INT DEFAULT 0; +REPEAT +SET a= a+1; +UPDATE t SET c = 'xx' WHERE pk = a; +UNTIL a = 100 +END REPEAT; +END +$ +connection default; +ALTER TABLE t NOWAIT ADD INDEX (c); +connection con1; +connection default; +FLUSH TABLE t FOR EXPORT; +UNLOCK TABLES; +DROP TABLE t; +ALTER TABLE t DISCARD TABLESPACE; +ALTER TABLE t IMPORT TABLESPACE; +DROP TABLE t; diff --git a/mysql-test/suite/innodb/t/import_tablespace_race.test b/mysql-test/suite/innodb/t/import_tablespace_race.test new file mode 100644 index 00000000000..532c2684dde --- /dev/null +++ b/mysql-test/suite/innodb/t/import_tablespace_race.test @@ -0,0 +1,54 @@ +--source include/have_innodb.inc +--source include/have_sequence.inc + +--echo # +--echo # MDEV-29144 ER_TABLE_SCHEMA_MISMATCH or crash on DISCARD/IMPORT +--echo # + +CREATE TABLE t (pk int PRIMARY KEY, c varchar(1024)) +ENGINE=InnoDB CHARSET latin1; +INSERT INTO t SELECT seq, 'x' FROM seq_1_to_100; + +--connect (con1,localhost,root,,test) +--delimiter $ +--send + BEGIN NOT ATOMIC + DECLARE a INT DEFAULT 0; + REPEAT + SET a= a+1; + UPDATE t SET c = 'xx' WHERE pk = a; + UNTIL a = 100 + END REPEAT; + END +$ +--delimiter ; + +--connection default +--error 0,ER_LOCK_WAIT_TIMEOUT +ALTER TABLE t NOWAIT ADD INDEX (c); + +--connection con1 +--reap + +--connection default + +--let $datadir= `select @@datadir` + +FLUSH TABLE t FOR EXPORT; +--let $create= query_get_value(SHOW CREATE TABLE t, Create Table, 1) +--copy_file $datadir/test/t.cfg $MYSQL_TMP_DIR/t.cfg +--copy_file $datadir/test/t.ibd $MYSQL_TMP_DIR/t.ibd +UNLOCK TABLES; + +DROP TABLE t; +--disable_query_log +eval $create; +--enable_query_log + +ALTER TABLE t DISCARD TABLESPACE; +--move_file $MYSQL_TMP_DIR/t.cfg $datadir/test/t.cfg +--move_file $MYSQL_TMP_DIR/t.ibd $datadir/test/t.ibd +ALTER TABLE t IMPORT TABLESPACE; + +# Cleanup +DROP TABLE t; diff --git a/sql/sql_table.cc b/sql/sql_table.cc index 042ed9e1d6f..62a46dbf430 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -6075,6 +6075,10 @@ int mysql_discard_or_import_tablespace(THD *thd, if (unlikely(error)) goto err; + if (discard) + tdc_remove_table(thd, TDC_RT_REMOVE_NOT_OWN, table_list->table->s->db.str, + table_list->table->s->table_name.str, true); + /* The 0 in the call below means 'not in a transaction', which means immediate invalidation; that is probably what we wish here diff --git a/storage/innobase/handler/ha_innodb.cc b/storage/innobase/handler/ha_innodb.cc index aa1bc5844e3..a1f58091a90 100644 --- a/storage/innobase/handler/ha_innodb.cc +++ b/storage/innobase/handler/ha_innodb.cc @@ -6268,11 +6268,6 @@ no_such_table: MONITOR_INC(MONITOR_TABLE_OPEN); if ((ib_table->flags2 & DICT_TF2_DISCARDED)) { - - ib_senderrf(thd, - IB_LOG_LEVEL_WARN, ER_TABLESPACE_DISCARDED, - table->s->table_name.str); - /* Allow an open because a proper DISCARD should have set all the flags and index root page numbers to FIL_NULL that should prevent any DML from running but it should allow DDL @@ -13631,6 +13626,12 @@ int ha_innobase::truncate() if (ib_table->is_temporary()) { info.options|= HA_LEX_CREATE_TMP_TABLE; } else { + if (!ib_table->space) { + ib_senderrf(m_user_thd, + IB_LOG_LEVEL_WARN, ER_TABLESPACE_DISCARDED, + table->s->table_name.str); + } + dict_get_and_save_data_dir_path(ib_table, false); } diff --git a/storage/innobase/handler/handler0alter.cc b/storage/innobase/handler/handler0alter.cc index cd40fe3145e..19c90fed74c 100644 --- a/storage/innobase/handler/handler0alter.cc +++ b/storage/innobase/handler/handler0alter.cc @@ -1013,6 +1013,12 @@ ha_innobase::check_if_supported_inplace_alter( update_thd(); + if (!m_prebuilt->table->space) { + ib_senderrf(m_user_thd, IB_LOG_LEVEL_WARN, + ER_TABLESPACE_DISCARDED, + table->s->table_name.str); + } + if (ha_alter_info->handler_flags & ~(INNOBASE_INPLACE_IGNORE | INNOBASE_ALTER_INSTANT diff --git a/storage/innobase/row/row0quiesce.cc b/storage/innobase/row/row0quiesce.cc index 94a372bd046..5af026ecbe8 100644 --- a/storage/innobase/row/row0quiesce.cc +++ b/storage/innobase/row/row0quiesce.cc @@ -106,11 +106,17 @@ row_quiesce_write_indexes( FILE* file, /*!< in: file to write to */ THD* thd) /*!< in/out: session */ { + ulint n_indexes = 0; + for (const dict_index_t* index = UT_LIST_GET_FIRST(table->indexes); + index; index = UT_LIST_GET_NEXT(indexes, index)) { + n_indexes += index->is_committed(); + } + { byte row[sizeof(ib_uint32_t)]; /* Write the number of indexes in the table. */ - mach_write_to_4(row, UT_LIST_GET_LEN(table->indexes)); + mach_write_to_4(row, n_indexes); DBUG_EXECUTE_IF("ib_export_io_write_failure_11", close(fileno(file));); @@ -132,6 +138,12 @@ row_quiesce_write_indexes( index != 0 && err == DB_SUCCESS; index = UT_LIST_GET_NEXT(indexes, index)) { + if (!index->is_committed()) { + continue; + } + + ut_ad(n_indexes); ut_d(n_indexes--); + byte* ptr; byte row[sizeof(index_id_t) + sizeof(ib_uint32_t) * 8]; @@ -202,6 +214,7 @@ row_quiesce_write_indexes( err = row_quiesce_write_index_fields(index, file, thd); } + ut_ad(!n_indexes); return(err); } |