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:
parent
ff3407a111
commit
1a600125ff
@ -338,6 +338,227 @@ set optimizer_switch=@optimizer_switch_save;
|
|||||||
drop view v_merge, vm;
|
drop view v_merge, vm;
|
||||||
drop table t1,tv;
|
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
|
# End of 5.5 tests
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
|
@ -43,7 +43,7 @@ insert into t3 values(connection_id());
|
|||||||
send update t2 set a = a + 1 + get_lock('crash_lock%20C', 10);
|
send update t2 set a = a + 1 + get_lock('crash_lock%20C', 10);
|
||||||
|
|
||||||
connection master1;
|
connection master1;
|
||||||
let $wait_condition= SELECT a > 1 FROM t2;
|
let $wait_condition= SELECT count(*) > 0 FROM information_schema.processlist WHERE info LIKE 'update%' AND state='User lock';
|
||||||
source include/wait_condition.inc;
|
source include/wait_condition.inc;
|
||||||
select (@id := id) - id from t3;
|
select (@id := id) - id from t3;
|
||||||
kill @id;
|
kill @id;
|
||||||
|
@ -88,7 +88,8 @@ insert into t3 select get_lock('crash_lock%20C', 1) from t2;
|
|||||||
connection master;
|
connection master;
|
||||||
send update t1 set n = n + get_lock('crash_lock%20C', 2);
|
send update t1 set n = n + get_lock('crash_lock%20C', 2);
|
||||||
connection master1;
|
connection master1;
|
||||||
sleep 3;
|
let $wait_condition= SELECT count(*) > 0 FROM information_schema.processlist WHERE info LIKE 'update%' AND state='User lock';
|
||||||
|
source include/wait_condition.inc;
|
||||||
select (@id := id) - id from t2;
|
select (@id := id) - id from t2;
|
||||||
kill @id;
|
kill @id;
|
||||||
# We don't drop t3 as this is a temporary table
|
# We don't drop t3 as this is a temporary table
|
||||||
|
@ -368,6 +368,183 @@ drop view v_merge, vm;
|
|||||||
drop table t1,tv;
|
drop table t1,tv;
|
||||||
|
|
||||||
|
|
||||||
|
--echo #
|
||||||
|
--echo # GET_LOCK, RELEASE_LOCK, IS_USED_LOCK functions test
|
||||||
|
--echo #
|
||||||
|
|
||||||
|
--echo # IS_USED_LOCK, IS_FREE_LOCK: the lock is not acquired
|
||||||
|
--echo # Note: IS_USED_LOCK returns NULL if the lock is unused
|
||||||
|
select is_used_lock('test');
|
||||||
|
select is_free_lock('test');
|
||||||
|
|
||||||
|
--echo # GET_LOCK returns 1 if it manages to acquire a lock
|
||||||
|
select get_lock('test', 0);
|
||||||
|
|
||||||
|
--echo # IS_USED_LOCK, IS_FREE_LOCK: the lock is acquired
|
||||||
|
select is_free_lock('test');
|
||||||
|
select is_used_lock('test') = connection_id();
|
||||||
|
|
||||||
|
connect (con1,localhost,root,,);
|
||||||
|
--echo # -> Switching to connection 'con1'
|
||||||
|
connection con1;
|
||||||
|
--echo # IS_USED_LOCK, IS_FREE_LOCK: the lock is acquired in another
|
||||||
|
--echo # connection
|
||||||
|
select is_used_lock('test') = connection_id();
|
||||||
|
select is_free_lock('test');
|
||||||
|
|
||||||
|
--echo # GET_LOCK returns 0 if it can't acquire a lock (wait timeout)
|
||||||
|
select get_lock('test', 0);
|
||||||
|
|
||||||
|
--echo # RELEASE_LOCK returns 0 if the lock belongs to another connection
|
||||||
|
select release_lock('test');
|
||||||
|
|
||||||
|
--echo # -> Switching to connection 'default'
|
||||||
|
connection default;
|
||||||
|
|
||||||
|
--echo # RELEASE_LOCK returns 1 if it successfully releases a lock
|
||||||
|
select release_lock('test');
|
||||||
|
--echo # RELEASE_LOCK returns NULL if it doesn't release a lock and there is no such lock
|
||||||
|
select release_lock('test');
|
||||||
|
|
||||||
|
--echo # Test that get_lock() returns NULL if error.
|
||||||
|
select get_lock('test', 0);
|
||||||
|
--echo # -> Switching to connection 'con1'
|
||||||
|
connection con1;
|
||||||
|
create table t1 select connection_id() as id;
|
||||||
|
send select get_lock('test', 7200);
|
||||||
|
|
||||||
|
--echo # -> Switching to connection 'default'
|
||||||
|
connection default;
|
||||||
|
let $wait_condition= SELECT count(*) > 0 FROM information_schema.processlist WHERE info LIKE 'select%' AND state='User lock';
|
||||||
|
source include/wait_condition.inc;
|
||||||
|
select (@id := id) - id from t1;
|
||||||
|
kill query @id;
|
||||||
|
|
||||||
|
--echo # -> Switching to connection 'con1'
|
||||||
|
connection con1;
|
||||||
|
reap;
|
||||||
|
|
||||||
|
--echo # -> Switching to connection 'default'
|
||||||
|
connection default;
|
||||||
|
|
||||||
|
--echo # GET_LOCK() works recursively
|
||||||
|
select get_lock('test', 0);
|
||||||
|
select get_lock('test', 0);
|
||||||
|
select get_lock('test', 0);
|
||||||
|
|
||||||
|
--echo # RELEASE_LOCK() needs to be called recursively then, too
|
||||||
|
select release_lock('test');
|
||||||
|
select release_lock('test');
|
||||||
|
select release_lock('test');
|
||||||
|
|
||||||
|
--echo # Once the last instance of the lock is released,
|
||||||
|
--echo # the next call returns NULL
|
||||||
|
select release_lock('test');
|
||||||
|
|
||||||
|
|
||||||
|
--echo # Multiple locks in the same session are OK
|
||||||
|
select get_lock('test1', 0);
|
||||||
|
select get_lock('test2', 0);
|
||||||
|
select get_lock('test3', 0);
|
||||||
|
|
||||||
|
select release_lock('test1');
|
||||||
|
select release_lock('test2');
|
||||||
|
select release_lock('test3');
|
||||||
|
|
||||||
|
--echo # Deadlocks are detected e.g. in case of a mutual wait
|
||||||
|
select get_lock('test1', 0);
|
||||||
|
|
||||||
|
--echo # -> Switching to connection 'con1'
|
||||||
|
connection con1;
|
||||||
|
select get_lock('test2', 0);
|
||||||
|
send select get_lock('test1', 7200);
|
||||||
|
|
||||||
|
--echo # -> Switching to connection 'default'
|
||||||
|
connection default;
|
||||||
|
let $wait_condition= SELECT count(*) > 0 FROM information_schema.processlist WHERE info LIKE 'select%' AND state='User lock';
|
||||||
|
source include/wait_condition.inc;
|
||||||
|
--error ER_LOCK_DEADLOCK
|
||||||
|
select get_lock('test2', 7200);
|
||||||
|
|
||||||
|
select release_lock('test1');
|
||||||
|
|
||||||
|
--echo # -> Switching to connection 'con1'
|
||||||
|
connection con1;
|
||||||
|
reap;
|
||||||
|
select release_lock('test2');
|
||||||
|
select release_lock('test1');
|
||||||
|
|
||||||
|
--echo # -> Switching to connection 'default'
|
||||||
|
connection default;
|
||||||
|
|
||||||
|
--echo # LOCK/UNLOCK TABLES works fine with a user lock.
|
||||||
|
lock table t1 write;
|
||||||
|
select get_lock('test', 0);
|
||||||
|
unlock tables;
|
||||||
|
commit;
|
||||||
|
select release_lock('test');
|
||||||
|
|
||||||
|
--echo # GLOBAL READ LOCK works with fine with user locks
|
||||||
|
select get_lock('test1', 0);
|
||||||
|
flush tables with read lock;
|
||||||
|
select get_lock('test2', 0);
|
||||||
|
unlock tables;
|
||||||
|
commit;
|
||||||
|
select release_lock('test1');
|
||||||
|
select release_lock('test2');
|
||||||
|
|
||||||
|
--echo # BEGIN/COMMIT/ROLLBACK don't unlock user locks.
|
||||||
|
begin;
|
||||||
|
select get_lock('test1', 0);
|
||||||
|
select get_lock('test2', 0);
|
||||||
|
select count(*) from t1;
|
||||||
|
rollback;
|
||||||
|
select release_lock('test1');
|
||||||
|
select release_lock('test2');
|
||||||
|
|
||||||
|
--echo # Deadlocks between user locks and LOCK TABLES locks
|
||||||
|
--echo # are detected OK.
|
||||||
|
select get_lock('test', 0);
|
||||||
|
|
||||||
|
--echo # -> Switching to connection 'con1'
|
||||||
|
connection con1;
|
||||||
|
lock table t1 write;
|
||||||
|
send select get_lock('test', 7200);
|
||||||
|
|
||||||
|
--echo # -> Switching to connection 'default'
|
||||||
|
connection default;
|
||||||
|
let $wait_condition= SELECT count(*) > 0 FROM information_schema.processlist WHERE info LIKE 'select%' AND state = 'User lock';
|
||||||
|
source include/wait_condition.inc;
|
||||||
|
--error ER_LOCK_DEADLOCK
|
||||||
|
lock table t1 read;
|
||||||
|
|
||||||
|
select release_lock('test');
|
||||||
|
|
||||||
|
--echo # -> Switching to connection 'con1'
|
||||||
|
connection con1;
|
||||||
|
reap;
|
||||||
|
select release_lock('test');
|
||||||
|
unlock tables;
|
||||||
|
|
||||||
|
--echo # cleanup
|
||||||
|
disconnect con1;
|
||||||
|
connection default;
|
||||||
|
drop table t1;
|
||||||
|
|
||||||
|
--echo # check too long identifier names
|
||||||
|
select get_lock(repeat('a', 192), 0);
|
||||||
|
select is_used_lock(repeat('a', 192)) = connection_id();
|
||||||
|
select is_free_lock(repeat('a', 192));
|
||||||
|
select release_lock(repeat('a', 192));
|
||||||
|
--error ER_TOO_LONG_IDENT
|
||||||
|
select get_lock(repeat('a', 193), 0);
|
||||||
|
--error ER_TOO_LONG_IDENT
|
||||||
|
select is_used_lock(repeat('a', 193));
|
||||||
|
--error ER_TOO_LONG_IDENT
|
||||||
|
select is_free_lock(repeat('a', 193));
|
||||||
|
--error ER_TOO_LONG_IDENT
|
||||||
|
select release_lock(repeat('a', 193));
|
||||||
|
|
||||||
--echo #
|
--echo #
|
||||||
--echo # End of 5.5 tests
|
--echo # End of 5.5 tests
|
||||||
--echo #
|
--echo #
|
||||||
|
@ -198,7 +198,7 @@ Hybrid_type_traits_integer::fix_length_and_dec(Item *item, Item *arg) const
|
|||||||
|
|
||||||
void item_init(void)
|
void item_init(void)
|
||||||
{
|
{
|
||||||
item_user_lock_init();
|
item_func_sleep_init();
|
||||||
uuid_short_init();
|
uuid_short_init();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
580
sql/item_func.cc
580
sql/item_func.cc
@ -3750,120 +3750,6 @@ udf_handler::~udf_handler()
|
|||||||
bool udf_handler::get_arguments() { return 0; }
|
bool udf_handler::get_arguments() { return 0; }
|
||||||
#endif /* HAVE_DLOPEN */
|
#endif /* HAVE_DLOPEN */
|
||||||
|
|
||||||
/*
|
|
||||||
** User level locks
|
|
||||||
*/
|
|
||||||
|
|
||||||
mysql_mutex_t LOCK_user_locks;
|
|
||||||
static HASH hash_user_locks;
|
|
||||||
|
|
||||||
class User_level_lock
|
|
||||||
{
|
|
||||||
uchar *key;
|
|
||||||
size_t key_length;
|
|
||||||
|
|
||||||
public:
|
|
||||||
int count;
|
|
||||||
bool locked;
|
|
||||||
mysql_cond_t cond;
|
|
||||||
my_thread_id thread_id;
|
|
||||||
void set_thread(THD *thd) { thread_id= thd->thread_id; }
|
|
||||||
|
|
||||||
User_level_lock(const uchar *key_arg,uint length, ulong id)
|
|
||||||
:key_length(length),count(1),locked(1), thread_id(id)
|
|
||||||
{
|
|
||||||
key= (uchar*) my_memdup(key_arg,length,MYF(0));
|
|
||||||
mysql_cond_init(key_user_level_lock_cond, &cond, NULL);
|
|
||||||
if (key)
|
|
||||||
{
|
|
||||||
if (my_hash_insert(&hash_user_locks,(uchar*) this))
|
|
||||||
{
|
|
||||||
my_free(key);
|
|
||||||
key=0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
~User_level_lock()
|
|
||||||
{
|
|
||||||
if (key)
|
|
||||||
{
|
|
||||||
my_hash_delete(&hash_user_locks,(uchar*) this);
|
|
||||||
my_free(key);
|
|
||||||
}
|
|
||||||
mysql_cond_destroy(&cond);
|
|
||||||
}
|
|
||||||
inline bool initialized() { return key != 0; }
|
|
||||||
friend void item_user_lock_release(User_level_lock *ull);
|
|
||||||
friend uchar *ull_get_key(const User_level_lock *ull, size_t *length,
|
|
||||||
my_bool not_used);
|
|
||||||
};
|
|
||||||
|
|
||||||
uchar *ull_get_key(const User_level_lock *ull, size_t *length,
|
|
||||||
my_bool not_used __attribute__((unused)))
|
|
||||||
{
|
|
||||||
*length= ull->key_length;
|
|
||||||
return ull->key;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef HAVE_PSI_INTERFACE
|
|
||||||
static PSI_mutex_key key_LOCK_user_locks;
|
|
||||||
|
|
||||||
static PSI_mutex_info all_user_mutexes[]=
|
|
||||||
{
|
|
||||||
{ &key_LOCK_user_locks, "LOCK_user_locks", PSI_FLAG_GLOBAL}
|
|
||||||
};
|
|
||||||
|
|
||||||
static void init_user_lock_psi_keys(void)
|
|
||||||
{
|
|
||||||
const char* category= "sql";
|
|
||||||
int count;
|
|
||||||
|
|
||||||
if (PSI_server == NULL)
|
|
||||||
return;
|
|
||||||
|
|
||||||
count= array_elements(all_user_mutexes);
|
|
||||||
PSI_server->register_mutex(category, all_user_mutexes, count);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
static bool item_user_lock_inited= 0;
|
|
||||||
|
|
||||||
void item_user_lock_init(void)
|
|
||||||
{
|
|
||||||
#ifdef HAVE_PSI_INTERFACE
|
|
||||||
init_user_lock_psi_keys();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
mysql_mutex_init(key_LOCK_user_locks, &LOCK_user_locks, MY_MUTEX_INIT_SLOW);
|
|
||||||
my_hash_init(&hash_user_locks,system_charset_info,
|
|
||||||
16,0,0,(my_hash_get_key) ull_get_key,NULL,0);
|
|
||||||
item_user_lock_inited= 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
void item_user_lock_free(void)
|
|
||||||
{
|
|
||||||
if (item_user_lock_inited)
|
|
||||||
{
|
|
||||||
item_user_lock_inited= 0;
|
|
||||||
my_hash_free(&hash_user_locks);
|
|
||||||
mysql_mutex_destroy(&LOCK_user_locks);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void item_user_lock_release(User_level_lock *ull)
|
|
||||||
{
|
|
||||||
ull->locked=0;
|
|
||||||
ull->thread_id= 0;
|
|
||||||
if (--ull->count)
|
|
||||||
mysql_cond_signal(&ull->cond);
|
|
||||||
else
|
|
||||||
delete ull;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
Wait until we are at or past the given position in the master binlog
|
|
||||||
on the slave.
|
|
||||||
*/
|
|
||||||
|
|
||||||
longlong Item_master_pos_wait::val_int()
|
longlong Item_master_pos_wait::val_int()
|
||||||
{
|
{
|
||||||
@ -4010,7 +3896,136 @@ int Interruptible_wait::wait(mysql_cond_t *cond, mysql_mutex_t *mutex)
|
|||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Get a user level lock. If the thread has an old lock this is first released.
|
For locks with EXPLICIT duration, MDL returns a new ticket
|
||||||
|
every time a lock is granted. This allows to implement recursive
|
||||||
|
locks without extra allocation or additional data structures, such
|
||||||
|
as below. However, if there are too many tickets in the same
|
||||||
|
MDL_context, MDL_context::find_ticket() is getting too slow,
|
||||||
|
since it's using a linear search.
|
||||||
|
This is why a separate structure is allocated for a user
|
||||||
|
level lock, and before requesting a new lock from MDL,
|
||||||
|
GET_LOCK() checks thd->ull_hash if such lock is already granted,
|
||||||
|
and if so, simply increments a reference counter.
|
||||||
|
*/
|
||||||
|
|
||||||
|
class User_level_lock
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
MDL_ticket *lock;
|
||||||
|
int refs;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/** Extract a hash key from User_level_lock. */
|
||||||
|
|
||||||
|
uchar *ull_get_key(const uchar *ptr, size_t *length,
|
||||||
|
my_bool not_used __attribute__((unused)))
|
||||||
|
{
|
||||||
|
User_level_lock *ull = (User_level_lock*) ptr;
|
||||||
|
MDL_key *key = ull->lock->get_key();
|
||||||
|
*length= key->length();
|
||||||
|
return (uchar*) key->ptr();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
Release all user level locks for this THD.
|
||||||
|
*/
|
||||||
|
|
||||||
|
void mysql_ull_cleanup(THD *thd)
|
||||||
|
{
|
||||||
|
User_level_lock *ull;
|
||||||
|
DBUG_ENTER("mysql_ull_cleanup");
|
||||||
|
|
||||||
|
for (uint i= 0; i < thd->ull_hash.records; i++)
|
||||||
|
{
|
||||||
|
ull = (User_level_lock*) my_hash_element(&thd->ull_hash, i);
|
||||||
|
thd->mdl_context.release_lock(ull->lock);
|
||||||
|
my_free(ull);
|
||||||
|
}
|
||||||
|
|
||||||
|
my_hash_free(&thd->ull_hash);
|
||||||
|
|
||||||
|
DBUG_VOID_RETURN;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
Set explicit duration for metadata locks corresponding to
|
||||||
|
user level locks to protect them from being released at the end
|
||||||
|
of transaction.
|
||||||
|
*/
|
||||||
|
|
||||||
|
void mysql_ull_set_explicit_lock_duration(THD *thd)
|
||||||
|
{
|
||||||
|
User_level_lock *ull;
|
||||||
|
DBUG_ENTER("mysql_ull_set_explicit_lock_duration");
|
||||||
|
|
||||||
|
for (uint i= 0; i < thd->ull_hash.records; i++)
|
||||||
|
{
|
||||||
|
ull= (User_level_lock*) my_hash_element(&thd->ull_hash, i);
|
||||||
|
thd->mdl_context.set_lock_duration(ull->lock, MDL_EXPLICIT);
|
||||||
|
}
|
||||||
|
DBUG_VOID_RETURN;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
When MDL detects a lock wait timeout, it pushes
|
||||||
|
an error into the statement diagnostics area.
|
||||||
|
For GET_LOCK(), lock wait timeout is not an error,
|
||||||
|
but a special return value (0). NULL is returned in
|
||||||
|
case of error.
|
||||||
|
Capture and suppress lock wait timeout.
|
||||||
|
*/
|
||||||
|
|
||||||
|
class Lock_wait_timeout_handler: public Internal_error_handler
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Lock_wait_timeout_handler() :m_lock_wait_timeout(false) {}
|
||||||
|
|
||||||
|
bool m_lock_wait_timeout;
|
||||||
|
|
||||||
|
bool handle_condition(THD * /* thd */, uint sql_errno,
|
||||||
|
const char * /* sqlstate */,
|
||||||
|
MYSQL_ERROR::enum_warning_level /* level */,
|
||||||
|
const char *message,
|
||||||
|
MYSQL_ERROR ** /* cond_hdl */);
|
||||||
|
};
|
||||||
|
|
||||||
|
bool
|
||||||
|
Lock_wait_timeout_handler::
|
||||||
|
handle_condition(THD * /* thd */, uint sql_errno,
|
||||||
|
const char * /* sqlstate */,
|
||||||
|
MYSQL_ERROR::enum_warning_level /* level */,
|
||||||
|
const char *message,
|
||||||
|
MYSQL_ERROR ** /* cond_hdl */)
|
||||||
|
{
|
||||||
|
if (sql_errno == ER_LOCK_WAIT_TIMEOUT)
|
||||||
|
{
|
||||||
|
m_lock_wait_timeout= true;
|
||||||
|
return true; /* condition handled */
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int ull_name_ok(String *name)
|
||||||
|
{
|
||||||
|
if (!name || !name->length())
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (name->length() > NAME_LEN)
|
||||||
|
{
|
||||||
|
my_error(ER_TOO_LONG_IDENT, MYF(0), name->c_ptr_safe());
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
Get a user level lock.
|
||||||
|
|
||||||
@retval
|
@retval
|
||||||
1 : Got lock
|
1 : Got lock
|
||||||
@ -4027,10 +4042,9 @@ longlong Item_func_get_lock::val_int()
|
|||||||
ulonglong timeout= args[1]->val_int();
|
ulonglong timeout= args[1]->val_int();
|
||||||
THD *thd= current_thd;
|
THD *thd= current_thd;
|
||||||
User_level_lock *ull;
|
User_level_lock *ull;
|
||||||
int error;
|
|
||||||
Interruptible_wait timed_cond(thd);
|
|
||||||
DBUG_ENTER("Item_func_get_lock::val_int");
|
DBUG_ENTER("Item_func_get_lock::val_int");
|
||||||
|
|
||||||
|
null_value= 1;
|
||||||
/*
|
/*
|
||||||
In slave thread no need to get locks, everything is serialized. Anyway
|
In slave thread no need to get locks, everything is serialized. Anyway
|
||||||
there is no way to make GET_LOCK() work on slave like it did on master
|
there is no way to make GET_LOCK() work on slave like it did on master
|
||||||
@ -4039,104 +4053,70 @@ longlong Item_func_get_lock::val_int()
|
|||||||
it's not guaranteed to be same as on master.
|
it's not guaranteed to be same as on master.
|
||||||
*/
|
*/
|
||||||
if (thd->slave_thread)
|
if (thd->slave_thread)
|
||||||
DBUG_RETURN(1);
|
|
||||||
|
|
||||||
mysql_mutex_lock(&LOCK_user_locks);
|
|
||||||
|
|
||||||
if (!res || !res->length())
|
|
||||||
{
|
{
|
||||||
mysql_mutex_unlock(&LOCK_user_locks);
|
null_value= 0;
|
||||||
null_value=1;
|
DBUG_RETURN(1);
|
||||||
DBUG_RETURN(0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!ull_name_ok(res))
|
||||||
|
DBUG_RETURN(0);
|
||||||
|
|
||||||
DBUG_PRINT("info", ("lock %.*s, thd=%ld", res->length(), res->ptr(),
|
DBUG_PRINT("info", ("lock %.*s, thd=%ld", res->length(), res->ptr(),
|
||||||
(long) thd->real_id));
|
(long) thd->real_id));
|
||||||
null_value=0;
|
/* HASH entries are of type User_level_lock. */
|
||||||
|
if (! my_hash_inited(&thd->ull_hash) &&
|
||||||
if (thd->ull)
|
my_hash_init(&thd->ull_hash, &my_charset_bin,
|
||||||
|
16 /* small hash */, 0, 0, ull_get_key, NULL, 0))
|
||||||
{
|
{
|
||||||
item_user_lock_release(thd->ull);
|
|
||||||
thd->ull=0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!(ull= ((User_level_lock *) my_hash_search(&hash_user_locks,
|
|
||||||
(uchar*) res->ptr(),
|
|
||||||
(size_t) res->length()))))
|
|
||||||
{
|
|
||||||
ull= new User_level_lock((uchar*) res->ptr(), (size_t) res->length(),
|
|
||||||
thd->thread_id);
|
|
||||||
if (!ull || !ull->initialized())
|
|
||||||
{
|
|
||||||
delete ull;
|
|
||||||
mysql_mutex_unlock(&LOCK_user_locks);
|
|
||||||
null_value=1; // Probably out of memory
|
|
||||||
DBUG_RETURN(0);
|
DBUG_RETURN(0);
|
||||||
}
|
}
|
||||||
ull->set_thread(thd);
|
|
||||||
thd->ull=ull;
|
|
||||||
mysql_mutex_unlock(&LOCK_user_locks);
|
|
||||||
DBUG_PRINT("info", ("made new lock"));
|
|
||||||
DBUG_RETURN(1); // Got new lock
|
|
||||||
}
|
|
||||||
ull->count++;
|
|
||||||
DBUG_PRINT("info", ("ull->count=%d", ull->count));
|
|
||||||
|
|
||||||
/*
|
MDL_request ull_request;
|
||||||
Structure is now initialized. Try to get the lock.
|
ull_request.init(MDL_key::USER_LOCK, res->c_ptr_safe(), "",
|
||||||
Set up control struct to allow others to abort locks.
|
MDL_SHARED_NO_WRITE, MDL_EXPLICIT);
|
||||||
*/
|
MDL_key *ull_key = &ull_request.key;
|
||||||
thd_proc_info(thd, "User lock");
|
|
||||||
thd->mysys_var->current_mutex= &LOCK_user_locks;
|
|
||||||
thd->mysys_var->current_cond= &ull->cond;
|
|
||||||
|
|
||||||
timed_cond.set_timeout(timeout * 1000000000ULL);
|
|
||||||
|
|
||||||
error= 0;
|
if ((ull= (User_level_lock*)
|
||||||
thd_wait_begin(thd, THD_WAIT_USER_LOCK);
|
my_hash_search(&thd->ull_hash, ull_key->ptr(), ull_key->length())))
|
||||||
while (ull->locked && !thd->killed)
|
|
||||||
{
|
{
|
||||||
DBUG_PRINT("info", ("waiting on lock"));
|
/* Recursive lock */
|
||||||
error= timed_cond.wait(&ull->cond, &LOCK_user_locks);
|
ull->refs++;
|
||||||
if (error == ETIMEDOUT || error == ETIME)
|
null_value = 0;
|
||||||
{
|
DBUG_RETURN(1);
|
||||||
DBUG_PRINT("info", ("lock wait timeout"));
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
error= 0;
|
|
||||||
}
|
|
||||||
thd_wait_end(thd);
|
|
||||||
|
|
||||||
if (ull->locked)
|
Lock_wait_timeout_handler lock_wait_timeout_handler;
|
||||||
|
thd->push_internal_handler(&lock_wait_timeout_handler);
|
||||||
|
bool error= thd->mdl_context.acquire_lock(&ull_request, timeout);
|
||||||
|
(void) thd->pop_internal_handler();
|
||||||
|
if (error)
|
||||||
{
|
{
|
||||||
if (!--ull->count)
|
if (lock_wait_timeout_handler.m_lock_wait_timeout)
|
||||||
{
|
null_value= 0;
|
||||||
DBUG_ASSERT(0);
|
DBUG_RETURN(0);
|
||||||
delete ull; // Should never happen
|
|
||||||
}
|
}
|
||||||
if (!error) // Killed (thd->killed != 0)
|
|
||||||
{
|
|
||||||
error=1;
|
|
||||||
null_value=1; // Return NULL
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else // We got the lock
|
|
||||||
{
|
|
||||||
ull->locked=1;
|
|
||||||
ull->set_thread(thd);
|
|
||||||
ull->thread_id= thd->thread_id;
|
|
||||||
thd->ull=ull;
|
|
||||||
error=0;
|
|
||||||
DBUG_PRINT("info", ("got the lock"));
|
|
||||||
}
|
|
||||||
mysql_mutex_unlock(&LOCK_user_locks);
|
|
||||||
|
|
||||||
mysql_mutex_lock(&thd->mysys_var->mutex);
|
ull= (User_level_lock*) my_malloc(sizeof(User_level_lock),
|
||||||
thd_proc_info(thd, 0);
|
MYF(MY_WME|MY_THREAD_SPECIFIC));
|
||||||
thd->mysys_var->current_mutex= 0;
|
if (ull == NULL)
|
||||||
thd->mysys_var->current_cond= 0;
|
{
|
||||||
mysql_mutex_unlock(&thd->mysys_var->mutex);
|
thd->mdl_context.release_lock(ull_request.ticket);
|
||||||
|
DBUG_RETURN(0);
|
||||||
|
}
|
||||||
|
|
||||||
DBUG_RETURN(!error ? 1 : 0);
|
ull->lock= ull_request.ticket;
|
||||||
|
ull->refs= 1;
|
||||||
|
|
||||||
|
if (my_hash_insert(&thd->ull_hash, (uchar*) ull))
|
||||||
|
{
|
||||||
|
thd->mdl_context.release_lock(ull->lock);
|
||||||
|
my_free(ull);
|
||||||
|
DBUG_RETURN(0);
|
||||||
|
}
|
||||||
|
null_value= 0;
|
||||||
|
|
||||||
|
DBUG_RETURN(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -4152,42 +4132,85 @@ longlong Item_func_release_lock::val_int()
|
|||||||
{
|
{
|
||||||
DBUG_ASSERT(fixed == 1);
|
DBUG_ASSERT(fixed == 1);
|
||||||
String *res= args[0]->val_str(&value);
|
String *res= args[0]->val_str(&value);
|
||||||
User_level_lock *ull;
|
|
||||||
longlong result;
|
|
||||||
THD *thd= current_thd;
|
THD *thd= current_thd;
|
||||||
DBUG_ENTER("Item_func_release_lock::val_int");
|
DBUG_ENTER("Item_func_release_lock::val_int");
|
||||||
if (!res || !res->length())
|
|
||||||
{
|
|
||||||
null_value= 1;
|
null_value= 1;
|
||||||
|
|
||||||
|
if (!ull_name_ok(res))
|
||||||
|
DBUG_RETURN(0);
|
||||||
|
|
||||||
|
DBUG_PRINT("info", ("lock %.*s", res->length(), res->ptr()));
|
||||||
|
|
||||||
|
MDL_key ull_key;
|
||||||
|
ull_key.mdl_key_init(MDL_key::USER_LOCK, res->c_ptr_safe(), "");
|
||||||
|
|
||||||
|
User_level_lock *ull;
|
||||||
|
|
||||||
|
if (!(ull=
|
||||||
|
(User_level_lock*) my_hash_search(&thd->ull_hash,
|
||||||
|
ull_key.ptr(), ull_key.length())))
|
||||||
|
{
|
||||||
|
null_value= thd->mdl_context.get_lock_owner(&ull_key) == 0;
|
||||||
DBUG_RETURN(0);
|
DBUG_RETURN(0);
|
||||||
}
|
}
|
||||||
DBUG_PRINT("info", ("lock %.*s", res->length(), res->ptr()));
|
|
||||||
null_value= 0;
|
null_value= 0;
|
||||||
|
if (--ull->refs == 0)
|
||||||
|
{
|
||||||
|
my_hash_delete(&thd->ull_hash, (uchar*) ull);
|
||||||
|
thd->mdl_context.release_lock(ull->lock);
|
||||||
|
my_free(ull);
|
||||||
|
}
|
||||||
|
DBUG_RETURN(1);
|
||||||
|
}
|
||||||
|
|
||||||
result=0;
|
|
||||||
mysql_mutex_lock(&LOCK_user_locks);
|
/**
|
||||||
if (!(ull= ((User_level_lock*) my_hash_search(&hash_user_locks,
|
Check a user level lock.
|
||||||
(const uchar*) res->ptr(),
|
|
||||||
(size_t) res->length()))))
|
Sets null_value=TRUE on error.
|
||||||
|
|
||||||
|
@retval
|
||||||
|
1 Available
|
||||||
|
@retval
|
||||||
|
0 Already taken, or error
|
||||||
|
*/
|
||||||
|
|
||||||
|
longlong Item_func_is_free_lock::val_int()
|
||||||
{
|
{
|
||||||
|
DBUG_ASSERT(fixed == 1);
|
||||||
|
String *res= args[0]->val_str(&value);
|
||||||
|
THD *thd= current_thd;
|
||||||
null_value= 1;
|
null_value= 1;
|
||||||
|
|
||||||
|
if (!ull_name_ok(res))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
MDL_key ull_key;
|
||||||
|
ull_key.mdl_key_init(MDL_key::USER_LOCK, res->c_ptr_safe(), "");
|
||||||
|
|
||||||
|
null_value= 0;
|
||||||
|
return thd->mdl_context.get_lock_owner(&ull_key) == 0;
|
||||||
}
|
}
|
||||||
else
|
|
||||||
|
|
||||||
|
longlong Item_func_is_used_lock::val_int()
|
||||||
{
|
{
|
||||||
DBUG_PRINT("info", ("ull->locked=%d ull->thread=%lu thd=%lu",
|
DBUG_ASSERT(fixed == 1);
|
||||||
(int) ull->locked,
|
String *res= args[0]->val_str(&value);
|
||||||
(long)ull->thread_id,
|
THD *thd= current_thd;
|
||||||
(long)thd->thread_id));
|
null_value= 1;
|
||||||
if (ull->locked && current_thd->thread_id == ull->thread_id)
|
|
||||||
{
|
if (!ull_name_ok(res))
|
||||||
DBUG_PRINT("info", ("release lock"));
|
return 0;
|
||||||
result=1; // Release is ok
|
|
||||||
item_user_lock_release(ull);
|
MDL_key ull_key;
|
||||||
thd->ull=0;
|
ull_key.mdl_key_init(MDL_key::USER_LOCK, res->c_ptr_safe(), "");
|
||||||
}
|
ulong thread_id = thd->mdl_context.get_lock_owner(&ull_key);
|
||||||
}
|
if (thread_id == 0)
|
||||||
mysql_mutex_unlock(&LOCK_user_locks);
|
return 0;
|
||||||
DBUG_RETURN(result);
|
|
||||||
|
null_value= 0;
|
||||||
|
return thread_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -4288,6 +4311,54 @@ void Item_func_benchmark::print(String *str, enum_query_type query_type)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
mysql_mutex_t LOCK_item_func_sleep;
|
||||||
|
|
||||||
|
#ifdef HAVE_PSI_INTERFACE
|
||||||
|
static PSI_mutex_key key_LOCK_item_func_sleep;
|
||||||
|
|
||||||
|
static PSI_mutex_info item_func_sleep_mutexes[]=
|
||||||
|
{
|
||||||
|
{ &key_LOCK_item_func_sleep, "LOCK_user_locks", PSI_FLAG_GLOBAL}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
static void init_item_func_sleep_psi_keys(void)
|
||||||
|
{
|
||||||
|
const char* category= "sql";
|
||||||
|
int count;
|
||||||
|
|
||||||
|
if (PSI_server == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
count= array_elements(item_func_sleep_mutexes);
|
||||||
|
PSI_server->register_mutex(category, item_func_sleep_mutexes, count);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static bool item_func_sleep_inited= 0;
|
||||||
|
|
||||||
|
|
||||||
|
void item_func_sleep_init(void)
|
||||||
|
{
|
||||||
|
#ifdef HAVE_PSI_INTERFACE
|
||||||
|
init_item_func_sleep_psi_keys();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
mysql_mutex_init(key_LOCK_item_func_sleep, &LOCK_item_func_sleep, MY_MUTEX_INIT_SLOW);
|
||||||
|
item_func_sleep_inited= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void item_func_sleep_free(void)
|
||||||
|
{
|
||||||
|
if (item_func_sleep_inited)
|
||||||
|
{
|
||||||
|
item_func_sleep_inited= 0;
|
||||||
|
mysql_mutex_destroy(&LOCK_item_func_sleep);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/** This function is just used to create tests with time gaps. */
|
/** This function is just used to create tests with time gaps. */
|
||||||
|
|
||||||
longlong Item_func_sleep::val_int()
|
longlong Item_func_sleep::val_int()
|
||||||
@ -4316,24 +4387,24 @@ longlong Item_func_sleep::val_int()
|
|||||||
timed_cond.set_timeout((ulonglong) (timeout * 1000000000.0));
|
timed_cond.set_timeout((ulonglong) (timeout * 1000000000.0));
|
||||||
|
|
||||||
mysql_cond_init(key_item_func_sleep_cond, &cond, NULL);
|
mysql_cond_init(key_item_func_sleep_cond, &cond, NULL);
|
||||||
mysql_mutex_lock(&LOCK_user_locks);
|
mysql_mutex_lock(&LOCK_item_func_sleep);
|
||||||
|
|
||||||
thd_proc_info(thd, "User sleep");
|
thd_proc_info(thd, "User sleep");
|
||||||
thd->mysys_var->current_mutex= &LOCK_user_locks;
|
thd->mysys_var->current_mutex= &LOCK_item_func_sleep;
|
||||||
thd->mysys_var->current_cond= &cond;
|
thd->mysys_var->current_cond= &cond;
|
||||||
|
|
||||||
error= 0;
|
error= 0;
|
||||||
thd_wait_begin(thd, THD_WAIT_SLEEP);
|
thd_wait_begin(thd, THD_WAIT_SLEEP);
|
||||||
while (!thd->killed)
|
while (!thd->killed)
|
||||||
{
|
{
|
||||||
error= timed_cond.wait(&cond, &LOCK_user_locks);
|
error= timed_cond.wait(&cond, &LOCK_item_func_sleep);
|
||||||
if (error == ETIMEDOUT || error == ETIME)
|
if (error == ETIMEDOUT || error == ETIME)
|
||||||
break;
|
break;
|
||||||
error= 0;
|
error= 0;
|
||||||
}
|
}
|
||||||
thd_wait_end(thd);
|
thd_wait_end(thd);
|
||||||
thd_proc_info(thd, 0);
|
thd_proc_info(thd, 0);
|
||||||
mysql_mutex_unlock(&LOCK_user_locks);
|
mysql_mutex_unlock(&LOCK_item_func_sleep);
|
||||||
mysql_mutex_lock(&thd->mysys_var->mutex);
|
mysql_mutex_lock(&thd->mysys_var->mutex);
|
||||||
thd->mysys_var->current_mutex= 0;
|
thd->mysys_var->current_mutex= 0;
|
||||||
thd->mysys_var->current_cond= 0;
|
thd->mysys_var->current_cond= 0;
|
||||||
@ -6208,61 +6279,6 @@ Item *get_system_var(THD *thd, enum_var_type var_type, LEX_STRING name,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
Check a user level lock.
|
|
||||||
|
|
||||||
Sets null_value=TRUE on error.
|
|
||||||
|
|
||||||
@retval
|
|
||||||
1 Available
|
|
||||||
@retval
|
|
||||||
0 Already taken, or error
|
|
||||||
*/
|
|
||||||
|
|
||||||
longlong Item_func_is_free_lock::val_int()
|
|
||||||
{
|
|
||||||
DBUG_ASSERT(fixed == 1);
|
|
||||||
String *res=args[0]->val_str(&value);
|
|
||||||
User_level_lock *ull;
|
|
||||||
|
|
||||||
null_value=0;
|
|
||||||
if (!res || !res->length())
|
|
||||||
{
|
|
||||||
null_value=1;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
mysql_mutex_lock(&LOCK_user_locks);
|
|
||||||
ull= (User_level_lock *) my_hash_search(&hash_user_locks, (uchar*) res->ptr(),
|
|
||||||
(size_t) res->length());
|
|
||||||
mysql_mutex_unlock(&LOCK_user_locks);
|
|
||||||
if (!ull || !ull->locked)
|
|
||||||
return 1;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
longlong Item_func_is_used_lock::val_int()
|
|
||||||
{
|
|
||||||
DBUG_ASSERT(fixed == 1);
|
|
||||||
String *res=args[0]->val_str(&value);
|
|
||||||
User_level_lock *ull;
|
|
||||||
|
|
||||||
null_value=1;
|
|
||||||
if (!res || !res->length())
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
mysql_mutex_lock(&LOCK_user_locks);
|
|
||||||
ull= (User_level_lock *) my_hash_search(&hash_user_locks, (uchar*) res->ptr(),
|
|
||||||
(size_t) res->length());
|
|
||||||
mysql_mutex_unlock(&LOCK_user_locks);
|
|
||||||
if (!ull || !ull->locked)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
null_value=0;
|
|
||||||
return ull->thread_id;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
longlong Item_func_row_count::val_int()
|
longlong Item_func_row_count::val_int()
|
||||||
{
|
{
|
||||||
DBUG_ASSERT(fixed == 1);
|
DBUG_ASSERT(fixed == 1);
|
||||||
|
@ -1257,6 +1257,9 @@ public:
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
void item_func_sleep_init(void);
|
||||||
|
void item_func_sleep_free(void);
|
||||||
|
|
||||||
class Item_func_sleep :public Item_int_func
|
class Item_func_sleep :public Item_int_func
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@ -1506,14 +1509,8 @@ public:
|
|||||||
|
|
||||||
#endif /* HAVE_DLOPEN */
|
#endif /* HAVE_DLOPEN */
|
||||||
|
|
||||||
/*
|
void mysql_ull_cleanup(THD *thd);
|
||||||
** User level locks
|
void mysql_ull_set_explicit_lock_duration(THD *thd);
|
||||||
*/
|
|
||||||
|
|
||||||
class User_level_lock;
|
|
||||||
void item_user_lock_init(void);
|
|
||||||
void item_user_lock_release(User_level_lock *ull);
|
|
||||||
void item_user_lock_free(void);
|
|
||||||
|
|
||||||
class Item_func_get_lock :public Item_int_func
|
class Item_func_get_lock :public Item_int_func
|
||||||
{
|
{
|
||||||
|
100
sql/mdl.cc
100
sql/mdl.cc
@ -85,7 +85,8 @@ const char *MDL_key::m_namespace_to_wait_state_name[NAMESPACE_END]=
|
|||||||
"Waiting for stored procedure metadata lock",
|
"Waiting for stored procedure metadata lock",
|
||||||
"Waiting for trigger metadata lock",
|
"Waiting for trigger metadata lock",
|
||||||
"Waiting for event metadata lock",
|
"Waiting for event metadata lock",
|
||||||
"Waiting for commit lock"
|
"Waiting for commit lock",
|
||||||
|
"User lock" /* Be compatible with old status. */
|
||||||
};
|
};
|
||||||
|
|
||||||
static bool mdl_initialized= 0;
|
static bool mdl_initialized= 0;
|
||||||
@ -107,6 +108,7 @@ public:
|
|||||||
void init();
|
void init();
|
||||||
void destroy();
|
void destroy();
|
||||||
MDL_lock *find_or_insert(const MDL_key *key);
|
MDL_lock *find_or_insert(const MDL_key *key);
|
||||||
|
unsigned long get_lock_owner(const MDL_key *key);
|
||||||
void remove(MDL_lock *lock);
|
void remove(MDL_lock *lock);
|
||||||
private:
|
private:
|
||||||
bool move_from_hash_to_lock_mutex(MDL_lock *lock);
|
bool move_from_hash_to_lock_mutex(MDL_lock *lock);
|
||||||
@ -382,6 +384,7 @@ public:
|
|||||||
bool ignore_lock_priority) const;
|
bool ignore_lock_priority) const;
|
||||||
|
|
||||||
inline static MDL_lock *create(const MDL_key *key);
|
inline static MDL_lock *create(const MDL_key *key);
|
||||||
|
inline unsigned long get_lock_owner() const;
|
||||||
|
|
||||||
void reschedule_waiters();
|
void reschedule_waiters();
|
||||||
|
|
||||||
@ -856,6 +859,43 @@ bool MDL_map::move_from_hash_to_lock_mutex(MDL_lock *lock)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return thread id of the owner of the lock, if it is owned.
|
||||||
|
*/
|
||||||
|
|
||||||
|
unsigned long
|
||||||
|
MDL_map::get_lock_owner(const MDL_key *mdl_key)
|
||||||
|
{
|
||||||
|
MDL_lock *lock;
|
||||||
|
unsigned long res= 0;
|
||||||
|
|
||||||
|
if (mdl_key->mdl_namespace() == MDL_key::GLOBAL ||
|
||||||
|
mdl_key->mdl_namespace() == MDL_key::COMMIT)
|
||||||
|
{
|
||||||
|
lock= (mdl_key->mdl_namespace() == MDL_key::GLOBAL) ? m_global_lock :
|
||||||
|
m_commit_lock;
|
||||||
|
mysql_prlock_rdlock(&lock->m_rwlock);
|
||||||
|
res= lock->get_lock_owner();
|
||||||
|
mysql_prlock_unlock(&lock->m_rwlock);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
my_hash_value_type hash_value= my_calc_hash(&m_locks,
|
||||||
|
mdl_key->ptr(),
|
||||||
|
mdl_key->length());
|
||||||
|
mysql_mutex_lock(&m_mutex);
|
||||||
|
lock= (MDL_lock*) my_hash_search_using_hash_value(&m_locks,
|
||||||
|
hash_value,
|
||||||
|
mdl_key->ptr(),
|
||||||
|
mdl_key->length());
|
||||||
|
if (lock)
|
||||||
|
res= lock->get_lock_owner();
|
||||||
|
mysql_mutex_unlock(&m_mutex);
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Destroy MDL_lock object or delegate this responsibility to
|
Destroy MDL_lock object or delegate this responsibility to
|
||||||
whatever thread that holds the last outstanding reference to
|
whatever thread that holds the last outstanding reference to
|
||||||
@ -1621,6 +1661,23 @@ MDL_lock::can_grant_lock(enum_mdl_type type_arg,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
Return thread id of the thread to which the first ticket was
|
||||||
|
granted.
|
||||||
|
*/
|
||||||
|
|
||||||
|
inline unsigned long
|
||||||
|
MDL_lock::get_lock_owner() const
|
||||||
|
{
|
||||||
|
Ticket_iterator it(m_granted);
|
||||||
|
MDL_ticket *ticket;
|
||||||
|
|
||||||
|
if ((ticket= it++))
|
||||||
|
return thd_get_thread_id(ticket->get_ctx()->get_thd());
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/** Remove a ticket from waiting or pending queue and wakeup up waiters. */
|
/** Remove a ticket from waiting or pending queue and wakeup up waiters. */
|
||||||
|
|
||||||
void MDL_lock::remove_ticket(Ticket_list MDL_lock::*list, MDL_ticket *ticket)
|
void MDL_lock::remove_ticket(Ticket_list MDL_lock::*list, MDL_ticket *ticket)
|
||||||
@ -2094,8 +2151,6 @@ MDL_context::acquire_lock(MDL_request *mdl_request, ulong lock_wait_timeout)
|
|||||||
|
|
||||||
find_deadlock();
|
find_deadlock();
|
||||||
|
|
||||||
if (lock->needs_notification(ticket))
|
|
||||||
{
|
|
||||||
struct timespec abs_shortwait;
|
struct timespec abs_shortwait;
|
||||||
set_timespec(abs_shortwait, 1);
|
set_timespec(abs_shortwait, 1);
|
||||||
wait_status= MDL_wait::EMPTY;
|
wait_status= MDL_wait::EMPTY;
|
||||||
@ -2108,8 +2163,20 @@ MDL_context::acquire_lock(MDL_request *mdl_request, ulong lock_wait_timeout)
|
|||||||
|
|
||||||
if (wait_status != MDL_wait::EMPTY)
|
if (wait_status != MDL_wait::EMPTY)
|
||||||
break;
|
break;
|
||||||
|
/* Check if the client is gone while we were waiting. */
|
||||||
|
if (! thd_is_connected(m_thd))
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* The client is disconnected. Don't wait forever:
|
||||||
|
* assume it's the same as a wait timeout, this
|
||||||
|
* ensures all error handling is correct.
|
||||||
|
*/
|
||||||
|
wait_status= MDL_wait::TIMEOUT;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
mysql_prlock_wrlock(&lock->m_rwlock);
|
mysql_prlock_wrlock(&lock->m_rwlock);
|
||||||
|
if (lock->needs_notification(ticket))
|
||||||
lock->notify_conflicting_locks(this);
|
lock->notify_conflicting_locks(this);
|
||||||
mysql_prlock_unlock(&lock->m_rwlock);
|
mysql_prlock_unlock(&lock->m_rwlock);
|
||||||
set_timespec(abs_shortwait, 1);
|
set_timespec(abs_shortwait, 1);
|
||||||
@ -2117,10 +2184,6 @@ MDL_context::acquire_lock(MDL_request *mdl_request, ulong lock_wait_timeout)
|
|||||||
if (wait_status == MDL_wait::EMPTY)
|
if (wait_status == MDL_wait::EMPTY)
|
||||||
wait_status= m_wait.timed_wait(m_thd, &abs_timeout, TRUE,
|
wait_status= m_wait.timed_wait(m_thd, &abs_timeout, TRUE,
|
||||||
mdl_request->key.get_wait_state_name());
|
mdl_request->key.get_wait_state_name());
|
||||||
}
|
|
||||||
else
|
|
||||||
wait_status= m_wait.timed_wait(m_thd, &abs_timeout, TRUE,
|
|
||||||
mdl_request->key.get_wait_state_name());
|
|
||||||
|
|
||||||
done_waiting_for();
|
done_waiting_for();
|
||||||
|
|
||||||
@ -2613,7 +2676,7 @@ void MDL_context::release_lock(MDL_ticket *ticket)
|
|||||||
the corresponding lists, i.e. stored in reverse temporal order.
|
the corresponding lists, i.e. stored in reverse temporal order.
|
||||||
This allows to employ this function to:
|
This allows to employ this function to:
|
||||||
- back off in case of a lock conflict.
|
- back off in case of a lock conflict.
|
||||||
- release all locks in the end of a statment or transaction
|
- release all locks in the end of a statement or transaction
|
||||||
- rollback to a savepoint.
|
- rollback to a savepoint.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@ -2724,6 +2787,22 @@ MDL_context::is_lock_owner(MDL_key::enum_mdl_namespace mdl_namespace,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
Return thread id of the owner of the lock or 0 if
|
||||||
|
there is no owner.
|
||||||
|
@note: Lock type is not considered at all, the function
|
||||||
|
simply checks that there is some lock for the given key.
|
||||||
|
|
||||||
|
@return thread id of the owner of the lock or 0
|
||||||
|
*/
|
||||||
|
|
||||||
|
unsigned long
|
||||||
|
MDL_context::get_lock_owner(MDL_key *key)
|
||||||
|
{
|
||||||
|
return mdl_locks.get_lock_owner(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Check if we have any pending locks which conflict with existing shared lock.
|
Check if we have any pending locks which conflict with existing shared lock.
|
||||||
|
|
||||||
@ -2737,6 +2816,11 @@ bool MDL_ticket::has_pending_conflicting_lock() const
|
|||||||
return m_lock->has_pending_conflicting_lock(m_type);
|
return m_lock->has_pending_conflicting_lock(m_type);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Return a key identifying this lock. */
|
||||||
|
MDL_key *MDL_ticket::get_key() const
|
||||||
|
{
|
||||||
|
return &m_lock->key;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Releases metadata locks that were acquired after a specific savepoint.
|
Releases metadata locks that were acquired after a specific savepoint.
|
||||||
|
43
sql/mdl.h
43
sql/mdl.h
@ -212,6 +212,7 @@ public:
|
|||||||
TRIGGER,
|
TRIGGER,
|
||||||
EVENT,
|
EVENT,
|
||||||
COMMIT,
|
COMMIT,
|
||||||
|
USER_LOCK, /* user level locks. */
|
||||||
/* This should be the last ! */
|
/* This should be the last ! */
|
||||||
NAMESPACE_END };
|
NAMESPACE_END };
|
||||||
|
|
||||||
@ -492,6 +493,7 @@ public:
|
|||||||
}
|
}
|
||||||
enum_mdl_type get_type() const { return m_type; }
|
enum_mdl_type get_type() const { return m_type; }
|
||||||
MDL_lock *get_lock() const { return m_lock; }
|
MDL_lock *get_lock() const { return m_lock; }
|
||||||
|
MDL_key *get_key() const;
|
||||||
void downgrade_exclusive_lock(enum_mdl_type type);
|
void downgrade_exclusive_lock(enum_mdl_type type);
|
||||||
|
|
||||||
bool has_stronger_or_equal_type(enum_mdl_type type) const;
|
bool has_stronger_or_equal_type(enum_mdl_type type) const;
|
||||||
@ -653,6 +655,7 @@ public:
|
|||||||
bool is_lock_owner(MDL_key::enum_mdl_namespace mdl_namespace,
|
bool is_lock_owner(MDL_key::enum_mdl_namespace mdl_namespace,
|
||||||
const char *db, const char *name,
|
const char *db, const char *name,
|
||||||
enum_mdl_type mdl_type);
|
enum_mdl_type mdl_type);
|
||||||
|
unsigned long get_lock_owner(MDL_key *mdl_key);
|
||||||
|
|
||||||
bool has_lock(const MDL_savepoint &mdl_savepoint, MDL_ticket *mdl_ticket);
|
bool has_lock(const MDL_savepoint &mdl_savepoint, MDL_ticket *mdl_ticket);
|
||||||
|
|
||||||
@ -721,9 +724,9 @@ private:
|
|||||||
Lists of MDL tickets:
|
Lists of MDL tickets:
|
||||||
---------------------
|
---------------------
|
||||||
The entire set of locks acquired by a connection can be separated
|
The entire set of locks acquired by a connection can be separated
|
||||||
in three subsets according to their: locks released at the end of
|
in three subsets according to their duration: locks released at
|
||||||
statement, at the end of transaction and locks are released
|
the end of statement, at the end of transaction and locks are
|
||||||
explicitly.
|
released explicitly.
|
||||||
|
|
||||||
Statement and transactional locks are locks with automatic scope.
|
Statement and transactional locks are locks with automatic scope.
|
||||||
They are accumulated in the course of a transaction, and released
|
They are accumulated in the course of a transaction, and released
|
||||||
@ -732,11 +735,12 @@ private:
|
|||||||
locks). They must not be (and never are) released manually,
|
locks). They must not be (and never are) released manually,
|
||||||
i.e. with release_lock() call.
|
i.e. with release_lock() call.
|
||||||
|
|
||||||
Locks with explicit duration are taken for locks that span
|
Tickets with explicit duration are taken for locks that span
|
||||||
multiple transactions or savepoints.
|
multiple transactions or savepoints.
|
||||||
These are: HANDLER SQL locks (HANDLER SQL is
|
These are: HANDLER SQL locks (HANDLER SQL is
|
||||||
transaction-agnostic), LOCK TABLES locks (you can COMMIT/etc
|
transaction-agnostic), LOCK TABLES locks (you can COMMIT/etc
|
||||||
under LOCK TABLES, and the locked tables stay locked), and
|
under LOCK TABLES, and the locked tables stay locked), user level
|
||||||
|
locks (GET_LOCK()/RELEASE_LOCK() functions) and
|
||||||
locks implementing "global read lock".
|
locks implementing "global read lock".
|
||||||
|
|
||||||
Statement/transactional locks are always prepended to the
|
Statement/transactional locks are always prepended to the
|
||||||
@ -745,20 +749,19 @@ private:
|
|||||||
a savepoint, we start popping and releasing tickets from the
|
a savepoint, we start popping and releasing tickets from the
|
||||||
front until we reach the last ticket acquired after the savepoint.
|
front until we reach the last ticket acquired after the savepoint.
|
||||||
|
|
||||||
Locks with explicit duration stored are not stored in any
|
Locks with explicit duration are not stored in any
|
||||||
particular order, and among each other can be split into
|
particular order, and among each other can be split into
|
||||||
three sets:
|
four sets:
|
||||||
|
|
||||||
[LOCK TABLES locks] [HANDLER locks] [GLOBAL READ LOCK locks]
|
[LOCK TABLES locks] [USER locks] [HANDLER locks] [GLOBAL READ LOCK locks]
|
||||||
|
|
||||||
The following is known about these sets:
|
The following is known about these sets:
|
||||||
|
|
||||||
* GLOBAL READ LOCK locks are always stored after LOCK TABLES
|
* GLOBAL READ LOCK locks are always stored last.
|
||||||
locks and after HANDLER locks. This is because one can't say
|
This is because one can't say SET GLOBAL read_only=1 or
|
||||||
SET GLOBAL read_only=1 or FLUSH TABLES WITH READ LOCK
|
FLUSH TABLES WITH READ LOCK if one has locked tables. One can,
|
||||||
if one has locked tables. One can, however, LOCK TABLES
|
however, LOCK TABLES after having entered the read only mode.
|
||||||
after having entered the read only mode. Note, that
|
Note, that subsequent LOCK TABLES statement will unlock the previous
|
||||||
subsequent LOCK TABLES statement will unlock the previous
|
|
||||||
set of tables, but not the GRL!
|
set of tables, but not the GRL!
|
||||||
There are no HANDLER locks after GRL locks because
|
There are no HANDLER locks after GRL locks because
|
||||||
SET GLOBAL read_only performs a FLUSH TABLES WITH
|
SET GLOBAL read_only performs a FLUSH TABLES WITH
|
||||||
@ -853,6 +856,18 @@ extern bool mysql_notify_thread_having_shared_lock(THD *thd, THD *in_use,
|
|||||||
extern "C" const char* thd_enter_cond(MYSQL_THD thd, mysql_cond_t *cond,
|
extern "C" const char* thd_enter_cond(MYSQL_THD thd, mysql_cond_t *cond,
|
||||||
mysql_mutex_t *mutex, const char *msg);
|
mysql_mutex_t *mutex, const char *msg);
|
||||||
extern "C" void thd_exit_cond(MYSQL_THD thd, const char *old_msg);
|
extern "C" void thd_exit_cond(MYSQL_THD thd, const char *old_msg);
|
||||||
|
extern "C" unsigned long thd_get_thread_id(const MYSQL_THD thd);
|
||||||
|
|
||||||
|
/**
|
||||||
|
Check if a connection in question is no longer connected.
|
||||||
|
|
||||||
|
@details
|
||||||
|
Replication apply thread is always connected. Otherwise,
|
||||||
|
does a poll on the associated socket to check if the client
|
||||||
|
is gone.
|
||||||
|
*/
|
||||||
|
extern "C" int thd_is_connected(MYSQL_THD thd);
|
||||||
|
|
||||||
|
|
||||||
#ifndef DBUG_OFF
|
#ifndef DBUG_OFF
|
||||||
extern mysql_mutex_t LOCK_open;
|
extern mysql_mutex_t LOCK_open;
|
||||||
|
@ -1824,7 +1824,7 @@ void clean_up(bool print_message)
|
|||||||
#endif
|
#endif
|
||||||
query_cache_destroy();
|
query_cache_destroy();
|
||||||
hostname_cache_free();
|
hostname_cache_free();
|
||||||
item_user_lock_free();
|
item_func_sleep_free();
|
||||||
lex_free(); /* Free some memory */
|
lex_free(); /* Free some memory */
|
||||||
item_create_cleanup();
|
item_create_cleanup();
|
||||||
if (!opt_noacl)
|
if (!opt_noacl)
|
||||||
|
@ -335,7 +335,7 @@ extern MYSQL_PLUGIN_IMPORT key_map key_map_full; /* Should be threaded
|
|||||||
Server mutex locks and condition variables.
|
Server mutex locks and condition variables.
|
||||||
*/
|
*/
|
||||||
extern mysql_mutex_t
|
extern mysql_mutex_t
|
||||||
LOCK_user_locks, LOCK_status,
|
LOCK_item_func_sleep, LOCK_status,
|
||||||
LOCK_error_log, LOCK_delayed_insert, LOCK_short_uuid_generator,
|
LOCK_error_log, LOCK_delayed_insert, LOCK_short_uuid_generator,
|
||||||
LOCK_delayed_status, LOCK_delayed_create, LOCK_crypt, LOCK_timezone,
|
LOCK_delayed_status, LOCK_delayed_create, LOCK_crypt, LOCK_timezone,
|
||||||
LOCK_slave_list, LOCK_active_mi, LOCK_manager,
|
LOCK_slave_list, LOCK_active_mi, LOCK_manager,
|
||||||
|
@ -827,6 +827,7 @@ THD::THD()
|
|||||||
col_access=0;
|
col_access=0;
|
||||||
is_slave_error= thread_specific_used= FALSE;
|
is_slave_error= thread_specific_used= FALSE;
|
||||||
my_hash_clear(&handler_tables_hash);
|
my_hash_clear(&handler_tables_hash);
|
||||||
|
my_hash_clear(&ull_hash);
|
||||||
tmp_table=0;
|
tmp_table=0;
|
||||||
cuted_fields= 0L;
|
cuted_fields= 0L;
|
||||||
sent_row_count= 0L;
|
sent_row_count= 0L;
|
||||||
@ -866,7 +867,6 @@ THD::THD()
|
|||||||
net.vio=0;
|
net.vio=0;
|
||||||
net.buff= 0;
|
net.buff= 0;
|
||||||
client_capabilities= 0; // minimalistic client
|
client_capabilities= 0; // minimalistic client
|
||||||
ull=0;
|
|
||||||
system_thread= NON_SYSTEM_THREAD;
|
system_thread= NON_SYSTEM_THREAD;
|
||||||
cleanup_done= abort_on_warning= 0;
|
cleanup_done= abort_on_warning= 0;
|
||||||
peer_port= 0; // For SHOW PROCESSLIST
|
peer_port= 0; // For SHOW PROCESSLIST
|
||||||
@ -1400,8 +1400,6 @@ void THD::cleanup(void)
|
|||||||
if (global_read_lock.is_acquired())
|
if (global_read_lock.is_acquired())
|
||||||
global_read_lock.unlock_global_read_lock(this);
|
global_read_lock.unlock_global_read_lock(this);
|
||||||
|
|
||||||
/* All metadata locks must have been released by now. */
|
|
||||||
DBUG_ASSERT(!mdl_context.has_locks());
|
|
||||||
if (user_connect)
|
if (user_connect)
|
||||||
{
|
{
|
||||||
decrease_user_connections(user_connect);
|
decrease_user_connections(user_connect);
|
||||||
@ -1419,13 +1417,9 @@ void THD::cleanup(void)
|
|||||||
sp_cache_clear(&sp_proc_cache);
|
sp_cache_clear(&sp_proc_cache);
|
||||||
sp_cache_clear(&sp_func_cache);
|
sp_cache_clear(&sp_func_cache);
|
||||||
|
|
||||||
if (ull)
|
mysql_ull_cleanup(this);
|
||||||
{
|
/* All metadata locks must have been released by now. */
|
||||||
mysql_mutex_lock(&LOCK_user_locks);
|
DBUG_ASSERT(!mdl_context.has_locks());
|
||||||
item_user_lock_release(ull);
|
|
||||||
mysql_mutex_unlock(&LOCK_user_locks);
|
|
||||||
ull= NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
apc_target.destroy();
|
apc_target.destroy();
|
||||||
cleanup_done=1;
|
cleanup_done=1;
|
||||||
@ -4001,6 +3995,15 @@ extern "C" unsigned long thd_get_thread_id(const MYSQL_THD thd)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
Check if THD socket is still connected.
|
||||||
|
*/
|
||||||
|
extern "C" int thd_is_connected(MYSQL_THD thd)
|
||||||
|
{
|
||||||
|
return thd->is_connected();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
#ifdef INNODB_COMPATIBILITY_HOOKS
|
#ifdef INNODB_COMPATIBILITY_HOOKS
|
||||||
extern "C" const struct charset_info_st *thd_charset(MYSQL_THD thd)
|
extern "C" const struct charset_info_st *thd_charset(MYSQL_THD thd)
|
||||||
{
|
{
|
||||||
@ -4321,6 +4324,8 @@ void THD::leave_locked_tables_mode()
|
|||||||
/* Also ensure that we don't release metadata locks for open HANDLERs. */
|
/* Also ensure that we don't release metadata locks for open HANDLERs. */
|
||||||
if (handler_tables_hash.records)
|
if (handler_tables_hash.records)
|
||||||
mysql_ha_set_explicit_lock_duration(this);
|
mysql_ha_set_explicit_lock_duration(this);
|
||||||
|
if (ull_hash.records)
|
||||||
|
mysql_ull_set_explicit_lock_duration(this);
|
||||||
}
|
}
|
||||||
locked_tables_mode= LTM_NONE;
|
locked_tables_mode= LTM_NONE;
|
||||||
}
|
}
|
||||||
|
@ -57,7 +57,6 @@ class Lex_input_stream;
|
|||||||
class Parser_state;
|
class Parser_state;
|
||||||
class Rows_log_event;
|
class Rows_log_event;
|
||||||
class Sroutine_hash_entry;
|
class Sroutine_hash_entry;
|
||||||
class User_level_lock;
|
|
||||||
class user_var_entry;
|
class user_var_entry;
|
||||||
|
|
||||||
enum enum_enable_or_disable { LEAVE_AS_IS, ENABLE, DISABLE };
|
enum enum_enable_or_disable { LEAVE_AS_IS, ENABLE, DISABLE };
|
||||||
@ -1682,11 +1681,11 @@ public:
|
|||||||
|
|
||||||
HASH handler_tables_hash;
|
HASH handler_tables_hash;
|
||||||
/*
|
/*
|
||||||
One thread can hold up to one named user-level lock. This variable
|
A thread can hold named user-level locks. This variable
|
||||||
points to a lock object if the lock is present. See item_func.cc and
|
contains granted tickets if a lock is present. See item_func.cc and
|
||||||
chapter 'Miscellaneous functions', for functions GET_LOCK, RELEASE_LOCK.
|
chapter 'Miscellaneous functions', for functions GET_LOCK, RELEASE_LOCK.
|
||||||
*/
|
*/
|
||||||
User_level_lock *ull;
|
HASH ull_hash;
|
||||||
#ifndef DBUG_OFF
|
#ifndef DBUG_OFF
|
||||||
uint dbug_sentry; // watch out for memory corruption
|
uint dbug_sentry; // watch out for memory corruption
|
||||||
#endif
|
#endif
|
||||||
|
@ -205,6 +205,7 @@ bool reload_acl_and_cache(THD *thd, unsigned long options,
|
|||||||
DBUG_ASSERT(!thd || thd->locked_tables_mode ||
|
DBUG_ASSERT(!thd || thd->locked_tables_mode ||
|
||||||
!thd->mdl_context.has_locks() ||
|
!thd->mdl_context.has_locks() ||
|
||||||
thd->handler_tables_hash.records ||
|
thd->handler_tables_hash.records ||
|
||||||
|
thd->ull_hash.records ||
|
||||||
thd->global_read_lock.is_acquired());
|
thd->global_read_lock.is_acquired());
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
Loading…
x
Reference in New Issue
Block a user