Commit 3394cbf7 authored by Jon Olav Hauglid's avatar Jon Olav Hauglid

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.
parent 700a361a
......@@ -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 @@ case SQLCOM_PREPARE:
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 @@ case SQLCOM_PREPARE:
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 @@ case SQLCOM_PREPARE:
#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));
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment