summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/my_pthread.h40
-rw-r--r--include/thr_lock.h12
-rw-r--r--mysql-test/r/lock_multi.result88
-rw-r--r--mysql-test/r/mysqld--help-notwin.result4
-rw-r--r--mysql-test/r/mysqld--help-win.result4
-rw-r--r--mysql-test/suite/sys_vars/r/lock_wait_timeout_basic.result177
-rw-r--r--mysql-test/suite/sys_vars/t/lock_wait_timeout_basic.test213
-rw-r--r--mysql-test/t/lock_multi.test161
-rw-r--r--mysys/thr_lock.c32
-rw-r--r--sql/lock.cc12
-rw-r--r--sql/mdl.cc114
-rw-r--r--sql/mdl.h14
-rw-r--r--sql/sql_base.cc43
-rw-r--r--sql/sql_class.h1
-rw-r--r--sql/sql_delete.cc3
-rw-r--r--sql/sql_insert.cc12
-rw-r--r--sql/sql_show.cc4
-rw-r--r--sql/sql_table.cc3
-rw-r--r--sql/sys_vars.cc6
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",