mirror of
https://github.com/MariaDB/server.git
synced 2025-08-18 17:42:20 +03:00
The InnoDB background DROP TABLE queue is something that we should really remove, but are unable to until we remove dict_operation_lock so that DDL and DML operations can be combined in a single transaction. Because the queue is not persistent, it is not crash-safe. We should in some way ensure that the deferred-dropped tables will be dropped after server restart. The existence of two separate transactions complicates the error handling of CREATE TABLE...SELECT. We should really not break locks in DROP TABLE. Our solution to these problems is to rename the table to a temporary name, and to drop such-named tables on InnoDB startup. Also, the queue will use table IDs instead of names from now on. check-testcase.test: Ignore #sql-ib*.ibd files, because tables may enter the background DROP TABLE queue shortly before the test finishes. innodb.drop_table_background: Test CREATE...SELECT and the creation of tables whose file name starts with #sql-ib. innodb.alter_crash: Adjust the recovery, now that the #sql-ib tables will be dropped on InnoDB startup. row_mysql_drop_garbage_tables(): New function, to drop all #sql-ib tables on InnoDB startup. row_drop_table_for_mysql_in_background(): Remove an unnecessary and misplaced call to log_buffer_flush_to_disk(). (The call should have been after the transaction commit. We do not care about flushing the redo log here, because the table would be dropped again at server startup.) Remove the entry from the list after the table no longer exists. If server shutdown has been initiated, empty the list without actually dropping any tables. They will be dropped again on startup. row_drop_table_for_mysql(): Do not call lock_remove_all_on_table(). Instead, if locks exist, defer the DROP TABLE until they do not exist. If the table name does not start with #sql-ib, rename it to that prefix before adding it to the background DROP TABLE queue.
212 lines
6.3 KiB
Plaintext
212 lines
6.3 KiB
Plaintext
# Crash-safe InnoDB ALTER operations
|
|
|
|
--source include/not_valgrind.inc
|
|
--source include/not_embedded.inc
|
|
--source include/have_innodb.inc
|
|
--source include/have_debug.inc
|
|
--source include/not_crashrep.inc
|
|
|
|
--disable_query_log
|
|
call mtr.add_suppression('InnoDB: cannot find a free slot for an undo log');
|
|
call mtr.add_suppression('InnoDB: row_merge_rename_index_to_add failed with error 47');
|
|
call mtr.add_suppression('InnoDB: Flagged corruption of `c[23]`');
|
|
call mtr.add_suppression('InnoDB: Index `c[23]` .*is corrupted');
|
|
--enable_query_log
|
|
|
|
--echo #
|
|
--echo # Bug#20015132 ALTER TABLE FAILS TO CHECK IF TABLE IS CORRUPTED
|
|
--echo #
|
|
|
|
CREATE TABLE t1(c1 INT PRIMARY KEY, c2 CHAR(1), c3 INT UNSIGNED) ENGINE=InnoDB;
|
|
SET @saved_debug_dbug = @@SESSION.debug_dbug;
|
|
SET DEBUG_DBUG='+d,ib_create_table_fail_too_many_trx';
|
|
--error ER_TOO_MANY_CONCURRENT_TRXS
|
|
ALTER TABLE t1 ADD INDEX (c2), ADD INDEX (c3);
|
|
|
|
SET DEBUG_DBUG=@saved_debug_dbug;
|
|
ALTER TABLE t1 ADD INDEX (c2), ADD INDEX (c3);
|
|
# Flag the secondary indexes corrupted.
|
|
SET DEBUG_DBUG='+d,dict_set_index_corrupted';
|
|
CHECK TABLE t1;
|
|
|
|
# Ensure that the corruption is permanent.
|
|
--source include/restart_mysqld.inc
|
|
CHECK TABLE t1;
|
|
ALTER TABLE t1 DROP INDEX c2;
|
|
CHECK TABLE t1;
|
|
# We refuse an ALTER TABLE that would modify the InnoDB data dictionary
|
|
# while leaving some of the table corrupted.
|
|
--error ER_INDEX_CORRUPT
|
|
ALTER TABLE t1 ADD INDEX (c2,c3);
|
|
# This will rebuild the table, uncorrupting all secondary indexes.
|
|
ALTER TABLE t1 CHANGE c3 c3 INT NOT NULL;
|
|
CHECK TABLE t1;
|
|
ALTER TABLE t1 ADD INDEX (c2,c3);
|
|
DROP TABLE t1;
|
|
|
|
let $MYSQLD_DATADIR= `select @@datadir`;
|
|
let datadir= `select @@datadir`;
|
|
|
|
# These are from include/shutdown_mysqld.inc and allow to call start_mysqld.inc
|
|
--let $_server_id= `SELECT @@server_id`
|
|
--let $_expect_file_name= $MYSQLTEST_VARDIR/tmp/mysqld.$_server_id.expect
|
|
|
|
--echo #
|
|
--echo # Bug #14669848 CRASH DURING ALTER MAKES ORIGINAL TABLE INACCESSIBLE
|
|
--echo #
|
|
--echo # -- Scenario 1:
|
|
--echo # Crash the server in ha_innobase::commit_inplace_alter_table()
|
|
--echo # just after committing the dictionary changes.
|
|
|
|
CREATE TABLE t1 (f1 INT NOT NULL, f2 INT NOT NULL) ENGINE=innodb;
|
|
INSERT INTO t1 VALUES (1,2),(3,4);
|
|
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'`;
|
|
|
|
# Write file to make mysql-test-run.pl expect crash
|
|
--exec echo "restart" > $MYSQLTEST_VARDIR/tmp/mysqld.1.expect
|
|
|
|
--error 2013
|
|
ALTER TABLE t1 ADD PRIMARY KEY (f2, f1);
|
|
|
|
--echo # Restart mysqld after the crash and reconnect.
|
|
--source include/start_mysqld.inc
|
|
|
|
let TABLENAME_INC= $MYSQLTEST_VARDIR/tmp/tablename.inc;
|
|
perl;
|
|
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;
|
|
|
|
--replace_result $orig_table_id ID
|
|
eval SELECT * FROM information_schema.innodb_sys_tables
|
|
WHERE table_id = $orig_table_id;
|
|
|
|
move_file $datadir/test/$tablename.frm $datadir/test/t1.frm;
|
|
|
|
--echo # Files in datadir after manual recovery.
|
|
--list_files $MYSQLD_DATADIR/test
|
|
|
|
SHOW TABLES;
|
|
SHOW CREATE TABLE t1;
|
|
INSERT INTO t1 VALUES (5,6),(7,8);
|
|
SELECT * FROM t1;
|
|
DROP TABLE t1;
|
|
|
|
CREATE TABLE t1 (f1 INT NOT NULL, f2 INT NOT NULL) ENGINE=InnoDB;
|
|
ALTER TABLE t1 ADD PRIMARY KEY (f2, f1);
|
|
DROP TABLE t1;
|
|
|
|
--echo # -- Scenario 2:
|
|
--echo # Crash the server in ha_innobase::commit_inplace_alter_table()
|
|
--echo # just before committing the dictionary changes, but after
|
|
--echo # writing the MLOG_FILE_RENAME records. As the mini-transaction
|
|
--echo # is not committed, the renames will not be replayed.
|
|
|
|
CREATE TABLE t2 (f1 int not null, f2 int not null) ENGINE=InnoDB;
|
|
INSERT INTO t2 VALUES (1,2),(3,4);
|
|
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/t2'`;
|
|
|
|
# Write file to make mysql-test-run.pl expect crash
|
|
--exec echo "restart" > $MYSQLTEST_VARDIR/tmp/mysqld.1.expect
|
|
|
|
--error 2013
|
|
ALTER TABLE t2 ADD PRIMARY KEY (f2, f1);
|
|
|
|
--echo # Startup the server after the crash
|
|
--source include/start_mysqld.inc
|
|
|
|
SELECT * FROM information_schema.innodb_sys_tables
|
|
WHERE name LIKE 'test/#sql-ib%';
|
|
|
|
perl;
|
|
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 # Drop the orphaned rebuilt table.
|
|
--disable_query_log
|
|
eval DROP TABLE `#mysql50#$tablename`;
|
|
--enable_query_log
|
|
|
|
SHOW TABLES;
|
|
INSERT INTO t2 VALUES (5,6),(7,8);
|
|
SELECT * from t2;
|
|
SHOW CREATE TABLE t2;
|
|
DROP TABLE t2;
|
|
|
|
CREATE TABLE t2 (f1 INT NOT NULL, f2 INT NOT NULL) ENGINE=InnoDB;
|
|
ALTER TABLE t2 ADD PRIMARY KEY (f2, f1);
|
|
DROP TABLE t2;
|
|
--list_files $MYSQLD_DATADIR/test
|
|
|
|
--echo # -------------------------
|
|
--echo # End of Testing Scenario 2
|
|
--echo # -------------------------
|
|
|
|
--echo #
|
|
--echo # Bug#19330255 WL#7142 - CRASH DURING ALTER TABLE LEADS TO
|
|
--echo # DATA DICTIONARY INCONSISTENCY
|
|
--echo #
|
|
|
|
CREATE TABLE t1(a int PRIMARY KEY, b varchar(255), c int NOT NULL)
|
|
ENGINE=InnoDB;
|
|
INSERT INTO t1 SET a=1,c=2;
|
|
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'`;
|
|
|
|
# Write file to make mysql-test-run.pl expect crash
|
|
--exec echo "restart" > $MYSQLTEST_VARDIR/tmp/mysqld.1.expect
|
|
#
|
|
--error 2013
|
|
ALTER TABLE t1 ADD INDEX (b), CHANGE c d int, ALGORITHM=INPLACE;
|
|
|
|
--echo # Restart mysqld after the crash and reconnect.
|
|
--source include/start_mysqld.inc
|
|
|
|
--replace_result $orig_table_id ID
|
|
eval SELECT * FROM information_schema.innodb_sys_tables
|
|
WHERE table_id = $orig_table_id;
|
|
|
|
perl;
|
|
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;
|
|
move_file $datadir/test/$tablename.frm $datadir/test/t1.frm;
|
|
|
|
FLUSH TABLES;
|
|
|
|
--echo # Files in datadir after manual recovery.
|
|
--list_files $MYSQLD_DATADIR/test
|
|
|
|
SHOW TABLES;
|
|
SHOW CREATE TABLE t1;
|
|
UPDATE t1 SET d=NULL;
|
|
SELECT * FROM t1;
|
|
DROP TABLE t1;
|