diff --git a/mysql-test/r/myisam_crash_before_flush_keys.result b/mysql-test/r/myisam_crash_before_flush_keys.result new file mode 100644 index 00000000000..372f2e41590 --- /dev/null +++ b/mysql-test/r/myisam_crash_before_flush_keys.result @@ -0,0 +1,45 @@ +# +# BUG#41330 - Myisam table open count set to zero before index blocks are written. +# +# Don't test this under valgrind, memory leaks will occur +# Binary must be compiled with debug for crash to occur +SET GLOBAL delay_key_write=ALL; +CREATE TABLE t1(a INT, +b INT, +PRIMARY KEY(a , b), +KEY(b)) ENGINE=MyISAM DELAY_KEY_WRITE = 1; +INSERT INTO t1 VALUES (1,2),(2,3),(3,4),(4,5),(5,6); +# Setup the mysqld to crash at certain point +SET SESSION debug="d,crash_before_flush_keys"; +# Write file to make mysql-test-run.pl expect crash +# Run the crashing query +FLUSH TABLE t1; +ERROR HY000: Lost connection to MySQL server during query +# Run MYISAMCHK tool to check the table t1 and repair +myisamchk: MyISAM file MYSQLD_DATADIR/test/t1 +myisamchk: warning: 1 client is using or hasn't closed the table properly +myisamchk: error: Size of indexfile is: 1024 Should be: 3072 +MYISAMCHK: Unknown error 126 +myisamchk: error: Can't read indexpage from filepos: 1024 +MyISAM-table 'MYSQLD_DATADIR/test/t1' is corrupted +Fix it using switch "-r" or "-o" +# Write file to make mysql-test-run.pl start the server +# Turn on reconnect +# Call script that will poll the server waiting for +# it to be back online again +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `a` int(11) NOT NULL DEFAULT '0', + `b` int(11) NOT NULL DEFAULT '0', + PRIMARY KEY (`a`,`b`), + KEY `b` (`b`) +) ENGINE=MyISAM DEFAULT CHARSET=latin1 DELAY_KEY_WRITE=1 +SELECT * FROM t1 FORCE INDEX (PRIMARY); +a b +1 2 +2 3 +3 4 +4 5 +5 6 +DROP TABLE t1; diff --git a/mysql-test/t/myisam_crash_before_flush_keys-master.opt b/mysql-test/t/myisam_crash_before_flush_keys-master.opt new file mode 100644 index 00000000000..425fda95086 --- /dev/null +++ b/mysql-test/t/myisam_crash_before_flush_keys-master.opt @@ -0,0 +1 @@ +--skip-stack-trace --skip-core-file diff --git a/mysql-test/t/myisam_crash_before_flush_keys.test b/mysql-test/t/myisam_crash_before_flush_keys.test new file mode 100644 index 00000000000..d6559f7760d --- /dev/null +++ b/mysql-test/t/myisam_crash_before_flush_keys.test @@ -0,0 +1,49 @@ +--echo # +--echo # BUG#41330 - Myisam table open count set to zero before index blocks are written. +--echo # +--source include/not_embedded.inc +--echo # Don't test this under valgrind, memory leaks will occur +--source include/not_valgrind.inc + +--echo # Binary must be compiled with debug for crash to occur +--source include/have_debug.inc + +let $MYSQLD_DATADIR= `select @@datadir`; +SET GLOBAL delay_key_write=ALL; +CREATE TABLE t1(a INT, + b INT, + PRIMARY KEY(a , b), + KEY(b)) ENGINE=MyISAM DELAY_KEY_WRITE = 1; +INSERT INTO t1 VALUES (1,2),(2,3),(3,4),(4,5),(5,6); + +--echo # Setup the mysqld to crash at certain point +SET SESSION debug="d,crash_before_flush_keys"; + +--echo # Write file to make mysql-test-run.pl expect crash +--exec echo "wait" > $MYSQLTEST_VARDIR/tmp/mysqld.1.expect + +--echo # Run the crashing query +--error 2013 +FLUSH TABLE t1; + +--echo # Run MYISAMCHK tool to check the table t1 and repair +--replace_result $MYISAMCHK MYISAMCHK $MYSQLD_DATADIR MYSQLD_DATADIR +--error 255 +--exec $MYISAMCHK -cs $MYSQLD_DATADIR/test/t1 2>&1 +--exec $MYISAMCHK -rs $MYSQLD_DATADIR/test/t1 + +--echo # Write file to make mysql-test-run.pl start the server +--exec echo "restart" > $MYSQLTEST_VARDIR/tmp/mysqld.1.expect + +--echo # Turn on reconnect +--enable_reconnect + +--echo # Call script that will poll the server waiting for +--echo # it to be back online again +--source include/wait_until_connected_again.inc + +SHOW CREATE TABLE t1; + +SELECT * FROM t1 FORCE INDEX (PRIMARY); + +DROP TABLE t1; diff --git a/storage/myisam/mi_close.c b/storage/myisam/mi_close.c index 07105aea88d..2066d092e1f 100644 --- a/storage/myisam/mi_close.c +++ b/storage/myisam/mi_close.c @@ -35,9 +35,6 @@ int mi_close(register MI_INFO *info) if (info->lock_type == F_EXTRA_LCK) info->lock_type=F_UNLCK; /* HA_EXTRA_NO_USER_CHANGE */ - if (share->reopen == 1 && share->kfile >= 0) - _mi_decrement_open_count(info); - if (info->lock_type != F_UNLCK) { if (mi_lock_database(info,F_UNLCK)) @@ -63,6 +60,8 @@ int mi_close(register MI_INFO *info) my_free(mi_get_rec_buff_ptr(info, info->rec_buff), MYF(MY_ALLOW_ZERO_PTR)); if (flag) { + DBUG_EXECUTE_IF("crash_before_flush_keys", + if (share->kfile >= 0) abort();); if (share->kfile >= 0 && flush_key_blocks(share->key_cache, share->kfile, share->temporary ? FLUSH_IGNORE_CHANGED : @@ -78,6 +77,8 @@ int mi_close(register MI_INFO *info) */ if (share->mode != O_RDONLY && mi_is_crashed(info)) mi_state_info_write(share->kfile, &share->state, 1); + /* Decrement open count must be last I/O on this file. */ + _mi_decrement_open_count(info); if (my_close(share->kfile,MYF(0))) error = my_errno; }