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
This commit is contained in:
parent
391bc11a21
commit
374ea106f5
76
sql/lock.cc
76
sql/lock.cc
@ -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)
|
|||||||
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)
|
|||||||
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);
|
|||||||
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,
|
|||||||
|
|
||||||
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)
|
|||||||
}
|
}
|
||||||
|
|
||||||
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 @@ end:
|
|||||||
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 @@ end:
|
|||||||
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)
|
|||||||
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)
|
|||||||
{
|
{
|
||||||
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)
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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)
|
|||||||
}
|
}
|
||||||
|
|
||||||
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;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user