mirror of
https://github.com/MariaDB/server.git
synced 2026-01-06 05:22:24 +03:00
MDEV-3917 multiple use locks (GET_LOCK) in one connection.
The patch contributed by Konstantin Osipov applied.
Native comments:
Implement multiple user-level locks per connection.
GET_LOCK() function in MySQL allows a connection to hold at most
one user level lock. Taking a new lock automatically releases the
old lock, if any.
The limit of one lock per session existed since early versions
of MySQL didn't have a deadlock detector for SQL locks.
MDL patches in MySQL 5.5 added a deadlock detector,
so starting from 5.5 it became possible to take multiple locks
in any order -- a deadlock, should it occur, would be detected
and an error returned to the client which closed the wait chain.
This is exactly what is done in this patch: ULLs are moved
to use MDL subsystem.
This commit is contained in:
@@ -338,6 +338,227 @@ set optimizer_switch=@optimizer_switch_save;
|
||||
drop view v_merge, vm;
|
||||
drop table t1,tv;
|
||||
#
|
||||
# GET_LOCK, RELEASE_LOCK, IS_USED_LOCK functions test
|
||||
#
|
||||
# IS_USED_LOCK, IS_FREE_LOCK: the lock is not acquired
|
||||
# Note: IS_USED_LOCK returns NULL if the lock is unused
|
||||
select is_used_lock('test');
|
||||
is_used_lock('test')
|
||||
NULL
|
||||
select is_free_lock('test');
|
||||
is_free_lock('test')
|
||||
1
|
||||
# GET_LOCK returns 1 if it manages to acquire a lock
|
||||
select get_lock('test', 0);
|
||||
get_lock('test', 0)
|
||||
1
|
||||
# IS_USED_LOCK, IS_FREE_LOCK: the lock is acquired
|
||||
select is_free_lock('test');
|
||||
is_free_lock('test')
|
||||
0
|
||||
select is_used_lock('test') = connection_id();
|
||||
is_used_lock('test') = connection_id()
|
||||
1
|
||||
# -> Switching to connection 'con1'
|
||||
# IS_USED_LOCK, IS_FREE_LOCK: the lock is acquired in another
|
||||
# connection
|
||||
select is_used_lock('test') = connection_id();
|
||||
is_used_lock('test') = connection_id()
|
||||
0
|
||||
select is_free_lock('test');
|
||||
is_free_lock('test')
|
||||
0
|
||||
# GET_LOCK returns 0 if it can't acquire a lock (wait timeout)
|
||||
select get_lock('test', 0);
|
||||
get_lock('test', 0)
|
||||
0
|
||||
# RELEASE_LOCK returns 0 if the lock belongs to another connection
|
||||
select release_lock('test');
|
||||
release_lock('test')
|
||||
0
|
||||
# -> Switching to connection 'default'
|
||||
# RELEASE_LOCK returns 1 if it successfully releases a lock
|
||||
select release_lock('test');
|
||||
release_lock('test')
|
||||
1
|
||||
# RELEASE_LOCK returns NULL if it doesn't release a lock and there is no such lock
|
||||
select release_lock('test');
|
||||
release_lock('test')
|
||||
NULL
|
||||
# Test that get_lock() returns NULL if error.
|
||||
select get_lock('test', 0);
|
||||
get_lock('test', 0)
|
||||
1
|
||||
# -> Switching to connection 'con1'
|
||||
create table t1 select connection_id() as id;
|
||||
select get_lock('test', 7200);
|
||||
# -> Switching to connection 'default'
|
||||
select (@id := id) - id from t1;
|
||||
(@id := id) - id
|
||||
0
|
||||
kill query @id;
|
||||
# -> Switching to connection 'con1'
|
||||
get_lock('test', 7200)
|
||||
NULL
|
||||
# -> Switching to connection 'default'
|
||||
# GET_LOCK() works recursively
|
||||
select get_lock('test', 0);
|
||||
get_lock('test', 0)
|
||||
1
|
||||
select get_lock('test', 0);
|
||||
get_lock('test', 0)
|
||||
1
|
||||
select get_lock('test', 0);
|
||||
get_lock('test', 0)
|
||||
1
|
||||
# RELEASE_LOCK() needs to be called recursively then, too
|
||||
select release_lock('test');
|
||||
release_lock('test')
|
||||
1
|
||||
select release_lock('test');
|
||||
release_lock('test')
|
||||
1
|
||||
select release_lock('test');
|
||||
release_lock('test')
|
||||
1
|
||||
# Once the last instance of the lock is released,
|
||||
# the next call returns NULL
|
||||
select release_lock('test');
|
||||
release_lock('test')
|
||||
1
|
||||
# Multiple locks in the same session are OK
|
||||
select get_lock('test1', 0);
|
||||
get_lock('test1', 0)
|
||||
1
|
||||
select get_lock('test2', 0);
|
||||
get_lock('test2', 0)
|
||||
1
|
||||
select get_lock('test3', 0);
|
||||
get_lock('test3', 0)
|
||||
1
|
||||
select release_lock('test1');
|
||||
release_lock('test1')
|
||||
1
|
||||
select release_lock('test2');
|
||||
release_lock('test2')
|
||||
1
|
||||
select release_lock('test3');
|
||||
release_lock('test3')
|
||||
1
|
||||
# Deadlocks are detected e.g. in case of a mutual wait
|
||||
select get_lock('test1', 0);
|
||||
get_lock('test1', 0)
|
||||
1
|
||||
# -> Switching to connection 'con1'
|
||||
select get_lock('test2', 0);
|
||||
get_lock('test2', 0)
|
||||
1
|
||||
select get_lock('test1', 7200);
|
||||
# -> Switching to connection 'default'
|
||||
select get_lock('test2', 7200);
|
||||
ERROR 40001: Deadlock found when trying to get lock; try restarting transaction
|
||||
select release_lock('test1');
|
||||
release_lock('test1')
|
||||
1
|
||||
# -> Switching to connection 'con1'
|
||||
get_lock('test1', 7200)
|
||||
1
|
||||
select release_lock('test2');
|
||||
release_lock('test2')
|
||||
1
|
||||
select release_lock('test1');
|
||||
release_lock('test1')
|
||||
1
|
||||
# -> Switching to connection 'default'
|
||||
# LOCK/UNLOCK TABLES works fine with a user lock.
|
||||
lock table t1 write;
|
||||
select get_lock('test', 0);
|
||||
get_lock('test', 0)
|
||||
1
|
||||
unlock tables;
|
||||
commit;
|
||||
select release_lock('test');
|
||||
release_lock('test')
|
||||
1
|
||||
# GLOBAL READ LOCK works with fine with user locks
|
||||
select get_lock('test1', 0);
|
||||
get_lock('test1', 0)
|
||||
1
|
||||
flush tables with read lock;
|
||||
select get_lock('test2', 0);
|
||||
get_lock('test2', 0)
|
||||
1
|
||||
unlock tables;
|
||||
commit;
|
||||
select release_lock('test1');
|
||||
release_lock('test1')
|
||||
1
|
||||
select release_lock('test2');
|
||||
release_lock('test2')
|
||||
1
|
||||
# BEGIN/COMMIT/ROLLBACK don't unlock user locks.
|
||||
begin;
|
||||
select get_lock('test1', 0);
|
||||
get_lock('test1', 0)
|
||||
1
|
||||
select get_lock('test2', 0);
|
||||
get_lock('test2', 0)
|
||||
1
|
||||
select count(*) from t1;
|
||||
count(*)
|
||||
1
|
||||
rollback;
|
||||
select release_lock('test1');
|
||||
release_lock('test1')
|
||||
1
|
||||
select release_lock('test2');
|
||||
release_lock('test2')
|
||||
1
|
||||
# Deadlocks between user locks and LOCK TABLES locks
|
||||
# are detected OK.
|
||||
select get_lock('test', 0);
|
||||
get_lock('test', 0)
|
||||
1
|
||||
# -> Switching to connection 'con1'
|
||||
lock table t1 write;
|
||||
select get_lock('test', 7200);
|
||||
# -> Switching to connection 'default'
|
||||
lock table t1 read;
|
||||
ERROR 40001: Deadlock found when trying to get lock; try restarting transaction
|
||||
select release_lock('test');
|
||||
release_lock('test')
|
||||
1
|
||||
# -> Switching to connection 'con1'
|
||||
get_lock('test', 7200)
|
||||
1
|
||||
select release_lock('test');
|
||||
release_lock('test')
|
||||
1
|
||||
unlock tables;
|
||||
# cleanup
|
||||
drop table t1;
|
||||
# check too long identifier names
|
||||
select get_lock(repeat('a', 192), 0);
|
||||
get_lock(repeat('a', 192), 0)
|
||||
1
|
||||
select is_used_lock(repeat('a', 192)) = connection_id();
|
||||
is_used_lock(repeat('a', 192)) = connection_id()
|
||||
1
|
||||
select is_free_lock(repeat('a', 192));
|
||||
is_free_lock(repeat('a', 192))
|
||||
0
|
||||
select release_lock(repeat('a', 192));
|
||||
release_lock(repeat('a', 192))
|
||||
1
|
||||
select get_lock(repeat('a', 193), 0);
|
||||
ERROR 42000: Identifier name 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' is too long
|
||||
select is_used_lock(repeat('a', 193));
|
||||
ERROR 42000: Identifier name 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' is too long
|
||||
select is_free_lock(repeat('a', 193));
|
||||
ERROR 42000: Identifier name 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' is too long
|
||||
select release_lock(repeat('a', 193));
|
||||
ERROR 42000: Identifier name 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' is too long
|
||||
#
|
||||
# End of 5.5 tests
|
||||
#
|
||||
#
|
||||
|
||||
Reference in New Issue
Block a user