summaryrefslogtreecommitdiff
path: root/sql
diff options
context:
space:
mode:
authorKonstantin Osipov <kostja@sun.com>2009-11-20 22:51:12 +0300
committerKonstantin Osipov <kostja@sun.com>2009-11-20 22:51:12 +0300
commit1ee8a58882daa8f5ba93bbc6032f0201ed4004d9 (patch)
treeeb1d7af310172485cbf7985efd1057955f7a9385 /sql
parent948ee7e5d9aa45970a3ba7208860982d093f540f (diff)
downloadmariadb-git-1ee8a58882daa8f5ba93bbc6032f0201ed4004d9.tar.gz
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.
Diffstat (limited to 'sql')
-rw-r--r--sql/lock.cc19
1 files changed, 19 insertions, 0 deletions
diff --git a/sql/lock.cc b/sql/lock.cc
index 112d3dc4a2b..6cdd654fb92 100644
--- a/sql/lock.cc
+++ b/sql/lock.cc
@@ -1028,6 +1028,7 @@ int lock_table_name(THD *thd, TABLE_LIST *table_list, bool check_in_use)
char key[MAX_DBKEY_LENGTH];
char *db= table_list->db;
uint key_length;
+ bool found_locked_table= FALSE;
HASH_SEARCH_STATE state;
DBUG_ENTER("lock_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)
table = (TABLE*) my_hash_next(&open_cache,(uchar*) key,
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)
{
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)
}
}
+ 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)))
DBUG_RETURN(-1);