mariadb/mysql-test/t/merge-big.test

151 lines
5.1 KiB
Text
Raw Normal View History

Bug#26379 - Combination of FLUSH TABLE and REPAIR TABLE corrupts a MERGE table Bug 26867 - LOCK TABLES + REPAIR + merge table result in memory/cpu hogging Bug 26377 - Deadlock with MERGE and FLUSH TABLE Bug 25038 - Waiting TRUNCATE Bug 25700 - merge base tables get corrupted by optimize/analyze/repair table Bug 30275 - Merge tables: flush tables or unlock tables causes server to crash Bug 19627 - temporary merge table locking Bug 27660 - Falcon: merge table possible Bug 30273 - merge tables: Can't lock file (errno: 155) The problems were: Bug 26379 - Combination of FLUSH TABLE and REPAIR TABLE corrupts a MERGE table 1. A thread trying to lock a MERGE table performs busy waiting while REPAIR TABLE or a similar table administration task is ongoing on one or more of its MyISAM tables. 2. A thread trying to lock a MERGE table performs busy waiting until all threads that did REPAIR TABLE or similar table administration tasks on one or more of its MyISAM tables in LOCK TABLES segments do UNLOCK TABLES. The difference against problem #1 is that the busy waiting takes place *after* the administration task. It is terminated by UNLOCK TABLES only. 3. Two FLUSH TABLES within a LOCK TABLES segment can invalidate the lock. This does *not* require a MERGE table. The first FLUSH TABLES can be replaced by any statement that requires other threads to reopen the table. In 5.0 and 5.1 a single FLUSH TABLES can provoke the problem. Bug 26867 - LOCK TABLES + REPAIR + merge table result in memory/cpu hogging Trying DML on a MERGE table, which has a child locked and repaired by another thread, made an infinite loop in the server. Bug 26377 - Deadlock with MERGE and FLUSH TABLE Locking a MERGE table and its children in parent-child order and flushing the child deadlocked the server. Bug 25038 - Waiting TRUNCATE Truncating a MERGE child, while the MERGE table was in use, let the truncate fail instead of waiting for the table to become free. Bug 25700 - merge base tables get corrupted by optimize/analyze/repair table Repairing a child of an open MERGE table corrupted the child. It was necessary to FLUSH the child first. Bug 30275 - Merge tables: flush tables or unlock tables causes server to crash Flushing and optimizing locked MERGE children crashed the server. Bug 19627 - temporary merge table locking Use of a temporary MERGE table with non-temporary children could corrupt the children. Temporary tables are never locked. So we do now prohibit non-temporary chidlren of a temporary MERGE table. Bug 27660 - Falcon: merge table possible It was possible to create a MERGE table with non-MyISAM children. Bug 30273 - merge tables: Can't lock file (errno: 155) This was a Windows-only bug. Table administration statements sometimes failed with "Can't lock file (errno: 155)". These bugs are fixed by a new implementation of MERGE table open. When opening a MERGE table in open_tables() we do now add the child tables to the list of tables to be opened by open_tables() (the "query_list"). The children are not opened in the handler at this stage. After opening the parent, open_tables() opens each child from the now extended query_list. When the last child is opened, we remove the children from the query_list again and attach the children to the parent. This behaves similar to the old open. However it does not open the MyISAM tables directly, but grabs them from the already open children. When closing a MERGE table in close_thread_table() we detach the children only. Closing of the children is done implicitly because they are in thd->open_tables. For more detail see the comment at the top of ha_myisammrg.cc. Changed from open_ltable() to open_and_lock_tables() in all places that can be relevant for MERGE tables. The latter can handle tables added to the list on the fly. When open_ltable() was used in a loop over a list of tables, the list must be temporarily terminated after every table for open_and_lock_tables(). table_list->required_type is set to FRMTYPE_TABLE to avoid open of special tables. Handling of derived tables is suppressed. These details are handled by the new function open_n_lock_single_table(), which has nearly the same signature as open_ltable() and can replace it in most cases. In reopen_tables() some of the tables open by a thread can be closed and reopened. When a MERGE child is affected, the parent must be closed and reopened too. Closing of the parent is forced before the first child is closed. Reopen happens in the order of thd->open_tables. MERGE parents do not attach their children automatically at open. This is done after all tables are reopened. So all children are open when attaching them. Special lock handling like mysql_lock_abort() or mysql_lock_remove() needs to be suppressed for MERGE children or forwarded to the parent. This depends on the situation. In loops over all open tables one suppresses child lock handling. When a single table is touched, forwarding is done. Behavioral changes: =================== This patch changes the behavior of temporary MERGE tables. Temporary MERGE must have temporary children. The old behavior was wrong. A temporary table is not locked. Hence even non-temporary children were not locked. See Bug 19627 - temporary merge table locking. You cannot change the union list of a non-temporary MERGE table when LOCK TABLES is in effect. The following does *not* work: CREATE TABLE m1 ... ENGINE=MRG_MYISAM ...; LOCK TABLES t1 WRITE, t2 WRITE, m1 WRITE; ALTER TABLE m1 ... UNION=(t1,t2) ...; However, you can do this with a temporary MERGE table. You cannot create a MERGE table with CREATE ... SELECT, neither as a temporary MERGE table, nor as a non-temporary MERGE table. CREATE TABLE m1 ... ENGINE=MRG_MYISAM ... SELECT ...; Gives error message: table is not BASE TABLE.
2007-11-15 20:25:43 +01:00
#
# Test of MERGE tables with multisession and many waits.
#
# This test takes rather long time so let us run it only in --big-test mode
--source include/big_test.inc
# We are using some debug-only features in this test
--source include/have_debug.inc
--disable_warnings
drop table if exists t1,t2,t3,t4,t5,t6;
--enable_warnings
--echo #
--echo # Bug#26379 - Combination of FLUSH TABLE and REPAIR TABLE
--echo # corrupts a MERGE table
--echo # Problem #3
--echo #
# Two FLUSH TABLES within a LOCK TABLES segment could invalidate the lock.
# This did *not* require a MERGE table.
#
# To increase reproducibility it was necessary to enter a sleep of 2
# seconds at the end of wait_for_tables() after unlock of LOCK_open. In
# 5.0 and 5.1 the sleep must be inserted in open_and_lock_tables() after
# open_tables() instead. wait_for_tables() is not used in this case. The
# problem was that FLUSH TABLES releases LOCK_open while having unlocked
# and closed all tables. When this happened while a thread was in the
# loop in mysql_lock_tables() right after wait_for_tables()
# (open_tables()) and before retrying to lock, the thread got the lock.
# And it did not notice that the table needed a refresh after the
# [re-]open. So it executed its statement on the table.
#
# The first FLUSH TABLES kicked the INSERT out of thr_multi_lock() and
# let it wait in wait_for_tables() (open_table()). The second FLUSH
# TABLES must happen while the INSERT was on its way from
# wait_for_tables() (open_table()) to the next call of thr_multi_lock().
# This needed to be supported by a sleep to make it repeatable.
#
CREATE TABLE t1 (c1 INT) ENGINE= MyISAM;
LOCK TABLE t1 WRITE;
#SELECT NOW();
--echo # connection con1
connect (con1,localhost,root,,);
let $con1_id= `SELECT CONNECTION_ID()`;
SET SESSION debug="+d,sleep_open_and_lock_after_open";
send INSERT INTO t1 VALUES (1);
--echo # connection default
connection default;
--echo # Let INSERT go into thr_multi_lock().
let $wait_condition= SELECT 1 FROM INFORMATION_SCHEMA.PROCESSLIST
WHERE ID = $con1_id AND STATE = 'Locked';
--source include/wait_condition.inc
#SELECT NOW();
--echo # Kick INSERT out of thr_multi_lock().
FLUSH TABLES;
#SELECT NOW();
--echo # Let INSERT go through open_tables() where it sleeps.
let $wait_condition= SELECT 1 FROM INFORMATION_SCHEMA.PROCESSLIST
WHERE ID = $con1_id AND STATE = 'DBUG sleep';
--source include/wait_condition.inc
#SELECT NOW();
--echo # Unlock and close table and wait for con1 to close too.
FLUSH TABLES;
#SELECT NOW();
--echo # This should give no result.
SELECT * FROM t1;
#SELECT NOW();
UNLOCK TABLES;
--echo # connection con1
connection con1;
reap;
SET SESSION debug="-d,sleep_open_and_lock_after_open";
disconnect con1;
--echo # connection default
connection default;
DROP TABLE t1;
--echo #
--echo # Extra tests for Bug#26379 - Combination of FLUSH TABLE and
--echo # REPAIR TABLE corrupts a MERGE table
--echo #
CREATE TABLE t1 (c1 INT);
CREATE TABLE t2 (c1 INT);
CREATE TABLE t3 (c1 INT);
INSERT INTO t1 VALUES (1);
INSERT INTO t2 VALUES (2);
INSERT INTO t3 VALUES (3);
--echo #
--echo # CREATE ... SELECT
--echo # try to access parent from another thread.
--echo #
#SELECT NOW();
--echo # connection con1
connect (con1,localhost,root,,);
let $con1_id= `SELECT CONNECTION_ID()`;
SET SESSION debug="+d,sleep_create_select_before_lock";
send CREATE TABLE t4 (c1 INT) ENGINE=MRG_MYISAM UNION=(t1,t2)
INSERT_METHOD=FIRST SELECT * FROM t3;
--echo # connection default
connection default;
# wait for the other query to start executing
let $wait_condition= SELECT 1 FROM INFORMATION_SCHEMA.PROCESSLIST
WHERE ID = $con1_id AND STATE = 'DBUG sleep';
--source include/wait_condition.inc
#SELECT NOW();
--echo # Now try to access the parent.
--echo # If 3 is in table, SELECT had to wait.
SELECT * FROM t4 ORDER BY c1;
#SELECT NOW();
--echo # connection con1
connection con1;
reap;
#SELECT NOW();
SET SESSION debug="-d,sleep_create_select_before_lock";
disconnect con1;
--echo # connection default
connection default;
--echo # Cleanup for next test.
DROP TABLE t4;
DELETE FROM t1 WHERE c1 != 1;
--echo #
--echo # CREATE ... SELECT
--echo # try to access child from another thread.
--echo #
#SELECT NOW();
--echo # connection con1
connect (con1,localhost,root,,);
let $con1_id= `SELECT CONNECTION_ID()`;
SET SESSION debug="+d,sleep_create_select_before_lock";
send CREATE TABLE t4 (c1 INT) ENGINE=MRG_MYISAM UNION=(t1,t2)
INSERT_METHOD=FIRST SELECT * FROM t3;
--echo # connection default
connection default;
# wait for the other query to start executing
let $wait_condition= SELECT 1 FROM INFORMATION_SCHEMA.PROCESSLIST
WHERE ID = $con1_id AND STATE = 'DBUG sleep';
--source include/wait_condition.inc
#SELECT NOW();
--echo # Now try to access a child.
--echo # If 3 is in table, SELECT had to wait.
SELECT * FROM t1 ORDER BY c1;
#SELECT NOW();
--echo # connection con1
connection con1;
reap;
#SELECT NOW();
SET SESSION debug="-d,sleep_create_select_before_lock";
disconnect con1;
--echo # connection default
connection default;
DROP TABLE t1, t2, t3, t4;