From 8e735d2c119bc74c75868e4c9ad4198e42e4d26d Mon Sep 17 00:00:00 2001 From: "konstantin@bodhi.netgear" <> Date: Thu, 6 Jul 2006 23:59:04 +0400 Subject: [PATCH] A fix and a test case for Bug#19399 "res 'Lost Connection' when dropping/creating tables". The bug could lead to a crash when multi-delete statements were prepared and used with temporary tables. The bug was caused by lack of clean-up of multi-delete tables before re-execution of a prepared statement. In a statement like DELETE t1 FROM t1, t2 WHERE ... the first table list (t1) is moved to lex->auxilliary_table_list and excluded from lex->query_tables or select_lex->tables. Thus it was unaccessible to reinit_stmt_before_use and not cleaned up before re-execution of a prepared statement. --- mysql-test/r/ps.result | 14 ++++++++++++++ mysql-test/t/ps.test | 25 +++++++++++++++++++++++++ sql/sql_lex.cc | 1 + sql/sql_prepare.cc | 20 +++++++++++++------- sql/table.cc | 17 +++++++++++++++++ sql/table.h | 5 +++++ 6 files changed, 75 insertions(+), 7 deletions(-) diff --git a/mysql-test/r/ps.result b/mysql-test/r/ps.result index 2be5366b180..01aa4ddf859 100644 --- a/mysql-test/r/ps.result +++ b/mysql-test/r/ps.result @@ -875,3 +875,17 @@ select @@max_prepared_stmt_count, @@prepared_stmt_count; @@max_prepared_stmt_count @@prepared_stmt_count 3 0 set global max_prepared_stmt_count= @old_max_prepared_stmt_count; +drop table if exists t1; +create temporary table if not exists t1 (a1 int); +prepare stmt from "delete t1 from t1 where (cast(a1/3 as unsigned) * 3) = a1"; +drop temporary table t1; +create temporary table if not exists t1 (a1 int); +execute stmt; +drop temporary table t1; +create temporary table if not exists t1 (a1 int); +execute stmt; +drop temporary table t1; +create temporary table if not exists t1 (a1 int); +execute stmt; +drop temporary table t1; +deallocate prepare stmt; diff --git a/mysql-test/t/ps.test b/mysql-test/t/ps.test index d6dcf02d5b7..0ca293eb1ba 100644 --- a/mysql-test/t/ps.test +++ b/mysql-test/t/ps.test @@ -926,4 +926,29 @@ select @@max_prepared_stmt_count, @@prepared_stmt_count; set global max_prepared_stmt_count= @old_max_prepared_stmt_count; --enable_ps_protocol +# +# Bug#19399 "Stored Procedures 'Lost Connection' when dropping/creating +# tables" +# Check that multi-delete tables are also cleaned up before re-execution. +# +--disable_warnings +drop table if exists t1; +create temporary table if not exists t1 (a1 int); +--enable_warnings +# exact delete syntax is essential +prepare stmt from "delete t1 from t1 where (cast(a1/3 as unsigned) * 3) = a1"; +drop temporary table t1; +create temporary table if not exists t1 (a1 int); +# the server crashed on the next statement without the fix +execute stmt; +drop temporary table t1; +create temporary table if not exists t1 (a1 int); +# the problem was in memory corruption: repeat the test just in case +execute stmt; +drop temporary table t1; +create temporary table if not exists t1 (a1 int); +execute stmt; +drop temporary table t1; +deallocate prepare stmt; + # End of 4.1 tests diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index 29ea8ab2ae2..fb9a765f12c 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -125,6 +125,7 @@ void lex_start(THD *thd, uchar *buf,uint length) lex->value_list.empty(); lex->update_list.empty(); lex->param_list.empty(); + lex->auxilliary_table_list.empty(); lex->unit.next= lex->unit.master= lex->unit.link_next= lex->unit.return_to= 0; lex->unit.prev= lex->unit.link_prev= 0; diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc index 2d9e80df63c..2688841d96c 100644 --- a/sql/sql_prepare.cc +++ b/sql/sql_prepare.cc @@ -1727,14 +1727,9 @@ static void reset_stmt_for_execute(Prepared_statement *stmt) tables; tables= tables->next) { - /* - Reset old pointers to TABLEs: they are not valid since the tables - were closed in the end of previous prepare or execute call. - */ - tables->table= 0; - tables->table_list= 0; + tables->reinit_before_use(thd); } - + { SELECT_LEX_UNIT *unit= sl->master_unit(); unit->unclean(); @@ -1743,6 +1738,17 @@ static void reset_stmt_for_execute(Prepared_statement *stmt) unit->reinit_exec_mechanism(); } } + /* + Cleanup of the special case of DELETE t1, t2 FROM t1, t2, t3 ... + (multi-delete). We do a full clean up, although at the moment all we + need to clean in the tables of MULTI-DELETE list is 'table' member. + */ + for (TABLE_LIST *tables= (TABLE_LIST*) lex->auxilliary_table_list.first; + tables; + tables= tables->next) + { + tables->reinit_before_use(thd); + } lex->current_select= &lex->select_lex; if (lex->result) lex->result->cleanup(); diff --git a/sql/table.cc b/sql/table.cc index 513f42665a6..26da6dc4605 100644 --- a/sql/table.cc +++ b/sql/table.cc @@ -1523,6 +1523,23 @@ db_type get_table_type(const char *name) DBUG_RETURN(ha_checktype((enum db_type) (uint) *(head+3))); } +/* + Cleanup this table for re-execution. + + SYNOPSIS + st_table_list::reinit_before_use() +*/ + +void st_table_list::reinit_before_use(THD * /* thd */) +{ + /* + Reset old pointers to TABLEs: they are not valid since the tables + were closed in the end of previous prepare or execute call. + */ + table= 0; + table_list= 0; +} + /***************************************************************************** ** Instansiate templates diff --git a/sql/table.h b/sql/table.h index 8cb15d3a69c..d615623cc37 100644 --- a/sql/table.h +++ b/sql/table.h @@ -238,6 +238,11 @@ typedef struct st_table_list bool cacheable_table; /* stop PS caching */ /* used in multi-upd privelege check */ bool table_in_update_from_clause; + /* + Cleanup for re-execution in a prepared statement or a stored + procedure. + */ + void reinit_before_use(THD *thd); } TABLE_LIST; typedef struct st_changed_table_list