Commit 1cdd6990 authored by Satya B's avatar Satya B

Fix for BUG#40827 - Killing insert-select to MyISAM can cause table corruption

                       
Killing the insert-select statement corrupts the MyISAM table only
when the destination table is empty and when it has indexes. When 
we bulk insert huge data and if the destination table is empty we 
disable the indexes for fast inserts, data is then inserted and 
indexes are re-enabled after bulk_insert operation
                        
Killing the query, aborts the repair table operation during enable
indexes phase leading to table corruption.
                      
We now truncate the table when we detect that enable indexes is
killed for bulk insert query.As we have an empty table before the 
operation, we can fix by truncating the table.
parents de2c4f0a f6c28cd9
...@@ -2252,4 +2252,43 @@ h+0 d + 0 e g + 0 ...@@ -2252,4 +2252,43 @@ h+0 d + 0 e g + 0
1 1 3 0 1 1 3 0
1 1 4 0 1 1 4 0
DROP TABLE t1; DROP TABLE t1;
#
# BUG#40827 - Killing insert-select to MyISAM can cause table corruption
#
CREATE TABLE `t1` (
`id` BIGINT(20) ,
`id1` BIGINT(20) AUTO_INCREMENT,
KEY(id1), KEY(id)
) ENGINE=MyISAM;
CREATE TABLE `t2` (
`id` BIGINT(20) ,
`id1` BIGINT(20) AUTO_INCREMENT,
KEY (id1), KEY(id)
) ENGINE=MyISAM;
INSERT INTO t2 (id) VALUES (123);
INSERT INTO t2 (id) SELECT id FROM t2;
INSERT INTO t2 (id) SELECT id FROM t2;
INSERT INTO t2 (id) SELECT id FROM t2;
INSERT INTO t2 (id) SELECT id FROM t2;
INSERT INTO t2 (id) SELECT id FROM t2;
INSERT INTO t2 (id) SELECT id FROM t2;
INSERT INTO t2 (id) SELECT id FROM t2;
INSERT INTO t2 (id) SELECT id FROM t2;
INSERT INTO t2 (id) SELECT id FROM t2;
INSERT INTO t2 (id) SELECT id FROM t2;
# Switch to insert Connection
SET SESSION debug='+d,wait_in_enable_indexes';
# Send insert data
INSERT INTO t1(id) SELECT id FROM t2;
# Switch to default Connection
# Wait for insert data to reach the debug point
SELECT ID FROM INFORMATION_SCHEMA.PROCESSLIST
WHERE STATE = 'wait_in_enable_indexes' AND
INFO = "INSERT INTO t1(id) SELECT id FROM t2"
INTO @thread_id;
KILL QUERY @thread_id;
CHECK TABLE t1;
Table Op Msg_type Msg_text
test.t1 check status OK
DROP TABLE t1,t2;
End of 5.1 tests End of 5.1 tests
...@@ -1503,5 +1503,57 @@ SELECT h+0, d + 0, e, g + 0 FROM t1; ...@@ -1503,5 +1503,57 @@ SELECT h+0, d + 0, e, g + 0 FROM t1;
DROP TABLE t1; DROP TABLE t1;
--echo #
--echo # BUG#40827 - Killing insert-select to MyISAM can cause table corruption
--echo #
CONNECT (insertConn, localhost, root,,);
CREATE TABLE `t1` (
`id` BIGINT(20) ,
`id1` BIGINT(20) AUTO_INCREMENT,
KEY(id1), KEY(id)
) ENGINE=MyISAM;
CREATE TABLE `t2` (
`id` BIGINT(20) ,
`id1` BIGINT(20) AUTO_INCREMENT,
KEY (id1), KEY(id)
) ENGINE=MyISAM;
INSERT INTO t2 (id) VALUES (123);
let $i = 10;
while ($i)
{
INSERT INTO t2 (id) SELECT id FROM t2;
dec $i;
}
--echo # Switch to insert Connection
CONNECTION insertConn;
SET SESSION debug='+d,wait_in_enable_indexes';
--echo # Send insert data
SEND INSERT INTO t1(id) SELECT id FROM t2;
--echo # Switch to default Connection
CONNECTION default;
--echo # Wait for insert data to reach the debug point
let $wait_condition=
SELECT COUNT(*) = 1 FROM INFORMATION_SCHEMA.PROCESSLIST
WHERE STATE = "wait_in_enable_indexes" AND
INFO = "INSERT INTO t1(id) SELECT id FROM t2";
--source include/wait_condition.inc
SELECT ID FROM INFORMATION_SCHEMA.PROCESSLIST
WHERE STATE = 'wait_in_enable_indexes' AND
INFO = "INSERT INTO t1(id) SELECT id FROM t2"
INTO @thread_id;
KILL QUERY @thread_id;
CHECK TABLE t1;
DROP TABLE t1,t2;
DISCONNECT insertConn;
--echo End of 5.1 tests --echo End of 5.1 tests
...@@ -43,6 +43,28 @@ TYPELIB myisam_stats_method_typelib= { ...@@ -43,6 +43,28 @@ TYPELIB myisam_stats_method_typelib= {
array_elements(myisam_stats_method_names) - 1, "", array_elements(myisam_stats_method_names) - 1, "",
myisam_stats_method_names, NULL}; myisam_stats_method_names, NULL};
#ifndef DBUG_OFF
/**
Causes the thread to wait in a spin lock for a query kill signal.
This function is used by the test frame work to identify race conditions.
The signal is caught and ignored and the thread is not killed.
*/
static void debug_wait_for_kill(const char *info)
{
DBUG_ENTER("debug_wait_for_kill");
const char *prev_info;
THD *thd;
thd= current_thd;
prev_info= thd_proc_info(thd, info);
while(!thd->killed)
my_sleep(1000);
DBUG_PRINT("info", ("Exit debug_wait_for_kill"));
thd_proc_info(thd, prev_info);
DBUG_VOID_RETURN;
}
#endif
/***************************************************************************** /*****************************************************************************
** MyISAM tables ** MyISAM tables
...@@ -1395,6 +1417,9 @@ int ha_myisam::enable_indexes(uint mode) ...@@ -1395,6 +1417,9 @@ int ha_myisam::enable_indexes(uint mode)
{ {
int error; int error;
DBUG_EXECUTE_IF("wait_in_enable_indexes",
debug_wait_for_kill("wait_in_enable_indexes"); );
if (mi_is_all_keys_active(file->s->state.key_map, file->s->base.keys)) if (mi_is_all_keys_active(file->s->state.key_map, file->s->base.keys))
{ {
/* All indexes are enabled already. */ /* All indexes are enabled already. */
...@@ -1508,8 +1533,9 @@ void ha_myisam::start_bulk_insert(ha_rows rows) ...@@ -1508,8 +1533,9 @@ void ha_myisam::start_bulk_insert(ha_rows rows)
/* /*
Only disable old index if the table was empty and we are inserting Only disable old index if the table was empty and we are inserting
a lot of rows. a lot of rows.
We should not do this for only a few rows as this is slower and Note that in end_bulk_insert() we may truncate the table if
we don't want to update the key statistics based of only a few rows. enable_indexes() failed, thus it's essential that indexes are
disabled ONLY for an empty table.
*/ */
if (file->state->records == 0 && can_enable_indexes && if (file->state->records == 0 && can_enable_indexes &&
(!rows || rows >= MI_MIN_ROWS_TO_DISABLE_INDEXES)) (!rows || rows >= MI_MIN_ROWS_TO_DISABLE_INDEXES))
...@@ -1541,8 +1567,27 @@ int ha_myisam::end_bulk_insert() ...@@ -1541,8 +1567,27 @@ int ha_myisam::end_bulk_insert()
{ {
mi_end_bulk_insert(file); mi_end_bulk_insert(file);
int err=mi_extra(file, HA_EXTRA_NO_CACHE, 0); int err=mi_extra(file, HA_EXTRA_NO_CACHE, 0);
return err ? err : can_enable_indexes ? if (!err)
enable_indexes(HA_KEY_SWITCH_NONUNIQ_SAVE) : 0; {
if (can_enable_indexes)
{
/*
Truncate the table when enable index operation is killed.
After truncating the table we don't need to enable the
indexes, because the last repair operation is aborted after
setting the indexes as active and trying to recreate them.
*/
if (((err= enable_indexes(HA_KEY_SWITCH_NONUNIQ_SAVE)) != 0) &&
current_thd->killed)
{
delete_all_rows();
/* not crashed, despite being killed during repair */
file->s->state.changed&= ~(STATE_CRASHED|STATE_CRASHED_ON_REPAIR);
}
}
}
return err;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment