Commit 8995277a authored by unknown's avatar unknown
Browse files

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:
  Updated test results (Bug#19399)
mysql-test/t/ps.test:
  A test case for Bug#19399 "Stored Procedures 'Lost Connection' when 
  dropping/creating tables": test that multi-delete
  tables are cleaned up properly before re-execution.
sql/sql_lex.cc:
  Always initialize auxilliary_table_list when we initialize the lex:
  this way we don't have to check that lex->sql_command equals to
  SQLCOM_DELETE_MULTI whenever we need to access auxilliary_table_list.
  In particular, in reinit_stmt_before_use we can simply check that
  auxilliary_table_list is not NULL and clean it up if the check returns
  a true value.
sql/sql_prepare.cc:
  Move the one table clean-up functionality to a method of st_table_list.
  Clean up auxiliary_table_list if it's not empty.
sql/table.cc:
  Implement st_table_list::reinit_before_use().
sql/table.h:
  Declare st_table_list::reinit_before_use().
parent d94521c3
Loading
Loading
Loading
Loading
+14 −0
Original line number Diff line number Diff line
@@ -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;
+25 −0
Original line number Diff line number Diff line
@@ -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
+1 −0
Original line number Diff line number Diff line
@@ -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;
+13 −7
Original line number Diff line number Diff line
@@ -1727,12 +1727,7 @@ 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);
    }

    {
@@ -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();
+17 −0
Original line number Diff line number Diff line
@@ -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
Loading