diff options
author | Marko Mäkelä <marko.makela@mariadb.com> | 2017-11-11 22:27:03 +0200 |
---|---|---|
committer | Marko Mäkelä <marko.makela@mariadb.com> | 2017-11-13 11:12:29 +0200 |
commit | e94c9d24f6e2942f473a59af271dbd5aff60e8b4 (patch) | |
tree | 9483258b3a7cdaff15b0adaaf3437ef1802b10d9 | |
parent | a48aa0cd569eda88bef98ed4abe41b0b570fcd51 (diff) | |
download | mariadb-git-e94c9d24f6e2942f473a59af271dbd5aff60e8b4.tar.gz |
MDEV-14378 In ALGORITHM=INPLACE, use a common name for the intermediate tables or partitions
Allow DROP TABLE `#mysql50##sql-...._.` to drop tables that were
being rebuilt by ALGORITHM=INPLACE
NOTE: If the server is killed after the table-rebuilding ALGORITHM=INPLACE
commits inside InnoDB but before the .frm file has been replaced, then
the recovery will involve something else than DROP TABLE.
NOTE: If the server is killed in a true inplace ALTER TABLE commits
inside InnoDB but before the .frm file has been replaced, then we
are really out of luck. To properly handle that situation, we would
need a transactional mysql.ddl_fixup table that directs recovery to
rename or remove files.
prepare_inplace_alter_table_dict(): Use the altered_table->s->table_name
for generating the new_table_name.
table_name_t::part_suffix: The start of the partition name suffix.
table_name_t::dbend(): Return the end of the schema name.
table_name_t::dblen(): Return the length of the schema name, in bytes.
table_name_t::basename(): Return the name without the schema name.
table_name_t::part(): Return the partition name, or NULL if none.
row_drop_table_for_mysql(): Assert for #sql, not #sql-ib.
-rw-r--r-- | mysql-test/suite/innodb/r/innodb-alter-tempfile.result | 16 | ||||
-rw-r--r-- | mysql-test/suite/innodb/t/alter_crash.test | 16 | ||||
-rw-r--r-- | mysql-test/suite/innodb/t/innodb-alter-tempfile.test | 47 | ||||
-rw-r--r-- | storage/innobase/dict/dict0mem.cc | 8 | ||||
-rw-r--r-- | storage/innobase/handler/ha_innodb.cc | 6 | ||||
-rw-r--r-- | storage/innobase/handler/handler0alter.cc | 17 | ||||
-rw-r--r-- | storage/innobase/include/dict0mem.h | 23 | ||||
-rw-r--r-- | storage/innobase/row/row0mysql.cc | 4 |
8 files changed, 70 insertions, 67 deletions
diff --git a/mysql-test/suite/innodb/r/innodb-alter-tempfile.result b/mysql-test/suite/innodb/r/innodb-alter-tempfile.result index ce13ad0978b..b164c3c26b0 100644 --- a/mysql-test/suite/innodb/r/innodb-alter-tempfile.result +++ b/mysql-test/suite/innodb/r/innodb-alter-tempfile.result @@ -4,17 +4,10 @@ # Temporary tablename will be unique. This makes sure that future # in-place ALTERs of the same table will not be blocked due to # temporary tablename. -# Crash the server in ha_innobase::commit_inplace_alter_table() CREATE TABLE t1 (f1 INT NOT NULL, f2 INT NOT NULL) ENGINE=innodb; -SET debug='d,innodb_alter_commit_crash_before_commit'; -Warnings: -Warning 1287 '@@debug' is deprecated and will be removed in a future release. Please use '@@debug_dbug' instead -# Write file to make mysql-test-run.pl expect crash -# Execute the statement that causes the crash +SET debug_dbug='+d,innodb_alter_commit_crash_before_commit'; ALTER TABLE t1 ADD PRIMARY KEY (f2, f1); ERROR HY000: Lost connection to MySQL server during query -# Startup the server after the crash -# Read and remember the temporary table name show create table t1; Table Create Table t1 CREATE TABLE `t1` ( @@ -23,13 +16,6 @@ t1 CREATE TABLE `t1` ( ) ENGINE=InnoDB DEFAULT CHARSET=latin1 # Consecutive Alter table does not create same temporary file name ALTER TABLE t1 ADD PRIMARY KEY (f2, f1); -# Shutdown the server to allow manual recovery -# Manual recovery begin. The dictionary was not updated -# and the files were not renamed. The rebuilt table -# was left behind on purpose, to faciliate data recovery. -# Manual recovery end -# Startup the server after manual recovery -# Drop the orphaned rebuilt table. show create table t1; Table Create Table t1 CREATE TABLE `t1` ( diff --git a/mysql-test/suite/innodb/t/alter_crash.test b/mysql-test/suite/innodb/t/alter_crash.test index 54cc51aecf4..b4fdfd2f2d5 100644 --- a/mysql-test/suite/innodb/t/alter_crash.test +++ b/mysql-test/suite/innodb/t/alter_crash.test @@ -138,24 +138,27 @@ ALTER TABLE t2 ADD PRIMARY KEY (f2, f1); let $temp_table_name = `SELECT SUBSTRING(name,6) FROM information_schema.innodb_sys_tables WHERE name LIKE "test/#sql-ib$orig_table_id%"`; -# This second copy is an environment variable for the perl script below. -let temp_table_name = $temp_table_name; --echo # Manual *.frm recovery begin. The dictionary was not updated --echo # and the files were not renamed. The rebuilt table --echo # was left behind on purpose, to faciliate data recovery. +let TABLENAME_INC= $MYSQLTEST_VARDIR/tmp/tablename.inc; perl; -my @frm_file = glob "$ENV{'datadir'}/test/#sql-*.frm"; -my $target_frm = "$ENV{'datadir'}/test/$ENV{'temp_table_name'}.frm"; -rename($frm_file[0], $target_frm); +die unless open OUT, ">$ENV{TABLENAME_INC}"; +chdir "$ENV{'datadir'}/test"; +my @frm_file = map { substr($_, 0, -4) } glob "#sql-*.frm"; +print OUT 'let $tablename=', $frm_file[0], ';'; +close OUT or die; EOF +source $TABLENAME_INC; +remove_file $TABLENAME_INC; --echo # Manual recovery end --echo # Drop the orphaned rebuilt table. --disable_query_log -eval DROP TABLE `#mysql50#$temp_table_name`; +eval DROP TABLE `#mysql50#$tablename`; --enable_query_log SHOW TABLES; @@ -186,7 +189,6 @@ SET DEBUG_DBUG='+d,innodb_alter_commit_crash_after_commit'; let $orig_table_id = `select table_id from information_schema.innodb_sys_tables where name = 'test/t1'`; -# FIXME: MDEV-9469 'Incorrect key file' on ALTER TABLE # Write file to make mysql-test-run.pl expect crash --exec echo "restart" > $MYSQLTEST_VARDIR/tmp/mysqld.1.expect # diff --git a/mysql-test/suite/innodb/t/innodb-alter-tempfile.test b/mysql-test/suite/innodb/t/innodb-alter-tempfile.test index d3f34b12ea6..f2038da8c4c 100644 --- a/mysql-test/suite/innodb/t/innodb-alter-tempfile.test +++ b/mysql-test/suite/innodb/t/innodb-alter-tempfile.test @@ -24,49 +24,30 @@ let datadir= `select @@datadir`; --let $_server_id= `SELECT @@server_id` --let $_expect_file_name=$MYSQLTEST_VARDIR/tmp/mysqld.$_server_id.expect ---echo # Crash the server in ha_innobase::commit_inplace_alter_table() CREATE TABLE t1 (f1 INT NOT NULL, f2 INT NOT NULL) ENGINE=innodb; -SET debug='d,innodb_alter_commit_crash_before_commit'; +SET debug_dbug='+d,innodb_alter_commit_crash_before_commit'; -let $orig_table_id = `SELECT table_id - FROM information_schema.innodb_sys_tables - WHERE name = 'test/t1'`; +--exec echo "wait" > $MYSQLTEST_VARDIR/tmp/mysqld.1.expect ---echo # Write file to make mysql-test-run.pl expect crash ---exec echo "restart" > $MYSQLTEST_VARDIR/tmp/mysqld.1.expect - ---echo # Execute the statement that causes the crash --error 2013 ALTER TABLE t1 ADD PRIMARY KEY (f2, f1); ---echo # Startup the server after the crash ---source include/start_mysqld.inc - ---echo # Read and remember the temporary table name -let $temp_table_name = `SELECT SUBSTRING(name,6) - FROM information_schema.innodb_sys_tables - WHERE name LIKE "test/#sql-ib$orig_table_id%"`; -# This second copy is an environment variable for the perl script below. -let temp_table_name = $temp_table_name; -show create table t1; ---echo # Consecutive Alter table does not create same temporary file name -ALTER TABLE t1 ADD PRIMARY KEY (f2, f1); ---echo # Shutdown the server to allow manual recovery ---source include/shutdown_mysqld.inc - ---echo # Manual recovery begin. The dictionary was not updated ---echo # and the files were not renamed. The rebuilt table ---echo # was left behind on purpose, to faciliate data recovery. +let TABLENAME_INC= $MYSQLTEST_VARDIR/tmp/tablename.inc; perl; -my @frm_file = glob "$ENV{'datadir'}/test/#sql-*.frm"; -my $target_frm = "$ENV{'datadir'}/test/$ENV{'temp_table_name'}.frm"; -rename($frm_file[0], $target_frm); +die unless open OUT, ">$ENV{TABLENAME_INC}"; +chdir "$ENV{'datadir'}/test"; +my @frm_file = map { substr($_, 0, -4) } glob "#sql-*.frm"; +print OUT 'let $temp_table_name=', $frm_file[0], ';'; +close OUT or die; EOF ---echo # Manual recovery end ---echo # Startup the server after manual recovery +source $TABLENAME_INC; +remove_file $TABLENAME_INC; + --source include/start_mysqld.inc ---echo # Drop the orphaned rebuilt table. +show create table t1; +--echo # Consecutive Alter table does not create same temporary file name +ALTER TABLE t1 ADD PRIMARY KEY (f2, f1); --disable_query_log eval DROP TABLE `#mysql50#$temp_table_name`; --enable_query_log diff --git a/storage/innobase/dict/dict0mem.cc b/storage/innobase/dict/dict0mem.cc index aaed29dc58c..e7a1d9c9b77 100644 --- a/storage/innobase/dict/dict0mem.cc +++ b/storage/innobase/dict/dict0mem.cc @@ -50,6 +50,14 @@ static const char* innobase_system_databases[] = { NullS }; +/** The start of the table basename suffix for partitioned tables */ +const char table_name_t::part_suffix[4] +#ifdef _WIN32 += "#p#"; +#else += "#P#"; +#endif + /** An interger randomly initialized at startup used to make a temporary table name as unuique as possible. */ static ib_uint32_t dict_temp_file_num; diff --git a/storage/innobase/handler/ha_innodb.cc b/storage/innobase/handler/ha_innodb.cc index 96aca74c217..ebbe32219c4 100644 --- a/storage/innobase/handler/ha_innodb.cc +++ b/storage/innobase/handler/ha_innodb.cc @@ -279,11 +279,7 @@ is_partition( { /* We look for pattern #P# to see if the table is partitioned MariaDB table. */ -#ifdef _WIN32 - return strstr(file_name, "#p#"); -#else - return strstr(file_name, "#P#"); -#endif /* _WIN32 */ + return strstr(file_name, table_name_t::part_suffix); } /** Signal to shut down InnoDB (NULL if shutdown was signaled, or if diff --git a/storage/innobase/handler/handler0alter.cc b/storage/innobase/handler/handler0alter.cc index 67174336a7c..5dff7e55c6c 100644 --- a/storage/innobase/handler/handler0alter.cc +++ b/storage/innobase/handler/handler0alter.cc @@ -4887,11 +4887,18 @@ new_clustered_failed: goto err_exit; } - const char* new_table_name - = dict_mem_create_temporary_tablename( - ctx->heap, - ctx->new_table->name.m_name, - ctx->new_table->id); + size_t dblen = ctx->old_table->name.dblen() + 1; + size_t tablen = altered_table->s->table_name.length; + const char* part = ctx->old_table->name.part(); + size_t partlen = part ? strlen(part) : 0; + char* new_table_name = static_cast<char*>( + mem_heap_alloc(ctx->heap, + dblen + tablen + partlen + 1)); + memcpy(new_table_name, ctx->old_table->name.m_name, dblen); + memcpy(new_table_name + dblen, + altered_table->s->table_name.str, tablen); + memcpy(new_table_name + dblen + tablen, + part ? part : "", partlen + 1); ulint n_cols = 0; ulint n_v_cols = 0; dtuple_t* add_cols; diff --git a/storage/innobase/include/dict0mem.h b/storage/innobase/include/dict0mem.h index ed39ed8a6c6..8dcd6bf2606 100644 --- a/storage/innobase/include/dict0mem.h +++ b/storage/innobase/include/dict0mem.h @@ -566,6 +566,29 @@ struct table_name_t { /** The name in internal representation */ char* m_name; + + /** @return the end of the schema name */ + const char* dbend() const + { + const char* sep = strchr(m_name, '/'); + ut_ad(sep); + return sep; + } + + /** @return the length of the schema name, in bytes */ + size_t dblen() const { return dbend() - m_name; } + + /** Determine the filename-safe encoded table name. + @return the filename-safe encoded table name */ + const char* basename() const { return dbend() + 1; } + + /** The start of the table basename suffix for partitioned tables */ + static const char part_suffix[4]; + + /** Determine the partition or subpartition name suffix. + @return the partition name + @retval NULL if the table is not partitioned */ + const char* part() const { return strstr(basename(), part_suffix); } }; /** Data structure for a column in a table */ diff --git a/storage/innobase/row/row0mysql.cc b/storage/innobase/row/row0mysql.cc index c729e5b95f4..f44c6586c4c 100644 --- a/storage/innobase/row/row0mysql.cc +++ b/storage/innobase/row/row0mysql.cc @@ -3850,8 +3850,8 @@ row_drop_table_for_mysql( TRX_DICT_OP_INDEX, we should be dropping auxiliary tables for full-text indexes or temp tables. */ ut_ad(strstr(table->name.m_name, "/FTS_") != NULL - || strstr(table->name.m_name, TEMP_FILE_PREFIX_INNODB) - != NULL); + || strstr(table->name.m_name, + TEMP_TABLE_PATH_PREFIX) != NULL); } /* Mark all indexes unavailable in the data dictionary cache |