diff --git a/mysql-test/r/mdl_sync.result b/mysql-test/r/mdl_sync.result index 37f18746c57..36451985a86 100644 --- a/mysql-test/r/mdl_sync.result +++ b/mysql-test/r/mdl_sync.result @@ -19,3 +19,46 @@ connection: con2 ERROR 42S02: Unknown table 't1' drop table t3; SET DEBUG_SYNC= 'RESET'; +# +# Test for bug #46748 "Assertion in MDL_context::wait_for_locks() +# on INSERT + CREATE TRIGGER". +# +drop tables if exists t1, t2, t3, t4, t5; +# Let us simulate scenario in which we open some tables from extended +# part of prelocking set but then encounter conflicting metadata lock, +# so have to back-off and wait for it to go away. +create table t1 (i int); +create table t2 (j int); +create table t3 (k int); +create table t4 (l int); +create trigger t1_bi before insert on t1 for each row +insert into t2 values (new.i); +create trigger t2_bi before insert on t2 for each row +insert into t3 values (new.j); +# +# Switching to connection 'con1root'. +lock tables t4 read; +# +# Switching to connection 'con2root'. +# Send : +rename table t3 to t5, t4 to t3;; +# +# Switching to connection 'default'. +# Wait until the above RENAME TABLE adds pending requests for exclusive +# metadata lock on its tables and blocks due to 't4' being used by LOCK +# TABLES. +# Send : +insert into t1 values (1);; +# +# Switching to connection 'con1root'. +# Wait until INSERT statement waits due to encountering pending +# exclusive metadata lock on 't3'. +unlock tables; +# +# Switching to connection 'con2root'. +# Reap RENAME TABLE. +# +# Switching to connection 'default'. +# Reap INSERT. +# Clean-up. +drop tables t1, t2, t3, t5; diff --git a/mysql-test/t/mdl_sync.test b/mysql-test/t/mdl_sync.test index ed03a9a5cb0..124e76bbc1f 100644 --- a/mysql-test/t/mdl_sync.test +++ b/mysql-test/t/mdl_sync.test @@ -3,6 +3,10 @@ # --source include/have_debug_sync.inc +# Save the initial number of concurrent sessions. +--source include/count_sessions.inc + + # Clean up resources used in this test case. --disable_warnings SET DEBUG_SYNC= 'RESET'; @@ -67,3 +71,76 @@ disconnect con3; --disable_warnings SET DEBUG_SYNC= 'RESET'; --enable_warnings + + +--echo # +--echo # Test for bug #46748 "Assertion in MDL_context::wait_for_locks() +--echo # on INSERT + CREATE TRIGGER". +--echo # +--disable_warnings +drop tables if exists t1, t2, t3, t4, t5; +--enable_warnings +--echo # Let us simulate scenario in which we open some tables from extended +--echo # part of prelocking set but then encounter conflicting metadata lock, +--echo # so have to back-off and wait for it to go away. +connect (con1root,localhost,root,,test,,); +connect (con2root,localhost,root,,test,,); +connection default; +create table t1 (i int); +create table t2 (j int); +create table t3 (k int); +create table t4 (l int); +create trigger t1_bi before insert on t1 for each row + insert into t2 values (new.i); +create trigger t2_bi before insert on t2 for each row + insert into t3 values (new.j); +--echo # +--echo # Switching to connection 'con1root'. +connection con1root; +lock tables t4 read; +--echo # +--echo # Switching to connection 'con2root'. +connection con2root; +--echo # Send : +--send rename table t3 to t5, t4 to t3; +--echo # +--echo # Switching to connection 'default'. +connection default; +--echo # Wait until the above RENAME TABLE adds pending requests for exclusive +--echo # metadata lock on its tables and blocks due to 't4' being used by LOCK +--echo # TABLES. +let $wait_condition= select count(*)= 1 from information_schema.processlist + where state= 'Waiting for table' and + info='rename table t3 to t5, t4 to t3'; +--source include/wait_condition.inc +--echo # Send : +--send insert into t1 values (1); +--echo # +--echo # Switching to connection 'con1root'. +connection con1root; +--echo # Wait until INSERT statement waits due to encountering pending +--echo # exclusive metadata lock on 't3'. +let $wait_condition= select count(*)= 1 from information_schema.processlist + where state= 'Waiting for table' and + info='insert into t1 values (1)'; +--source include/wait_condition.inc +unlock tables; +--echo # +--echo # Switching to connection 'con2root'. +connection con2root; +--echo # Reap RENAME TABLE. +--reap +--echo # +--echo # Switching to connection 'default'. +connection default; +--echo # Reap INSERT. +--reap +--echo # Clean-up. +disconnect con1root; +disconnect con2root; +drop tables t1, t2, t3, t5; + + +# Check that all connections opened by test cases in this file are really +# gone so execution of other tests won't be affected by their presence. +--source include/wait_until_count_sessions.inc diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 0a5e89b279f..42f987133dd 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -5104,19 +5104,31 @@ bool lock_tables(THD *thd, TABLE_LIST *tables, uint count, void close_tables_for_reopen(THD *thd, TABLE_LIST **tables) { + TABLE_LIST *first_not_own_table= thd->lex->first_not_own_table(); + TABLE_LIST *tmp; + /* If table list consists only from tables from prelocking set, table list for new attempt should be empty, so we have to update list's root pointer. */ - if (thd->lex->first_not_own_table() == *tables) + if (first_not_own_table == *tables) *tables= 0; thd->lex->chop_off_not_own_tables(); sp_remove_not_own_routines(thd->lex); - for (TABLE_LIST *tmp= *tables; tmp; tmp= tmp->next_global) + for (tmp= *tables; tmp; tmp= tmp->next_global) { tmp->table= 0; tmp->mdl_request.ticket= NULL; } + /* + Metadata lock requests for tables from extended part of prelocking set + are part of list of requests to be waited for in Open_table_context. + So to satisfy assumptions in MDL_context::wait_for_locks(), which will + performs the waiting, we have to reset MDL_request::ticket values for + them as well. + */ + for (tmp= first_not_own_table; tmp; tmp= tmp->next_global) + tmp->mdl_request.ticket= NULL; close_thread_tables(thd); if (!thd->locked_tables_mode) thd->mdl_context.release_all_locks();