Commit 9c1ffac0 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/handler.result:
  Bug#25856 test result
mysql-test/t/handler.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 e6701d74
...@@ -482,3 +482,10 @@ ERROR 42S02: Table 'test.t1' doesn't exist ...@@ -482,3 +482,10 @@ ERROR 42S02: Table 'test.t1' doesn't exist
drop table if exists t1; drop table if exists t1;
Warnings: Warnings:
Note 1051 Unknown table 't1' Note 1051 Unknown 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;
...@@ -427,3 +427,17 @@ select * from t1; ...@@ -427,3 +427,17 @@ select * from t1;
# Just to be sure and not confuse the next test case writer. # Just to be sure and not confuse the next test case writer.
drop table if exists t1; drop table if exists 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 con2;
--error 1031
handler t1 open;
--echo --> client 1
connection default;
drop table t1;
...@@ -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 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,11 +262,7 @@ bool mysql_ha_open(THD *thd, TABLE_LIST *tables, bool reopen) ...@@ -225,11 +262,7 @@ 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, (byte*) hash_tables)) if (my_hash_insert(&thd->handler_tables_hash, (byte*) hash_tables))
{
my_free((char*) hash_tables, MYF(0));
mysql_ha_close(thd, tables);
goto err; goto err;
}
} }
if (! reopen) if (! reopen)
...@@ -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)
(byte*) tables->alias, (byte*) 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, (byte*) hash_tables); hash_delete(&thd->handler_tables_hash, (byte*) 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