summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMarko Mäkelä <marko.makela@mariadb.com>2018-09-06 11:40:27 +0300
committerMarko Mäkelä <marko.makela@mariadb.com>2018-09-07 22:10:03 +0300
commit754727bb8a4a1850666ef024d5caad04512822db (patch)
tree701b077fe5d4166f9dd174172231e86f97075fd6
parentcf2a4426a2e864f35d002b1595fdffd4fe85ee4e (diff)
downloadmariadb-git-754727bb8a4a1850666ef024d5caad04512822db.tar.gz
MDEV-14378 In ALGORITHM=INPLACE, use a common name for the intermediate tables or partitions
This is a backport of commit 07e9ff1fe18999e1acd640ee3b2169c3f506fb35. 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.result16
-rw-r--r--mysql-test/suite/innodb/t/alter_crash.test16
-rw-r--r--mysql-test/suite/innodb/t/innodb-alter-tempfile.test47
-rw-r--r--storage/innobase/dict/dict0mem.cc8
-rw-r--r--storage/innobase/handler/ha_innodb.cc6
-rw-r--r--storage/innobase/handler/handler0alter.cc17
-rw-r--r--storage/innobase/include/dict0mem.h23
-rw-r--r--storage/innobase/row/row0mysql.cc3
8 files changed, 69 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 dcf52c93de1..bde8303ead5 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 0fdfaa449fd..7bb84b41a11 100644
--- a/storage/innobase/handler/ha_innodb.cc
+++ b/storage/innobase/handler/ha_innodb.cc
@@ -294,11 +294,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 8565cad96ec..412438237fa 100644
--- a/storage/innobase/handler/handler0alter.cc
+++ b/storage/innobase/handler/handler0alter.cc
@@ -4520,11 +4520,18 @@ prepare_inplace_alter_table_dict(
to rebuild the table with a temporary name. */
if (new_clustered) {
- 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 44d1a11ab33..fc120149c5f 100644
--- a/storage/innobase/include/dict0mem.h
+++ b/storage/innobase/include/dict0mem.h
@@ -568,6 +568,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 4677dfc296a..58d90cfb2ac 100644
--- a/storage/innobase/row/row0mysql.cc
+++ b/storage/innobase/row/row0mysql.cc
@@ -3629,8 +3629,7 @@ 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_")
- || strstr(table->name.m_name,
- "/" TEMP_FILE_PREFIX_INNODB));
+ || strstr(table->name.m_name, TEMP_TABLE_PATH_PREFIX));
}
/* Mark all indexes unavailable in the data dictionary cache