Commit 1b34d593 authored by Konstantin Osipov's avatar Konstantin Osipov

Backport of:

------------------------------------------------------------
revno: 2476.784.3
committer: davi@moksha.local
timestamp: Tue 2007-10-02 21:27:31 -0300
message:
Bug#25858 Some DROP TABLE under LOCK TABLES can cause deadlocks
        
When a client (connection) holds a lock on a table and attempts to
drop (obtain a exclusive lock) on a second table that is already
held by a second client and the second client then attempts to
drop the table that is held by the first client, leads to a
circular wait deadlock. This scenario is very similar to trying to
drop (or rename) a table while holding read locks and are
correctly forbidden.
        
The solution is to allow a drop table operation to continue only
if the table being dropped is write (exclusively) locked, or if
the table is temporary, or if the client is not holding any
locks. Using this scheme prevents the creation of a circular
chain in which each client is waiting for one table that the
next client in the chain is holding.
            
This is incompatible change, as can be seen by number of tests
cases that needed to be fixed, but is consistent with respect to
behavior of the different scenarios in which the circular wait
might happen.

mysql-test/r/drop.result:
  Test case result for Bug#25858
mysql-test/r/group_by.result:
  Fix test case result wrt drop table under lock tables -- unlock tables
  before dropping table.
mysql-test/r/insert_notembedded.result:
  Fix test case result wrt drop table under lock tables -- unlock tables
  before dropping table
mysql-test/r/lock.result:
  Fix test case result wrt drop table under lock tables -- unlock tables
  before dropping table.
mysql-test/r/lock_multi.result:
  Fix test case result wrt drop table under lock tables -- unlock tables
  before dropping table.
mysql-test/r/myisam.result:
  Fix test case result wrt drop table under lock tables -- unlock tables
  before dropping table.
mysql-test/r/view.result:
  Fix test case result wrt drop table under lock tables -- unlock tables
  before dropping table.
mysql-test/t/drop.test:
  Add test case for Bug#25858
mysql-test/t/group_by.test:
  Fix test case: unlock tables in preparation for a drop table. In some
  circumstances, dropping tables while holding locks leads to a deadlock.
mysql-test/t/insert_notembedded.test:
  Fix test case: unlock tables in preparation for a drop table. In some
  circumstances, dropping tables while holding locks leads to a deadlock.
mysql-test/t/lock.test:
  Fix test case: unlock tables in preparation for a drop table. In some
  circumstances, dropping tables while holding locks leads to a deadlock.
mysql-test/t/lock_multi.test:
  Fix test case: unlock tables in preparation for a drop table. In some
  circumstances, dropping tables while holding locks leads to a deadlock.
mysql-test/t/myisam.test:
  Fix test case: unlock tables in preparation for a drop table. In some
  circumstances, dropping tables while holding locks leads to a deadlock.
mysql-test/t/query_cache_notembedded.test:
  Fix test case: unlock tables in preparation for a drop table. In some
  circumstances, dropping tables while holding locks leads to a deadlock.
mysql-test/t/view.test:
  Fix test case: unlock tables in preparation for a drop table. In some
  circumstances, dropping tables while holding locks leads to a deadlock.
sql/lock.cc:
  When trying to obtain a name lock under lock tables, ensure that the table is properly exclusively locked and fail otherwise.
parent fcf6c154
...@@ -86,6 +86,26 @@ select 1; ...@@ -86,6 +86,26 @@ select 1;
1 1
1 1
unlock tables; unlock tables;
drop table if exists t1,t2;
create table t1 (a int);
create table t2 (a int);
lock table t1 read;
drop table t2;
ERROR HY000: Table 't2' was not locked with LOCK TABLES
drop table t1;
ERROR HY000: Table 't1' was locked with a READ lock and can't be updated
unlock tables;
drop table t1,t2;
create table t1 (i int);
create table t2 (i int);
lock tables t1 read;
lock tables t2 read;
drop table t1;
ERROR HY000: Table 't1' was not locked with LOCK TABLES
drop table t1,t2;
ERROR HY000: Table 't1' was locked with a READ lock and can't be updated
unlock tables;
drop table t1,t2;
End of 5.0 tests End of 5.0 tests
create database mysql_test; create database mysql_test;
create table mysql_test.t1(f1 int); create table mysql_test.t1(f1 int);
......
...@@ -107,8 +107,9 @@ SELECT cid, CONCAT(firstname, ' ', surname), COUNT(call_id) FROM t1 LEFT JOIN t2 ...@@ -107,8 +107,9 @@ SELECT cid, CONCAT(firstname, ' ', surname), COUNT(call_id) FROM t1 LEFT JOIN t2
cid CONCAT(firstname, ' ', surname) COUNT(call_id) cid CONCAT(firstname, ' ', surname) COUNT(call_id)
SELECT HIGH_PRIORITY cid, CONCAT(firstname, ' ', surname), COUNT(call_id) FROM t1 LEFT JOIN t2 ON cid=contact_id WHERE firstname like '%foo%' GROUP BY cid ORDER BY surname, firstname; SELECT HIGH_PRIORITY cid, CONCAT(firstname, ' ', surname), COUNT(call_id) FROM t1 LEFT JOIN t2 ON cid=contact_id WHERE firstname like '%foo%' GROUP BY cid ORDER BY surname, firstname;
cid CONCAT(firstname, ' ', surname) COUNT(call_id) cid CONCAT(firstname, ' ', surname) COUNT(call_id)
drop table t1,t2; drop table t2;
unlock tables; unlock tables;
drop table t1;
CREATE TABLE t1 ( CREATE TABLE t1 (
bug_id mediumint(9) NOT NULL auto_increment, bug_id mediumint(9) NOT NULL auto_increment,
groupset bigint(20) DEFAULT '0' NOT NULL, groupset bigint(20) DEFAULT '0' NOT NULL,
......
...@@ -122,5 +122,6 @@ a b ...@@ -122,5 +122,6 @@ a b
connection: default connection: default
select * from t1; select * from t1;
a b a b
unlock tables;
drop table t1; drop table t1;
set low_priority_updates=default; set low_priority_updates=default;
...@@ -48,6 +48,9 @@ unlock tables; ...@@ -48,6 +48,9 @@ unlock tables;
lock tables t1 write, t1 as t1_alias read; lock tables t1 write, t1 as t1_alias read;
insert into t1 select index1,nr from t1 as t1_alias; insert into t1 select index1,nr from t1 as t1_alias;
drop table t1,t2; drop table t1,t2;
ERROR HY000: Table 't2' was not locked with LOCK TABLES
unlock tables;
drop table t1,t2;
create table t1 (c1 int); create table t1 (c1 int);
create table t2 (c1 int); create table t2 (c1 int);
create table t3 (c1 int); create table t3 (c1 int);
...@@ -69,6 +72,9 @@ ERROR HY000: Table 't2' was locked with a READ lock and can't be updated ...@@ -69,6 +72,9 @@ ERROR HY000: Table 't2' was locked with a READ lock and can't be updated
delete t2 from t1,t2 where t1.a=t2.a; delete t2 from t1,t2 where t1.a=t2.a;
ERROR HY000: Table 't2' was locked with a READ lock and can't be updated ERROR HY000: Table 't2' was locked with a READ lock and can't be updated
drop table t1,t2; drop table t1,t2;
ERROR HY000: Table 't2' was locked with a READ lock and can't be updated
unlock tables;
drop table t2,t1;
End of 4.1 tests. End of 4.1 tests.
drop table if exists t1; drop table if exists t1;
create table t1 (a int); create table t1 (a int);
......
...@@ -27,6 +27,7 @@ update t1,t2 set c=a where b=d; ...@@ -27,6 +27,7 @@ update t1,t2 set c=a where b=d;
select c from t2; select c from t2;
c c
2 2
unlock tables;
drop table t1; drop table t1;
drop table t2; drop table t2;
create table t1 (a int); create table t1 (a int);
......
...@@ -529,6 +529,7 @@ select straight_join * from t1,t2 force index (primary) where t1.a=t2.a; ...@@ -529,6 +529,7 @@ select straight_join * from t1,t2 force index (primary) where t1.a=t2.a;
a a b a a b
1 1 1 1 1 1
2 2 1 2 2 1
unlock tables;
drop table t1,t2; drop table t1,t2;
CREATE TABLE t1 (c1 varchar(250) NOT NULL); CREATE TABLE t1 (c1 varchar(250) NOT NULL);
CREATE TABLE t2 (c1 varchar(250) NOT NULL, PRIMARY KEY (c1)); CREATE TABLE t2 (c1 varchar(250) NOT NULL, PRIMARY KEY (c1));
...@@ -542,6 +543,7 @@ INSERT INTO t2 VALUES ('test000001'), ('test000005'); ...@@ -542,6 +543,7 @@ INSERT INTO t2 VALUES ('test000001'), ('test000005');
SELECT t1.c1 AS t1c1, t2.c1 AS t2c1 FROM t1, t2 SELECT t1.c1 AS t1c1, t2.c1 AS t2c1 FROM t1, t2
WHERE t1.c1 = t2.c1 HAVING t1c1 != t2c1; WHERE t1.c1 = t2.c1 HAVING t1c1 != t2c1;
t1c1 t2c1 t1c1 t2c1
UNLOCK TABLES;
DROP TABLE t1,t2; DROP TABLE t1,t2;
CREATE TABLE t1 (`a` int(11) NOT NULL default '0', `b` int(11) NOT NULL default '0', UNIQUE KEY `a` USING RTREE (`a`,`b`)) ENGINE=MyISAM; CREATE TABLE t1 (`a` int(11) NOT NULL default '0', `b` int(11) NOT NULL default '0', UNIQUE KEY `a` USING RTREE (`a`,`b`)) ENGINE=MyISAM;
Got one of the listed errors Got one of the listed errors
......
...@@ -93,6 +93,7 @@ a ...@@ -93,6 +93,7 @@ a
3 3
SELECT * FROM t1; SELECT * FROM t1;
a a
UNLOCK TABLES;
drop table t1; drop table t1;
flush query cache; flush query cache;
reset query cache; reset query cache;
......
...@@ -1104,6 +1104,9 @@ select * from t2; ...@@ -1104,6 +1104,9 @@ select * from t2;
ERROR HY000: Table 't2' was not locked with LOCK TABLES ERROR HY000: Table 't2' was not locked with LOCK TABLES
drop view v1; drop view v1;
drop table t1, t2; drop table t1, t2;
ERROR HY000: Table 't1' was locked with a READ lock and can't be updated
unlock tables;
drop table t1, t2;
create table t1 (a int); create table t1 (a int);
create view v1 as select * from t1 where a < 2 with check option; create view v1 as select * from t1 where a < 2 with check option;
insert into v1 values(1); insert into v1 values(1);
......
...@@ -124,6 +124,39 @@ disconnect addconroot1; ...@@ -124,6 +124,39 @@ disconnect addconroot1;
--source include/wait_until_disconnected.inc --source include/wait_until_disconnected.inc
connection default; connection default;
#
# Bug#25858 Some DROP TABLE under LOCK TABLES can cause deadlocks
#
--disable_warnings
drop table if exists t1,t2;
--enable_warnings
create table t1 (a int);
create table t2 (a int);
lock table t1 read;
--error ER_TABLE_NOT_LOCKED
drop table t2;
--error ER_TABLE_NOT_LOCKED_FOR_WRITE
drop table t1;
unlock tables;
drop table t1,t2;
connect (addconroot, localhost, root,,);
connection default;
create table t1 (i int);
create table t2 (i int);
lock tables t1 read;
connection addconroot;
lock tables t2 read;
--error ER_TABLE_NOT_LOCKED
drop table t1;
connection default;
--error ER_TABLE_NOT_LOCKED_FOR_WRITE
drop table t1,t2;
disconnect addconroot;
connection default;
unlock tables;
drop table t1,t2;
--echo End of 5.0 tests --echo End of 5.0 tests
# #
......
...@@ -120,8 +120,9 @@ SELECT cid, CONCAT(firstname, ' ', surname), COUNT(call_id) FROM t1 LEFT JOIN t2 ...@@ -120,8 +120,9 @@ SELECT cid, CONCAT(firstname, ' ', surname), COUNT(call_id) FROM t1 LEFT JOIN t2
SELECT cid, CONCAT(firstname, ' ', surname), COUNT(call_id) FROM t1 LEFT JOIN t2 ON cid=contact_id WHERE firstname like '%foo%' GROUP BY cid ORDER BY NULL; SELECT cid, CONCAT(firstname, ' ', surname), COUNT(call_id) FROM t1 LEFT JOIN t2 ON cid=contact_id WHERE firstname like '%foo%' GROUP BY cid ORDER BY NULL;
SELECT HIGH_PRIORITY cid, CONCAT(firstname, ' ', surname), COUNT(call_id) FROM t1 LEFT JOIN t2 ON cid=contact_id WHERE firstname like '%foo%' GROUP BY cid ORDER BY surname, firstname; SELECT HIGH_PRIORITY cid, CONCAT(firstname, ' ', surname), COUNT(call_id) FROM t1 LEFT JOIN t2 ON cid=contact_id WHERE firstname like '%foo%' GROUP BY cid ORDER BY surname, firstname;
drop table t1,t2; drop table t2;
unlock tables; unlock tables;
drop table t1;
# #
# Test of group by bug in bugzilla # Test of group by bug in bugzilla
......
...@@ -185,5 +185,6 @@ select * from t1; ...@@ -185,5 +185,6 @@ select * from t1;
connection default; connection default;
disconnect update; disconnect update;
disconnect select; disconnect select;
unlock tables;
drop table t1; drop table t1;
set low_priority_updates=default; set low_priority_updates=default;
...@@ -58,6 +58,9 @@ insert into t1 select index1,nr from t1; ...@@ -58,6 +58,9 @@ insert into t1 select index1,nr from t1;
unlock tables; unlock tables;
lock tables t1 write, t1 as t1_alias read; lock tables t1 write, t1 as t1_alias read;
insert into t1 select index1,nr from t1 as t1_alias; insert into t1 select index1,nr from t1 as t1_alias;
--error ER_TABLE_NOT_LOCKED
drop table t1,t2;
unlock tables;
drop table t1,t2; drop table t1,t2;
# #
...@@ -90,7 +93,10 @@ delete t1 from t1,t2 where t1.a=t2.a; ...@@ -90,7 +93,10 @@ delete t1 from t1,t2 where t1.a=t2.a;
delete from t2 using t1,t2 where t1.a=t2.a; delete from t2 using t1,t2 where t1.a=t2.a;
--error 1099 --error 1099
delete t2 from t1,t2 where t1.a=t2.a; delete t2 from t1,t2 where t1.a=t2.a;
--error ER_TABLE_NOT_LOCKED_FOR_WRITE
drop table t1,t2; drop table t1,t2;
unlock tables;
drop table t2,t1;
--echo End of 4.1 tests. --echo End of 4.1 tests.
......
...@@ -76,6 +76,7 @@ update t1,t2 set c=a where b=d; ...@@ -76,6 +76,7 @@ update t1,t2 set c=a where b=d;
connection reader; connection reader;
select c from t2; select c from t2;
connection locker; connection locker;
unlock tables;
drop table t1; drop table t1;
drop table t2; drop table t2;
......
...@@ -503,6 +503,7 @@ insert into t2 values(2,0); ...@@ -503,6 +503,7 @@ insert into t2 values(2,0);
disconnect root; disconnect root;
connection default; connection default;
select straight_join * from t1,t2 force index (primary) where t1.a=t2.a; select straight_join * from t1,t2 force index (primary) where t1.a=t2.a;
unlock tables;
drop table t1,t2; drop table t1,t2;
# #
# Full key. # Full key.
...@@ -520,6 +521,7 @@ disconnect con1; ...@@ -520,6 +521,7 @@ disconnect con1;
connection default; connection default;
SELECT t1.c1 AS t1c1, t2.c1 AS t2c1 FROM t1, t2 SELECT t1.c1 AS t1c1, t2.c1 AS t2c1 FROM t1, t2
WHERE t1.c1 = t2.c1 HAVING t1c1 != t2c1; WHERE t1.c1 = t2.c1 HAVING t1c1 != t2c1;
UNLOCK TABLES;
DROP TABLE t1,t2; DROP TABLE t1,t2;
# End of 4.0 tests # End of 4.0 tests
......
...@@ -99,6 +99,7 @@ connection root2; ...@@ -99,6 +99,7 @@ connection root2;
SELECT * FROM t1; SELECT * FROM t1;
connection root; connection root;
SELECT * FROM t1; SELECT * FROM t1;
UNLOCK TABLES;
drop table t1; drop table t1;
connection default; connection default;
disconnect root; disconnect root;
......
...@@ -1017,6 +1017,9 @@ select * from v1; ...@@ -1017,6 +1017,9 @@ select * from v1;
-- error ER_TABLE_NOT_LOCKED -- error ER_TABLE_NOT_LOCKED
select * from t2; select * from t2;
drop view v1; drop view v1;
--error ER_TABLE_NOT_LOCKED_FOR_WRITE
drop table t1, t2;
unlock tables;
drop table t1, t2; drop table t1, t2;
# #
......
...@@ -1028,6 +1028,7 @@ int lock_table_name(THD *thd, TABLE_LIST *table_list, bool check_in_use) ...@@ -1028,6 +1028,7 @@ int lock_table_name(THD *thd, TABLE_LIST *table_list, bool check_in_use)
char key[MAX_DBKEY_LENGTH]; char key[MAX_DBKEY_LENGTH];
char *db= table_list->db; char *db= table_list->db;
uint key_length; uint key_length;
bool found_locked_table= FALSE;
HASH_SEARCH_STATE state; HASH_SEARCH_STATE state;
DBUG_ENTER("lock_table_name"); DBUG_ENTER("lock_table_name");
DBUG_PRINT("enter",("db: %s name: %s", db, table_list->table_name)); DBUG_PRINT("enter",("db: %s name: %s", db, table_list->table_name));
...@@ -1043,6 +1044,13 @@ int lock_table_name(THD *thd, TABLE_LIST *table_list, bool check_in_use) ...@@ -1043,6 +1044,13 @@ int lock_table_name(THD *thd, TABLE_LIST *table_list, bool check_in_use)
table = (TABLE*) my_hash_next(&open_cache,(uchar*) key, table = (TABLE*) my_hash_next(&open_cache,(uchar*) key,
key_length, &state)) key_length, &state))
{ {
if (table->reginfo.lock_type < TL_WRITE)
{
if (table->in_use == thd)
found_locked_table= TRUE;
continue;
}
if (table->in_use == thd) if (table->in_use == thd)
{ {
DBUG_PRINT("info", ("Table is in use")); DBUG_PRINT("info", ("Table is in use"));
...@@ -1053,6 +1061,17 @@ int lock_table_name(THD *thd, TABLE_LIST *table_list, bool check_in_use) ...@@ -1053,6 +1061,17 @@ int lock_table_name(THD *thd, TABLE_LIST *table_list, bool check_in_use)
} }
} }
if (thd->locked_tables && thd->locked_tables->table_count &&
! find_temporary_table(thd, table_list->db, table_list->table_name))
{
if (found_locked_table)
my_error(ER_TABLE_NOT_LOCKED_FOR_WRITE, MYF(0), table_list->alias);
else
my_error(ER_TABLE_NOT_LOCKED, MYF(0), table_list->alias);
DBUG_RETURN(-1);
}
if (!(table= table_cache_insert_placeholder(thd, key, key_length))) if (!(table= table_cache_insert_placeholder(thd, key, key_length)))
DBUG_RETURN(-1); DBUG_RETURN(-1);
......
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