Fix for bug #12704 "Server crashes during trigger execution".

This bug occurs when some trigger for table used by DML statement is created
or changed while statement was waiting in lock_tables(). In this situation
prelocking set which we have calculated becames invalid which can easily lead
to errors and even in some cases to crashes.

With proposed patch we no longer silently reopen tables in lock_tables(),
instead caller of lock_tables() becomes responsible for reopening tables and
recalculation of prelocking set.


mysql-test/t/trigger.test:
  Added tests for bug #12704 "Server crashes during trigger execution".
  Unfortunately these tests rely on the order in which tables are locked
  by statement so they are non-determenistic and therefore should be disabled.
sql/lock.cc:
  mysql_lock_tables():
    Now instead of always reopening altered or dropped tables by itself
    mysql_lock_tables() can notify upper level and rely on caller doing this.
sql/mysql_priv.h:
  Now mysql_lock_tables() can either reopen deleted or altered tables by itself
  or notify caller about such situation through 'need_reopen' argument and rely
  on it in this.
  Also lock_tables() has new 'need_reopen' out parameter through which it
  notifies caller that some tables were altered or dropped so he needs to reopen
  them (and rebuild prelocking set some triggers may change or simply appear).
sql/sp.cc:
  sp_add_used_routine():
    To be able to restore LEX::sroutines_list to its state right after parsing
    we now adjust  LEX::sroutines_list_own_last/sroutines_list_own_elements when
    we add directly used routine.
  sp_remove_not_own_routines():
    Added procedure for restoring LEX::sroutines/sroutines_list to their state
    right after parsing (by throwing out non-directly used routines).
  sp_cache_routines_and_add_tables_for_view()/sp_update_stmt_used_routines():
    We should use LEX::sroutines_list instead of LEX::sroutines as source of
    routines used by view, since LEX::sroutines is not availiable for view
    on second attempt to open it (see comment in open_tables() about it).
sql/sp.h:
  sp_remove_not_own_routines():
    Added procedure for restoring LEX::sroutines/sroutines_list to their state
    right after parsing (by throwing out non-directly used routines).
sql/sp_head.cc:
  Removed assert which is no longer always true.
sql/sql_base.cc:
  reopen_table():
    When we re-open table and do shallow copy of TABLE object we should adjust
    pointers to it in associated Table_triggers_list object. Removed nil
    operation.
  open_tables():
    Now this function is able to rebuild prelocking set for statement if it is
    needed. It also correctly handles FLUSH TABLES which may occur during its
    execution.
  lock_tables():
    Instead of allowing mysql_lock_tables() to silently reopen altered or dropped
    tables let us notify caller and rely on that it will do reopen itself.
    This solves the problem when trigger suddenly appears or changed during
    mysq_lock_tables().
  close_tables_for_reopen():
    Added routine for properly preparing for reopening of tables and recalculation
    of set of prelocked tables.
sql/sql_handler.cc:
  Here we let mysql_lock_tables() to reopen dropped or altered tables by itself.
sql/sql_insert.cc:
  Here we let mysql_lock_tables() to reopen dropped or altered tables by itself.
sql/sql_lex.cc:
  LEX:
    Added 'sroutines_list_own_last' and 'sroutines_list_own_elements' members
    which are used for keeping state in which 'sroutines_list' was right after
    statement parsing (and for restoring of this list to this state).
sql/sql_lex.h:
  LEX:
    Added 'sroutines_list_own_last' and 'sroutines_list_own_elements' members
    which are used for keeping state in which 'sroutines_list' was right after
    statement parsing (and for restoring of this list to this state).
    Added chop_off_not_own_tables() method to simplify throwing away list
    of implicitly used (prelocked) tables.
sql/sql_prepare.cc:
  Now instead of silently reopening altered or dropped tables in
  lock_tables() we notify caller and rely on that the caller will
  reopen tables.
sql/sql_table.cc:
  Here we let mysql_lock_tables() to reopen dropped or altered tables by itself.
sql/sql_trigger.cc:
  Added Table_triggers_list::set_table() method to adjust Table_triggers_list
  to new pointer to TABLE instance.
sql/sql_trigger.h:
  Added Table_triggers_list::set_table() method to adjust Table_triggers_list
  to new pointer to TABLE instance.
sql/sql_update.cc:
  Now instead of silently reopening altered or dropped tables in
  lock_tables() we notify caller and rely on that the caller will
  reopen tables.
This commit is contained in:
unknown 2005-09-15 03:56:09 +04:00
commit d5303b8ab8
16 changed files with 414 additions and 97 deletions

View file

@ -107,7 +107,7 @@ TABLE *open_proc_table_for_read(THD *thd, Open_tables_state *backup)
{
TABLE_LIST tables;
TABLE *table;
bool refresh;
bool not_used;
DBUG_ENTER("open_proc_table");
/*
@ -122,7 +122,7 @@ TABLE *open_proc_table_for_read(THD *thd, Open_tables_state *backup)
bzero((char*) &tables, sizeof(tables));
tables.db= (char*) "mysql";
tables.table_name= tables.alias= (char*)"proc";
if (!(table= open_table(thd, &tables, thd->mem_root, &refresh,
if (!(table= open_table(thd, &tables, thd->mem_root, &not_used,
MYSQL_LOCK_IGNORE_FLUSH)))
{
thd->restore_backup_open_tables_state(backup);
@ -138,7 +138,7 @@ TABLE *open_proc_table_for_read(THD *thd, Open_tables_state *backup)
could lead to a deadlock if we have other tables opened.
*/
if (!(thd->lock= mysql_lock_tables(thd, &table, 1,
MYSQL_LOCK_IGNORE_FLUSH)))
MYSQL_LOCK_IGNORE_FLUSH, &not_used)))
{
close_proc_table(thd, backup);
DBUG_RETURN(0);
@ -1265,7 +1265,8 @@ static bool add_used_routine(LEX *lex, Query_arena *arena,
/*
Add routine to the set of stored routines used by statement.
Add routine which is explicitly used by statement to the set of stored
routines used by this statement.
SYNOPSIS
sp_add_used_routine()
@ -1276,7 +1277,8 @@ static bool add_used_routine(LEX *lex, Query_arena *arena,
rt_type - routine type (one of TYPE_ENUM_PROCEDURE/...)
NOTES
Will also add element to end of 'LEX::sroutines_list' list.
Will also add element to end of 'LEX::sroutines_list' list (and will
take into account that this is explicitly used routine).
To be friendly towards prepared statements one should pass
persistent arena as second argument.
@ -1287,6 +1289,37 @@ void sp_add_used_routine(LEX *lex, Query_arena *arena,
{
rt->set_routine_type(rt_type);
(void)add_used_routine(lex, arena, &rt->m_sroutines_key);
lex->sroutines_list_own_last= lex->sroutines_list.next;
lex->sroutines_list_own_elements= lex->sroutines_list.elements;
}
/*
Remove routines which are only indirectly used by statement from
the set of routines used by this statement.
SYNOPSIS
sp_remove_not_own_routines()
lex LEX representing statement
*/
void sp_remove_not_own_routines(LEX *lex)
{
Sroutine_hash_entry *not_own_rt, *next_rt;
for (not_own_rt= *(Sroutine_hash_entry **)lex->sroutines_list_own_last;
not_own_rt; not_own_rt= next_rt)
{
/*
It is safe to obtain not_own_rt->next after calling hash_delete() now
but we want to be more future-proof.
*/
next_rt= not_own_rt->next;
hash_delete(&lex->sroutines, (byte *)not_own_rt);
}
*(Sroutine_hash_entry **)lex->sroutines_list_own_last= NULL;
lex->sroutines_list.next= lex->sroutines_list_own_last;
lex->sroutines_list.elements= lex->sroutines_list_own_elements;
}
@ -1343,6 +1376,28 @@ static void sp_update_stmt_used_routines(THD *thd, LEX *lex, HASH *src)
}
/*
Add contents of list representing set of routines to the set of
routines used by statement.
SYNOPSIS
sp_update_stmt_used_routines()
thd Thread context
lex LEX representing statement
src List representing set from which routines will be added
NOTE
It will also add elements to end of 'LEX::sroutines_list' list.
*/
static void sp_update_stmt_used_routines(THD *thd, LEX *lex, SQL_LIST *src)
{
for (Sroutine_hash_entry *rt= (Sroutine_hash_entry *)src->first;
rt; rt= rt->next)
(void)add_used_routine(lex, thd->stmt_arena, &rt->key);
}
/*
Cache sub-set of routines used by statement, add tables used by these
routines to statement table list. Do the same for all routines used
@ -1463,7 +1518,7 @@ sp_cache_routines_and_add_tables_for_view(THD *thd, LEX *lex, LEX *aux_lex)
{
Sroutine_hash_entry **last_cached_routine_ptr=
(Sroutine_hash_entry **)lex->sroutines_list.next;
sp_update_stmt_used_routines(thd, lex, &aux_lex->sroutines);
sp_update_stmt_used_routines(thd, lex, &aux_lex->sroutines_list);
(void)sp_cache_routines_and_add_tables_aux(thd, lex,
*last_cached_routine_ptr, FALSE);
}