Commit 8bdb043f authored by unknown's avatar unknown

Bug#25856 (HANDLER table OPEN in one connection lock DROP TABLE in another one)

mysql_ha_open calls mysql_ha_close on the error path (unsupported) to close the (opened) table before inserting it into the tables hash list handler_tables_hash) but mysql_ha_close only closes tables which are on the hash list, causing the table to be left open and locked.

This change moves the table close logic into a separate function that is always called on the error path of mysql_ha_open or on a normal handler close (mysql_ha_close).


mysql-test/r/lock_multi.result:
  Bug#25856 test result
mysql-test/t/lock_multi.test:
  Bug#25856 test case
sql/sql_handler.cc:
  Move the table close logic into a separate function that is always called on the error path of mysql_ha_open or on a normal handler close.
parent d811750f
...@@ -106,4 +106,11 @@ i ...@@ -106,4 +106,11 @@ i
ERROR 70100: Query execution was interrupted ERROR 70100: Query execution was interrupted
unlock tables; unlock tables;
drop table t1; drop table t1;
drop table if exists t1;
create table t1 (a int) ENGINE=MEMORY;
--> client 2
handler t1 open;
ERROR HY000: Table storage engine for 't1' doesn't have this option
--> client 1
drop table t1;
End of 5.1 tests End of 5.1 tests
...@@ -328,4 +328,19 @@ unlock tables; ...@@ -328,4 +328,19 @@ unlock tables;
connection default; connection default;
drop table t1; drop table t1;
#
# Bug#25856 - HANDLER table OPEN in one connection lock DROP TABLE in another one
#
--disable_warnings
drop table if exists t1;
--enable_warnings
create table t1 (a int) ENGINE=MEMORY;
--echo --> client 2
connection locker;
--error 1031
handler t1 open;
--echo --> client 1
connection default;
drop table t1;
--echo End of 5.1 tests --echo End of 5.1 tests
...@@ -119,6 +119,44 @@ static void mysql_ha_hash_free(TABLE_LIST *tables) ...@@ -119,6 +119,44 @@ static void mysql_ha_hash_free(TABLE_LIST *tables)
my_free((char*) tables, MYF(0)); my_free((char*) tables, MYF(0));
} }
/**
Close a HANDLER table.
@param thd Thread identifier.
@param tables A list of tables with the first entry to close.
@note Though this function takes a list of tables, only the first list entry
will be closed.
@note Broadcasts refresh if it closed the table.
*/
static void mysql_ha_close_table(THD *thd, TABLE_LIST *tables)
{
TABLE **table_ptr;
/*
Though we could take the table pointer from hash_tables->table,
we must follow the thd->handler_tables chain anyway, as we need the
address of the 'next' pointer referencing this table
for close_thread_table().
*/
for (table_ptr= &(thd->handler_tables);
*table_ptr && (*table_ptr != tables->table);
table_ptr= &(*table_ptr)->next)
;
if (*table_ptr)
{
(*table_ptr)->file->ha_index_or_rnd_end();
VOID(pthread_mutex_lock(&LOCK_open));
if (close_thread_table(thd, table_ptr))
{
/* Tell threads waiting for refresh that something has happened */
broadcast_refresh();
}
VOID(pthread_mutex_unlock(&LOCK_open));
}
}
/* /*
Open a HANDLER table. Open a HANDLER table.
...@@ -145,7 +183,7 @@ static void mysql_ha_hash_free(TABLE_LIST *tables) ...@@ -145,7 +183,7 @@ static void mysql_ha_hash_free(TABLE_LIST *tables)
bool mysql_ha_open(THD *thd, TABLE_LIST *tables, bool reopen) bool mysql_ha_open(THD *thd, TABLE_LIST *tables, bool reopen)
{ {
TABLE_LIST *hash_tables; TABLE_LIST *hash_tables = NULL;
char *db, *name, *alias; char *db, *name, *alias;
uint dblen, namelen, aliaslen, counter; uint dblen, namelen, aliaslen, counter;
int error; int error;
...@@ -197,7 +235,6 @@ bool mysql_ha_open(THD *thd, TABLE_LIST *tables, bool reopen) ...@@ -197,7 +235,6 @@ bool mysql_ha_open(THD *thd, TABLE_LIST *tables, bool reopen)
{ {
if (! reopen) if (! reopen)
my_error(ER_ILLEGAL_HA, MYF(0), tables->alias); my_error(ER_ILLEGAL_HA, MYF(0), tables->alias);
mysql_ha_close(thd, tables);
goto err; goto err;
} }
...@@ -225,12 +262,8 @@ bool mysql_ha_open(THD *thd, TABLE_LIST *tables, bool reopen) ...@@ -225,12 +262,8 @@ bool mysql_ha_open(THD *thd, TABLE_LIST *tables, bool reopen)
/* add to hash */ /* add to hash */
if (my_hash_insert(&thd->handler_tables_hash, (uchar*) hash_tables)) if (my_hash_insert(&thd->handler_tables_hash, (uchar*) hash_tables))
{
my_free((char*) hash_tables, MYF(0));
mysql_ha_close(thd, tables);
goto err; goto err;
} }
}
if (! reopen) if (! reopen)
send_ok(thd); send_ok(thd);
...@@ -238,13 +271,17 @@ bool mysql_ha_open(THD *thd, TABLE_LIST *tables, bool reopen) ...@@ -238,13 +271,17 @@ bool mysql_ha_open(THD *thd, TABLE_LIST *tables, bool reopen)
DBUG_RETURN(FALSE); DBUG_RETURN(FALSE);
err: err:
if (hash_tables)
my_free((char*) hash_tables, MYF(0));
if (tables->table)
mysql_ha_close_table(thd, tables);
DBUG_PRINT("exit",("ERROR")); DBUG_PRINT("exit",("ERROR"));
DBUG_RETURN(TRUE); DBUG_RETURN(TRUE);
} }
/* /*
Close a HANDLER table. Close a HANDLER table by alias or table name
SYNOPSIS SYNOPSIS
mysql_ha_close() mysql_ha_close()
...@@ -252,9 +289,8 @@ err: ...@@ -252,9 +289,8 @@ err:
tables A list of tables with the first entry to close. tables A list of tables with the first entry to close.
DESCRIPTION DESCRIPTION
Though this function takes a list of tables, only the first list entry Closes the table that is associated (on the handler tables hash) with the
will be closed. name (table->alias) of the specified table.
Broadcasts refresh if it closed the table.
RETURN RETURN
FALSE ok FALSE ok
...@@ -264,7 +300,6 @@ err: ...@@ -264,7 +300,6 @@ err:
bool mysql_ha_close(THD *thd, TABLE_LIST *tables) bool mysql_ha_close(THD *thd, TABLE_LIST *tables)
{ {
TABLE_LIST *hash_tables; TABLE_LIST *hash_tables;
TABLE **table_ptr;
DBUG_ENTER("mysql_ha_close"); DBUG_ENTER("mysql_ha_close");
DBUG_PRINT("enter",("'%s'.'%s' as '%s'", DBUG_PRINT("enter",("'%s'.'%s' as '%s'",
tables->db, tables->table_name, tables->alias)); tables->db, tables->table_name, tables->alias));
...@@ -273,28 +308,7 @@ bool mysql_ha_close(THD *thd, TABLE_LIST *tables) ...@@ -273,28 +308,7 @@ bool mysql_ha_close(THD *thd, TABLE_LIST *tables)
(uchar*) tables->alias, (uchar*) tables->alias,
strlen(tables->alias) + 1))) strlen(tables->alias) + 1)))
{ {
/* mysql_ha_close_table(thd, hash_tables);
Though we could take the table pointer from hash_tables->table,
we must follow the thd->handler_tables chain anyway, as we need the
address of the 'next' pointer referencing this table
for close_thread_table().
*/
for (table_ptr= &(thd->handler_tables);
*table_ptr && (*table_ptr != hash_tables->table);
table_ptr= &(*table_ptr)->next)
;
if (*table_ptr)
{
(*table_ptr)->file->ha_index_or_rnd_end();
VOID(pthread_mutex_lock(&LOCK_open));
if (close_thread_table(thd, table_ptr))
{
/* Tell threads waiting for refresh that something has happened */
broadcast_refresh();
}
VOID(pthread_mutex_unlock(&LOCK_open));
}
hash_delete(&thd->handler_tables_hash, (uchar*) hash_tables); hash_delete(&thd->handler_tables_hash, (uchar*) hash_tables);
} }
else else
......
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