diff --git a/mysql-test/r/lock_multi.result b/mysql-test/r/lock_multi.result index d67868e927a..c440dafb228 100644 --- a/mysql-test/r/lock_multi.result +++ b/mysql-test/r/lock_multi.result @@ -436,3 +436,29 @@ UNLOCK TABLES; # Reaping: DROP TABLE t1, t2 # Connection default # Cleanup +# +# Test for bug #51134 "Crash in MDL_lock::destroy on a concurrent +# DDL workload". +# +drop tables if exists t1, t2, t3; +create table t3 (i int); +# Switching to connection 'con1' +# Lock 't3' so upcoming RENAME is blocked. +lock table t3 read; +# Switching to connection 'con2' +# Remember ID for this connection. +# Start statement which will try to acquire two instances +# of X metadata lock on the same object. +# Sending: +rename tables t1 to t2, t2 to t3;; +# Switching to connection 'default' +# Wait until RENAME TABLE is blocked on table 't3'. +# Kill RENAME TABLE. +kill query ID; +# Switching to connection 'con2' +# RENAME TABLE should be aborted but should not crash. +ERROR 70100: Query execution was interrupted +# Switching to connection 'con1' +unlock tables; +# Switching to connection 'default' +drop table t3; diff --git a/mysql-test/t/lock_multi.test b/mysql-test/t/lock_multi.test index 1080b44c448..c34dcb05dd4 100644 --- a/mysql-test/t/lock_multi.test +++ b/mysql-test/t/lock_multi.test @@ -1038,5 +1038,59 @@ disconnect con2; disconnect con3; +--echo # +--echo # Test for bug #51134 "Crash in MDL_lock::destroy on a concurrent +--echo # DDL workload". +--echo # +--disable_warnings +drop tables if exists t1, t2, t3; +--enable_warnings +connect (con1, localhost, root, , ); +connect (con2, localhost, root, , ); +connection default; +create table t3 (i int); + +--echo # Switching to connection 'con1' +connection con1; +--echo # Lock 't3' so upcoming RENAME is blocked. +lock table t3 read; + +--echo # Switching to connection 'con2' +connection con2; +--echo # Remember ID for this connection. +let $ID= `select connection_id()`; +--echo # Start statement which will try to acquire two instances +--echo # of X metadata lock on the same object. +--echo # Sending: +--send rename tables t1 to t2, t2 to t3; + +--echo # Switching to connection 'default' +connection default; +--echo # Wait until RENAME TABLE is blocked on table 't3'. +let $wait_condition= + select count(*) = 1 from information_schema.processlist + where state = "Waiting for table" and info = "rename tables t1 to t2, t2 to t3"; +--source include/wait_condition.inc +--echo # Kill RENAME TABLE. +--replace_result $ID ID +eval kill query $ID; + +--echo # Switching to connection 'con2' +connection con2; +--echo # RENAME TABLE should be aborted but should not crash. +--error ER_QUERY_INTERRUPTED +--reap + +--echo # Switching to connection 'con1' +connection con1; +unlock tables; + +--echo # Switching to connection 'default' +connection default; +disconnect con1; +disconnect con2; +drop table t3; + + # Wait till all disconnects are completed --source include/wait_until_count_sessions.inc diff --git a/sql/mdl.cc b/sql/mdl.cc index 245d6d8a018..e493b42ca69 100644 --- a/sql/mdl.cc +++ b/sql/mdl.cc @@ -1532,6 +1532,7 @@ bool MDL_context::acquire_locks(MDL_request_list *mdl_requests, { MDL_request_list::Iterator it(*mdl_requests); MDL_request **sort_buf, **p_req; + MDL_ticket *mdl_svp= mdl_savepoint(); ssize_t req_count= static_cast(mdl_requests->elements()); if (req_count == 0) @@ -1565,12 +1566,16 @@ bool MDL_context::acquire_locks(MDL_request_list *mdl_requests, return FALSE; err: - /* Release locks we have managed to acquire so far. */ + /* + Release locks we have managed to acquire so far. + Use rollback_to_savepoint() since there may be duplicate + requests that got assigned the same ticket. + */ + rollback_to_savepoint(mdl_svp); + /* Reset lock requests back to its initial state. */ for (req_count= p_req - sort_buf, p_req= sort_buf; p_req < sort_buf + req_count; p_req++) { - release_lock((*p_req)->ticket); - /* Reset lock request back to its initial state. */ (*p_req)->ticket= NULL; } my_free(sort_buf, MYF(0));