mirror of
https://github.com/MariaDB/server.git
synced 2025-07-30 16:24:05 +03:00
A prerequisite patch for the fix for Bug#46224
"HANDLER statements within a transaction might lead to deadlocks". Introduce a notion of a sentinel to MDL_context. A sentinel is a ticket that separates all tickets in the context into two groups: before and after it. Currently we can have (and need) only one designated sentinel -- it separates all locks taken by LOCK TABLE or HANDLER statement, which must survive COMMIT and ROLLBACK and all other locks, which must be released at COMMIT or ROLLBACK. The tricky part is maintaining the sentinel up to date when someone release its corresponding ticket. This can happen, e.g. if someone issues DROP TABLE under LOCK TABLES (generally, see all calls to release_all_locks_for_name()). MDL_context::release_ticket() is modified to take care of it. ****** A fix and a test case for Bug#46224 "HANDLER statements within a transaction might lead to deadlocks". An attempt to mix HANDLER SQL statements, which are transaction- agnostic, an open multi-statement transaction, and DDL against the involved tables (in a concurrent connection) could lead to a deadlock. The deadlock would occur when HANDLER OPEN or HANDLER READ would have to wait on a conflicting metadata lock. If the connection that issued HANDLER statement also had other metadata locks (say, acquired in scope of a transaction), a classical deadlock situation of mutual wait could occur. Incompatible change: entering LOCK TABLES mode automatically closes all open HANDLERs in the current connection. Incompatible change: previously an attempt to wait on a lock in a connection that has an open HANDLER statement could wait indefinitely/deadlock. After this patch, an error ER_LOCK_DEADLOCK is produced. The idea of the fix is to merge thd->handler_mdl_context with the main mdl_context of the connection, used for transactional locks. This makes deadlock detection possible, since all waits with locks are "visible" and available to analysis in a single MDL context of the connection. Since HANDLER locks and transactional locks have a different life cycle -- HANDLERs are explicitly open and closed, and so are HANDLER locks, explicitly acquired and released, whereas transactional locks "accumulate" till the end of a transaction and are released only with COMMIT, ROLLBACK and ROLLBACK TO SAVEPOINT, a concept of "sentinel" was introduced to MDL_context. All locks, HANDLER and others, reside in the same linked list. However, a selected element of the list separates locks with different life cycle. HANDLER locks always reside at the end of the list, after the sentinel. Transactional locks are prepended to the beginning of the list, before the sentinel. Thus, ROLLBACK, COMMIT or ROLLBACK TO SAVEPOINT, only release those locks that reside before the sentinel. HANDLER locks must be released explicitly as part of HANDLER CLOSE statement, or an implicit close. The same approach with sentinel is also employed for LOCK TABLES locks. Since HANDLER and LOCK TABLES statement has never worked together, the implementation is made simple and only maintains one sentinel, which is used either for HANDLER locks, or for LOCK TABLES locks.
This commit is contained in:
@ -570,13 +570,25 @@ connection: flush
|
||||
rename table t1 to t2;;
|
||||
connection: waiter
|
||||
connection: default
|
||||
#
|
||||
# RENAME placed two pending locks and waits.
|
||||
# When HANDLER t2 OPEN does open_tables(), it calls
|
||||
# mysql_ha_flush(), which in turn closes the open HANDLER for t1.
|
||||
# RENAME TABLE gets unblocked. If it gets scheduled quickly
|
||||
# and manages to complete before open_tables()
|
||||
# of HANDLER t2 OPEN, open_tables() and therefore the whole
|
||||
# HANDLER t2 OPEN succeeds. Otherwise open_tables()
|
||||
# notices a pending or active exclusive metadata lock on t2
|
||||
# and the whole HANDLER t2 OPEN fails with ER_LOCK_DEADLOCK
|
||||
# error.
|
||||
#
|
||||
handler t2 open;
|
||||
handler t2 read first;
|
||||
c1
|
||||
handler t1 read next;
|
||||
ERROR 42S02: Table 'test.t1' doesn't exist
|
||||
handler t1 close;
|
||||
handler t2 close;
|
||||
connection: flush
|
||||
handler t1 read next;
|
||||
ERROR 42S02: Unknown table 't1' in HANDLER
|
||||
handler t1 close;
|
||||
ERROR 42S02: Unknown table 't1' in HANDLER
|
||||
drop table t2;
|
||||
drop table if exists t1;
|
||||
create temporary table t1 (a int, b char(1), key a(a), key b(a,b));
|
||||
@ -745,3 +757,569 @@ USE information_schema;
|
||||
HANDLER COLUMNS OPEN;
|
||||
ERROR HY000: Incorrect usage of HANDLER OPEN and information_schema
|
||||
USE test;
|
||||
#
|
||||
# Add test coverage for HANDLER and LOCK TABLES, HANDLER and DDL.
|
||||
#
|
||||
drop table if exists t1, t2, t3;
|
||||
create table t1 (a int, key a (a));
|
||||
insert into t1 (a) values (1), (2), (3), (4), (5);
|
||||
create table t2 (a int, key a (a)) select * from t1;
|
||||
create temporary table t3 (a int, key a (a)) select * from t2;
|
||||
handler t1 open;
|
||||
handler t2 open;
|
||||
handler t3 open;
|
||||
#
|
||||
# LOCK TABLES implicitly closes all handlers.
|
||||
#
|
||||
lock table t3 read;
|
||||
#
|
||||
# No HANDLER sql is available under lock tables anyway.
|
||||
#
|
||||
handler t1 open;
|
||||
ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction
|
||||
handler t1 read next;
|
||||
ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction
|
||||
handler t2 close;
|
||||
ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction
|
||||
handler t3 open;
|
||||
ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction
|
||||
# After UNLOCK TABLES no handlers are around, they were
|
||||
# implicitly closed.
|
||||
unlock tables;
|
||||
drop temporary table t3;
|
||||
handler t1 read next;
|
||||
ERROR 42S02: Unknown table 't1' in HANDLER
|
||||
handler t2 close;
|
||||
ERROR 42S02: Unknown table 't2' in HANDLER
|
||||
handler t3 read next;
|
||||
ERROR 42S02: Unknown table 't3' in HANDLER
|
||||
#
|
||||
# Other operations also implicitly close handler:
|
||||
#
|
||||
# TRUNCATE
|
||||
#
|
||||
handler t1 open;
|
||||
truncate table t1;
|
||||
handler t1 read next;
|
||||
ERROR 42S02: Unknown table 't1' in HANDLER
|
||||
handler t1 open;
|
||||
#
|
||||
# CREATE TRIGGER
|
||||
#
|
||||
create trigger t1_ai after insert on t1 for each row set @a=1;
|
||||
handler t1 read next;
|
||||
ERROR 42S02: Unknown table 't1' in HANDLER
|
||||
#
|
||||
# DROP TRIGGER
|
||||
#
|
||||
handler t1 open;
|
||||
drop trigger t1_ai;
|
||||
handler t1 read next;
|
||||
ERROR 42S02: Unknown table 't1' in HANDLER
|
||||
#
|
||||
# ALTER TABLE
|
||||
#
|
||||
handler t1 open;
|
||||
alter table t1 add column b int;
|
||||
handler t1 read next;
|
||||
ERROR 42S02: Unknown table 't1' in HANDLER
|
||||
#
|
||||
# ANALYZE TABLE
|
||||
#
|
||||
handler t1 open;
|
||||
analyze table t1;
|
||||
Table Op Msg_type Msg_text
|
||||
test.t1 analyze status OK
|
||||
handler t1 read next;
|
||||
ERROR 42S02: Unknown table 't1' in HANDLER
|
||||
#
|
||||
# OPTIMIZE TABLE
|
||||
#
|
||||
handler t1 open;
|
||||
optimize table t1;
|
||||
Table Op Msg_type Msg_text
|
||||
test.t1 optimize note Table does not support optimize, doing recreate + analyze instead
|
||||
test.t1 optimize status OK
|
||||
handler t1 read next;
|
||||
ERROR 42S02: Unknown table 't1' in HANDLER
|
||||
#
|
||||
# REPAIR TABLE
|
||||
#
|
||||
handler t1 open;
|
||||
repair table t1;
|
||||
Table Op Msg_type Msg_text
|
||||
test.t1 repair note The storage engine for the table doesn't support repair
|
||||
handler t1 read next;
|
||||
ERROR 42S02: Unknown table 't1' in HANDLER
|
||||
#
|
||||
# DROP TABLE, naturally.
|
||||
#
|
||||
handler t1 open;
|
||||
drop table t1;
|
||||
handler t1 read next;
|
||||
ERROR 42S02: Unknown table 't1' in HANDLER
|
||||
create table t1 (a int, b int, key a (a)) select a from t2;
|
||||
#
|
||||
# RENAME TABLE, naturally
|
||||
#
|
||||
handler t1 open;
|
||||
rename table t1 to t3;
|
||||
handler t1 read next;
|
||||
ERROR 42S02: Unknown table 't1' in HANDLER
|
||||
#
|
||||
# CREATE TABLE (even with IF NOT EXISTS clause,
|
||||
# and the table exists).
|
||||
#
|
||||
handler t2 open;
|
||||
create table if not exists t2 (a int);
|
||||
Warnings:
|
||||
Note 1050 Table 't2' already exists
|
||||
handler t2 read next;
|
||||
ERROR 42S02: Unknown table 't2' in HANDLER
|
||||
rename table t3 to t1;
|
||||
drop table t2;
|
||||
#
|
||||
# FLUSH TABLE doesn't close the table but loses the position
|
||||
#
|
||||
handler t1 open;
|
||||
handler t1 read a prev;
|
||||
b a
|
||||
NULL 5
|
||||
flush table t1;
|
||||
handler t1 read a prev;
|
||||
b a
|
||||
NULL 5
|
||||
handler t1 close;
|
||||
#
|
||||
# FLUSH TABLES WITH READ LOCK behaves like FLUSH TABLE.
|
||||
#
|
||||
handler t1 open;
|
||||
handler t1 read a prev;
|
||||
b a
|
||||
NULL 5
|
||||
flush tables with read lock;
|
||||
handler t1 read a prev;
|
||||
b a
|
||||
NULL 5
|
||||
handler t1 close;
|
||||
unlock tables;
|
||||
#
|
||||
# Explore the effect of HANDLER locks on concurrent DDL
|
||||
#
|
||||
handler t1 open;
|
||||
# Establishing auxiliary connections con1, con2, con3
|
||||
# --> connection con1;
|
||||
# Sending:
|
||||
drop table t1 ;
|
||||
# We can't use connection 'default' as wait_condition will
|
||||
# autoclose handlers.
|
||||
# --> connection con2
|
||||
# Waitng for 'drop table t1' to get blocked...
|
||||
# --> connection default
|
||||
handler t1 read a prev;
|
||||
b a
|
||||
NULL 5
|
||||
handler t1 read a prev;
|
||||
b a
|
||||
NULL 4
|
||||
handler t1 close;
|
||||
# --> connection con1
|
||||
# Reaping 'drop table t1'...
|
||||
# --> connection default
|
||||
#
|
||||
# Explore the effect of HANDLER locks in parallel with SELECT
|
||||
#
|
||||
create table t1 (a int, key a (a));
|
||||
insert into t1 (a) values (1), (2), (3), (4), (5);
|
||||
begin;
|
||||
select * from t1;
|
||||
a
|
||||
1
|
||||
2
|
||||
3
|
||||
4
|
||||
5
|
||||
handler t1 open;
|
||||
handler t1 read a prev;
|
||||
a
|
||||
5
|
||||
handler t1 read a prev;
|
||||
a
|
||||
4
|
||||
handler t1 close;
|
||||
# --> connection con1;
|
||||
# Sending:
|
||||
drop table t1 ;
|
||||
# --> connection con2
|
||||
# Waiting for 'drop table t1' to get blocked...
|
||||
# --> connection default
|
||||
# We can still use the table, it's part of the transaction
|
||||
select * from t1;
|
||||
a
|
||||
1
|
||||
2
|
||||
3
|
||||
4
|
||||
5
|
||||
# Such are the circumstances that t1 is a part of transaction,
|
||||
# thus we can reopen it in the handler
|
||||
handler t1 open;
|
||||
# We can commit the transaction, it doesn't close the handler
|
||||
# and doesn't let DROP to proceed.
|
||||
commit;
|
||||
handler t1 read a prev;
|
||||
a
|
||||
5
|
||||
handler t1 read a prev;
|
||||
a
|
||||
4
|
||||
handler t1 read a prev;
|
||||
a
|
||||
3
|
||||
handler t1 close;
|
||||
# --> connection con1
|
||||
# Now drop can proceed
|
||||
# Reaping 'drop table t1'...
|
||||
# --> connection default
|
||||
#
|
||||
# Demonstrate that HANDLER locks and transaction locks
|
||||
# reside in the same context, and we don't back-off
|
||||
# when have transaction or handler locks.
|
||||
#
|
||||
create table t1 (a int, key a (a));
|
||||
insert into t1 (a) values (1), (2), (3), (4), (5);
|
||||
create table t2 (a int, key a (a));
|
||||
insert into t2 (a) values (1), (2), (3), (4), (5);
|
||||
begin;
|
||||
select * from t1;
|
||||
a
|
||||
1
|
||||
2
|
||||
3
|
||||
4
|
||||
5
|
||||
# --> connection con1
|
||||
lock table t2 read;
|
||||
# --> connection con2
|
||||
# Sending:
|
||||
drop table t2;
|
||||
# --> connection con1
|
||||
# Waiting for 'drop table t2' to get blocked...
|
||||
# --> connection default
|
||||
handler t2 open;
|
||||
ERROR 40001: Deadlock found when trying to get lock; try restarting transaction
|
||||
select * from t2;
|
||||
ERROR 40001: Deadlock found when trying to get lock; try restarting transaction
|
||||
handler t1 open;
|
||||
commit;
|
||||
handler t2 open;
|
||||
ERROR 40001: Deadlock found when trying to get lock; try restarting transaction
|
||||
handler t1 close;
|
||||
# --> connection con1
|
||||
unlock tables;
|
||||
# --> connection con2
|
||||
# Reaping 'drop table t2'...
|
||||
# --> connection default
|
||||
handler t1 open;
|
||||
handler t1 read a prev;
|
||||
a
|
||||
5
|
||||
handler t1 close;
|
||||
#
|
||||
# Likewise, this doesn't require a multi-statement transaction.
|
||||
# ER_LOCK_DEADLOCK is also produced when we have an open
|
||||
# HANDLER and try to acquire locks for a single statement.
|
||||
#
|
||||
create table t2 (a int, key a (a));
|
||||
handler t1 open;
|
||||
# --> connection con1
|
||||
lock tables t2 read;
|
||||
# --> connection con2
|
||||
# Sending 'drop table t2'...
|
||||
drop table t2;
|
||||
# --> connection con1
|
||||
# Waiting for 'drop table t2' to get blocked...
|
||||
# --> connection default
|
||||
select * from t2;
|
||||
ERROR 40001: Deadlock found when trying to get lock; try restarting transaction
|
||||
# --> connection con1
|
||||
unlock tables;
|
||||
# --> connection con2
|
||||
# Reaping 'drop table t2'...
|
||||
# --> connection default
|
||||
handler t1 close;
|
||||
#
|
||||
# ROLLBACK TO SAVEPOINT releases transactional locks,
|
||||
# but has no effect on open HANDLERs
|
||||
#
|
||||
create table t2 like t1;
|
||||
create table t3 like t1;
|
||||
begin;
|
||||
# Have something before the savepoint
|
||||
select * from t3;
|
||||
a
|
||||
savepoint sv;
|
||||
handler t1 open;
|
||||
handler t1 read a first;
|
||||
a
|
||||
1
|
||||
handler t1 read a next;
|
||||
a
|
||||
2
|
||||
select * from t2;
|
||||
a
|
||||
# --> connection con1
|
||||
# Sending:
|
||||
drop table t1;
|
||||
# --> connection con2
|
||||
# Sending:
|
||||
drop table t2;
|
||||
# --> connection default
|
||||
# Let DROP TABLE statements sync in. We must use
|
||||
# a separate connection for that, because otherwise SELECT
|
||||
# will auto-close the HANDLERs, becaues there are pending
|
||||
# exclusive locks against them.
|
||||
# --> connection con3
|
||||
# Waiting for 'drop table t1' to get blocked...
|
||||
# Waiting for 'drop table t2' to get blocked...
|
||||
# Demonstrate that t2 lock was released and t2 was dropped
|
||||
# after ROLLBACK TO SAVEPOINT
|
||||
# --> connection default
|
||||
rollback to savepoint sv;
|
||||
# --> connection con2
|
||||
# Reaping 'drop table t2'...
|
||||
# Demonstrate that ROLLBACK TO SAVEPOINT didn't release the handler
|
||||
# lock.
|
||||
# --> connection default
|
||||
handler t1 read a next;
|
||||
a
|
||||
3
|
||||
handler t1 read a next;
|
||||
a
|
||||
4
|
||||
# Demonstrate that the drop will go through as soon as we close the
|
||||
# HANDLER
|
||||
handler t1 close;
|
||||
# connection con1
|
||||
# Reaping 'drop table t1'...
|
||||
# --> connection default
|
||||
commit;
|
||||
drop table t3;
|
||||
#
|
||||
# A few special cases when using SAVEPOINT/ROLLBACK TO
|
||||
# SAVEPOINT and HANDLER.
|
||||
#
|
||||
# Show that rollback to the savepoint taken in the beginning
|
||||
# of the transaction doesn't release mdl lock on
|
||||
# the HANDLER that was opened later.
|
||||
#
|
||||
create table t1 (a int, key a(a));
|
||||
insert into t1 (a) values (1), (2), (3), (4), (5);
|
||||
create table t2 like t1;
|
||||
begin;
|
||||
savepoint sv;
|
||||
handler t1 open;
|
||||
handler t1 read a first;
|
||||
a
|
||||
1
|
||||
handler t1 read a next;
|
||||
a
|
||||
2
|
||||
select * from t2;
|
||||
a
|
||||
# --> connection con1
|
||||
# Sending:
|
||||
drop table t1;
|
||||
# --> connection con2
|
||||
# Sending:
|
||||
drop table t2;
|
||||
# --> connection default
|
||||
# Let DROP TABLE statements sync in. We must use
|
||||
# a separate connection for that, because otherwise SELECT
|
||||
# will auto-close the HANDLERs, becaues there are pending
|
||||
# exclusive locks against them.
|
||||
# --> connection con3
|
||||
# Waiting for 'drop table t1' to get blocked...
|
||||
# Waiting for 'drop table t2' to get blocked...
|
||||
# Demonstrate that t2 lock was released and t2 was dropped
|
||||
# after ROLLBACK TO SAVEPOINT
|
||||
# --> connection default
|
||||
rollback to savepoint sv;
|
||||
# --> connection con2
|
||||
# Reaping 'drop table t2'...
|
||||
# Demonstrate that ROLLBACK TO SAVEPOINT didn't release the handler
|
||||
# lock.
|
||||
# --> connection default
|
||||
handler t1 read a next;
|
||||
a
|
||||
3
|
||||
handler t1 read a next;
|
||||
a
|
||||
4
|
||||
# Demonstrate that the drop will go through as soon as we close the
|
||||
# HANDLER
|
||||
handler t1 close;
|
||||
# connection con1
|
||||
# Reaping 'drop table t1'...
|
||||
# --> connection default
|
||||
commit;
|
||||
#
|
||||
# Show that rollback to the savepoint taken in the beginning
|
||||
# of the transaction works properly (no valgrind warnins, etc),
|
||||
# even though it's done after the HANDLER mdl lock that was there
|
||||
# at the beginning is released and added again.
|
||||
#
|
||||
create table t1 (a int, key a(a));
|
||||
insert into t1 (a) values (1), (2), (3), (4), (5);
|
||||
create table t2 like t1;
|
||||
create table t3 like t1;
|
||||
insert into t3 (a) select a from t1;
|
||||
begin;
|
||||
handler t1 open;
|
||||
savepoint sv;
|
||||
handler t1 read a first;
|
||||
a
|
||||
1
|
||||
select * from t2;
|
||||
a
|
||||
handler t1 close;
|
||||
handler t3 open;
|
||||
handler t3 read a first;
|
||||
a
|
||||
1
|
||||
rollback to savepoint sv;
|
||||
# --> connection con1
|
||||
drop table t1, t2;
|
||||
# Sending:
|
||||
drop table t3;
|
||||
# Let DROP TABLE statement sync in.
|
||||
# --> connection con2
|
||||
# Waiting for 'drop table t3' to get blocked...
|
||||
# Demonstrate that ROLLBACK TO SAVEPOINT didn't release the handler
|
||||
# lock.
|
||||
# --> connection default
|
||||
handler t3 read a next;
|
||||
a
|
||||
2
|
||||
# Demonstrate that the drop will go through as soon as we close the
|
||||
# HANDLER
|
||||
handler t3 close;
|
||||
# connection con1
|
||||
# Reaping 'drop table t3'...
|
||||
# --> connection default
|
||||
commit;
|
||||
#
|
||||
# If we have to wait on an exclusive locks while having
|
||||
# an open HANDLER, ER_LOCK_DEADLOCK is reported.
|
||||
#
|
||||
create table t1 (a int, key a(a));
|
||||
create table t2 like t1;
|
||||
handler t1 open;
|
||||
# --> connection con1
|
||||
lock table t2 read;
|
||||
# --> connection default
|
||||
drop table t2;
|
||||
ERROR 40001: Deadlock found when trying to get lock; try restarting transaction
|
||||
rename table t2 to t3;
|
||||
ERROR 40001: Deadlock found when trying to get lock; try restarting transaction
|
||||
# Demonstrate that there is no deadlock with FLUSH TABLE,
|
||||
# even though it is waiting for the other table to go away
|
||||
# Sending:
|
||||
flush table t2;
|
||||
# --> connection con2
|
||||
drop table t1;
|
||||
# --> connection con1
|
||||
unlock tables;
|
||||
# --> connection default
|
||||
# Reaping 'flush table t2'...
|
||||
drop table t2;
|
||||
#
|
||||
# Bug #46224 HANDLER statements within a transaction might
|
||||
# lead to deadlocks
|
||||
#
|
||||
create table t1 (a int, key a(a));
|
||||
# --> connection default
|
||||
begin;
|
||||
select * from t1;
|
||||
a
|
||||
handler t1 open;
|
||||
# --> connection con1
|
||||
lock tables t1 write;
|
||||
# --> connection default
|
||||
# Sending:
|
||||
handler t1 read a next;
|
||||
# --> connection con1
|
||||
# Waiting for 'handler t1 read a next' to get blocked...
|
||||
# Sending:
|
||||
drop table t1;
|
||||
# --> connection con2
|
||||
# Waiting for 'drop table t1' to get blocked...
|
||||
# --> connection default
|
||||
# Reaping 'handler t1 read a next'...
|
||||
ERROR 40001: Deadlock found when trying to get lock; try restarting transaction
|
||||
handler t1 close;
|
||||
commit;
|
||||
# --> connection con1
|
||||
# Reaping 'drop table t1'...
|
||||
# --> connection con1
|
||||
# --> connection con2
|
||||
# --> connection con3
|
||||
#
|
||||
# A temporary table test.
|
||||
# Check that we don't loose positions of HANDLER opened
|
||||
# against a temporary table.
|
||||
#
|
||||
create table t1 (a int, b int, key a (a));
|
||||
insert into t1 (a) values (1), (2), (3), (4), (5);
|
||||
create temporary table t2 (a int, b int, key a (a));
|
||||
insert into t2 (a) select a from t1;
|
||||
handler t1 open;
|
||||
handler t1 read a next;
|
||||
a b
|
||||
1 NULL
|
||||
handler t2 open;
|
||||
handler t2 read a next;
|
||||
a b
|
||||
1 NULL
|
||||
flush table t1;
|
||||
handler t2 read a next;
|
||||
a b
|
||||
2 NULL
|
||||
# Sic: the position is lost
|
||||
handler t1 read a next;
|
||||
a b
|
||||
1 NULL
|
||||
select * from t1;
|
||||
a b
|
||||
1 NULL
|
||||
2 NULL
|
||||
3 NULL
|
||||
4 NULL
|
||||
5 NULL
|
||||
# Sic: the position is not lost
|
||||
handler t2 read a next;
|
||||
a b
|
||||
3 NULL
|
||||
select * from t2;
|
||||
ERROR HY000: Can't reopen table: 't2'
|
||||
handler t2 read a next;
|
||||
a b
|
||||
4 NULL
|
||||
drop table t1;
|
||||
drop temporary table t2;
|
||||
#
|
||||
# A test for lock_table_names()/unlock_table_names() function.
|
||||
# It should work properly in presence of open HANDLER.
|
||||
#
|
||||
create table t1 (a int, b int, key a (a));
|
||||
create table t2 like t1;
|
||||
create table t3 like t1;
|
||||
create table t4 like t1;
|
||||
handler t1 open;
|
||||
handler t2 open;
|
||||
rename table t4 to t5, t3 to t4, t5 to t3;
|
||||
handler t1 read first;
|
||||
a b
|
||||
handler t2 read first;
|
||||
a b
|
||||
drop table t1, t2, t3, t4;
|
||||
|
Reference in New Issue
Block a user