From c407ee0976bb10d64a902bb4df36b30b3532012a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Fri, 11 May 2018 16:24:51 +0300 Subject: [PATCH] =?UTF-8?q?MDEV-16145=20Crash=20in=20ALTER=20TABLE?= =?UTF-8?q?=E2=80=A6AUTO=5FINCREMENT=3D1=20after=20DISCARD=20TABLESPACE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is the MariaDB equivalent of fixing the MySQL 5.7 regression Bug #26935001 ALTER TABLE AUTO_INCREMENT TRIES TO READ INDEX FROM DISCARDED TABLESPACE Oracle did not publish a test case, but it is easy to guess based on the commit message. The MariaDB code is different due to MDEV-6076 implementing persistent AUTO_INCREMENT. commit_set_autoinc(): Report ER_TABLESPACE_DISCARDED if the tablespace is missing. prepare_inplace_alter_table_dict(): Avoid accessing a discarded tablespace. (This avoids generating warnings in fil_space_acquire().) --- .../innodb/r/alter_missing_tablespace.result | 23 ++++++++++++--- .../innodb/t/alter_missing_tablespace.test | 22 +++++++++++++-- storage/innobase/handler/handler0alter.cc | 28 +++++++++++++------ 3 files changed, 58 insertions(+), 15 deletions(-) diff --git a/mysql-test/suite/innodb/r/alter_missing_tablespace.result b/mysql-test/suite/innodb/r/alter_missing_tablespace.result index 1517afd1a39..237d0df26ff 100644 --- a/mysql-test/suite/innodb/r/alter_missing_tablespace.result +++ b/mysql-test/suite/innodb/r/alter_missing_tablespace.result @@ -3,8 +3,10 @@ # OR DISCARDED TABLESPACES # SET GLOBAL innodb_file_per_table=1; -CREATE TABLE t(a INT)ENGINE=InnoDB; +CREATE TABLE t(a SERIAL)ENGINE=InnoDB; CREATE TABLE `x..d` (a INT PRIMARY KEY, b INT) ENGINE=InnoDB; +CREATE TABLE t1(a SERIAL)ENGINE=InnoDB; +INSERT INTO t1 VALUES(1),(2),(3); SELECT * FROM t; ERROR 42S02: Table 'test.t' doesn't exist in engine ALTER TABLE t ADD INDEX (a), ALGORITHM=INPLACE; @@ -13,11 +15,16 @@ SHOW WARNINGS; Level Code Message Warning 1812 Tablespace is missing for table 'test/t' Error 1932 Table 'test.t' doesn't exist in engine -ALTER TABLE t1 ADD INDEX (a), ALGORITHM=COPY; -ERROR 42S02: Table 'test.t1' doesn't exist +ALTER TABLE t ADD INDEX (a), ALGORITHM=COPY; +ERROR 42S02: Table 'test.t' doesn't exist in engine SHOW WARNINGS; Level Code Message -Error 1146 Table 'test.t1' doesn't exist +Warning 1812 Tablespace is missing for table 'test/t' +Error 1932 Table 'test.t' doesn't exist in engine +ALTER TABLE t AUTO_INCREMENT=1, ALGORITHM=INPLACE; +ERROR 42S02: Table 'test.t' doesn't exist in engine +ALTER TABLE t AUTO_INCREMENT=1, ALGORITHM=COPY; +ERROR 42S02: Table 'test.t' doesn't exist in engine ALTER TABLE t ALGORITHM=INPLACE, DISCARD TABLESPACE; ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near 'DISCARD TABLESPACE' at line 1 ALTER TABLE t ALGORITHM=COPY, DISCARD TABLESPACE; @@ -32,3 +39,11 @@ DROP TABLE t; SELECT * FROM `x..d`; ERROR 42S02: Table 'test.x..d' doesn't exist in engine DROP TABLE `x..d`; +ALTER TABLE t1 DISCARD TABLESPACE; +ALTER TABLE t1 AUTO_INCREMENT=1, ALGORITHM=INPLACE; +ERROR HY000: Tablespace has been discarded for table `t1` +ALTER TABLE t1 AUTO_INCREMENT=1, FORCE, ALGORITHM=INPLACE; +ERROR HY000: Tablespace has been discarded for table `t1` +ALTER TABLE t1 AUTO_INCREMENT=1, ALGORITHM=COPY; +ERROR HY000: Tablespace has been discarded for table `t1` +DROP TABLE t1; diff --git a/mysql-test/suite/innodb/t/alter_missing_tablespace.test b/mysql-test/suite/innodb/t/alter_missing_tablespace.test index d877b8f3b5d..4ab291a69f8 100644 --- a/mysql-test/suite/innodb/t/alter_missing_tablespace.test +++ b/mysql-test/suite/innodb/t/alter_missing_tablespace.test @@ -21,8 +21,10 @@ call mtr.add_suppression("Table .* in the InnoDB data dictionary has tablespace let $MYSQLD_DATADIR=`select @@datadir`; SET GLOBAL innodb_file_per_table=1; -CREATE TABLE t(a INT)ENGINE=InnoDB; +CREATE TABLE t(a SERIAL)ENGINE=InnoDB; CREATE TABLE `x..d` (a INT PRIMARY KEY, b INT) ENGINE=InnoDB; +CREATE TABLE t1(a SERIAL)ENGINE=InnoDB; +INSERT INTO t1 VALUES(1),(2),(3); --source include/shutdown_mysqld.inc @@ -41,10 +43,15 @@ SELECT * FROM t; ALTER TABLE t ADD INDEX (a), ALGORITHM=INPLACE; SHOW WARNINGS; ---error ER_NO_SUCH_TABLE -ALTER TABLE t1 ADD INDEX (a), ALGORITHM=COPY; +--error ER_NO_SUCH_TABLE_IN_ENGINE +ALTER TABLE t ADD INDEX (a), ALGORITHM=COPY; SHOW WARNINGS; +--error ER_NO_SUCH_TABLE_IN_ENGINE +ALTER TABLE t AUTO_INCREMENT=1, ALGORITHM=INPLACE; +--error ER_NO_SUCH_TABLE_IN_ENGINE +ALTER TABLE t AUTO_INCREMENT=1, ALGORITHM=COPY; + --error ER_PARSE_ERROR ALTER TABLE t ALGORITHM=INPLACE, DISCARD TABLESPACE; --error ER_PARSE_ERROR @@ -56,3 +63,12 @@ DROP TABLE t; --error ER_NO_SUCH_TABLE_IN_ENGINE SELECT * FROM `x..d`; DROP TABLE `x..d`; + +ALTER TABLE t1 DISCARD TABLESPACE; +--error ER_TABLESPACE_DISCARDED +ALTER TABLE t1 AUTO_INCREMENT=1, ALGORITHM=INPLACE; +--error ER_TABLESPACE_DISCARDED +ALTER TABLE t1 AUTO_INCREMENT=1, FORCE, ALGORITHM=INPLACE; +--error ER_TABLESPACE_DISCARDED +ALTER TABLE t1 AUTO_INCREMENT=1, ALGORITHM=COPY; +DROP TABLE t1; diff --git a/storage/innobase/handler/handler0alter.cc b/storage/innobase/handler/handler0alter.cc index 1004a574cd1..c483e2dea59 100644 --- a/storage/innobase/handler/handler0alter.cc +++ b/storage/innobase/handler/handler0alter.cc @@ -4518,8 +4518,9 @@ prepare_inplace_alter_table_dict( uint32_t key_id = FIL_DEFAULT_ENCRYPTION_KEY; fil_encryption_t mode = FIL_ENCRYPTION_DEFAULT; - if (fil_space_t* space - = fil_space_acquire(ctx->prebuilt->table->space)) { + if (dict_table_is_discarded(ctx->prebuilt->table)) { + } else if (fil_space_t* space + = fil_space_acquire(ctx->prebuilt->table->space)) { if (const fil_space_crypt_t* crypt_data = space->crypt_data) { key_id = crypt_data->key_id; @@ -4917,7 +4918,8 @@ new_clustered_failed: /* Initialize the AUTO_INCREMENT sequence to the rebuilt table from the old one. */ - if (!old_table->found_next_number_field) { + if (!old_table->found_next_number_field + || dict_table_is_discarded(user_table)) { } else if (ib_uint64_t autoinc = btr_read_autoinc(clust_index)) { btr_write_autoinc(new_clust_index, autoinc); @@ -7372,9 +7374,10 @@ innobase_rename_or_enlarge_columns_cache( @param ha_alter_info Data used during in-place alter @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 old_table MySQL table as it is before the ALTER operation +@return whether the operation failed (and my_error() was called) */ static MY_ATTRIBUTE((nonnull)) -void +bool commit_set_autoinc( Alter_inplace_info* ha_alter_info, ha_innobase_inplace_ctx*ctx, @@ -7402,6 +7405,13 @@ commit_set_autoinc( & Alter_inplace_info::CHANGE_CREATE_OPTION) && (ha_alter_info->create_info->used_fields & HA_CREATE_USED_AUTO)) { + + if (dict_table_is_discarded(ctx->old_table)) { + my_error(ER_TABLESPACE_DISCARDED, MYF(0), + old_table->s->table_name.str); + DBUG_RETURN(true); + } + /* An AUTO_INCREMENT value was supplied by the user. It must be persisted to the data file. */ const Field* ai = old_table->found_next_number_field; @@ -7481,7 +7491,7 @@ commit_set_autoinc( between prepare_inplace and commit_inplace. */ } - DBUG_VOID_RETURN; + DBUG_RETURN(false); } /** Add or drop foreign key constraints to the data dictionary tables, @@ -8604,9 +8614,11 @@ ha_innobase::commit_inplace_alter_table( DBUG_ASSERT(new_clustered == ctx->need_rebuild()); - commit_set_autoinc(ha_alter_info, ctx, altered_table, table); + fail = commit_set_autoinc(ha_alter_info, ctx, altered_table, + table); - if (ctx->need_rebuild()) { + if (fail) { + } else if (ctx->need_rebuild()) { ctx->tmp_name = dict_mem_create_temporary_tablename( ctx->heap, ctx->new_table->name.m_name, ctx->new_table->id);