diff options
-rw-r--r-- | include/my_pthread.h | 40 | ||||
-rw-r--r-- | include/thr_lock.h | 12 | ||||
-rw-r--r-- | mysql-test/r/lock_multi.result | 88 | ||||
-rw-r--r-- | mysql-test/r/mysqld--help-notwin.result | 4 | ||||
-rw-r--r-- | mysql-test/r/mysqld--help-win.result | 4 | ||||
-rw-r--r-- | mysql-test/suite/sys_vars/r/lock_wait_timeout_basic.result | 177 | ||||
-rw-r--r-- | mysql-test/suite/sys_vars/t/lock_wait_timeout_basic.test | 213 | ||||
-rw-r--r-- | mysql-test/t/lock_multi.test | 161 | ||||
-rw-r--r-- | mysys/thr_lock.c | 32 | ||||
-rw-r--r-- | sql/lock.cc | 12 | ||||
-rw-r--r-- | sql/mdl.cc | 114 | ||||
-rw-r--r-- | sql/mdl.h | 14 | ||||
-rw-r--r-- | sql/sql_base.cc | 43 | ||||
-rw-r--r-- | sql/sql_class.h | 1 | ||||
-rw-r--r-- | sql/sql_delete.cc | 3 | ||||
-rw-r--r-- | sql/sql_insert.cc | 12 | ||||
-rw-r--r-- | sql/sql_show.cc | 4 | ||||
-rw-r--r-- | sql/sql_table.cc | 3 | ||||
-rw-r--r-- | sql/sys_vars.cc | 6 |
19 files changed, 855 insertions, 88 deletions
diff --git a/include/my_pthread.h b/include/my_pthread.h index 343bff6ba72..5402d3929de 100644 --- a/include/my_pthread.h +++ b/include/my_pthread.h @@ -102,6 +102,19 @@ struct timespec { (ABSTIME).max_timeout_msec= (long)((NSEC)/1000000); \ } +/** + Compare two timespec structs. + + @retval 1 If TS1 ends after TS2. + + @retval 0 If TS1 is equal to TS2. + + @retval -1 If TS1 ends before TS2. +*/ +#define cmp_timespec(TS1, TS2) \ + ((TS1.tv.i64 > TS2.tv.i64) ? 1 : \ + ((TS1.tv.i64 < TS2.tv.i64) ? -1 : 0)) + int win_pthread_mutex_trylock(pthread_mutex_t *mutex); int pthread_create(pthread_t *, const pthread_attr_t *, pthread_handler, void *); @@ -414,6 +427,33 @@ int my_pthread_mutex_trylock(pthread_mutex_t *mutex); #endif /* !set_timespec_nsec */ #endif /* HAVE_TIMESPEC_TS_SEC */ +/** + Compare two timespec structs. + + @retval 1 If TS1 ends after TS2. + + @retval 0 If TS1 is equal to TS2. + + @retval -1 If TS1 ends before TS2. +*/ +#ifdef HAVE_TIMESPEC_TS_SEC +#ifndef cmp_timespec +#define cmp_timespec(TS1, TS2) \ + ((TS1.ts_sec > TS2.ts_sec || \ + (TS1.ts_sec == TS2.ts_sec && TS1.ts_nsec > TS2.ts_nsec)) ? 1 : \ + ((TS1.ts_sec < TS2.ts_sec || \ + (TS1.ts_sec == TS2.ts_sec && TS1.ts_nsec < TS2.ts_nsec)) ? -1 : 0)) +#endif /* !cmp_timespec */ +#else +#ifndef cmp_timespec +#define cmp_timespec(TS1, TS2) \ + ((TS1.tv_sec > TS2.tv_sec || \ + (TS1.tv_sec == TS2.tv_sec && TS1.tv_nsec > TS2.tv_nsec)) ? 1 : \ + ((TS1.tv_sec < TS2.tv_sec || \ + (TS1.tv_sec == TS2.tv_sec && TS1.tv_nsec < TS2.tv_nsec)) ? -1 : 0)) +#endif /* !cmp_timespec */ +#endif /* HAVE_TIMESPEC_TS_SEC */ + /* safe_mutex adds checking to mutex for easier debugging */ #if defined(__NETWARE__) && !defined(SAFE_MUTEX_DETECT_DESTROY) diff --git a/include/thr_lock.h b/include/thr_lock.h index 3dc8857dcd0..527d1288fe4 100644 --- a/include/thr_lock.h +++ b/include/thr_lock.h @@ -156,10 +156,12 @@ void thr_lock_data_init(THR_LOCK *lock,THR_LOCK_DATA *data, void *status_param); enum enum_thr_lock_result thr_lock(THR_LOCK_DATA *data, THR_LOCK_OWNER *owner, - enum thr_lock_type lock_type); + enum thr_lock_type lock_type, + ulong lock_wait_timeout); void thr_unlock(THR_LOCK_DATA *data); enum enum_thr_lock_result thr_multi_lock(THR_LOCK_DATA **data, - uint count, THR_LOCK_OWNER *owner); + uint count, THR_LOCK_OWNER *owner, + ulong lock_wait_timeout); void thr_multi_unlock(THR_LOCK_DATA **data,uint count); void thr_lock_merge_status(THR_LOCK_DATA **data, uint count); @@ -167,10 +169,12 @@ void thr_abort_locks(THR_LOCK *lock, my_bool upgrade_lock); my_bool thr_abort_locks_for_thread(THR_LOCK *lock, my_thread_id thread); void thr_print_locks(void); /* For debugging */ my_bool thr_upgrade_write_delay_lock(THR_LOCK_DATA *data, - enum thr_lock_type new_lock_type); + enum thr_lock_type new_lock_type, + ulong lock_wait_timeout); void thr_downgrade_write_lock(THR_LOCK_DATA *data, enum thr_lock_type new_lock_type); -my_bool thr_reschedule_write_lock(THR_LOCK_DATA *data); +my_bool thr_reschedule_write_lock(THR_LOCK_DATA *data, + ulong lock_wait_timeout); #ifdef __cplusplus } #endif diff --git a/mysql-test/r/lock_multi.result b/mysql-test/r/lock_multi.result index 7a5fb445edf..d67868e927a 100644 --- a/mysql-test/r/lock_multi.result +++ b/mysql-test/r/lock_multi.result @@ -348,3 +348,91 @@ commit; # Switching to connection 'default'. drop view v1; drop table t1; +# +# Bug#45225 Locking: hang if drop table with no timeout +# +# These tests also provide function coverage for the +# lock_wait_timeout server variable. +# +DROP TABLE IF EXISTS t1; +CREATE TABLE t1 (id int); +SET SESSION lock_wait_timeout= 1; +# +# Test 1: acquire exclusive lock +# +# Connection default +START TRANSACTION; +INSERT INTO t1 VALUES (1); +# Connection 2 +DROP TABLE t1; +ERROR HY000: Lock wait timeout exceeded; try restarting transaction +# Connection default +COMMIT; +# +# Test 2: upgrade shared lock +# +# Connection default +START TRANSACTION; +SELECT * FROM t1; +id +1 +# Connection 2 +ALTER TABLE t1 RENAME TO t2; +ERROR HY000: Lock wait timeout exceeded; try restarting transaction +# Connection default +COMMIT; +# +# Test 3: acquire shared lock +# +# Connection default +LOCK TABLE t1 WRITE; +# Connection 2 +INSERT INTO t1(id) VALUES (2); +ERROR HY000: Lock wait timeout exceeded; try restarting transaction +# Connection default +UNLOCK TABLES; +# +# Test 4: table level locks +# +# Connection default +LOCK TABLE t1 READ; +# Connection 2 +INSERT INTO t1(id) VALUES(4); +ERROR HY000: Lock wait timeout exceeded; try restarting transaction +# Connection default +UNLOCK TABLES; +# +# Test 5: Waiting on Table Definition Cache (TDC) +# +# Connection default +LOCK TABLE t1 READ; +# Connection con3 +# Sending: +FLUSH TABLES; +# Connection con2 +SELECT * FROM t1; +ERROR HY000: Lock wait timeout exceeded; try restarting transaction +# Connection default +UNLOCK TABLES; +# Connection con3 +# Reaping: FLUSH TABLES +# +# Test 6: Timeouts in I_S queries +# +# Connection default +CREATE TABLE t2 (id INT); +LOCK TABLE t2 WRITE; +# Connection con3 +# Sending: +DROP TABLE t1, t2; +# Connection con2 +SELECT table_name, table_comment FROM information_schema.tables +WHERE table_schema= 'test' AND table_name= 't1'; +table_name table_comment +t1 Lock wait timeout exceeded; try restarting transaction +# Connection default +UNLOCK TABLES; +# Connection con3 +# Reaping: DROP TABLE t1, t2 +# Connection default +# Cleanup diff --git a/mysql-test/r/mysqld--help-notwin.result b/mysql-test/r/mysqld--help-notwin.result index a66b722ba4b..1bb98581341 100644 --- a/mysql-test/r/mysqld--help-notwin.result +++ b/mysql-test/r/mysqld--help-notwin.result @@ -225,6 +225,9 @@ The following options may be given as the first argument: the week. --local-infile Enable LOAD DATA LOCAL INFILE (Defaults to on; use --skip-local-infile to disable.) + --lock-wait-timeout=# + Timeout in seconds to wait for a lock before returning an + error. -l, --log[=name] Log connections and queries to file (deprecated option, use --general-log/--general-log-file instead). --log-bin[=name] Log update queries in binary format. Optional (but @@ -839,6 +842,7 @@ lc-messages en_US lc-messages-dir MYSQL_SHAREDIR/ lc-time-names en_US local-infile TRUE +lock-wait-timeout 31536000 log-bin (No default value) log-bin-index (No default value) log-bin-trust-function-creators FALSE diff --git a/mysql-test/r/mysqld--help-win.result b/mysql-test/r/mysqld--help-win.result index e7048c71a48..e48d2a9faa7 100644 --- a/mysql-test/r/mysqld--help-win.result +++ b/mysql-test/r/mysqld--help-win.result @@ -224,6 +224,9 @@ The following options may be given as the first argument: the week. --local-infile Enable LOAD DATA LOCAL INFILE (Defaults to on; use --skip-local-infile to disable.) + --lock-wait-timeout=# + Timeout in seconds to wait for a lock before returning an + error. -l, --log[=name] Log connections and queries to file (deprecated option, use --general-log/--general-log-file instead). --log-bin[=name] Log update queries in binary format. Optional (but @@ -842,6 +845,7 @@ lc-messages en_US lc-messages-dir MYSQL_SHAREDIR/ lc-time-names en_US local-infile TRUE +lock-wait-timeout 31536000 log-bin (No default value) log-bin-index (No default value) log-bin-trust-function-creators FALSE diff --git a/mysql-test/suite/sys_vars/r/lock_wait_timeout_basic.result b/mysql-test/suite/sys_vars/r/lock_wait_timeout_basic.result new file mode 100644 index 00000000000..6d1e8d3bb59 --- /dev/null +++ b/mysql-test/suite/sys_vars/r/lock_wait_timeout_basic.result @@ -0,0 +1,177 @@ +SET @start_global_value = @@global.lock_wait_timeout; +SELECT @start_global_value; +@start_global_value +31536000 +SET @start_session_value = @@session.lock_wait_timeout; +SELECT @start_session_value; +@start_session_value +31536000 +'#--------------------FN_DYNVARS_002_01-------------------------#' +SET @@global.lock_wait_timeout = 100; +SET @@global.lock_wait_timeout = DEFAULT; +SELECT @@global.lock_wait_timeout; +@@global.lock_wait_timeout +31536000 +SET @@session.lock_wait_timeout = 200; +SET @@session.lock_wait_timeout = DEFAULT; +SELECT @@session.lock_wait_timeout; +@@session.lock_wait_timeout +31536000 +'#--------------------FN_DYNVARS_002_02-------------------------#' +SET @@global.lock_wait_timeout = @start_global_value; +SELECT @@global.lock_wait_timeout = 31536000; +@@global.lock_wait_timeout = 31536000 +1 +SET @@session.lock_wait_timeout = @start_session_value; +SELECT @@session.lock_wait_timeout = 31536000; +@@session.lock_wait_timeout = 31536000 +1 +'#--------------------FN_DYNVARS_002_03-------------------------#' +SET @@global.lock_wait_timeout = 1; +SELECT @@global.lock_wait_timeout; +@@global.lock_wait_timeout +1 +SET @@global.lock_wait_timeout = 60020; +SELECT @@global.lock_wait_timeout; +@@global.lock_wait_timeout +60020 +SET @@global.lock_wait_timeout = 65535; +SELECT @@global.lock_wait_timeout; +@@global.lock_wait_timeout +65535 +'#--------------------FN_DYNVARS_002_04-------------------------#' +SET @@session.lock_wait_timeout = 1; +SELECT @@session.lock_wait_timeout; +@@session.lock_wait_timeout +1 +SET @@session.lock_wait_timeout = 50050; +SELECT @@session.lock_wait_timeout; +@@session.lock_wait_timeout +50050 +SET @@session.lock_wait_timeout = 65535; +SELECT @@session.lock_wait_timeout; +@@session.lock_wait_timeout +65535 +'#------------------FN_DYNVARS_002_05-----------------------#' +SET @@global.lock_wait_timeout = 0; +Warnings: +Warning 1292 Truncated incorrect lock_wait_timeout value: '0' +SELECT @@global.lock_wait_timeout; +@@global.lock_wait_timeout +1 +SET @@global.lock_wait_timeout = -1024; +Warnings: +Warning 1292 Truncated incorrect lock_wait_timeout value: '-1024' +SELECT @@global.lock_wait_timeout; +@@global.lock_wait_timeout +1 +SET @@global.lock_wait_timeout = 31536001; +Warnings: +Warning 1292 Truncated incorrect lock_wait_timeout value: '31536001' +SELECT @@global.lock_wait_timeout; +@@global.lock_wait_timeout +31536000 +SET @@global.lock_wait_timeout = ON; +ERROR 42000: Incorrect argument type to variable 'lock_wait_timeout' +SELECT @@global.lock_wait_timeout; +@@global.lock_wait_timeout +31536000 +SET @@global.lock_wait_timeout = OFF; +ERROR 42000: Incorrect argument type to variable 'lock_wait_timeout' +SELECT @@global.lock_wait_timeout; +@@global.lock_wait_timeout +31536000 +SET @@global.lock_wait_timeout = test; +ERROR 42000: Incorrect argument type to variable 'lock_wait_timeout' +SELECT @@global.lock_wait_timeout; +@@global.lock_wait_timeout +31536000 +SET @@session.lock_wait_timeout = 0; +Warnings: +Warning 1292 Truncated incorrect lock_wait_timeout value: '0' +SELECT @@session.lock_wait_timeout; +@@session.lock_wait_timeout +1 +SET @@session.lock_wait_timeout = -2; +Warnings: +Warning 1292 Truncated incorrect lock_wait_timeout value: '-2' +SELECT @@session.lock_wait_timeout; +@@session.lock_wait_timeout +1 +SET @@session.lock_wait_timeout = 31537000; +Warnings: +Warning 1292 Truncated incorrect lock_wait_timeout value: '31537000' +SELECT @@session.lock_wait_timeout; +@@session.lock_wait_timeout +31536000 +SET @@session.lock_wait_timeout = ON; +ERROR 42000: Incorrect argument type to variable 'lock_wait_timeout' +SELECT @@session.lock_wait_timeout; +@@session.lock_wait_timeout +31536000 +SET @@session.lock_wait_timeout = OFF; +ERROR 42000: Incorrect argument type to variable 'lock_wait_timeout' +SELECT @@session.lock_wait_timeout; +@@session.lock_wait_timeout +31536000 +SET @@session.lock_wait_timeout = test; +ERROR 42000: Incorrect argument type to variable 'lock_wait_timeout' +SELECT @@session.lock_wait_timeout; +@@session.lock_wait_timeout +31536000 +'#------------------FN_DYNVARS_002_06-----------------------#' +SELECT @@global.lock_wait_timeout = VARIABLE_VALUE +FROM INFORMATION_SCHEMA.GLOBAL_VARIABLES +WHERE VARIABLE_NAME='lock_wait_timeout'; +@@global.lock_wait_timeout = VARIABLE_VALUE +1 +'#------------------FN_DYNVARS_002_07-----------------------#' +SELECT @@session.lock_wait_timeout = VARIABLE_VALUE +FROM INFORMATION_SCHEMA.SESSION_VARIABLES +WHERE VARIABLE_NAME='lock_wait_timeout'; +@@session.lock_wait_timeout = VARIABLE_VALUE +1 +'#------------------FN_DYNVARS_002_08-----------------------#' +SET @@global.lock_wait_timeout = TRUE; +SELECT @@global.lock_wait_timeout; +@@global.lock_wait_timeout +1 +SET @@global.lock_wait_timeout = FALSE; +Warnings: +Warning 1292 Truncated incorrect lock_wait_timeout value: '0' +SELECT @@global.lock_wait_timeout; +@@global.lock_wait_timeout +1 +'#---------------------FN_DYNVARS_001_09----------------------#' +SET @@global.lock_wait_timeout = 10; +SET @@session.lock_wait_timeout = 11; +SELECT @@lock_wait_timeout = @@global.lock_wait_timeout; +@@lock_wait_timeout = @@global.lock_wait_timeout +0 +'#---------------------FN_DYNVARS_001_10----------------------#' +SET @@lock_wait_timeout = 100; +SELECT @@lock_wait_timeout = @@local.lock_wait_timeout; +@@lock_wait_timeout = @@local.lock_wait_timeout +1 +SELECT @@local.lock_wait_timeout = @@session.lock_wait_timeout; +@@local.lock_wait_timeout = @@session.lock_wait_timeout +1 +'#---------------------FN_DYNVARS_001_11----------------------#' +SET lock_wait_timeout = 1; +SELECT @@lock_wait_timeout; +@@lock_wait_timeout +1 +SELECT local.lock_wait_timeout; +ERROR 42S02: Unknown table 'local' in field list +SELECT session.lock_wait_timeout; +ERROR 42S02: Unknown table 'session' in field list +SELECT lock_wait_timeout = @@session.lock_wait_timeout; +ERROR 42S22: Unknown column 'lock_wait_timeout' in 'field list' +SET @@global.lock_wait_timeout = @start_global_value; +SELECT @@global.lock_wait_timeout; +@@global.lock_wait_timeout +31536000 +SET @@session.lock_wait_timeout = @start_session_value; +SELECT @@session.lock_wait_timeout; +@@session.lock_wait_timeout +31536000 diff --git a/mysql-test/suite/sys_vars/t/lock_wait_timeout_basic.test b/mysql-test/suite/sys_vars/t/lock_wait_timeout_basic.test new file mode 100644 index 00000000000..770bda0e053 --- /dev/null +++ b/mysql-test/suite/sys_vars/t/lock_wait_timeout_basic.test @@ -0,0 +1,213 @@ +############## mysql-test\t\lock_wait_timeout_basic.test ####################### +# # +# Variable Name: lock_wait_timeout # +# Scope: GLOBAL & SESSION # +# Access Type: Dynamic # +# Data Type: Numeric # +# Default Value: 1 # +# Range: 1 - 31536000 # +# # +# # +# Creation Date: 2010-02-08 # +# Author: Jon Olav Hauglid # +# # +# Description: Test Cases of Dynamic System Variable "lock_wait_timeout" # +# that checks behavior of this variable in the following ways # +# * Default Value # +# * Valid & Invalid values # +# * Scope & Access method # +# * Data Integrity # +# # +# Reference: http://dev.mysql.com/doc/refman/5.5/en/ # +# server-system-variables.html#option_mysqld_lock-wait-timeout # +# # +################################################################################ + +--source include/load_sysvars.inc + +##################################################################### +# START OF lock_wait_timeout TESTS # +##################################################################### + +############################################################# +# Save initial value # +############################################################# + +SET @start_global_value = @@global.lock_wait_timeout; +SELECT @start_global_value; +SET @start_session_value = @@session.lock_wait_timeout; +SELECT @start_session_value; + + +--echo '#--------------------FN_DYNVARS_002_01-------------------------#' +##################################################################### +# Display the DEFAULT value of lock_wait_timeout # +##################################################################### + +SET @@global.lock_wait_timeout = 100; +SET @@global.lock_wait_timeout = DEFAULT; +SELECT @@global.lock_wait_timeout; + +SET @@session.lock_wait_timeout = 200; +SET @@session.lock_wait_timeout = DEFAULT; +SELECT @@session.lock_wait_timeout; + + +--echo '#--------------------FN_DYNVARS_002_02-------------------------#' +##################################################################### +# Check the DEFAULT value of lock_wait_timeout # +##################################################################### + +SET @@global.lock_wait_timeout = @start_global_value; +SELECT @@global.lock_wait_timeout = 31536000; +SET @@session.lock_wait_timeout = @start_session_value; +SELECT @@session.lock_wait_timeout = 31536000; + + +--echo '#--------------------FN_DYNVARS_002_03-------------------------#' +############################################################################### +# Change the value of lock_wait_timeout to a valid value for GLOBAL Scope # +############################################################################### + +SET @@global.lock_wait_timeout = 1; +SELECT @@global.lock_wait_timeout; +SET @@global.lock_wait_timeout = 60020; +SELECT @@global.lock_wait_timeout; +SET @@global.lock_wait_timeout = 65535; +SELECT @@global.lock_wait_timeout; + + +--echo '#--------------------FN_DYNVARS_002_04-------------------------#' +############################################################################### +# Change the value of lock_wait_timeout to a valid value for SESSION Scope # +############################################################################### + +SET @@session.lock_wait_timeout = 1; +SELECT @@session.lock_wait_timeout; +SET @@session.lock_wait_timeout = 50050; +SELECT @@session.lock_wait_timeout; +SET @@session.lock_wait_timeout = 65535; +SELECT @@session.lock_wait_timeout; + + +--echo '#------------------FN_DYNVARS_002_05-----------------------#' +################################################################# +# Change the value of lock_wait_timeout to an invalid value # +################################################################# +# for global scope +SET @@global.lock_wait_timeout = 0; +SELECT @@global.lock_wait_timeout; +SET @@global.lock_wait_timeout = -1024; +SELECT @@global.lock_wait_timeout; +SET @@global.lock_wait_timeout = 31536001; +SELECT @@global.lock_wait_timeout; +--Error ER_WRONG_TYPE_FOR_VAR +SET @@global.lock_wait_timeout = ON; +SELECT @@global.lock_wait_timeout; +--Error ER_WRONG_TYPE_FOR_VAR +SET @@global.lock_wait_timeout = OFF; +SELECT @@global.lock_wait_timeout; +--Error ER_WRONG_TYPE_FOR_VAR +SET @@global.lock_wait_timeout = test; +SELECT @@global.lock_wait_timeout; +# for session scope +SET @@session.lock_wait_timeout = 0; +SELECT @@session.lock_wait_timeout; +SET @@session.lock_wait_timeout = -2; +SELECT @@session.lock_wait_timeout; +SET @@session.lock_wait_timeout = 31537000; +SELECT @@session.lock_wait_timeout; + +--Error ER_WRONG_TYPE_FOR_VAR +SET @@session.lock_wait_timeout = ON; +SELECT @@session.lock_wait_timeout; +--Error ER_WRONG_TYPE_FOR_VAR +SET @@session.lock_wait_timeout = OFF; +SELECT @@session.lock_wait_timeout; +--Error ER_WRONG_TYPE_FOR_VAR +SET @@session.lock_wait_timeout = test; +SELECT @@session.lock_wait_timeout; + + + +--echo '#------------------FN_DYNVARS_002_06-----------------------#' +#################################################################### +# Check if the value in GLOBAL Table matches value in variable # +#################################################################### + +SELECT @@global.lock_wait_timeout = VARIABLE_VALUE +FROM INFORMATION_SCHEMA.GLOBAL_VARIABLES +WHERE VARIABLE_NAME='lock_wait_timeout'; + + +--echo '#------------------FN_DYNVARS_002_07-----------------------#' +#################################################################### +# Check if the value in SESSION Table matches value in variable # +#################################################################### + +SELECT @@session.lock_wait_timeout = VARIABLE_VALUE +FROM INFORMATION_SCHEMA.SESSION_VARIABLES +WHERE VARIABLE_NAME='lock_wait_timeout'; + + +--echo '#------------------FN_DYNVARS_002_08-----------------------#' +#################################################################### +# Check if TRUE and FALSE values can be used on variable # +#################################################################### + +SET @@global.lock_wait_timeout = TRUE; +SELECT @@global.lock_wait_timeout; +SET @@global.lock_wait_timeout = FALSE; +SELECT @@global.lock_wait_timeout; + + +--echo '#---------------------FN_DYNVARS_001_09----------------------#' +############################################################################### +# Check if global and session variables are independant of each other # +############################################################################### + +SET @@global.lock_wait_timeout = 10; +SET @@session.lock_wait_timeout = 11; +SELECT @@lock_wait_timeout = @@global.lock_wait_timeout; + + +--echo '#---------------------FN_DYNVARS_001_10----------------------#' +############################################################################## +# Check if accessing variable with SESSION,LOCAL and without SCOPE points # +# to same session variable # +############################################################################## + +SET @@lock_wait_timeout = 100; +SELECT @@lock_wait_timeout = @@local.lock_wait_timeout; +SELECT @@local.lock_wait_timeout = @@session.lock_wait_timeout; + + +--echo '#---------------------FN_DYNVARS_001_11----------------------#' +############################################################################### +# Check if lock_wait_timeout can be accessed with and without @@ sign # +############################################################################### + +SET lock_wait_timeout = 1; +SELECT @@lock_wait_timeout; +--Error ER_UNKNOWN_TABLE +SELECT local.lock_wait_timeout; +--Error ER_UNKNOWN_TABLE +SELECT session.lock_wait_timeout; +--Error ER_BAD_FIELD_ERROR +SELECT lock_wait_timeout = @@session.lock_wait_timeout; + + +#################################### +# Restore initial value # +#################################### + +SET @@global.lock_wait_timeout = @start_global_value; +SELECT @@global.lock_wait_timeout; +SET @@session.lock_wait_timeout = @start_session_value; +SELECT @@session.lock_wait_timeout; + + +################################################### +# END OF lock_wait_timeout TESTS # +################################################### + diff --git a/mysql-test/t/lock_multi.test b/mysql-test/t/lock_multi.test index 5b64f60b6eb..1080b44c448 100644 --- a/mysql-test/t/lock_multi.test +++ b/mysql-test/t/lock_multi.test @@ -877,5 +877,166 @@ drop view v1; drop table t1; +--echo # +--echo # Bug#45225 Locking: hang if drop table with no timeout +--echo # +--echo # These tests also provide function coverage for the +--echo # lock_wait_timeout server variable. +--echo # + +--disable_warnings +DROP TABLE IF EXISTS t1; +--enable_warnings + +CREATE TABLE t1 (id int); + +connect(con2, localhost, root,,); +SET SESSION lock_wait_timeout= 1; + +--echo # +--echo # Test 1: acquire exclusive lock +--echo # + +--echo # Connection default +connection default; +START TRANSACTION; +INSERT INTO t1 VALUES (1); + +--echo # Connection 2 +connection con2; +--error ER_LOCK_WAIT_TIMEOUT +DROP TABLE t1; + +--echo # Connection default +connection default; +COMMIT; + +--echo # +--echo # Test 2: upgrade shared lock +--echo # + +--echo # Connection default +connection default; +START TRANSACTION; +SELECT * FROM t1; + +--echo # Connection 2 +connection con2; +--error ER_LOCK_WAIT_TIMEOUT +ALTER TABLE t1 RENAME TO t2; + +--echo # Connection default +connection default; +COMMIT; + +--echo # +--echo # Test 3: acquire shared lock +--echo # + +--echo # Connection default +connection default; +LOCK TABLE t1 WRITE; + +--echo # Connection 2 +connection con2; +--error ER_LOCK_WAIT_TIMEOUT +INSERT INTO t1(id) VALUES (2); + +--echo # Connection default +connection default; +UNLOCK TABLES; + +--echo # +--echo # Test 4: table level locks +--echo # + +--echo # Connection default +connection default; +LOCK TABLE t1 READ; + +--echo # Connection 2 +connection con2; +--error ER_LOCK_WAIT_TIMEOUT +INSERT INTO t1(id) VALUES(4); + +--echo # Connection default +connection default; +UNLOCK TABLES; + +--echo # +--echo # Test 5: Waiting on Table Definition Cache (TDC) +--echo # + +connect(con3, localhost, root); + +--echo # Connection default +connection default; +LOCK TABLE t1 READ; + +--echo # Connection con3 +connection con3; +--echo # Sending: +--send FLUSH TABLES + +--echo # Connection con2 +connection con2; +let $wait_condition= + SELECT COUNT(*) = 1 FROM information_schema.processlist + WHERE state = "Flushing tables" AND info = "FLUSH TABLES"; +--source include/wait_condition.inc +--error ER_LOCK_WAIT_TIMEOUT +SELECT * FROM t1; + +--echo # Connection default +connection default; +UNLOCK TABLES; + +--echo # Connection con3 +connection con3; +--echo # Reaping: FLUSH TABLES +--reap + +--echo # +--echo # Test 6: Timeouts in I_S queries +--echo # + +--echo # Connection default +connection default; +CREATE TABLE t2 (id INT); +LOCK TABLE t2 WRITE; + +--echo # Connection con3 +connection con3; +--echo # Sending: +--send DROP TABLE t1, t2 + +--echo # Connection con2 +connection con2; +let $wait_condition= + SELECT COUNT(*) = 1 FROM information_schema.processlist + WHERE state = "Waiting for table" AND info = "DROP TABLE t1, t2"; +--source include/wait_condition.inc +# Note: This query causes two timeouts. +# 1: try_acquire_high_prio_shared_mdl_lock on t1 +# 2: recover_from_failed_open on t1 +SELECT table_name, table_comment FROM information_schema.tables + WHERE table_schema= 'test' AND table_name= 't1'; + +--echo # Connection default +connection default; +UNLOCK TABLES; + +--echo # Connection con3 +connection con3; +--echo # Reaping: DROP TABLE t1, t2 +--reap + +--echo # Connection default +connection default; +--echo # Cleanup +disconnect con2; +disconnect con3; + + # Wait till all disconnects are completed --source include/wait_until_count_sessions.inc diff --git a/mysys/thr_lock.c b/mysys/thr_lock.c index 901620d3a93..2590654c672 100644 --- a/mysys/thr_lock.c +++ b/mysys/thr_lock.c @@ -389,13 +389,12 @@ static void wake_up_waiters(THR_LOCK *lock); static enum enum_thr_lock_result wait_for_lock(struct st_lock_list *wait, THR_LOCK_DATA *data, - my_bool in_wait_list) + my_bool in_wait_list, ulong lock_wait_timeout) { struct st_my_thread_var *thread_var= my_thread_var; mysql_cond_t *cond= &thread_var->suspend; struct timespec wait_timeout; enum enum_thr_lock_result result= THR_LOCK_ABORTED; - my_bool can_deadlock= test(data->owner->info->n_cursors); const char *old_proc_info; DBUG_ENTER("wait_for_lock"); @@ -438,14 +437,10 @@ wait_for_lock(struct st_lock_list *wait, THR_LOCK_DATA *data, old_proc_info= proc_info_hook(NULL, "Table lock", __func__, __FILE__, __LINE__); - if (can_deadlock) - set_timespec(wait_timeout, table_lock_wait_timeout); + set_timespec(wait_timeout, lock_wait_timeout); while (!thread_var->abort || in_wait_list) { - int rc= (can_deadlock ? - mysql_cond_timedwait(cond, &data->lock->mutex, - &wait_timeout) : - mysql_cond_wait(cond, &data->lock->mutex)); + int rc= mysql_cond_timedwait(cond, &data->lock->mutex, &wait_timeout); /* We must break the wait if one of the following occurs: - the connection has been aborted (!thread_var->abort), but @@ -517,7 +512,7 @@ wait_for_lock(struct st_lock_list *wait, THR_LOCK_DATA *data, enum enum_thr_lock_result thr_lock(THR_LOCK_DATA *data, THR_LOCK_OWNER *owner, - enum thr_lock_type lock_type) + enum thr_lock_type lock_type, ulong lock_wait_timeout) { THR_LOCK *lock=data->lock; enum enum_thr_lock_result result= THR_LOCK_SUCCESS; @@ -782,7 +777,7 @@ thr_lock(THR_LOCK_DATA *data, THR_LOCK_OWNER *owner, goto end; } /* Can't get lock yet; Wait for it */ - DBUG_RETURN(wait_for_lock(wait_queue, data, 0)); + DBUG_RETURN(wait_for_lock(wait_queue, data, 0, lock_wait_timeout)); end: mysql_mutex_unlock(&lock->mutex); DBUG_RETURN(result); @@ -1041,7 +1036,8 @@ static void sort_locks(THR_LOCK_DATA **data,uint count) enum enum_thr_lock_result -thr_multi_lock(THR_LOCK_DATA **data, uint count, THR_LOCK_OWNER *owner) +thr_multi_lock(THR_LOCK_DATA **data, uint count, THR_LOCK_OWNER *owner, + ulong lock_wait_timeout) { THR_LOCK_DATA **pos,**end; DBUG_ENTER("thr_multi_lock"); @@ -1051,7 +1047,8 @@ thr_multi_lock(THR_LOCK_DATA **data, uint count, THR_LOCK_OWNER *owner) /* lock everything */ for (pos=data,end=data+count; pos < end ; pos++) { - enum enum_thr_lock_result result= thr_lock(*pos, owner, (*pos)->type); + enum enum_thr_lock_result result= thr_lock(*pos, owner, (*pos)->type, + lock_wait_timeout); if (result != THR_LOCK_SUCCESS) { /* Aborted */ thr_multi_unlock(data,(uint) (pos-data)); @@ -1472,7 +1469,8 @@ void thr_downgrade_write_lock(THR_LOCK_DATA *in_data, /* Upgrade a WRITE_DELAY lock to a WRITE_LOCK */ my_bool thr_upgrade_write_delay_lock(THR_LOCK_DATA *data, - enum thr_lock_type new_lock_type) + enum thr_lock_type new_lock_type, + ulong lock_wait_timeout) { THR_LOCK *lock=data->lock; DBUG_ENTER("thr_upgrade_write_delay_lock"); @@ -1515,13 +1513,14 @@ my_bool thr_upgrade_write_delay_lock(THR_LOCK_DATA *data, { check_locks(lock,"waiting for lock",0); } - DBUG_RETURN(wait_for_lock(&lock->write_wait,data,1)); + DBUG_RETURN(wait_for_lock(&lock->write_wait,data,1, lock_wait_timeout)); } /* downgrade a WRITE lock to a WRITE_DELAY lock if there is pending locks */ -my_bool thr_reschedule_write_lock(THR_LOCK_DATA *data) +my_bool thr_reschedule_write_lock(THR_LOCK_DATA *data, + ulong lock_wait_timeout) { THR_LOCK *lock=data->lock; enum thr_lock_type write_lock_type; @@ -1553,7 +1552,8 @@ my_bool thr_reschedule_write_lock(THR_LOCK_DATA *data) free_all_read_locks(lock,0); mysql_mutex_unlock(&lock->mutex); - DBUG_RETURN(thr_upgrade_write_delay_lock(data, write_lock_type)); + DBUG_RETURN(thr_upgrade_write_delay_lock(data, write_lock_type, + lock_wait_timeout)); } diff --git a/sql/lock.cc b/sql/lock.cc index 97756893e2b..fa58e43f6bb 100644 --- a/sql/lock.cc +++ b/sql/lock.cc @@ -336,7 +336,8 @@ MYSQL_LOCK *mysql_lock_tables(THD *thd, TABLE **tables, uint count, rc= thr_lock_errno_to_mysql[(int) thr_multi_lock(sql_lock->locks + sql_lock->lock_count, sql_lock->lock_count, - thd->lock_id)]; + thd->lock_id, + thd->variables.lock_wait_timeout)]; if (rc > 1) /* a timeout or a deadlock */ { if (sql_lock->table_count) @@ -983,7 +984,8 @@ bool lock_table_names(THD *thd, TABLE_LIST *table_list) mdl_requests.push_front(&global_request); - if (thd->mdl_context.acquire_locks(&mdl_requests)) + if (thd->mdl_context.acquire_locks(&mdl_requests, + thd->variables.lock_wait_timeout)) return 1; return 0; @@ -1055,7 +1057,8 @@ bool lock_routine_name(THD *thd, bool is_function, mdl_requests.push_front(&mdl_request); mdl_requests.push_front(&global_request); - if (thd->mdl_context.acquire_locks(&mdl_requests)) + if (thd->mdl_context.acquire_locks(&mdl_requests, + thd->variables.lock_wait_timeout)) return TRUE; DEBUG_SYNC(thd, "after_wait_locked_pname"); @@ -1258,7 +1261,8 @@ bool Global_read_lock::lock_global_read_lock(THD *thd) MDL_SHARED)); mdl_request.init(MDL_key::GLOBAL, "", "", MDL_SHARED); - if (thd->mdl_context.acquire_lock(&mdl_request)) + if (thd->mdl_context.acquire_lock(&mdl_request, + thd->variables.lock_wait_timeout)) { /* Our thread was killed -- return back to initial state. */ mysql_mutex_lock(&LOCK_global_read_lock); diff --git a/sql/mdl.cc b/sql/mdl.cc index e48ae8a3f59..245d6d8a018 100644 --- a/sql/mdl.cc +++ b/sql/mdl.cc @@ -772,45 +772,25 @@ static inline void mdl_exit_cond(THD *thd, } -MDL_context::mdl_signal_type MDL_context::wait() +MDL_context::mdl_signal_type MDL_context::timed_wait(struct timespec + *abs_timeout) { const char *old_msg; - st_my_thread_var *mysys_var= my_thread_var; - mdl_signal_type result; - - mysql_mutex_lock(&m_signal_lock); - - old_msg= MDL_ENTER_COND(m_thd, mysys_var, &m_signal_cond, &m_signal_lock); - - while (! m_signal && !mysys_var->abort) - mysql_cond_wait(&m_signal_cond, &m_signal_lock); - - result= m_signal; - - MDL_EXIT_COND(m_thd, mysys_var, &m_signal_lock, old_msg); - - return result; -} - - -MDL_context::mdl_signal_type MDL_context::timed_wait(ulong timeout) -{ - struct timespec abstime; - const char *old_msg; mdl_signal_type result; st_my_thread_var *mysys_var= my_thread_var; + int wait_result= 0; mysql_mutex_lock(&m_signal_lock); old_msg= MDL_ENTER_COND(m_thd, mysys_var, &m_signal_cond, &m_signal_lock); - if (! m_signal) - { - set_timespec(abstime, timeout); - mysql_cond_timedwait(&m_signal_cond, &m_signal_lock, &abstime); - } + while (!m_signal && !mysys_var->abort && + wait_result != ETIMEDOUT && wait_result != ETIME) + wait_result= mysql_cond_timedwait(&m_signal_cond, &m_signal_lock, + abs_timeout); - result= (m_signal != NO_WAKE_UP) ? m_signal : TIMEOUT_WAKE_UP; + result= (m_signal != NO_WAKE_UP || mysys_var->abort) ? + m_signal : TIMEOUT_WAKE_UP; MDL_EXIT_COND(m_thd, mysys_var, &m_signal_lock, old_msg); @@ -1197,15 +1177,17 @@ MDL_context::find_ticket(MDL_request *mdl_request, @param mdl_request [in/out] Lock request object for lock to be acquired + @param lock_wait_timeout [in] Seconds to wait before timeout. + @retval FALSE Success. MDL_request::ticket points to the ticket for the lock. @retval TRUE Failure (Out of resources or waiting is aborted), */ bool -MDL_context::acquire_lock(MDL_request *mdl_request) +MDL_context::acquire_lock(MDL_request *mdl_request, ulong lock_wait_timeout) { - return acquire_lock_impl(mdl_request); + return acquire_lock_impl(mdl_request, lock_wait_timeout); } @@ -1397,6 +1379,8 @@ void notify_shared_lock(THD *thd, MDL_ticket *conflicting_ticket) @param mdl_request Request for the lock to be acqured. + @param lock_wait_timeout Seconds to wait before timeout. + @note Should not be used outside of MDL subsystem. Instead one should call acquire_lock() or acquire_locks() methods which ensure that conditions for deadlock-free @@ -1406,13 +1390,17 @@ void notify_shared_lock(THD *thd, MDL_ticket *conflicting_ticket) @retval TRUE Failure */ -bool MDL_context::acquire_lock_impl(MDL_request *mdl_request) +bool MDL_context::acquire_lock_impl(MDL_request *mdl_request, + ulong lock_wait_timeout) { MDL_lock *lock; MDL_ticket *ticket; bool not_used; st_my_thread_var *mysys_var= my_thread_var; MDL_key *key= &mdl_request->key; + struct timespec abs_timeout; + struct timespec abs_shortwait; + set_timespec(abs_timeout, lock_wait_timeout); mysql_mutex_assert_not_owner(&LOCK_open); @@ -1464,16 +1452,31 @@ bool MDL_context::acquire_lock_impl(MDL_request *mdl_request) /* There is a shared or exclusive lock on the object. */ DEBUG_SYNC(m_thd, "mdl_acquire_lock_wait"); - bool is_deadlock= (find_deadlock() || timed_wait(1) == VICTIM_WAKE_UP); + bool is_deadlock= find_deadlock(); + bool is_timeout= FALSE; + if (!is_deadlock) + { + set_timespec(abs_shortwait, 1); + bool timeout_is_near= cmp_timespec(abs_shortwait, abs_timeout) > 0; + mdl_signal_type wait_result= + timed_wait(timeout_is_near ? &abs_timeout : &abs_shortwait); + + if (timeout_is_near && wait_result == TIMEOUT_WAKE_UP) + is_timeout= TRUE; + else if (wait_result == VICTIM_WAKE_UP) + is_deadlock= TRUE; + } stop_waiting(); - if (is_deadlock || mysys_var->abort) + if (mysys_var->abort || is_deadlock || is_timeout) { lock->remove_ticket(&MDL_lock::m_waiting, ticket); MDL_ticket::destroy(ticket); if (is_deadlock) my_error(ER_LOCK_DEADLOCK, MYF(0)); + else if (is_timeout) + my_error(ER_LOCK_WAIT_TIMEOUT, MYF(0)); return TRUE; } rw_wrlock(&lock->m_rwlock); @@ -1513,6 +1516,8 @@ extern "C" int mdl_request_ptr_cmp(const void* ptr1, const void* ptr2) @param mdl_requests List of requests for locks to be acquired. + @param lock_wait_timeout Seconds to wait before timeout. + @note The list of requests should not contain non-exclusive lock requests. There should not be any acquired locks in the context. @@ -1522,7 +1527,8 @@ extern "C" int mdl_request_ptr_cmp(const void* ptr1, const void* ptr2) @retval TRUE Failure */ -bool MDL_context::acquire_locks(MDL_request_list *mdl_requests) +bool MDL_context::acquire_locks(MDL_request_list *mdl_requests, + ulong lock_wait_timeout) { MDL_request_list::Iterator it(*mdl_requests); MDL_request **sort_buf, **p_req; @@ -1552,7 +1558,7 @@ bool MDL_context::acquire_locks(MDL_request_list *mdl_requests) for (p_req= sort_buf; p_req < sort_buf + req_count; p_req++) { - if (acquire_lock_impl(*p_req)) + if (acquire_lock_impl(*p_req, lock_wait_timeout)) goto err; } my_free(sort_buf, MYF(0)); @@ -1578,6 +1584,8 @@ err: Used in ALTER TABLE, when a copy of the table with the new definition has been constructed. + @param lock_wait_timeout Seconds to wait before timeout. + @note In case of failure to upgrade lock (e.g. because upgrader was killed) leaves lock in its original state (locked in shared mode). @@ -1592,7 +1600,8 @@ err: */ bool -MDL_context::upgrade_shared_lock_to_exclusive(MDL_ticket *mdl_ticket) +MDL_context::upgrade_shared_lock_to_exclusive(MDL_ticket *mdl_ticket, + ulong lock_wait_timeout) { MDL_request mdl_xlock_request; MDL_ticket *mdl_svp= mdl_savepoint(); @@ -1614,7 +1623,7 @@ MDL_context::upgrade_shared_lock_to_exclusive(MDL_ticket *mdl_ticket) mdl_xlock_request.init(&mdl_ticket->m_lock->key, MDL_EXCLUSIVE); - if (acquire_lock_impl(&mdl_xlock_request)) + if (acquire_lock_impl(&mdl_xlock_request, lock_wait_timeout)) DBUG_RETURN(TRUE); is_new_ticket= ! has_lock(mdl_svp, mdl_xlock_request.ticket); @@ -1803,21 +1812,25 @@ bool MDL_context::find_deadlock() Does not acquire the locks! + @param lock_wait_timeout Seconds to wait before timeout. + @retval FALSE Success. One can try to obtain metadata locks. @retval TRUE Failure (thread was killed or deadlock is possible). */ bool -MDL_context::wait_for_lock(MDL_request *mdl_request) +MDL_context::wait_for_lock(MDL_request *mdl_request, ulong lock_wait_timeout) { MDL_lock *lock; st_my_thread_var *mysys_var= my_thread_var; + struct timespec abs_timeout; + set_timespec(abs_timeout, lock_wait_timeout); mysql_mutex_assert_not_owner(&LOCK_open); DBUG_ASSERT(mdl_request->ticket == NULL); - while (!mysys_var->abort) + while (TRUE) { /* We have to check if there are some HANDLERs open by this thread @@ -1860,19 +1873,32 @@ MDL_context::wait_for_lock(MDL_request *mdl_request) set_deadlock_weight(MDL_DEADLOCK_WEIGHT_DML); will_wait_for(pending_ticket); - bool is_deadlock= (find_deadlock() || wait() == VICTIM_WAKE_UP); + bool is_deadlock= find_deadlock(); + bool is_timeout= FALSE; + if (!is_deadlock) + { + mdl_signal_type wait_result= timed_wait(&abs_timeout); + if (wait_result == TIMEOUT_WAKE_UP) + is_timeout= TRUE; + else if (wait_result == VICTIM_WAKE_UP) + is_deadlock= TRUE; + } stop_waiting(); lock->remove_ticket(&MDL_lock::m_waiting, pending_ticket); MDL_ticket::destroy(pending_ticket); - if (is_deadlock) + + if (mysys_var->abort || is_deadlock || is_timeout) { - my_error(ER_LOCK_DEADLOCK, MYF(0)); + if (is_deadlock) + my_error(ER_LOCK_DEADLOCK, MYF(0)); + else if (is_timeout) + my_error(ER_LOCK_WAIT_TIMEOUT, MYF(0)); return TRUE; } } - return mysys_var->abort; + return TRUE; } diff --git a/sql/mdl.h b/sql/mdl.h index 05a0b44dc9b..d43548fb65f 100644 --- a/sql/mdl.h +++ b/sql/mdl.h @@ -466,13 +466,14 @@ public: void destroy(); bool try_acquire_lock(MDL_request *mdl_request); - bool acquire_lock(MDL_request *mdl_request); - bool acquire_locks(MDL_request_list *requests); - bool upgrade_shared_lock_to_exclusive(MDL_ticket *mdl_ticket); + bool acquire_lock(MDL_request *mdl_request, ulong lock_wait_timeout); + bool acquire_locks(MDL_request_list *requests, ulong lock_wait_timeout); + bool upgrade_shared_lock_to_exclusive(MDL_ticket *mdl_ticket, + ulong lock_wait_timeout); bool clone_ticket(MDL_request *mdl_request); - bool wait_for_lock(MDL_request *mdl_request); + bool wait_for_lock(MDL_request *mdl_request, ulong lock_wait_timeout); void release_all_locks_for_name(MDL_ticket *ticket); void release_lock(MDL_ticket *ticket); @@ -644,7 +645,7 @@ private: MDL_ticket *find_ticket(MDL_request *mdl_req, bool *is_transactional); void release_locks_stored_before(MDL_ticket *sentinel); - bool acquire_lock_impl(MDL_request *mdl_request); + bool acquire_lock_impl(MDL_request *mdl_request, ulong lock_wait_timeout); bool find_deadlock(); @@ -680,8 +681,7 @@ private: mysql_mutex_unlock(&m_signal_lock); } - mdl_signal_type wait(); - mdl_signal_type timed_wait(ulong timeout); + mdl_signal_type timed_wait(struct timespec *abs_timeout); mdl_signal_type peek_signal() { diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 1564838548f..ae5c5803545 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -2148,7 +2148,8 @@ bool wait_while_table_is_used(THD *thd, TABLE *table, table->s->table_name.str, (ulong) table->s, table->db_stat, table->s->version)); - if (thd->mdl_context.upgrade_shared_lock_to_exclusive(table->mdl_ticket)) + if (thd->mdl_context.upgrade_shared_lock_to_exclusive( + table->mdl_ticket, thd->variables.lock_wait_timeout)) DBUG_RETURN(TRUE); mysql_mutex_lock(&LOCK_open); @@ -2362,7 +2363,8 @@ open_table_get_mdl_lock(THD *thd, TABLE_LIST *table_list, mdl_requests.push_front(mdl_request); mdl_requests.push_front(global_request); - if (thd->mdl_context.acquire_locks(&mdl_requests)) + if (thd->mdl_context.acquire_locks(&mdl_requests, + thd->variables.lock_wait_timeout)) return 1; } else @@ -3843,7 +3845,8 @@ recover_from_failed_open(THD *thd, MDL_request *mdl_request, switch (m_action) { case OT_WAIT_MDL_LOCK: - result= thd->mdl_context.wait_for_lock(mdl_request); + result= thd->mdl_context.wait_for_lock(mdl_request, + thd->variables.lock_wait_timeout); break; case OT_WAIT_TDC: result= tdc_wait_for_old_versions(thd, &m_mdl_requests); @@ -3862,7 +3865,9 @@ recover_from_failed_open(THD *thd, MDL_request *mdl_request, mdl_requests.push_front(&mdl_xlock_request); mdl_requests.push_front(&mdl_global_request); - if ((result= thd->mdl_context.acquire_locks(&mdl_requests))) + if ((result= + thd->mdl_context.acquire_locks(&mdl_requests, + thd->variables.lock_wait_timeout))) break; DBUG_ASSERT(mdl_request->key.mdl_namespace() == MDL_key::TABLE); @@ -3893,7 +3898,9 @@ recover_from_failed_open(THD *thd, MDL_request *mdl_request, mdl_requests.push_front(&mdl_xlock_request); mdl_requests.push_front(&mdl_global_request); - if ((result= thd->mdl_context.acquire_locks(&mdl_requests))) + if ((result= + thd->mdl_context.acquire_locks(&mdl_requests, + thd->variables.lock_wait_timeout))) break; DBUG_ASSERT(mdl_request->key.mdl_namespace() == MDL_key::TABLE); @@ -4381,7 +4388,8 @@ open_tables_acquire_upgradable_mdl(THD *thd, TABLE_LIST *tables_start, mdl_requests.push_front(global_request); } - if (thd->mdl_context.acquire_locks(&mdl_requests)) + if (thd->mdl_context.acquire_locks(&mdl_requests, + thd->variables.lock_wait_timeout)) return TRUE; for (table= tables_start; table && table != tables_end; @@ -8530,6 +8538,9 @@ tdc_wait_for_old_versions(THD *thd, MDL_request_list *mdl_requests) TABLE_SHARE *share; const char *old_msg; MDL_request *mdl_request; + struct timespec abstime; + set_timespec(abstime, thd->variables.lock_wait_timeout); + int wait_result= 0; while (!thd->killed) { @@ -8557,15 +8568,31 @@ tdc_wait_for_old_versions(THD *thd, MDL_request_list *mdl_requests) } if (!mdl_request) { + /* + Reset wait_result here in case this was the final check + after getting a timeout from mysql_cond_timedwait(). + */ + wait_result= 0; + mysql_mutex_unlock(&LOCK_open); + break; + } + if (wait_result == ETIMEDOUT || wait_result == ETIME) + { + /* + Test for timeout here instead of right after mysql_cond_timedwait(). + This allows for a final iteration and a final check before reporting + ER_LOCK_WAIT_TIMEOUT. + */ mysql_mutex_unlock(&LOCK_open); + my_error(ER_LOCK_WAIT_TIMEOUT, MYF(0)); break; } old_msg= thd->enter_cond(&COND_refresh, &LOCK_open, "Waiting for table"); - mysql_cond_wait(&COND_refresh, &LOCK_open); + wait_result= mysql_cond_timedwait(&COND_refresh, &LOCK_open, &abstime); /* LOCK_open mutex is unlocked by THD::exit_cond() as side-effect. */ thd->exit_cond(old_msg); } - return thd->killed; + return thd->killed || wait_result == ETIMEDOUT || wait_result == ETIME; } diff --git a/sql/sql_class.h b/sql/sql_class.h index 9e33bea25c0..7c935d376f9 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -342,6 +342,7 @@ typedef struct system_variables ulong auto_increment_increment, auto_increment_offset; ulong bulk_insert_buff_size; ulong join_buff_size; + ulong lock_wait_timeout; ulong max_allowed_packet; ulong max_error_count; ulong max_length_for_sort_data; diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc index 0f5d51f924d..ea466da8ea1 100644 --- a/sql/sql_delete.cc +++ b/sql/sql_delete.cc @@ -1210,7 +1210,8 @@ bool mysql_truncate(THD *thd, TABLE_LIST *table_list, bool dont_send_ok) mdl_requests.push_front(&mdl_request); mdl_requests.push_front(&mdl_global_request); - if (thd->mdl_context.acquire_locks(&mdl_requests)) + if (thd->mdl_context.acquire_locks(&mdl_requests, + thd->variables.lock_wait_timeout)) DBUG_RETURN(TRUE); has_mdl_lock= TRUE; diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc index 7587a447445..45c9c0363dd 100644 --- a/sql/sql_insert.cc +++ b/sql/sql_insert.cc @@ -1823,6 +1823,12 @@ public: */ thd.lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_INSERT_DELAYED); thd.set_current_stmt_binlog_format_row_if_mixed(); + /* + Prevent changes to global.lock_wait_timeout from affecting + delayed insert threads as any timeouts in delayed inserts + are not communicated to the client. + */ + thd.variables.lock_wait_timeout= LONG_TIMEOUT; bzero((char*) &thd.net, sizeof(thd.net)); // Safety bzero((char*) &table_list, sizeof(table_list)); // Safety @@ -2708,7 +2714,8 @@ bool Delayed_insert::handle_inserts(void) table->use_all_columns(); thd_proc_info(&thd, "upgrading lock"); - if (thr_upgrade_write_delay_lock(*thd.lock->locks, delayed_lock)) + if (thr_upgrade_write_delay_lock(*thd.lock->locks, delayed_lock, + thd.variables.lock_wait_timeout)) { /* This can happen if thread is killed either by a shutdown @@ -2893,7 +2900,8 @@ bool Delayed_insert::handle_inserts(void) goto err; } query_cache_invalidate3(&thd, table, 1); - if (thr_reschedule_write_lock(*thd.lock->locks)) + if (thr_reschedule_write_lock(*thd.lock->locks, + thd.variables.lock_wait_timeout)) { /* This is not known to happen. */ my_error(ER_DELAYED_CANT_CHANGE_LOCK,MYF(ME_FATALERROR), diff --git a/sql/sql_show.cc b/sql/sql_show.cc index 2a05cbc561d..2e1827f9a35 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -3103,7 +3103,9 @@ try_acquire_high_prio_shared_mdl_lock(THD *thd, TABLE_LIST *table, thd->mdl_context.try_acquire_lock(&table->mdl_request)) && !table->mdl_request.ticket && !can_deadlock) { - if ((error= thd->mdl_context.wait_for_lock(&table->mdl_request))) + if ((error= + thd->mdl_context.wait_for_lock(&table->mdl_request, + thd->variables.lock_wait_timeout))) break; } return error; diff --git a/sql/sql_table.cc b/sql/sql_table.cc index 30725d2035c..d6c656080b4 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -4403,7 +4403,8 @@ static int prepare_for_repair(THD *thd, TABLE_LIST *table_list, mdl_requests.push_front(&table_list->mdl_request); mdl_requests.push_front(&mdl_global_request); - if (thd->mdl_context.acquire_locks(&mdl_requests)) + if (thd->mdl_context.acquire_locks(&mdl_requests, + thd->variables.lock_wait_timeout)) DBUG_RETURN(0); has_mdl_lock= TRUE; diff --git a/sql/sys_vars.cc b/sql/sys_vars.cc index 73d01f5341d..085e8b8ee1e 100644 --- a/sql/sys_vars.cc +++ b/sql/sys_vars.cc @@ -853,6 +853,12 @@ static Sys_var_mybool Sys_local_infile( "local_infile", "Enable LOAD DATA LOCAL INFILE", GLOBAL_VAR(opt_local_infile), CMD_LINE(OPT_ARG), DEFAULT(TRUE)); +static Sys_var_ulong Sys_lock_wait_timeout( + "lock_wait_timeout", + "Timeout in seconds to wait for a lock before returning an error.", + SESSION_VAR(lock_wait_timeout), CMD_LINE(REQUIRED_ARG), + VALID_RANGE(1, LONG_TIMEOUT), DEFAULT(LONG_TIMEOUT), BLOCK_SIZE(1)); + #ifdef HAVE_MLOCKALL static Sys_var_mybool Sys_locked_in_memory( "locked_in_memory", |