# test of check/repair of partitioned myisam tables --source include/have_partition.inc --disable_warnings --disable_query_log drop table if exists t1_will_crash; call mtr.add_suppression("Got an error from thread_id=.*ha_myisam.cc:"); call mtr.add_suppression("MariaDB thread id .*, query id .* localhost.*root Checking table"); call mtr.add_suppression("is marked as crashed and should be repaired"); --enable_query_log --enable_warnings --echo # REPAIR USE_FRM is not implemented for partitioned tables. let $MYSQLD_DATADIR= `select @@datadir`; --echo # Test what happens if we exchange a crashed partition with a table SHOW VARIABLES LIKE 'myisam_recover_options'; CREATE TABLE t (a INT, KEY (a)) ENGINE=MyISAM; CREATE TABLE tp (a INT, KEY (a)) ENGINE=MyISAM PARTITION BY RANGE (a) (PARTITION pCrashed VALUES LESS THAN (15), PARTITION pMAX VALUES LESS THAN MAXVALUE); INSERT INTO t VALUES (1), (2), (3), (4), (5), (6), (7), (8), (9), (10), (11); INSERT INTO tp VALUES (1), (2), (3), (4), (5), (6), (7), (8), (9), (10), (11); FLUSH TABLES; --echo # replacing tp#P#pCrashed.MYI with a corrupt + unclosed one created by doing: --echo # 'create table t1 (a int key(a))' head -c1024 t1.MYI > corrupt_t1.MYI --remove_file $MYSQLD_DATADIR/test/tp#P#pCrashed.MYI --copy_file std_data/corrupt_t1.MYI $MYSQLD_DATADIR/test/tp#P#pCrashed.MYI CHECK TABLE tp; --replace_regex /[^']*test[^']*t/.\/test\/t/ --error 145 ALTER TABLE tp EXCHANGE PARTITION pCrashed WITH TABLE t; REPAIR TABLE tp; CHECK TABLE tp; ALTER TABLE tp EXCHANGE PARTITION pCrashed WITH TABLE t; CHECK TABLE t; CHECK TABLE tp; FLUSH TABLES; --remove_file $MYSQLD_DATADIR/test/t.MYI --copy_file std_data/corrupt_t1.MYI $MYSQLD_DATADIR/test/t.MYI CHECK TABLE t; --replace_regex /[^']*test[^']*t/.\/test\/t/ --error 145 ALTER TABLE tp EXCHANGE PARTITION pCrashed WITH TABLE t; REPAIR TABLE t; CHECK TABLE t; ALTER TABLE tp EXCHANGE PARTITION pCrashed WITH TABLE t; CHECK TABLE tp; CHECK TABLE t; DROP TABLE t, tp; --echo # test of non partitioned myisam for reference CREATE TABLE t1_will_crash (a INT, KEY (a)) ENGINE=MyISAM; INSERT INTO t1_will_crash VALUES (1), (2), (3), (4), (5), (6), (7), (8), (9), (10), (11); FLUSH TABLES; --echo # replacing t1.MYI with a corrupt + unclosed one created by doing: --echo # 'create table t1 (a int key(a))' head -c1024 t1.MYI > corrupt_t1.MYI --remove_file $MYSQLD_DATADIR/test/t1_will_crash.MYI --copy_file std_data/corrupt_t1.MYI $MYSQLD_DATADIR/test/t1_will_crash.MYI CHECK TABLE t1_will_crash; REPAIR TABLE t1_will_crash; SELECT * FROM t1_will_crash; DROP TABLE t1_will_crash; --echo # test of check/repair of a damaged partition's MYI-file CREATE TABLE t1_will_crash (a INT, KEY (a)) ENGINE=MyISAM PARTITION BY HASH (a) PARTITIONS 3; INSERT INTO t1_will_crash VALUES (1), (2), (3), (4), (5), (6), (7), (8), (9), (10), (11); FLUSH TABLES; --echo # test with CHECK/REPAIR TABLE --echo # replacing t1#P#p1.MYI with a corrupt + unclosed one created by doing: --echo # 'create table t1 (a int key(a)) partition by hash (a) partitions 3' --echo # head -c1024 t1#P#p1.MYI > corrupt_t1#P#p1.MYI --remove_file $MYSQLD_DATADIR/test/t1_will_crash#P#p1.MYI --copy_file std_data/corrupt_t1#P#p1.MYI $MYSQLD_DATADIR/test/t1_will_crash#P#p1.MYI CHECK TABLE t1_will_crash; REPAIR TABLE t1_will_crash; SELECT * FROM t1_will_crash; FLUSH TABLES; --echo # test with ALTER TABLE ... CHECK/REPAIR PARTITION --echo # replacing t1_will_crash#P#p1.MYI with a corrupt + unclosed one --remove_file $MYSQLD_DATADIR/test/t1_will_crash#P#p1.MYI --copy_file std_data/corrupt_t1#P#p1.MYI $MYSQLD_DATADIR/test/t1_will_crash#P#p1.MYI ALTER TABLE t1_will_crash CHECK PARTITION p0, p2; ALTER TABLE t1_will_crash CHECK PARTITION p0, p1; ALTER TABLE t1_will_crash CHECK PARTITION p1, p2; ALTER TABLE t1_will_crash REPAIR PARTITION p0, p2; ALTER TABLE t1_will_crash REPAIR PARTITION p0, p1; SELECT * FROM t1_will_crash; DROP TABLE t1_will_crash; --echo # test of check/repair of a damaged subpartition's MYI-file CREATE TABLE t1_will_crash (a INT, KEY (a)) ENGINE=MyISAM PARTITION BY RANGE (a) SUBPARTITION BY HASH (a) SUBPARTITIONS 2 (PARTITION p0 VALUES LESS THAN (7), PARTITION p1 VALUES LESS THAN MAXVALUE); INSERT INTO t1_will_crash VALUES (1), (2), (3), (4), (5), (6), (7), (8), (9), (10), (11); SELECT * FROM t1_will_crash; FLUSH TABLES; --echo # test with CHECK/REPAIR TABLE --echo # replacing t1_will_crash#P#p1#SP#p1sp0.MYI with a corrupt + unclosed one --remove_file $MYSQLD_DATADIR/test/t1_will_crash#P#p1#SP#p1sp0.MYI --copy_file std_data/corrupt_t1#P#p1.MYI $MYSQLD_DATADIR/test/t1_will_crash#P#p1#SP#p1sp0.MYI CHECK TABLE t1_will_crash; REPAIR TABLE t1_will_crash; SELECT * FROM t1_will_crash; FLUSH TABLES; --echo # test with ALTER TABLE ... CHECK/REPAIR PARTITION --echo # replacing t1_will_crash#P#p1#SP#p1sp0.MYI with a corrupt + unclosed one --remove_file $MYSQLD_DATADIR/test/t1_will_crash#P#p1#SP#p1sp0.MYI --copy_file std_data/corrupt_t1#P#p1.MYI $MYSQLD_DATADIR/test/t1_will_crash#P#p1#SP#p1sp0.MYI ALTER TABLE t1_will_crash CHECK PARTITION p0; ALTER TABLE t1_will_crash CHECK PARTITION all; ALTER TABLE t1_will_crash CHECK PARTITION p1; ALTER TABLE t1_will_crash REPAIR PARTITION p0; ALTER TABLE t1_will_crash REPAIR PARTITION p0, p1; SELECT * FROM t1_will_crash; DROP TABLE t1_will_crash; --echo # test of check/repair of crashed partitions in variuos states CREATE TABLE t1_will_crash ( a VARCHAR(255), b INT, c LONGTEXT, PRIMARY KEY (a, b)) ENGINE=MyISAM PARTITION BY HASH (b) PARTITIONS 7; # creating a longer string for for filling the records let $i= 3; let $lt= longtext; while ($i) { let $lt= $lt$lt; dec $i; } # Tests (mapped to partition) # Partition # 0 - truncated datafile (size = 0 bytes) # 1 - head -c 1024 of datafile (simulates crashed write) # 2 - after _mi_mark_file_changed (only marked index as opened) # 3 - after write_record (updated datafile + not closed/updated index) # 4 - after flush_cached_blocks (updated index/datafiles, not closed index) # 5 - (Not used) after mi_state_info_write (fully updated/closed index file) # (this was verified to be a harmless crash, since everything was written) # 6 - partly updated datafile (insert 6 small records, delete 5,3,1, # insert one larger record (2.5 X small) and break in gdb before it has # been completely written (in write_dynamic_record) # (done with 3 different MYD files, since it also affects # the delete-linked-list) --disable_query_log eval INSERT INTO t1_will_crash VALUES ('abc', 1, '$lt'), ('def', 2, '$lt'), ('ghi', 3, '$lt'), ('jkl', 6, '$lt'), ('mno', 5, '$lt'), ('pqr', 4, '$lt'), ('tuw', 8, '$lt'), ('vxy', 9, '$lt'), ('z lost', 7, '$lt'), ('aaa', 10, '$lt'), ('bbb', 11, '$lt'), ('zzzzzZzzzzz', 97, '$lt'), ('a', 89, '$lt'), (' ', 83, '$lt'), ('ccc', 79, '$lt'), ('ddd', 73, '$lt'), ('eee', 71, '$lt'), ('fff', 67, '$lt'), ('ooo', 13, '$lt'), ('nnn', 17, '$lt'), ('mmm', 19, '$lt'), ('lll', 23, '$lt'), ('kkkkkkkkKkk', 29, '$lt'), (' lost', 0, '$lt'), ('1 broken when head -c1024 on datafile', 71, '$lt$lt$lt$lt$lt$lt$lt$lt$lt$lt$lt$lt$lt$lt$lt$lt'), ('3 crashed after write_record', 24, '$lt'); eval INSERT INTO t1_will_crash VALUES ('2 crashed after _mi_mark_changed', 30, '$lt'); # if crashed here, part p5 would need to be repaired before next statement # but since we use pre fabricated crashed files, we can skip that here. eval INSERT INTO t1_will_crash VALUES ('5 still here since crash in next row in multirow insert?', 40, '$lt'), ('4 crashed after flush_cached_blocks', 18, '$lt'); # There is no write after mi_state_info_write, so this is not tested. #eval INSERT INTO t1_will_crash VALUES # ('5 crashed after mi_state_info_write', 12, '$lt'); eval INSERT INTO t1_will_crash VALUES ('6 row 1', 27, '$lt'), ('6 row 2', 34, '$lt'), ('6 row 3', 41, '$lt'), ('6 row 4', 48, '$lt'), ('6 row 5', 55, '$lt'), ('6 row 6', 62, '$lt'); DELETE FROM t1_will_crash WHERE b in (27, 55); DELETE FROM t1_will_crash WHERE b = 41; eval INSERT INTO t1_will_crash VALUES ('6 row 7 (crash before completely written to datafile)', 27, '$lt$lt'); --enable_query_log SELECT COUNT(*) FROM t1_will_crash; SELECT (b % 7) AS `partition`, COUNT(*) AS rows_cnt FROM t1_will_crash GROUP BY (b % 7); SELECT (b % 7) AS `partition`, b, a, length(c) FROM t1_will_crash ORDER BY `partition`, b, a; FLUSH TABLES; # testing p0, p1, p3, p6(1) --echo # truncating p0 to simulate an empty datafile (not recovered!) --remove_file $MYSQLD_DATADIR/test/t1_will_crash#P#p0.MYD --write_file $MYSQLD_DATADIR/test/t1_will_crash#P#p0.MYD EOF --echo # replacing p1 with only the first 1024 bytes (not recovered!) --remove_file $MYSQLD_DATADIR/test/t1_will_crash#P#p1.MYD --copy_file std_data/parts/t1_will_crash#P#p1_first_1024.MYD $MYSQLD_DATADIR/test/t1_will_crash#P#p1.MYD --echo # replacing p3 with a crashed one at the last row in first insert --echo # (crashed right after *share->write_record()) --remove_file $MYSQLD_DATADIR/test/t1_will_crash#P#p3.MYI --copy_file std_data/parts/t1_will_crash#P#p3.MYI $MYSQLD_DATADIR/test/t1_will_crash#P#p3.MYI --echo # replacing p6 with a crashed MYD file (1) (splitted dynamic record) --remove_file $MYSQLD_DATADIR/test/t1_will_crash#P#p6.MYD --copy_file std_data/parts/t1_will_crash#P#p6.MYD $MYSQLD_DATADIR/test/t1_will_crash#P#p6.MYD ANALYZE TABLE t1_will_crash; OPTIMIZE TABLE t1_will_crash; CHECK TABLE t1_will_crash; REPAIR TABLE t1_will_crash; SELECT COUNT(*) FROM t1_will_crash; SELECT (b % 7) AS `partition`, COUNT(*) AS rows_cnt FROM t1_will_crash GROUP BY (b % 7); SELECT (b % 7) AS `partition`, b, a, length(c) FROM t1_will_crash ORDER BY `partition`, b, a; FLUSH TABLES; # testing p2, p4, p6(2, 3) --echo # --echo # replacing p2 with crashed files (after _mi_mark_changed) --remove_file $MYSQLD_DATADIR/test/t1_will_crash#P#p2.MYI --remove_file $MYSQLD_DATADIR/test/t1_will_crash#P#p2.MYD --copy_file std_data/parts/t1_will_crash#P#p2.MYI $MYSQLD_DATADIR/test/t1_will_crash#P#p2.MYI --copy_file std_data/parts/t1_will_crash#P#p2.MYD $MYSQLD_DATADIR/test/t1_will_crash#P#p2.MYD ALTER TABLE t1_will_crash CHECK PARTITION p2; --echo # crash was when index only marked as opened, no real corruption ALTER TABLE t1_will_crash CHECK PARTITION p2; FLUSH TABLES; --echo # --echo # replacing p4 with updated but not closed index file --remove_file $MYSQLD_DATADIR/test/t1_will_crash#P#p4.MYI --copy_file std_data/parts/t1_will_crash#P#p4.MYI $MYSQLD_DATADIR/test/t1_will_crash#P#p4.MYI #SHOW TABLE STATUS like 't1_will_crash'; #ALTER TABLE t1_will_crash ANALYZE PARTITION p4; #SHOW TABLE STATUS like 't1_will_crash'; ALTER TABLE t1_will_crash OPTIMIZE PARTITION p4; #SHOW TABLE STATUS like 't1_will_crash'; ALTER TABLE t1_will_crash CHECK PARTITION p4; #SHOW TABLE STATUS like 't1_will_crash'; ALTER TABLE t1_will_crash REPAIR PARTITION p4; #SHOW TABLE STATUS like 't1_will_crash'; FLUSH TABLES; --echo # --echo # replacing p6 with a crashed MYD file (2) (splitted dynamic record) --remove_file $MYSQLD_DATADIR/test/t1_will_crash#P#p6.MYD --copy_file std_data/parts/t1_will_crash#P#p6_2.MYD $MYSQLD_DATADIR/test/t1_will_crash#P#p6.MYD #ALTER TABLE t1_will_crash OPTIMIZE PARTITION p6; ALTER TABLE t1_will_crash CHECK PARTITION p6; ALTER TABLE t1_will_crash REPAIR PARTITION p6; SELECT (b % 7) AS `partition`, b, a, length(c) FROM t1_will_crash WHERE (b % 7) = 6 ORDER BY `partition`, b, a; FLUSH TABLES; --echo # --echo # replacing p6 with a crashed MYD file (3) (splitted dynamic record) --remove_file $MYSQLD_DATADIR/test/t1_will_crash#P#p6.MYD --copy_file std_data/parts/t1_will_crash#P#p6_3.MYD $MYSQLD_DATADIR/test/t1_will_crash#P#p6.MYD --echo # Different results from the corrupt table, which can lead to dropping --echo # of the not completely written rows when using REBUILD on a corrupt --echo # table, depending if one reads via index or direct on datafile. --echo # Since crash when reuse of deleted row space, CHECK MEDIUM or EXTENDED --echo # is required (MEDIUM is default) to verify correct behavior! SELECT (b % 7) AS `partition`, b, a, length(c) FROM t1_will_crash WHERE (b % 7) = 6 ORDER BY `partition`, b, a; SET @save_optimizer_switch= @@optimizer_switch; SET @@optimizer_switch='derived_merge=off'; SELECT (b % 7) AS `partition`, b, a FROM (SELECT b,a FROM t1_will_crash) q WHERE (b % 7) = 6 ORDER BY `partition`, b, a; SET @@optimizer_switch=@save_optimizer_switch; # NOTE: REBUILD PARTITION without CHECK before, 2 + (1) records will be lost! #ALTER TABLE t1_will_crash REBUILD PARTITION p6; ALTER TABLE t1_will_crash CHECK PARTITION p6; ALTER TABLE t1_will_crash REPAIR PARTITION p6; SELECT COUNT(*) FROM t1_will_crash; SELECT (b % 7) AS `partition`, COUNT(*) AS rows_cnt FROM t1_will_crash GROUP BY (b % 7); SELECT (b % 7) AS `partition`, b, a, length(c) FROM t1_will_crash ORDER BY `partition`, b, a; ALTER TABLE t1_will_crash CHECK PARTITION all EXTENDED; DROP TABLE t1_will_crash;