Commit c407ee09 authored by Marko Mäkelä's avatar Marko Mäkelä

MDEV-16145 Crash in ALTER TABLE…AUTO_INCREMENT=1 after DISCARD TABLESPACE

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().)
parent 64f4576b
......@@ -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;
......@@ -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;
......@@ -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 @@ prepare_inplace_alter_table_dict(
/* 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);
......
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