mirror of
https://github.com/MariaDB/server.git
synced 2025-12-24 11:21:21 +03:00
Bug #48940 MDL deadlocks against mysql_rm_db
This deadlock would occur between two connections A and B if statements where executed in the following way: 1) Connection A executes a DML statement against table s1.t1 with autocommit off. This causes a shared metadata lock on s1.t1 to be acquired. (With autocommit on, the metadata lock will be dropped once the statment completes and the deadlock will not occour.) 2) Connection B tries to DROP DATABASE s1. This will block against the metadata lock connection A holds on s1.t1. While blocking, connection B will hold the LOCK_mysql_create_db mutex. 3) Connection A tries to ALTER DATABASE s1. This will block when trying to get LOCK_mysql_create_db mutex held by connection B. 4) Deadlock between DROP DATABASE and ALTER DATABASE (which has autocommit off). If Connection A used an explicitly started transaction rather than having autocommit off, this deadlock did not happen as ALTER DATABASE is disallowed inside transactions. This patch fixes the problem by changing ALTER DATABASE to cause an implicit commit before executing. This will cause the metadata lock on s1.t1 to be dropped, allowing DROP DATABASE to proceed. This will in turn cause the LOCK_mysql_create_db mutex to be unlocked, allowing ALTER DATABASE to proceed. Note that SQL commands other than ALTER DATABASE that also use LOCK_mysql_create_db, already cause an implicit commit. Incompatible change: ALTER DATABASE (and its synonym ALTER SCHEMA) now cause an implicit commit. This must be reflected in the documentation. Test case added to schema.test. sql/sql_parse.cc: Added CF_AUTO_COMMIT_TRANS to SQLCOM_ALTER_DB. Removed thd->active_transaction() checks from SQLCOM_DROP_DB, SQLCOM_ALTER_DB_UPGRADE and SQLCOM_ALTER_DB as these statements cause an implicit commit.
This commit is contained in:
@@ -11,3 +11,20 @@ mtr
|
||||
mysql
|
||||
test
|
||||
drop schema foo;
|
||||
#
|
||||
# Bug #48940 MDL deadlocks against mysql_rm_db
|
||||
#
|
||||
DROP SCHEMA IF EXISTS schema1;
|
||||
# Connection default
|
||||
CREATE SCHEMA schema1;
|
||||
CREATE TABLE schema1.t1 (a INT);
|
||||
SET autocommit= FALSE;
|
||||
INSERT INTO schema1.t1 VALUES (1);
|
||||
# Connection 2
|
||||
DROP SCHEMA schema1;
|
||||
# Connection default
|
||||
ALTER SCHEMA schema1 DEFAULT CHARACTER SET utf8;
|
||||
ERROR HY000: Can't create/write to file './schema1/db.opt' (Errcode: 2)
|
||||
SET autocommit= TRUE;
|
||||
# Connection 2
|
||||
# Connection default
|
||||
|
||||
@@ -4,6 +4,9 @@
|
||||
# Drop mysqltest1 database, as it can left from the previous tests.
|
||||
#
|
||||
|
||||
# Save the initial number of concurrent sessions.
|
||||
--source include/count_sessions.inc
|
||||
|
||||
--disable_warnings
|
||||
drop database if exists mysqltest1;
|
||||
--enable_warnings
|
||||
@@ -12,3 +15,50 @@ create schema foo;
|
||||
show create schema foo;
|
||||
show schemas;
|
||||
drop schema foo;
|
||||
|
||||
|
||||
--echo #
|
||||
--echo # Bug #48940 MDL deadlocks against mysql_rm_db
|
||||
--echo #
|
||||
|
||||
--disable_warnings
|
||||
DROP SCHEMA IF EXISTS schema1;
|
||||
--enable_warnings
|
||||
|
||||
connect(con2, localhost, root);
|
||||
|
||||
--echo # Connection default
|
||||
connection default;
|
||||
|
||||
CREATE SCHEMA schema1;
|
||||
CREATE TABLE schema1.t1 (a INT);
|
||||
|
||||
SET autocommit= FALSE;
|
||||
INSERT INTO schema1.t1 VALUES (1);
|
||||
|
||||
--echo # Connection 2
|
||||
connection con2;
|
||||
--send DROP SCHEMA schema1
|
||||
|
||||
--echo # Connection default
|
||||
connection default;
|
||||
let $wait_condition= SELECT COUNT(*)= 1 FROM information_schema.processlist
|
||||
WHERE state= 'Waiting for table'
|
||||
AND info='DROP SCHEMA schema1';
|
||||
--source include/wait_condition.inc
|
||||
--error 1
|
||||
ALTER SCHEMA schema1 DEFAULT CHARACTER SET utf8;
|
||||
SET autocommit= TRUE;
|
||||
|
||||
--echo # Connection 2
|
||||
connection con2;
|
||||
--reap
|
||||
|
||||
--echo # Connection default
|
||||
connection default;
|
||||
disconnect con2;
|
||||
|
||||
|
||||
# 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
|
||||
|
||||
@@ -193,6 +193,8 @@ void init_update_queries(void)
|
||||
CF_PROTECT_AGAINST_GRL;
|
||||
sql_command_flags[SQLCOM_CREATE_DB]= CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS;
|
||||
sql_command_flags[SQLCOM_DROP_DB]= CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS;
|
||||
sql_command_flags[SQLCOM_ALTER_DB_UPGRADE]= CF_AUTO_COMMIT_TRANS;
|
||||
sql_command_flags[SQLCOM_ALTER_DB]= CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS;
|
||||
sql_command_flags[SQLCOM_RENAME_TABLE]= CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS;
|
||||
sql_command_flags[SQLCOM_DROP_INDEX]= CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS;
|
||||
sql_command_flags[SQLCOM_CREATE_VIEW]= CF_CHANGES_DATA | CF_REEXECUTION_FRAGILE |
|
||||
@@ -203,7 +205,6 @@ void init_update_queries(void)
|
||||
sql_command_flags[SQLCOM_CREATE_EVENT]= CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS;
|
||||
sql_command_flags[SQLCOM_ALTER_EVENT]= CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS;
|
||||
sql_command_flags[SQLCOM_DROP_EVENT]= CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS;
|
||||
sql_command_flags[SQLCOM_ALTER_DB_UPGRADE]= CF_AUTO_COMMIT_TRANS;
|
||||
sql_command_flags[SQLCOM_CREATE_TRIGGER]= CF_AUTO_COMMIT_TRANS;
|
||||
sql_command_flags[SQLCOM_DROP_TRIGGER]= CF_AUTO_COMMIT_TRANS;
|
||||
|
||||
@@ -283,7 +284,6 @@ void init_update_queries(void)
|
||||
sql_command_flags[SQLCOM_GRANT]= CF_CHANGES_DATA;
|
||||
sql_command_flags[SQLCOM_REVOKE]= CF_CHANGES_DATA;
|
||||
sql_command_flags[SQLCOM_REVOKE_ALL]= CF_PROTECT_AGAINST_GRL;
|
||||
sql_command_flags[SQLCOM_ALTER_DB]= CF_CHANGES_DATA;
|
||||
sql_command_flags[SQLCOM_CREATE_FUNCTION]= CF_CHANGES_DATA;
|
||||
sql_command_flags[SQLCOM_DROP_FUNCTION]= CF_CHANGES_DATA | CF_PROTECT_AGAINST_GRL;
|
||||
sql_command_flags[SQLCOM_OPTIMIZE]= CF_CHANGES_DATA;
|
||||
@@ -3430,7 +3430,7 @@ end_with_restore_list:
|
||||
if (check_access(thd,DROP_ACL,lex->name.str,0,1,0,
|
||||
is_schema_db(lex->name.str)))
|
||||
break;
|
||||
if (thd->locked_tables_mode || thd->active_transaction())
|
||||
if (thd->locked_tables_mode)
|
||||
{
|
||||
my_message(ER_LOCK_OR_ACTIVE_TRANSACTION,
|
||||
ER(ER_LOCK_OR_ACTIVE_TRANSACTION), MYF(0));
|
||||
@@ -3464,7 +3464,7 @@ end_with_restore_list:
|
||||
res= 1;
|
||||
break;
|
||||
}
|
||||
if (thd->locked_tables_mode || thd->active_transaction())
|
||||
if (thd->locked_tables_mode)
|
||||
{
|
||||
res= 1;
|
||||
my_message(ER_LOCK_OR_ACTIVE_TRANSACTION,
|
||||
@@ -3504,7 +3504,7 @@ end_with_restore_list:
|
||||
#endif
|
||||
if (check_access(thd, ALTER_ACL, db->str, 0, 1, 0, is_schema_db(db->str)))
|
||||
break;
|
||||
if (thd->locked_tables_mode || thd->active_transaction())
|
||||
if (thd->locked_tables_mode)
|
||||
{
|
||||
my_message(ER_LOCK_OR_ACTIVE_TRANSACTION,
|
||||
ER(ER_LOCK_OR_ACTIVE_TRANSACTION), MYF(0));
|
||||
|
||||
Reference in New Issue
Block a user