1# Crash-safe InnoDB ALTER operations 2 3--source include/not_valgrind.inc 4--source include/not_asan.inc 5--source include/not_embedded.inc 6--source include/have_innodb.inc 7--source include/have_debug.inc 8--source include/not_crashrep.inc 9 10--disable_query_log 11call mtr.add_suppression('InnoDB: cannot find a free slot for an undo log'); 12call mtr.add_suppression('InnoDB: row_merge_rename_index_to_add failed with error 47'); 13call mtr.add_suppression('InnoDB: Flagged corruption of `c[23]`'); 14call mtr.add_suppression('InnoDB: Index `c[23]` .*is corrupted'); 15--enable_query_log 16 17--echo # 18--echo # Bug#20015132 ALTER TABLE FAILS TO CHECK IF TABLE IS CORRUPTED 19--echo # 20 21CREATE TABLE t1(c1 INT PRIMARY KEY, c2 CHAR(1), c3 INT UNSIGNED) ENGINE=InnoDB; 22SET DEBUG='+d,ib_create_table_fail_too_many_trx'; 23--error ER_TOO_MANY_CONCURRENT_TRXS 24ALTER TABLE t1 ADD INDEX (c2), ADD INDEX (c3); 25 26SET DEBUG='-d,ib_create_table_fail_too_many_trx'; 27ALTER TABLE t1 ADD INDEX (c2), ADD INDEX (c3); 28# Flag the secondary indexes corrupted. 29SET DEBUG='+d,dict_set_index_corrupted'; 30CHECK TABLE t1; 31 32# Ensure that the corruption is permanent. 33--source include/restart_mysqld.inc 34CHECK TABLE t1; 35ALTER TABLE t1 DROP INDEX c2; 36CHECK TABLE t1; 37# We refuse an ALTER TABLE that would modify the InnoDB data dictionary 38# while leaving some of the table corrupted. 39--error ER_CHECK_NO_SUCH_TABLE 40ALTER TABLE t1 ADD INDEX (c2,c3); 41# This will rebuild the table, uncorrupting all secondary indexes. 42ALTER TABLE t1 CHANGE c3 c3 INT NOT NULL; 43CHECK TABLE t1; 44ALTER TABLE t1 ADD INDEX (c2,c3); 45DROP TABLE t1; 46 47let $MYSQLD_DATADIR= `select @@datadir`; 48let datadir= `select @@datadir`; 49 50# These are from include/shutdown_mysqld.inc and allow to call start_mysqld.inc 51--let $_server_id= `SELECT @@server_id` 52--let $_expect_file_name= $MYSQLTEST_VARDIR/tmp/mysqld.$_server_id.expect 53 54--echo # 55--echo # Bug #14669848 CRASH DURING ALTER MAKES ORIGINAL TABLE INACCESSIBLE 56--echo # 57--echo # -- Scenario 1: 58--echo # Crash the server in ha_innobase::commit_inplace_alter_table() 59--echo # just after committing the dictionary changes. 60 61CREATE TABLE t1 (f1 INT NOT NULL, f2 INT NOT NULL) ENGINE=innodb; 62INSERT INTO t1 VALUES (1,2),(3,4); 63SET DEBUG='d,innodb_alter_commit_crash_after_commit'; 64 65let $orig_table_id = `SELECT table_id 66 FROM information_schema.innodb_sys_tables 67 WHERE name = 'test/t1'`; 68 69# Write file to make mysql-test-run.pl expect crash 70--exec echo "restart" > $MYSQLTEST_VARDIR/tmp/mysqld.1.expect 71 72--error 2013 73ALTER TABLE t1 ADD PRIMARY KEY (f2, f1); 74 75--echo # Restart mysqld after the crash and reconnect. 76--source include/start_mysqld.inc 77 78let $temp_table_name = `SELECT SUBSTR(name, 6) 79 FROM information_schema.innodb_sys_tables 80 WHERE table_id = $orig_table_id`; 81 82--echo # Manual *.frm recovery begin. 83 84--move_file $MYSQLD_DATADIR/test/t1.frm $MYSQLD_DATADIR/test/$temp_table_name.frm 85 86perl; 87my @frm_file = glob "$ENV{'datadir'}/test/#sql-*.frm"; 88my $t1_frm = "$ENV{'datadir'}/test/t1.frm"; 89rename($frm_file[0], $t1_frm); 90EOF 91 92--echo # Manual recovery end 93 94FLUSH TABLES; 95 96--echo # Drop the orphaned original table. 97--disable_query_log 98eval DROP TABLE `#mysql50#$temp_table_name`; 99--enable_query_log 100 101--echo # Files in datadir after manual recovery. 102--list_files $MYSQLD_DATADIR/test 103 104SHOW TABLES; 105SHOW CREATE TABLE t1; 106INSERT INTO t1 VALUES (5,6),(7,8); 107SELECT * FROM t1; 108DROP TABLE t1; 109 110CREATE TABLE t1 (f1 INT NOT NULL, f2 INT NOT NULL) ENGINE=InnoDB; 111ALTER TABLE t1 ADD PRIMARY KEY (f2, f1); 112DROP TABLE t1; 113 114--echo # -- Scenario 2: 115--echo # Crash the server in ha_innobase::commit_inplace_alter_table() 116--echo # just before committing the dictionary changes, but after 117--echo # writing the MLOG_FILE_RENAME records. As the mini-transaction 118--echo # is not committed, the renames will not be replayed. 119 120CREATE TABLE t2 (f1 int not null, f2 int not null) ENGINE=InnoDB; 121INSERT INTO t2 VALUES (1,2),(3,4); 122SET DEBUG='d,innodb_alter_commit_crash_before_commit'; 123 124let $orig_table_id = `SELECT table_id 125 FROM information_schema.innodb_sys_tables 126 WHERE name = 'test/t2'`; 127 128# Write file to make mysql-test-run.pl expect crash 129--exec echo "restart" > $MYSQLTEST_VARDIR/tmp/mysqld.1.expect 130 131--error 2013 132ALTER TABLE t2 ADD PRIMARY KEY (f2, f1); 133 134--echo # Startup the server after the crash 135--source include/start_mysqld.inc 136 137--echo # Read and remember the temporary table name 138let $temp_table_name = `SELECT SUBSTRING(name,6) 139 FROM information_schema.innodb_sys_tables 140 WHERE name LIKE "test/#sql-ib$orig_table_id%"`; 141# This second copy is an environment variable for the perl script below. 142let temp_table_name = $temp_table_name; 143 144--echo # Manual *.frm recovery begin. The dictionary was not updated 145--echo # and the files were not renamed. The rebuilt table 146--echo # was left behind on purpose, to faciliate data recovery. 147 148perl; 149my @frm_file = glob "$ENV{'datadir'}/test/#sql-*.frm"; 150my $target_frm = "$ENV{'datadir'}/test/$ENV{'temp_table_name'}.frm"; 151rename($frm_file[0], $target_frm); 152EOF 153 154--echo # Manual recovery end 155 156--echo # Drop the orphaned rebuilt table. 157--disable_query_log 158eval DROP TABLE `#mysql50#$temp_table_name`; 159--enable_query_log 160 161SHOW TABLES; 162INSERT INTO t2 VALUES (5,6),(7,8); 163SELECT * from t2; 164SHOW CREATE TABLE t2; 165DROP TABLE t2; 166 167CREATE TABLE t2 (f1 INT NOT NULL, f2 INT NOT NULL) ENGINE=InnoDB; 168ALTER TABLE t2 ADD PRIMARY KEY (f2, f1); 169DROP TABLE t2; 170--list_files $MYSQLD_DATADIR/test 171 172--echo # ------------------------- 173--echo # End of Testing Scenario 2 174--echo # ------------------------- 175 176--echo # 177--echo # Bug#19330255 WL#7142 - CRASH DURING ALTER TABLE LEADS TO 178--echo # DATA DICTIONARY INCONSISTENCY 179--echo # 180 181CREATE TABLE t1(a int PRIMARY KEY, b varchar(255), c int NOT NULL); 182INSERT INTO t1 SET a=1,c=2; 183SET DEBUG='d,innodb_alter_commit_crash_after_commit'; 184 185let $orig_table_id = `select table_id from 186 information_schema.innodb_sys_tables where name = 'test/t1'`; 187 188# Write file to make mysql-test-run.pl expect crash 189--exec echo "restart" > $MYSQLTEST_VARDIR/tmp/mysqld.1.expect 190 191--error 2013 192ALTER TABLE t1 ADD INDEX (b), CHANGE c d int; 193 194--echo # Restart mysqld after the crash and reconnect. 195--source include/start_mysqld.inc 196 197let $temp_table_name = `SELECT SUBSTR(name, 6) 198 FROM information_schema.innodb_sys_tables 199 WHERE table_id = $orig_table_id`; 200 201--echo # Manual *.frm recovery begin. 202--move_file $MYSQLD_DATADIR/test/t1.frm $MYSQLD_DATADIR/test/$temp_table_name.frm 203 204perl; 205my @frm_file = glob "$ENV{'datadir'}/test/#sql-*.frm"; 206my $t1_frm = "$ENV{'datadir'}/test/t1.frm"; 207rename($frm_file[0], $t1_frm); 208EOF 209 210--echo # Manual recovery end 211 212FLUSH TABLES; 213 214--echo # Drop the orphaned original table. 215--disable_query_log 216eval DROP TABLE `#mysql50#$temp_table_name`; 217--enable_query_log 218 219--echo # Files in datadir after manual recovery. 220--list_files $MYSQLD_DATADIR/test 221 222SHOW TABLES; 223SHOW CREATE TABLE t1; 224UPDATE t1 SET d=NULL; 225SELECT * FROM t1; 226DROP TABLE t1; 227