Commit 374ea106 authored by unknown's avatar unknown

Fixed a deadlock problem when using LOCK TABLE in one thread and DROP TABLE in another


sql/lock.cc:
  Added functions to handle list of table name locks
sql/mysql_priv.h:
  Added functions to handle list of named locks
sql/sql_rename.cc:
  Use new general table name lock functions
sql/sql_table.cc:
  Require table name locks when doing drop table.
  This fixed a deadlock problem when using LOCK TABLE in one thread and DROP TABLE in another
parent 391bc11a
...@@ -416,10 +416,11 @@ int lock_table_name(THD *thd, TABLE_LIST *table_list) ...@@ -416,10 +416,11 @@ int lock_table_name(THD *thd, TABLE_LIST *table_list)
{ {
TABLE *table; TABLE *table;
char key[MAX_DBKEY_LENGTH]; char key[MAX_DBKEY_LENGTH];
char *db= table_list->db ? table_list->db : (thd->db ? thd->db : (char*) "");
uint key_length; uint key_length;
DBUG_ENTER("lock_table_name"); DBUG_ENTER("lock_table_name");
key_length=(uint) (strmov(strmov(key,table_list->db)+1,table_list->real_name) key_length=(uint) (strmov(strmov(key,db)+1,table_list->real_name)
-key)+ 1; -key)+ 1;
/* Only insert the table if we haven't insert it already */ /* Only insert the table if we haven't insert it already */
...@@ -447,7 +448,7 @@ int lock_table_name(THD *thd, TABLE_LIST *table_list) ...@@ -447,7 +448,7 @@ int lock_table_name(THD *thd, TABLE_LIST *table_list)
my_free((gptr) table,MYF(0)); my_free((gptr) table,MYF(0));
DBUG_RETURN(-1); DBUG_RETURN(-1);
} }
if (remove_table_from_cache(thd, table_list->db, table_list->real_name)) if (remove_table_from_cache(thd, db, table_list->real_name))
DBUG_RETURN(1); // Table is in use DBUG_RETURN(1); // Table is in use
DBUG_RETURN(0); DBUG_RETURN(0);
} }
...@@ -490,6 +491,77 @@ bool wait_for_locked_table_names(THD *thd, TABLE_LIST *table_list) ...@@ -490,6 +491,77 @@ bool wait_for_locked_table_names(THD *thd, TABLE_LIST *table_list)
DBUG_RETURN(result); DBUG_RETURN(result);
} }
/*
Lock all tables in list with a name lock
SYNOPSIS
lock_table_names()
thd Thread handle
table_list Names of tables to lock
NOTES
One must have a lock on LOCK_open when calling this
RETURN
0 ok
1 Fatal error (end of memory ?)
*/
bool lock_table_names(THD *thd, TABLE_LIST *table_list)
{
bool got_all_locks=1;
TABLE_LIST *lock_table;
for (lock_table=table_list ; lock_table ; lock_table=lock_table->next)
{
int got_lock;
if ((got_lock=lock_table_name(thd,lock_table)) < 0)
goto end; // Fatal error
if (got_lock)
got_all_locks=0; // Someone is using table
}
/* If some table was in use, wait until we got the lock */
if (!got_all_locks && wait_for_locked_table_names(thd, table_list))
goto end;
return 0;
end:
unlock_table_names(thd, table_list, lock_table);
return 1;
}
/*
Unlock all tables in list with a name lock
SYNOPSIS
unlock_table_names()
thd Thread handle
table_list Names of tables to unlock
last_table Don't unlock any tables after this one.
(default 0, which will unlock all tables)
NOTES
One must have a lock on LOCK_open when calling this
This function will send a COND_refresh signal to inform other threads
that the name locks are removed
RETURN
0 ok
1 Fatal error (end of memory ?)
*/
void unlock_table_names(THD *thd, TABLE_LIST *table_list,
TABLE_LIST *last_table)
{
for (TABLE_LIST *table=table_list ; table != last_table ; table=table->next)
unlock_table_name(thd,table);
pthread_cond_broadcast(&COND_refresh);
}
static void print_lock_error(int error) static void print_lock_error(int error)
{ {
int textno; int textno;
......
...@@ -594,6 +594,9 @@ MYSQL_LOCK *mysql_lock_merge(MYSQL_LOCK *a,MYSQL_LOCK *b); ...@@ -594,6 +594,9 @@ MYSQL_LOCK *mysql_lock_merge(MYSQL_LOCK *a,MYSQL_LOCK *b);
int lock_table_name(THD *thd, TABLE_LIST *table_list); int lock_table_name(THD *thd, TABLE_LIST *table_list);
void unlock_table_name(THD *thd, TABLE_LIST *table_list); void unlock_table_name(THD *thd, TABLE_LIST *table_list);
bool wait_for_locked_table_names(THD *thd, TABLE_LIST *table_list); bool wait_for_locked_table_names(THD *thd, TABLE_LIST *table_list);
bool lock_table_names(THD *thd, TABLE_LIST *table_list);
void unlock_table_names(THD *thd, TABLE_LIST *table_list,
TABLE_LIST *last_table= 0);
/* old unireg functions */ /* old unireg functions */
......
...@@ -31,8 +31,8 @@ static TABLE_LIST *rename_tables(THD *thd, TABLE_LIST *table_list, ...@@ -31,8 +31,8 @@ static TABLE_LIST *rename_tables(THD *thd, TABLE_LIST *table_list,
bool mysql_rename_tables(THD *thd, TABLE_LIST *table_list) bool mysql_rename_tables(THD *thd, TABLE_LIST *table_list)
{ {
bool error=1,cerror,got_all_locks=1; bool error=1, cerror;
TABLE_LIST *lock_table,*ren_table=0; TABLE_LIST *ren_table= 0;
DBUG_ENTER("mysql_rename_tables"); DBUG_ENTER("mysql_rename_tables");
/* Avoid problems with a rename on a table that we have locked or /* Avoid problems with a rename on a table that we have locked or
...@@ -45,23 +45,11 @@ bool mysql_rename_tables(THD *thd, TABLE_LIST *table_list) ...@@ -45,23 +45,11 @@ bool mysql_rename_tables(THD *thd, TABLE_LIST *table_list)
} }
VOID(pthread_mutex_lock(&LOCK_open)); VOID(pthread_mutex_lock(&LOCK_open));
for (lock_table=table_list ; lock_table ; lock_table=lock_table->next) if (lock_table_names(thd, table_list))
{ goto err;
int got_lock;
if ((got_lock=lock_table_name(thd,lock_table)) < 0)
goto end;
if (got_lock)
got_all_locks=0;
}
if (!got_all_locks && wait_for_locked_table_names(thd,table_list))
goto end;
if (!(ren_table=rename_tables(thd,table_list,0))) error= 0;
error=0; if ((ren_table=rename_tables(thd,table_list,0)))
end:
if (ren_table)
{ {
/* Rename didn't succeed; rename back the tables in reverse order */ /* Rename didn't succeed; rename back the tables in reverse order */
TABLE_LIST *prev=0,*table; TABLE_LIST *prev=0,*table;
...@@ -83,7 +71,7 @@ bool mysql_rename_tables(THD *thd, TABLE_LIST *table_list) ...@@ -83,7 +71,7 @@ bool mysql_rename_tables(THD *thd, TABLE_LIST *table_list)
table=table->next->next; // Skipp error table table=table->next->next; // Skipp error table
/* Revert to old names */ /* Revert to old names */
rename_tables(thd, table, 1); rename_tables(thd, table, 1);
/* Note that lock_table == 0 here, so the unlock loop will work */ error= 1;
} }
/* Lets hope this doesn't fail as the result will be messy */ /* Lets hope this doesn't fail as the result will be messy */
...@@ -103,9 +91,9 @@ bool mysql_rename_tables(THD *thd, TABLE_LIST *table_list) ...@@ -103,9 +91,9 @@ bool mysql_rename_tables(THD *thd, TABLE_LIST *table_list)
send_ok(&thd->net); send_ok(&thd->net);
} }
for (TABLE_LIST *table=table_list ; table != lock_table ; table=table->next) unlock_table_names(thd,table_list);
unlock_table_name(thd,table);
pthread_cond_broadcast(&COND_refresh); err:
pthread_mutex_unlock(&LOCK_open); pthread_mutex_unlock(&LOCK_open);
DBUG_RETURN(error); DBUG_RETURN(error);
} }
......
...@@ -49,7 +49,7 @@ int mysql_rm_table(THD *thd,TABLE_LIST *tables, my_bool if_exists) ...@@ -49,7 +49,7 @@ int mysql_rm_table(THD *thd,TABLE_LIST *tables, my_bool if_exists)
char path[FN_REFLEN]; char path[FN_REFLEN];
String wrong_tables; String wrong_tables;
bool some_tables_deleted=0; bool some_tables_deleted=0;
uint error; uint error= 1;
db_type table_type; db_type table_type;
TABLE_LIST *table; TABLE_LIST *table;
DBUG_ENTER("mysql_rm_table"); DBUG_ENTER("mysql_rm_table");
...@@ -66,7 +66,6 @@ int mysql_rm_table(THD *thd,TABLE_LIST *tables, my_bool if_exists) ...@@ -66,7 +66,6 @@ int mysql_rm_table(THD *thd,TABLE_LIST *tables, my_bool if_exists)
{ {
my_error(ER_TABLE_NOT_LOCKED_FOR_WRITE,MYF(0), my_error(ER_TABLE_NOT_LOCKED_FOR_WRITE,MYF(0),
tables->real_name); tables->real_name);
error = 1;
goto err; goto err;
} }
while (global_read_lock && ! thd->killed) while (global_read_lock && ! thd->killed)
...@@ -76,9 +75,12 @@ int mysql_rm_table(THD *thd,TABLE_LIST *tables, my_bool if_exists) ...@@ -76,9 +75,12 @@ int mysql_rm_table(THD *thd,TABLE_LIST *tables, my_bool if_exists)
} }
if (lock_table_names(thd, tables))
goto err;
for (table=tables ; table ; table=table->next) for (table=tables ; table ; table=table->next)
{ {
char *db=table->db ? table->db : thd->db; char *db=table->db ? table->db : (thd->db ? thd->db : (char*) "");
if (!close_temporary_table(thd, db, table->real_name)) if (!close_temporary_table(thd, db, table->real_name))
{ {
some_tables_deleted=1; // Log query some_tables_deleted=1; // Log query
...@@ -149,9 +151,10 @@ int mysql_rm_table(THD *thd,TABLE_LIST *tables, my_bool if_exists) ...@@ -149,9 +151,10 @@ int mysql_rm_table(THD *thd,TABLE_LIST *tables, my_bool if_exists)
} }
error = 0; error = 0;
unlock_table_names(thd, tables);
err: err:
pthread_mutex_unlock(&LOCK_open); pthread_mutex_unlock(&LOCK_open);
VOID(pthread_cond_broadcast(&COND_refresh)); // Signal to refresh
pthread_mutex_lock(&thd->mysys_var->mutex); pthread_mutex_lock(&thd->mysys_var->mutex);
thd->mysys_var->current_mutex= 0; thd->mysys_var->current_mutex= 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