diff --git a/mysql-test/r/lock_multi.result b/mysql-test/r/lock_multi.result index 930983bae27..ef9292ad8c0 100644 --- a/mysql-test/r/lock_multi.result +++ b/mysql-test/r/lock_multi.result @@ -219,3 +219,34 @@ flush tables with read lock;; connection: default flush tables; drop table t1; +# +# Bug#47249 assert in MDL_global_lock::is_lock_type_compatible +# +DROP TABLE IF EXISTS t1; +DROP VIEW IF EXISTS v1; +# +# Test 1: LOCK TABLES v1 WRITE, t1 READ; +# +CREATE TABLE t1 ( f1 integer ); +CREATE VIEW v1 AS SELECT f1 FROM t1 ; +# Connection 2 +LOCK TABLES v1 WRITE, t1 READ; +FLUSH TABLE t1; +# Connection 1 +LOCK TABLES t1 WRITE; +FLUSH TABLE t1; +DROP TABLE t1; +DROP VIEW v1; +# +# Test 2: LOCK TABLES t1 WRITE, v1 READ; +# +CREATE TABLE t1 ( f1 integer ); +CREATE VIEW v1 AS SELECT f1 FROM t1 ; +# Connection 2 +LOCK TABLES t1 WRITE, v1 READ; +FLUSH TABLE t1; +# Connection 1 +LOCK TABLES t1 WRITE; +FLUSH TABLE t1; +DROP TABLE t1; +DROP VIEW v1; diff --git a/mysql-test/t/lock_multi.test b/mysql-test/t/lock_multi.test index c82e351dfef..cbb99c04967 100644 --- a/mysql-test/t/lock_multi.test +++ b/mysql-test/t/lock_multi.test @@ -664,5 +664,63 @@ connection flush; --reap connection default; disconnect flush; + + +--echo # +--echo # Bug#47249 assert in MDL_global_lock::is_lock_type_compatible +--echo # + +--disable_warnings +DROP TABLE IF EXISTS t1; +DROP VIEW IF EXISTS v1; +--enable_warnings + +--echo # +--echo # Test 1: LOCK TABLES v1 WRITE, t1 READ; +--echo # + +CREATE TABLE t1 ( f1 integer ); +CREATE VIEW v1 AS SELECT f1 FROM t1 ; + +--echo # Connection 2 +connect (con2,localhost,root); +LOCK TABLES v1 WRITE, t1 READ; +FLUSH TABLE t1; +disconnect con2; +--source include/wait_until_disconnected.inc + +--echo # Connection 1 +connection default; +LOCK TABLES t1 WRITE; +FLUSH TABLE t1; # Assertion happened here + +# Cleanup +DROP TABLE t1; +DROP VIEW v1; + +--echo # +--echo # Test 2: LOCK TABLES t1 WRITE, v1 READ; +--echo # + +CREATE TABLE t1 ( f1 integer ); +CREATE VIEW v1 AS SELECT f1 FROM t1 ; + +--echo # Connection 2 +connect (con2,localhost,root); +LOCK TABLES t1 WRITE, v1 READ; +FLUSH TABLE t1; +disconnect con2; +--source include/wait_until_disconnected.inc + +--echo # Connection 1 +connection default; +LOCK TABLES t1 WRITE; +FLUSH TABLE t1; # Assertion happened here + +# Cleanup +DROP TABLE t1; +DROP VIEW v1; + + # Wait till all disconnects are completed --source include/wait_until_count_sessions.inc diff --git a/sql/mdl.cc b/sql/mdl.cc index f9b52b17f5f..879b12a4cac 100644 --- a/sql/mdl.cc +++ b/sql/mdl.cc @@ -1005,6 +1005,9 @@ MDL_ticket::upgrade_shared_lock_to_exclusive() if (m_type == MDL_EXCLUSIVE) DBUG_RETURN(FALSE); + /* Only allow upgrades from MDL_SHARED_UPGRADABLE */ + DBUG_ASSERT(m_type == MDL_SHARED_UPGRADABLE); + pthread_mutex_lock(&LOCK_mdl); old_msg= MDL_ENTER_COND(thd, mysys_var); diff --git a/sql/mdl.h b/sql/mdl.h index 87982bc6af1..03631bb9dd6 100644 --- a/sql/mdl.h +++ b/sql/mdl.h @@ -254,6 +254,10 @@ public: mdl_cached_object_release_hook release_hook); const MDL_context *get_ctx() const { return m_ctx; } bool is_shared() const { return m_type < MDL_EXCLUSIVE; } + bool is_upgradable_or_exclusive() const + { + return m_type == MDL_SHARED_UPGRADABLE || m_type == MDL_EXCLUSIVE; + } bool upgrade_shared_lock_to_exclusive(); void downgrade_exclusive_lock(); private: diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 42f987133dd..f9514a055ca 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -130,6 +130,8 @@ static bool tdc_wait_for_old_versions(THD *thd, static bool has_write_table_with_auto_increment(TABLE_LIST *tables); +TABLE *find_table_for_mdl_upgrade(TABLE *list, const char *db, + const char *table_name); uint cached_open_tables(void) { @@ -999,8 +1001,8 @@ bool close_cached_tables(THD *thd, TABLE_LIST *tables, bool have_lock, table_list= table_list->next_global) { /* A check that the table was locked for write is done by the caller. */ - TABLE *table= find_locked_table(thd->open_tables, table_list->db, - table_list->table_name); + TABLE *table= find_table_for_mdl_upgrade(thd->open_tables, table_list->db, + table_list->table_name); /* May return NULL if this table has already been closed via an alias. */ if (! table) @@ -2942,6 +2944,34 @@ TABLE *find_write_locked_table(TABLE *list, const char *db, const char *table_na } +/** + Find instance of TABLE with MDL_SHARED_UPGRADABLE or + MDL_EXCLUSIVE lock from the list of open tables. + + @param list List of TABLE objects to be searched + @param db Database name. + @param table_name Name of table. + + @return Pointer to MDL_SHARED_UPGRADABLE or MDL_EXCLUSIVE + TABLE instance, NULL otherwise. +*/ + +TABLE *find_table_for_mdl_upgrade(TABLE *list, const char *db, + const char *table_name) +{ + TABLE *tab= find_locked_table(list, db, table_name); + + while (tab != NULL) + { + if (tab->mdl_ticket != NULL && + tab->mdl_ticket->is_upgradable_or_exclusive()) + return tab; + tab= find_locked_table(tab->next, db, table_name); + } + return NULL; +} + + /*********************************************************************** class Locked_tables_list implementation. Declared in sql_class.h ************************************************************************/