Commit e4f47653 authored by Davi Arnaut's avatar Davi Arnaut

Bug#41110: crash with handler command when used concurrently with alter table

Bug#41112: crash in mysql_ha_close_table/get_lock_data with alter table

The problem is that the server wasn't handling robustly failures
to re-open a table during a HANDLER .. READ statement. If the
table needed to be re-opened due to it's storage engine being
altered to one that doesn't support HANDLER, a reference (dangling
pointer) to a closed table could be left in place and accessed in
later attempts to fetch from the table using the handler. Also,
if the server failed to set a error message if the re-open
failed. These problems could lead to server crashes or hangs.

The solution is to remove any references to a closed table and
to set a error if reopening a table during a HANDLER .. READ
statement fails.

mysql-test/include/handler.inc:
  Add test case for Bug#41110 and Bug#41112
mysql-test/r/handler_innodb.result:
  Add test case result for Bug#41110 and Bug#41112
mysql-test/r/handler_myisam.result:
  Add test case result for Bug#41110 and Bug#41112
sql/sql_handler.cc:
  Remove redundant reopen check.
  Set errors even if reopening table.
  Reset TABLE_LIST::table reference when the table is closed.
parents 87fcb23d 11b20f27
...@@ -692,3 +692,29 @@ unlock tables; ...@@ -692,3 +692,29 @@ unlock tables;
drop table t1; drop table t1;
--error ER_UNKNOWN_TABLE --error ER_UNKNOWN_TABLE
handler t1 read a next; handler t1 read a next;
#
# Bug#41110: crash with handler command when used concurrently with alter table
# Bug#41112: crash in mysql_ha_close_table/get_lock_data with alter table
#
--disable_warnings
drop table if exists t1;
--enable_warnings
create table t1 (a int);
insert into t1 values (1);
handler t1 open;
connect(con1,localhost,root,,);
send alter table t1 engine=memory;
connection default;
let $wait_condition=
select count(*) = 1 from information_schema.processlist
where state = "rename result table" and info = "alter table t1 engine=memory";
--source include/wait_condition.inc
--error ER_ILLEGAL_HA
handler t1 read a next;
handler t1 close;
connection con1;
--reap
drop table t1;
connection default;
...@@ -730,3 +730,12 @@ unlock tables; ...@@ -730,3 +730,12 @@ unlock tables;
drop table t1; drop table t1;
handler t1 read a next; handler t1 read a next;
ERROR 42S02: Unknown table 't1' in HANDLER ERROR 42S02: Unknown table 't1' in HANDLER
drop table if exists t1;
create table t1 (a int);
insert into t1 values (1);
handler t1 open;
alter table t1 engine=memory;
handler t1 read a next;
ERROR HY000: Table storage engine for 't1' doesn't have this option
handler t1 close;
drop table t1;
...@@ -728,3 +728,12 @@ unlock tables; ...@@ -728,3 +728,12 @@ unlock tables;
drop table t1; drop table t1;
handler t1 read a next; handler t1 read a next;
ERROR 42S02: Unknown table 't1' in HANDLER ERROR 42S02: Unknown table 't1' in HANDLER
drop table if exists t1;
create table t1 (a int);
insert into t1 values (1);
handler t1 open;
alter table t1 engine=memory;
handler t1 read a next;
ERROR HY000: Table storage engine for 't1' doesn't have this option
handler t1 close;
drop table t1;
...@@ -160,6 +160,9 @@ static void mysql_ha_close_table(THD *thd, TABLE_LIST *tables, ...@@ -160,6 +160,9 @@ static void mysql_ha_close_table(THD *thd, TABLE_LIST *tables,
table->query_id= thd->query_id; table->query_id= thd->query_id;
table->open_by_handler= 0; table->open_by_handler= 0;
} }
/* Mark table as closed, ready for re-open if necessary. */
tables->table= NULL;
} }
/* /*
...@@ -177,8 +180,7 @@ static void mysql_ha_close_table(THD *thd, TABLE_LIST *tables, ...@@ -177,8 +180,7 @@ static void mysql_ha_close_table(THD *thd, TABLE_LIST *tables,
'reopen' is set when a handler table is to be re-opened. In this case, 'reopen' is set when a handler table is to be re-opened. In this case,
'tables' is the pointer to the hashed TABLE_LIST object which has been 'tables' is the pointer to the hashed TABLE_LIST object which has been
saved on the original open. saved on the original open.
'reopen' is also used to suppress the sending of an 'ok' message or 'reopen' is also used to suppress the sending of an 'ok' message.
error messages.
RETURN RETURN
FALSE OK FALSE OK
...@@ -214,7 +216,6 @@ bool mysql_ha_open(THD *thd, TABLE_LIST *tables, bool reopen) ...@@ -214,7 +216,6 @@ bool mysql_ha_open(THD *thd, TABLE_LIST *tables, bool reopen)
strlen(tables->alias) + 1)) strlen(tables->alias) + 1))
{ {
DBUG_PRINT("info",("duplicate '%s'", tables->alias)); DBUG_PRINT("info",("duplicate '%s'", tables->alias));
if (! reopen)
my_error(ER_NONUNIQ_TABLE, MYF(0), tables->alias); my_error(ER_NONUNIQ_TABLE, MYF(0), tables->alias);
goto err; goto err;
} }
...@@ -259,7 +260,6 @@ bool mysql_ha_open(THD *thd, TABLE_LIST *tables, bool reopen) ...@@ -259,7 +260,6 @@ bool mysql_ha_open(THD *thd, TABLE_LIST *tables, bool reopen)
/* There can be only one table in '*tables'. */ /* There can be only one table in '*tables'. */
if (! (tables->table->file->ha_table_flags() & HA_CAN_SQL_HANDLER)) if (! (tables->table->file->ha_table_flags() & HA_CAN_SQL_HANDLER))
{ {
if (! reopen)
my_error(ER_ILLEGAL_HA, MYF(0), tables->alias); my_error(ER_ILLEGAL_HA, MYF(0), tables->alias);
goto err; goto err;
} }
...@@ -479,8 +479,7 @@ retry: ...@@ -479,8 +479,7 @@ retry:
if (need_reopen) if (need_reopen)
{ {
mysql_ha_close_table(thd, tables, FALSE); mysql_ha_close_table(thd, hash_tables, FALSE);
hash_tables->table= NULL;
/* /*
The lock might have been aborted, we need to manually reset The lock might have been aborted, we need to manually reset
thd->some_tables_deleted because handler's tables are closed thd->some_tables_deleted because handler's tables are closed
...@@ -761,11 +760,7 @@ void mysql_ha_flush(THD *thd) ...@@ -761,11 +760,7 @@ void mysql_ha_flush(THD *thd)
{ {
hash_tables= (TABLE_LIST*) hash_element(&thd->handler_tables_hash, i); hash_tables= (TABLE_LIST*) hash_element(&thd->handler_tables_hash, i);
if (hash_tables->table && hash_tables->table->needs_reopen_or_name_lock()) if (hash_tables->table && hash_tables->table->needs_reopen_or_name_lock())
{
mysql_ha_close_table(thd, hash_tables, TRUE); mysql_ha_close_table(thd, hash_tables, TRUE);
/* Mark table as closed, ready for re-open. */
hash_tables->table= NULL;
}
} }
DBUG_VOID_RETURN; DBUG_VOID_RETURN;
......
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