Commit e231ebe5 authored by dlenev@mysql.com's avatar dlenev@mysql.com

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.
parent 9300833c
...@@ -10,6 +10,11 @@ drop function if exists f1; ...@@ -10,6 +10,11 @@ drop function if exists f1;
drop procedure if exists p1; drop procedure if exists p1;
--enable_warnings --enable_warnings
# Create additional connections used through test
connect (addconroot1, localhost, root,,);
connect (addconroot2, localhost, root,,);
connection default;
create table t1 (i int); create table t1 (i int);
# let us test some very simple trigger # let us test some very simple trigger
...@@ -680,12 +685,10 @@ end| ...@@ -680,12 +685,10 @@ end|
delimiter ;| delimiter ;|
update t1 set data = 1; update t1 set data = 1;
connect (addconroot, localhost, root,,); connection addconroot1;
connection addconroot;
update t1 set data = 2; update t1 set data = 2;
connection default; connection default;
disconnect addconroot;
drop table t1; drop table t1;
# #
...@@ -765,3 +768,110 @@ insert into t1 values (3); ...@@ -765,3 +768,110 @@ insert into t1 values (3);
select * from t1; select * from t1;
drop trigger t1_bi; drop trigger t1_bi;
drop tables t1, t2; drop tables t1, t2;
# Tests for bug #12704 "Server crashes during trigger execution".
# If we run DML statements and CREATE TRIGGER statements concurrently
# it may happen that trigger will be created while DML statement is
# waiting for table lock. In this case we have to reopen tables and
# recalculate prelocking set.
# Unfortunately these tests rely on the order in which tables are locked
# by statement so they are non determenistic and are disabled.
--disable_parsing
create table t1 (id int);
create table t2 (id int);
create table t3 (id int);
create function f1() returns int return (select max(id)+2 from t2);
create view v1 as select f1() as f;
# Let us check that we notice trigger at all
connection addconroot1;
lock tables t2 write;
connection default;
send insert into t1 values ((select max(id) from t2)), (2);
--sleep 1
connection addconroot2;
create trigger t1_trg before insert on t1 for each row set NEW.id:= 1;
connection addconroot1;
unlock tables;
connection default;
reap;
select * from t1;
# Check that we properly calculate new prelocking set
insert into t2 values (3);
connection addconroot1;
lock tables t2 write;
connection default;
send insert into t1 values ((select max(id) from t2)), (4);
--sleep 1
connection addconroot2;
drop trigger t1_trg;
create trigger t1_trg before insert on t1 for each row
insert into t3 values (new.id);
connection addconroot1;
unlock tables;
connection default;
reap;
select * from t1;
select * from t3;
# We should be able to do this even if fancy views are involved
connection addconroot1;
lock tables t2 write;
connection default;
send insert into t1 values ((select max(f) from v1)), (6);
--sleep 1
connection addconroot2;
drop trigger t1_trg;
create trigger t1_trg before insert on t1 for each row
insert into t3 values (new.id + 100);
connection addconroot1;
unlock tables;
connection default;
reap;
select * from t1;
select * from t3;
# This also should work for multi-update
# Let us drop trigger to demonstrate that prelocking set is really
# rebuilt
drop trigger t1_trg;
connection addconroot1;
lock tables t2 write;
connection default;
send update t1, t2 set t1.id=10 where t1.id=t2.id;
--sleep 1
connection addconroot2;
create trigger t1_trg before update on t1 for each row
insert into t3 values (new.id);
connection addconroot1;
unlock tables;
connection default;
reap;
select * from t1;
select * from t3;
# And even for multi-update converted from ordinary update thanks to view
drop view v1;
drop trigger t1_trg;
create view v1 as select t1.id as id1 from t1, t2 where t1.id= t2.id;
insert into t2 values (10);
connection addconroot1;
lock tables t2 write;
connection default;
send update v1 set id1= 11;
--sleep 1
connection addconroot2;
create trigger t1_trg before update on t1 for each row
insert into t3 values (new.id + 100);
connection addconroot1;
unlock tables;
connection default;
reap;
select * from t1;
select * from t3;
drop function f1;
drop view v1;
drop table t1, t2, t3;
--enable_parsing
...@@ -93,23 +93,33 @@ static void print_lock_error(int error, const char *); ...@@ -93,23 +93,33 @@ static void print_lock_error(int error, const char *);
flags Options: flags Options:
MYSQL_LOCK_IGNORE_GLOBAL_READ_LOCK Ignore a global read lock MYSQL_LOCK_IGNORE_GLOBAL_READ_LOCK Ignore a global read lock
MYSQL_LOCK_IGNORE_FLUSH Ignore a flush tables. MYSQL_LOCK_IGNORE_FLUSH Ignore a flush tables.
MYSQL_LOCK_NOTIFY_IF_NEED_REOPEN Instead of reopening altered
or dropped tables by itself,
mysql_lock_tables() should
notify upper level and rely
on caller doing this.
need_reopen Out parameter, TRUE if some tables were altered
or deleted and should be reopened by caller.
RETURN RETURN
A lock structure pointer on success. A lock structure pointer on success.
NULL on error. NULL on error or if some tables should be reopen.
*/ */
/* Map the return value of thr_lock to an error from errmsg.txt */
static int thr_lock_errno_to_mysql[]= static int thr_lock_errno_to_mysql[]=
{ 0, 1, ER_LOCK_WAIT_TIMEOUT, ER_LOCK_DEADLOCK }; { 0, 1, ER_LOCK_WAIT_TIMEOUT, ER_LOCK_DEADLOCK };
MYSQL_LOCK *mysql_lock_tables(THD *thd, TABLE **tables, uint count, uint flags) MYSQL_LOCK *mysql_lock_tables(THD *thd, TABLE **tables, uint count,
uint flags, bool *need_reopen)
{ {
MYSQL_LOCK *sql_lock; MYSQL_LOCK *sql_lock;
TABLE *write_lock_used; TABLE *write_lock_used;
int rc; int rc;
/* Map the return value of thr_lock to an error from errmsg.txt */
DBUG_ENTER("mysql_lock_tables"); DBUG_ENTER("mysql_lock_tables");
*need_reopen= FALSE;
for (;;) for (;;)
{ {
if (!(sql_lock = get_lock_data(thd,tables,count, 0,&write_lock_used))) if (!(sql_lock = get_lock_data(thd,tables,count, 0,&write_lock_used)))
...@@ -178,6 +188,11 @@ MYSQL_LOCK *mysql_lock_tables(THD *thd, TABLE **tables, uint count, uint flags) ...@@ -178,6 +188,11 @@ MYSQL_LOCK *mysql_lock_tables(THD *thd, TABLE **tables, uint count, uint flags)
thd->locked=0; thd->locked=0;
retry: retry:
sql_lock=0; sql_lock=0;
if (flags & MYSQL_LOCK_NOTIFY_IF_NEED_REOPEN)
{
*need_reopen= TRUE;
break;
}
if (wait_for_tables(thd)) if (wait_for_tables(thd))
break; // Couldn't open tables break; // Couldn't open tables
} }
......
...@@ -942,7 +942,7 @@ int open_tables(THD *thd, TABLE_LIST **tables, uint *counter, uint flags); ...@@ -942,7 +942,7 @@ int open_tables(THD *thd, TABLE_LIST **tables, uint *counter, uint flags);
int simple_open_n_lock_tables(THD *thd,TABLE_LIST *tables); int simple_open_n_lock_tables(THD *thd,TABLE_LIST *tables);
bool open_and_lock_tables(THD *thd,TABLE_LIST *tables); bool open_and_lock_tables(THD *thd,TABLE_LIST *tables);
bool open_normal_and_derived_tables(THD *thd, TABLE_LIST *tables, uint flags); bool open_normal_and_derived_tables(THD *thd, TABLE_LIST *tables, uint flags);
int lock_tables(THD *thd, TABLE_LIST *tables, uint counter); int lock_tables(THD *thd, TABLE_LIST *tables, uint counter, bool *need_reopen);
TABLE *open_temporary_table(THD *thd, const char *path, const char *db, TABLE *open_temporary_table(THD *thd, const char *path, const char *db,
const char *table_name, bool link_in_list); const char *table_name, bool link_in_list);
bool rm_temporary_table(enum db_type base, char *path); bool rm_temporary_table(enum db_type base, char *path);
...@@ -950,6 +950,7 @@ void free_io_cache(TABLE *entry); ...@@ -950,6 +950,7 @@ void free_io_cache(TABLE *entry);
void intern_close_table(TABLE *entry); void intern_close_table(TABLE *entry);
bool close_thread_table(THD *thd, TABLE **table_ptr); bool close_thread_table(THD *thd, TABLE **table_ptr);
void close_temporary_tables(THD *thd); void close_temporary_tables(THD *thd);
void close_tables_for_reopen(THD *thd, TABLE_LIST *tables);
TABLE_LIST *find_table_in_list(TABLE_LIST *table, TABLE_LIST *find_table_in_list(TABLE_LIST *table,
uint offset_to_list, uint offset_to_list,
const char *db_name, const char *db_name,
...@@ -1227,10 +1228,12 @@ extern pthread_t signal_thread; ...@@ -1227,10 +1228,12 @@ extern pthread_t signal_thread;
extern struct st_VioSSLAcceptorFd * ssl_acceptor_fd; extern struct st_VioSSLAcceptorFd * ssl_acceptor_fd;
#endif /* HAVE_OPENSSL */ #endif /* HAVE_OPENSSL */
MYSQL_LOCK *mysql_lock_tables(THD *thd, TABLE **table, uint count, uint flags); MYSQL_LOCK *mysql_lock_tables(THD *thd, TABLE **table, uint count,
uint flags, bool *need_reopen);
/* mysql_lock_tables() flags bits */ /* mysql_lock_tables() flags bits */
#define MYSQL_LOCK_IGNORE_GLOBAL_READ_LOCK 0x0001 #define MYSQL_LOCK_IGNORE_GLOBAL_READ_LOCK 0x0001
#define MYSQL_LOCK_IGNORE_FLUSH 0x0002 #define MYSQL_LOCK_IGNORE_FLUSH 0x0002
#define MYSQL_LOCK_NOTIFY_IF_NEED_REOPEN 0x0004
void mysql_unlock_tables(THD *thd, MYSQL_LOCK *sql_lock); void mysql_unlock_tables(THD *thd, MYSQL_LOCK *sql_lock);
void mysql_unlock_read_tables(THD *thd, MYSQL_LOCK *sql_lock); void mysql_unlock_read_tables(THD *thd, MYSQL_LOCK *sql_lock);
......
...@@ -107,7 +107,7 @@ TABLE *open_proc_table_for_read(THD *thd, Open_tables_state *backup) ...@@ -107,7 +107,7 @@ TABLE *open_proc_table_for_read(THD *thd, Open_tables_state *backup)
{ {
TABLE_LIST tables; TABLE_LIST tables;
TABLE *table; TABLE *table;
bool refresh; bool not_used;
DBUG_ENTER("open_proc_table"); DBUG_ENTER("open_proc_table");
/* /*
...@@ -122,7 +122,7 @@ TABLE *open_proc_table_for_read(THD *thd, Open_tables_state *backup) ...@@ -122,7 +122,7 @@ TABLE *open_proc_table_for_read(THD *thd, Open_tables_state *backup)
bzero((char*) &tables, sizeof(tables)); bzero((char*) &tables, sizeof(tables));
tables.db= (char*) "mysql"; tables.db= (char*) "mysql";
tables.table_name= tables.alias= (char*)"proc"; 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))) MYSQL_LOCK_IGNORE_FLUSH)))
{ {
thd->restore_backup_open_tables_state(backup); thd->restore_backup_open_tables_state(backup);
...@@ -138,7 +138,7 @@ TABLE *open_proc_table_for_read(THD *thd, 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. could lead to a deadlock if we have other tables opened.
*/ */
if (!(thd->lock= mysql_lock_tables(thd, &table, 1, if (!(thd->lock= mysql_lock_tables(thd, &table, 1,
MYSQL_LOCK_IGNORE_FLUSH))) MYSQL_LOCK_IGNORE_FLUSH, &not_used)))
{ {
close_proc_table(thd, backup); close_proc_table(thd, backup);
DBUG_RETURN(0); DBUG_RETURN(0);
...@@ -1265,7 +1265,8 @@ static bool add_used_routine(LEX *lex, Query_arena *arena, ...@@ -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 SYNOPSIS
sp_add_used_routine() sp_add_used_routine()
...@@ -1276,7 +1277,8 @@ static bool add_used_routine(LEX *lex, Query_arena *arena, ...@@ -1276,7 +1277,8 @@ static bool add_used_routine(LEX *lex, Query_arena *arena,
rt_type - routine type (one of TYPE_ENUM_PROCEDURE/...) rt_type - routine type (one of TYPE_ENUM_PROCEDURE/...)
NOTES 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 To be friendly towards prepared statements one should pass
persistent arena as second argument. persistent arena as second argument.
...@@ -1287,6 +1289,37 @@ void sp_add_used_routine(LEX *lex, Query_arena *arena, ...@@ -1287,6 +1289,37 @@ void sp_add_used_routine(LEX *lex, Query_arena *arena,
{ {
rt->set_routine_type(rt_type); rt->set_routine_type(rt_type);
(void)add_used_routine(lex, arena, &rt->m_sroutines_key); (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) ...@@ -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 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 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) ...@@ -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 **last_cached_routine_ptr=
(Sroutine_hash_entry **)lex->sroutines_list.next; (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, (void)sp_cache_routines_and_add_tables_aux(thd, lex,
*last_cached_routine_ptr, FALSE); *last_cached_routine_ptr, FALSE);
} }
......
...@@ -84,6 +84,7 @@ void sp_get_prelocking_info(THD *thd, bool *need_prelocking, ...@@ -84,6 +84,7 @@ void sp_get_prelocking_info(THD *thd, bool *need_prelocking,
bool *first_no_prelocking); bool *first_no_prelocking);
void sp_add_used_routine(LEX *lex, Query_arena *arena, void sp_add_used_routine(LEX *lex, Query_arena *arena,
sp_name *rt, char rt_type); sp_name *rt, char rt_type);
void sp_remove_not_own_routines(LEX *lex);
void sp_update_sp_used_routines(HASH *dst, HASH *src); void sp_update_sp_used_routines(HASH *dst, HASH *src);
bool sp_cache_routines_and_add_tables(THD *thd, LEX *lex, bool sp_cache_routines_and_add_tables(THD *thd, LEX *lex,
bool first_no_prelock); bool first_no_prelock);
......
...@@ -1887,10 +1887,6 @@ sp_lex_keeper::reset_lex_and_exec_core(THD *thd, uint *nextp, ...@@ -1887,10 +1887,6 @@ sp_lex_keeper::reset_lex_and_exec_core(THD *thd, uint *nextp,
attached it above in this function). attached it above in this function).
Now we'll save the 'tail', and detach it. Now we'll save the 'tail', and detach it.
*/ */
DBUG_ASSERT(!lex_query_tables_own_last ||
lex_query_tables_own_last == m_lex->query_tables_own_last &&
prelocking_tables == *(m_lex->query_tables_own_last));
lex_query_tables_own_last= m_lex->query_tables_own_last; lex_query_tables_own_last= m_lex->query_tables_own_last;
prelocking_tables= *lex_query_tables_own_last; prelocking_tables= *lex_query_tables_own_last;
*lex_query_tables_own_last= NULL; *lex_query_tables_own_last= NULL;
......
...@@ -1397,7 +1397,6 @@ bool reopen_table(TABLE *table,bool locked) ...@@ -1397,7 +1397,6 @@ bool reopen_table(TABLE *table,bool locked)
tmp.status= table->status; tmp.status= table->status;
tmp.keys_in_use_for_query= tmp.s->keys_in_use; tmp.keys_in_use_for_query= tmp.s->keys_in_use;
tmp.used_keys= tmp.s->keys_for_keyread; tmp.used_keys= tmp.s->keys_for_keyread;
tmp.force_index= tmp.force_index;
/* Get state */ /* Get state */
tmp.s->key_length= table->s->key_length; tmp.s->key_length= table->s->key_length;
...@@ -1428,6 +1427,9 @@ bool reopen_table(TABLE *table,bool locked) ...@@ -1428,6 +1427,9 @@ bool reopen_table(TABLE *table,bool locked)
for (key=0 ; key < table->s->keys ; key++) for (key=0 ; key < table->s->keys ; key++)
for (part=0 ; part < table->key_info[key].usable_key_parts ; part++) for (part=0 ; part < table->key_info[key].usable_key_parts ; part++)
table->key_info[key].key_part[part].field->table= table; table->key_info[key].key_part[part].field->table= table;
if (table->triggers)
table->triggers->set_table(table);
VOID(pthread_cond_broadcast(&COND_refresh)); VOID(pthread_cond_broadcast(&COND_refresh));
error=0; error=0;
...@@ -1476,7 +1478,7 @@ bool reopen_tables(THD *thd,bool get_locks,bool in_refresh) ...@@ -1476,7 +1478,7 @@ bool reopen_tables(THD *thd,bool get_locks,bool in_refresh)
TABLE *table,*next,**prev; TABLE *table,*next,**prev;
TABLE **tables,**tables_ptr; // For locks TABLE **tables,**tables_ptr; // For locks
bool error=0; bool error=0, not_used;
if (get_locks) if (get_locks)
{ {
/* The ptr is checked later */ /* The ptr is checked later */
...@@ -1517,7 +1519,8 @@ bool reopen_tables(THD *thd,bool get_locks,bool in_refresh) ...@@ -1517,7 +1519,8 @@ bool reopen_tables(THD *thd,bool get_locks,bool in_refresh)
MYSQL_LOCK *lock; MYSQL_LOCK *lock;
/* We should always get these locks */ /* We should always get these locks */
thd->some_tables_deleted=0; thd->some_tables_deleted=0;
if ((lock= mysql_lock_tables(thd, tables, (uint) (tables_ptr - tables), 0))) if ((lock= mysql_lock_tables(thd, tables, (uint) (tables_ptr - tables),
0, &not_used)))
{ {
thd->locked_tables=mysql_lock_merge(thd->locked_tables,lock); thd->locked_tables=mysql_lock_merge(thd->locked_tables,lock);
} }
...@@ -1967,9 +1970,15 @@ int open_tables(THD *thd, TABLE_LIST **start, uint *counter, uint flags) ...@@ -1967,9 +1970,15 @@ int open_tables(THD *thd, TABLE_LIST **start, uint *counter, uint flags)
/* /*
Ignore placeholders for derived tables. After derived tables Ignore placeholders for derived tables. After derived tables
processing, link to created temporary table will be put here. processing, link to created temporary table will be put here.
If this is derived table for view then we still want to process
routines used by this view.
*/ */
if (tables->derived) if (tables->derived)
{
if (tables->view)
goto process_view_routines;
continue; continue;
}
if (tables->schema_table) if (tables->schema_table)
{ {
if (!mysql_schema_table(thd, thd->lex, tables)) if (!mysql_schema_table(thd, thd->lex, tables))
...@@ -2001,23 +2010,12 @@ int open_tables(THD *thd, TABLE_LIST **start, uint *counter, uint flags) ...@@ -2001,23 +2010,12 @@ int open_tables(THD *thd, TABLE_LIST **start, uint *counter, uint flags)
if (query_tables_last_own == &(tables->next_global) && if (query_tables_last_own == &(tables->next_global) &&
tables->view->query_tables) tables->view->query_tables)
query_tables_last_own= tables->view->query_tables_last; query_tables_last_own= tables->view->query_tables_last;
/* /*
Again if needed we have to get cache all routines used by this view Let us free memory used by 'sroutines' hash here since we never
and add tables used by them to table list. call destructor for this LEX.
*/ */
if (!thd->prelocked_mode && !thd->lex->requires_prelocking() &&
tables->view->sroutines.records)
{
/* We have at least one table in TL here */
if (!query_tables_last_own)
query_tables_last_own= thd->lex->query_tables_last;
sp_cache_routines_and_add_tables_for_view(thd, thd->lex,
tables->view);
}
/* Cleanup hashes because destructo for this LEX is never called */
hash_free(&tables->view->sroutines); hash_free(&tables->view->sroutines);
continue; goto process_view_routines;
} }
if (refresh) // Refresh in progress if (refresh) // Refresh in progress
...@@ -2029,11 +2027,6 @@ int open_tables(THD *thd, TABLE_LIST **start, uint *counter, uint flags) ...@@ -2029,11 +2027,6 @@ int open_tables(THD *thd, TABLE_LIST **start, uint *counter, uint flags)
thd->version=refresh_version; thd->version=refresh_version;
TABLE **prev_table= &thd->open_tables; TABLE **prev_table= &thd->open_tables;
bool found=0; bool found=0;
/*
QQ: What we should do if we have started building of table list
for prelocking ??? Probably throw it away ? But before we should
mark all temporary tables as free? How about locked ?
*/
for (TABLE_LIST *tmp= *start; tmp; tmp= tmp->next_global) for (TABLE_LIST *tmp= *start; tmp; tmp= tmp->next_global)
{ {
/* Close normal (not temporary) changed tables */ /* Close normal (not temporary) changed tables */
...@@ -2057,6 +2050,18 @@ int open_tables(THD *thd, TABLE_LIST **start, uint *counter, uint flags) ...@@ -2057,6 +2050,18 @@ int open_tables(THD *thd, TABLE_LIST **start, uint *counter, uint flags)
pthread_mutex_unlock(&LOCK_open); pthread_mutex_unlock(&LOCK_open);
if (found) if (found)
VOID(pthread_cond_broadcast(&COND_refresh)); // Signal to refresh VOID(pthread_cond_broadcast(&COND_refresh)); // Signal to refresh
/*
Let us prepare for recalculation of set of prelocked tables.
First we pretend that we have finished calculation which we
were doing currently. Then we restore list of tables to be
opened and set of used routines to the state in which they were
before first open_tables() call for this statement (i.e. before
we have calculated current set of tables for prelocking).
*/
if (query_tables_last_own)
thd->lex->mark_as_requiring_prelocking(query_tables_last_own);
thd->lex->chop_off_not_own_tables();
sp_remove_not_own_routines(thd->lex);
goto restart; goto restart;
} }
result= -1; // Fatal error result= -1; // Fatal error
...@@ -2087,6 +2092,21 @@ int open_tables(THD *thd, TABLE_LIST **start, uint *counter, uint flags) ...@@ -2087,6 +2092,21 @@ int open_tables(THD *thd, TABLE_LIST **start, uint *counter, uint flags)
if (tables->lock_type != TL_UNLOCK && ! thd->locked_tables) if (tables->lock_type != TL_UNLOCK && ! thd->locked_tables)
tables->table->reginfo.lock_type=tables->lock_type; tables->table->reginfo.lock_type=tables->lock_type;
tables->table->grant= tables->grant; tables->table->grant= tables->grant;
process_view_routines:
/*
Again we may need cache all routines used by this view and add
tables used by them to table list.
*/
if (tables->view && !thd->prelocked_mode &&
!thd->lex->requires_prelocking() &&
tables->view->sroutines_list.elements)
{
/* We have at least one table in TL here. */
if (!query_tables_last_own)
query_tables_last_own= thd->lex->query_tables_last;
sp_cache_routines_and_add_tables_for_view(thd, thd->lex, tables->view);
}
} }
thd->proc_info=0; thd->proc_info=0;
free_root(&new_frm_mem, MYF(0)); // Free pre-alloced block free_root(&new_frm_mem, MYF(0)); // Free pre-alloced block
...@@ -2191,7 +2211,8 @@ TABLE *open_ltable(THD *thd, TABLE_LIST *table_list, thr_lock_type lock_type) ...@@ -2191,7 +2211,8 @@ TABLE *open_ltable(THD *thd, TABLE_LIST *table_list, thr_lock_type lock_type)
{ {
DBUG_ASSERT(thd->lock == 0); // You must lock everything at once DBUG_ASSERT(thd->lock == 0); // You must lock everything at once
if ((table->reginfo.lock_type= lock_type) != TL_UNLOCK) if ((table->reginfo.lock_type= lock_type) != TL_UNLOCK)
if (! (thd->lock= mysql_lock_tables(thd, &table_list->table, 1, 0))) if (! (thd->lock= mysql_lock_tables(thd, &table_list->table, 1, 0,
&refresh)))
table= 0; table= 0;
} }
} }
...@@ -2219,11 +2240,20 @@ TABLE *open_ltable(THD *thd, TABLE_LIST *table_list, thr_lock_type lock_type) ...@@ -2219,11 +2240,20 @@ TABLE *open_ltable(THD *thd, TABLE_LIST *table_list, thr_lock_type lock_type)
int simple_open_n_lock_tables(THD *thd, TABLE_LIST *tables) int simple_open_n_lock_tables(THD *thd, TABLE_LIST *tables)
{ {
DBUG_ENTER("simple_open_n_lock_tables");
uint counter; uint counter;
if (open_tables(thd, &tables, &counter, 0) || bool need_reopen;
lock_tables(thd, tables, counter)) DBUG_ENTER("simple_open_n_lock_tables");
DBUG_RETURN(-1); /* purecov: inspected */
for ( ; ; )
{
if (open_tables(thd, &tables, &counter, 0))
DBUG_RETURN(-1);
if (!lock_tables(thd, tables, counter, &need_reopen))
break;
if (!need_reopen)
DBUG_RETURN(-1);
close_tables_for_reopen(thd, tables);
}
DBUG_RETURN(0); DBUG_RETURN(0);
} }
...@@ -2248,10 +2278,20 @@ int simple_open_n_lock_tables(THD *thd, TABLE_LIST *tables) ...@@ -2248,10 +2278,20 @@ int simple_open_n_lock_tables(THD *thd, TABLE_LIST *tables)
bool open_and_lock_tables(THD *thd, TABLE_LIST *tables) bool open_and_lock_tables(THD *thd, TABLE_LIST *tables)
{ {
uint counter; uint counter;
bool need_reopen;
DBUG_ENTER("open_and_lock_tables"); DBUG_ENTER("open_and_lock_tables");
if (open_tables(thd, &tables, &counter, 0) ||
lock_tables(thd, tables, counter) || for ( ; ; )
mysql_handle_derived(thd->lex, &mysql_derived_prepare) || {
if (open_tables(thd, &tables, &counter, 0))
DBUG_RETURN(-1);
if (!lock_tables(thd, tables, counter, &need_reopen))
break;
if (!need_reopen)
DBUG_RETURN(-1);
close_tables_for_reopen(thd, tables);
}
if (mysql_handle_derived(thd->lex, &mysql_derived_prepare) ||
(thd->fill_derived_tables() && (thd->fill_derived_tables() &&
mysql_handle_derived(thd->lex, &mysql_derived_filling))) mysql_handle_derived(thd->lex, &mysql_derived_filling)))
DBUG_RETURN(TRUE); /* purecov: inspected */ DBUG_RETURN(TRUE); /* purecov: inspected */
...@@ -2319,7 +2359,12 @@ static void mark_real_tables_as_free_for_reuse(TABLE_LIST *table) ...@@ -2319,7 +2359,12 @@ static void mark_real_tables_as_free_for_reuse(TABLE_LIST *table)
lock_tables() lock_tables()
thd Thread handler thd Thread handler
tables Tables to lock tables Tables to lock
count umber of opened tables count Number of opened tables
need_reopen Out parameter which if TRUE indicates that some
tables were dropped or altered during this call
and therefore invoker should reopen tables and
try to lock them once again (in this case
lock_tables() will also return error).
NOTES NOTES
You can't call lock_tables twice, as this would break the dead-lock-free You can't call lock_tables twice, as this would break the dead-lock-free
...@@ -2335,7 +2380,7 @@ static void mark_real_tables_as_free_for_reuse(TABLE_LIST *table) ...@@ -2335,7 +2380,7 @@ static void mark_real_tables_as_free_for_reuse(TABLE_LIST *table)
-1 Error -1 Error
*/ */
int lock_tables(THD *thd, TABLE_LIST *tables, uint count) int lock_tables(THD *thd, TABLE_LIST *tables, uint count, bool *need_reopen)
{ {
TABLE_LIST *table; TABLE_LIST *table;
...@@ -2351,6 +2396,8 @@ int lock_tables(THD *thd, TABLE_LIST *tables, uint count) ...@@ -2351,6 +2396,8 @@ int lock_tables(THD *thd, TABLE_LIST *tables, uint count)
*/ */
DBUG_ASSERT(!thd->lex->requires_prelocking() || tables); DBUG_ASSERT(!thd->lex->requires_prelocking() || tables);
*need_reopen= FALSE;
if (!tables) if (!tables)
DBUG_RETURN(0); DBUG_RETURN(0);
...@@ -2383,7 +2430,9 @@ int lock_tables(THD *thd, TABLE_LIST *tables, uint count) ...@@ -2383,7 +2430,9 @@ int lock_tables(THD *thd, TABLE_LIST *tables, uint count)
thd->options|= OPTION_TABLE_LOCK; thd->options|= OPTION_TABLE_LOCK;
} }
if (! (thd->lock= mysql_lock_tables(thd, start, (uint) (ptr - start), 0))) if (! (thd->lock= mysql_lock_tables(thd, start, (uint) (ptr - start),
MYSQL_LOCK_NOTIFY_IF_NEED_REOPEN,
need_reopen)))
{ {
if (thd->lex->requires_prelocking()) if (thd->lex->requires_prelocking())
{ {
...@@ -2462,6 +2511,28 @@ int lock_tables(THD *thd, TABLE_LIST *tables, uint count) ...@@ -2462,6 +2511,28 @@ int lock_tables(THD *thd, TABLE_LIST *tables, uint count)
} }
/*
Prepare statement for reopening of tables and recalculation of set of
prelocked tables.
SYNOPSIS
close_tables_for_reopen()
thd Thread context
tables List of tables which we were trying to open and lock
*/
void close_tables_for_reopen(THD *thd, TABLE_LIST *tables)
{
thd->lex->chop_off_not_own_tables();
sp_remove_not_own_routines(thd->lex);
for (TABLE_LIST *tmp= tables; tmp; tmp= tmp->next_global)
if (tmp->table && !tmp->table->s->tmp_table)
tmp->table= 0;
close_thread_tables(thd);
}
/* /*
Open a single table without table caching and don't set it in open_list Open a single table without table caching and don't set it in open_list
Used by alter_table to open a temporary table and when creating Used by alter_table to open a temporary table and when creating
......
...@@ -346,6 +346,7 @@ bool mysql_ha_read(THD *thd, TABLE_LIST *tables, ...@@ -346,6 +346,7 @@ bool mysql_ha_read(THD *thd, TABLE_LIST *tables,
uint num_rows; uint num_rows;
byte *key; byte *key;
uint key_len; uint key_len;
bool not_used;
DBUG_ENTER("mysql_ha_read"); DBUG_ENTER("mysql_ha_read");
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));
...@@ -431,7 +432,7 @@ bool mysql_ha_read(THD *thd, TABLE_LIST *tables, ...@@ -431,7 +432,7 @@ bool mysql_ha_read(THD *thd, TABLE_LIST *tables,
protocol->send_fields(&list, Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF); protocol->send_fields(&list, Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF);
HANDLER_TABLES_HACK(thd); HANDLER_TABLES_HACK(thd);
lock= mysql_lock_tables(thd, &tables->table, 1, 0); lock= mysql_lock_tables(thd, &tables->table, 1, 0, &not_used);
HANDLER_TABLES_HACK(thd); HANDLER_TABLES_HACK(thd);
if (!lock) if (!lock)
......
...@@ -1826,6 +1826,7 @@ extern "C" pthread_handler_decl(handle_delayed_insert,arg) ...@@ -1826,6 +1826,7 @@ extern "C" pthread_handler_decl(handle_delayed_insert,arg)
if (di->tables_in_use && ! thd->lock) if (di->tables_in_use && ! thd->lock)
{ {
bool not_used;
/* /*
Request for new delayed insert. Request for new delayed insert.
Lock the table, but avoid to be blocked by a global read lock. Lock the table, but avoid to be blocked by a global read lock.
...@@ -1837,7 +1838,8 @@ extern "C" pthread_handler_decl(handle_delayed_insert,arg) ...@@ -1837,7 +1838,8 @@ extern "C" pthread_handler_decl(handle_delayed_insert,arg)
inserts are done. inserts are done.
*/ */
if (! (thd->lock= mysql_lock_tables(thd, &di->table, 1, if (! (thd->lock= mysql_lock_tables(thd, &di->table, 1,
MYSQL_LOCK_IGNORE_GLOBAL_READ_LOCK))) MYSQL_LOCK_IGNORE_GLOBAL_READ_LOCK,
&not_used)))
{ {
/* Fatal error */ /* Fatal error */
di->dead= 1; di->dead= 1;
......
...@@ -179,6 +179,8 @@ void lex_start(THD *thd, uchar *buf,uint length) ...@@ -179,6 +179,8 @@ void lex_start(THD *thd, uchar *buf,uint length)
if (lex->sroutines.records) if (lex->sroutines.records)
my_hash_reset(&lex->sroutines); my_hash_reset(&lex->sroutines);
lex->sroutines_list.empty(); lex->sroutines_list.empty();
lex->sroutines_list_own_last= lex->sroutines_list.next;
lex->sroutines_list_own_elements= 0;
DBUG_VOID_RETURN; DBUG_VOID_RETURN;
} }
...@@ -1613,6 +1615,8 @@ st_lex::st_lex() ...@@ -1613,6 +1615,8 @@ st_lex::st_lex()
{ {
hash_init(&sroutines, system_charset_info, 0, 0, 0, sp_sroutine_key, 0, 0); hash_init(&sroutines, system_charset_info, 0, 0, 0, sp_sroutine_key, 0, 0);
sroutines_list.empty(); sroutines_list.empty();
sroutines_list_own_last= sroutines_list.next;
sroutines_list_own_elements= 0;
} }
...@@ -2025,6 +2029,8 @@ void st_lex::cleanup_after_one_table_open() ...@@ -2025,6 +2029,8 @@ void st_lex::cleanup_after_one_table_open()
if (sroutines.records) if (sroutines.records)
my_hash_reset(&sroutines); my_hash_reset(&sroutines);
sroutines_list.empty(); sroutines_list.empty();
sroutines_list_own_last= sroutines_list.next;
sroutines_list_own_elements= 0;
} }
......
...@@ -843,8 +843,15 @@ typedef struct st_lex ...@@ -843,8 +843,15 @@ typedef struct st_lex
/* /*
List linking elements of 'sroutines' set. Allows you to add new elements List linking elements of 'sroutines' set. Allows you to add new elements
to this set as you iterate through the list of existing elements. to this set as you iterate through the list of existing elements.
'sroutines_list_own_last' is pointer to ::next member of last element of
this list which represents routine which is explicitly used by query.
'sroutines_list_own_elements' number of explicitly used routines.
We use these two members for restoring of 'sroutines_list' to the state
in which it was right after query parsing.
*/ */
SQL_LIST sroutines_list; SQL_LIST sroutines_list;
byte **sroutines_list_own_last;
uint sroutines_list_own_elements;
st_sp_chistics sp_chistics; st_sp_chistics sp_chistics;
bool only_view; /* used for SHOW CREATE TABLE/VIEW */ bool only_view; /* used for SHOW CREATE TABLE/VIEW */
...@@ -956,6 +963,15 @@ typedef struct st_lex ...@@ -956,6 +963,15 @@ typedef struct st_lex
{ {
return ( query_tables_own_last ? *query_tables_own_last : 0); return ( query_tables_own_last ? *query_tables_own_last : 0);
} }
void chop_off_not_own_tables()
{
if (query_tables_own_last)
{
*query_tables_own_last= 0;
query_tables_last= query_tables_own_last;
query_tables_own_last= 0;
}
}
void cleanup_after_one_table_open(); void cleanup_after_one_table_open();
void push_context(Name_resolution_context *context) void push_context(Name_resolution_context *context)
......
...@@ -1094,30 +1094,39 @@ static int mysql_test_update(Prepared_statement *stmt, ...@@ -1094,30 +1094,39 @@ static int mysql_test_update(Prepared_statement *stmt,
#ifndef NO_EMBEDDED_ACCESS_CHECKS #ifndef NO_EMBEDDED_ACCESS_CHECKS
uint want_privilege; uint want_privilege;
#endif #endif
bool need_reopen;
DBUG_ENTER("mysql_test_update"); DBUG_ENTER("mysql_test_update");
if (update_precheck(thd, table_list)) if (update_precheck(thd, table_list))
goto error; goto error;
if (open_tables(thd, &table_list, &table_count, 0)) for ( ; ; )
goto error;
if (table_list->multitable_view)
{ {
DBUG_ASSERT(table_list->view != 0); if (open_tables(thd, &table_list, &table_count, 0))
DBUG_PRINT("info", ("Switch to multi-update")); goto error;
/* pass counter value */
thd->lex->table_count= table_count; if (table_list->multitable_view)
/* convert to multiupdate */ {
DBUG_RETURN(2); DBUG_ASSERT(table_list->view != 0);
DBUG_PRINT("info", ("Switch to multi-update"));
/* pass counter value */
thd->lex->table_count= table_count;
/* convert to multiupdate */
DBUG_RETURN(2);
}
if (!lock_tables(thd, table_list, table_count, &need_reopen))
break;
if (!need_reopen)
goto error;
close_tables_for_reopen(thd, table_list);
} }
/* /*
thd->fill_derived_tables() is false here for sure (because it is thd->fill_derived_tables() is false here for sure (because it is
preparation of PS, so we even do not check it). preparation of PS, so we even do not check it).
*/ */
if (lock_tables(thd, table_list, table_count) || if (mysql_handle_derived(thd->lex, &mysql_derived_prepare))
mysql_handle_derived(thd->lex, &mysql_derived_prepare))
goto error; goto error;
#ifndef NO_EMBEDDED_ACCESS_CHECKS #ifndef NO_EMBEDDED_ACCESS_CHECKS
......
...@@ -1706,6 +1706,7 @@ TABLE *create_table_from_items(THD *thd, HA_CREATE_INFO *create_info, ...@@ -1706,6 +1706,7 @@ TABLE *create_table_from_items(THD *thd, HA_CREATE_INFO *create_info,
List_iterator_fast<Item> it(*items); List_iterator_fast<Item> it(*items);
Item *item; Item *item;
Field *tmp_field; Field *tmp_field;
bool not_used;
DBUG_ENTER("create_table_from_items"); DBUG_ENTER("create_table_from_items");
tmp_table.alias= 0; tmp_table.alias= 0;
...@@ -1762,8 +1763,15 @@ TABLE *create_table_from_items(THD *thd, HA_CREATE_INFO *create_info, ...@@ -1762,8 +1763,15 @@ TABLE *create_table_from_items(THD *thd, HA_CREATE_INFO *create_info,
DBUG_RETURN(0); DBUG_RETURN(0);
} }
/*
FIXME: What happens if trigger manages to be created while we are
obtaining this lock ? May be it is sensible just to disable
trigger execution in this case ? Or will MYSQL_LOCK_IGNORE_FLUSH
save us from that ?
*/
table->reginfo.lock_type=TL_WRITE; table->reginfo.lock_type=TL_WRITE;
if (! ((*lock)= mysql_lock_tables(thd, &table, 1, MYSQL_LOCK_IGNORE_FLUSH))) if (! ((*lock)= mysql_lock_tables(thd, &table, 1,
MYSQL_LOCK_IGNORE_FLUSH, &not_used)))
{ {
VOID(pthread_mutex_lock(&LOCK_open)); VOID(pthread_mutex_lock(&LOCK_open));
hash_delete(&open_cache,(byte*) table); hash_delete(&open_cache,(byte*) table);
......
...@@ -510,6 +510,25 @@ bool Table_triggers_list::prepare_record1_accessors(TABLE *table) ...@@ -510,6 +510,25 @@ bool Table_triggers_list::prepare_record1_accessors(TABLE *table)
} }
/*
Adjust Table_triggers_list with new TABLE pointer.
SYNOPSIS
set_table()
new_table - new pointer to TABLE instance
*/
void Table_triggers_list::set_table(TABLE *new_table)
{
table= new_table;
for (Field **field= table->triggers->record1_field ; *field ; field++)
{
(*field)->table= (*field)->orig_table= new_table;
(*field)->table_name= &new_table->alias;
}
}
/* /*
Check whenever .TRG file for table exist and load all triggers it contains. Check whenever .TRG file for table exist and load all triggers it contains.
......
...@@ -98,6 +98,8 @@ public: ...@@ -98,6 +98,8 @@ public:
return test(bodies[TRG_EVENT_UPDATE][TRG_ACTION_BEFORE]); return test(bodies[TRG_EVENT_UPDATE][TRG_ACTION_BEFORE]);
} }
void set_table(TABLE *new_table);
friend class Item_trigger_field; friend class Item_trigger_field;
friend void sp_cache_routines_and_add_tables_for_triggers(THD *thd, LEX *lex, friend void sp_cache_routines_and_add_tables_for_triggers(THD *thd, LEX *lex,
Table_triggers_list *triggers); Table_triggers_list *triggers);
......
...@@ -134,25 +134,33 @@ int mysql_update(THD *thd, ...@@ -134,25 +134,33 @@ int mysql_update(THD *thd,
SQL_SELECT *select; SQL_SELECT *select;
READ_RECORD info; READ_RECORD info;
SELECT_LEX *select_lex= &thd->lex->select_lex; SELECT_LEX *select_lex= &thd->lex->select_lex;
bool need_reopen;
DBUG_ENTER("mysql_update"); DBUG_ENTER("mysql_update");
LINT_INIT(timestamp_query_id); LINT_INIT(timestamp_query_id);
if (open_tables(thd, &table_list, &table_count, 0)) for ( ; ; )
DBUG_RETURN(1);
if (table_list->multitable_view)
{ {
DBUG_ASSERT(table_list->view != 0); if (open_tables(thd, &table_list, &table_count, 0))
DBUG_PRINT("info", ("Switch to multi-update")); DBUG_RETURN(1);
/* pass counter value */
thd->lex->table_count= table_count; if (table_list->multitable_view)
/* convert to multiupdate */ {
return 2; DBUG_ASSERT(table_list->view != 0);
DBUG_PRINT("info", ("Switch to multi-update"));
/* pass counter value */
thd->lex->table_count= table_count;
/* convert to multiupdate */
DBUG_RETURN(2);
}
if (!lock_tables(thd, table_list, table_count, &need_reopen))
break;
if (!need_reopen)
DBUG_RETURN(1);
close_tables_for_reopen(thd, table_list);
} }
if (lock_tables(thd, table_list, table_count) || if (mysql_handle_derived(thd->lex, &mysql_derived_prepare) ||
mysql_handle_derived(thd->lex, &mysql_derived_prepare) ||
(thd->fill_derived_tables() && (thd->fill_derived_tables() &&
mysql_handle_derived(thd->lex, &mysql_derived_filling))) mysql_handle_derived(thd->lex, &mysql_derived_filling)))
DBUG_RETURN(1); DBUG_RETURN(1);
...@@ -616,7 +624,6 @@ static table_map get_table_map(List<Item> *items) ...@@ -616,7 +624,6 @@ static table_map get_table_map(List<Item> *items)
bool mysql_multi_update_prepare(THD *thd) bool mysql_multi_update_prepare(THD *thd)
{ {
LEX *lex= thd->lex; LEX *lex= thd->lex;
ulong opened_tables;
TABLE_LIST *table_list= lex->query_tables; TABLE_LIST *table_list= lex->query_tables;
TABLE_LIST *tl, *leaves; TABLE_LIST *tl, *leaves;
List<Item> *fields= &lex->select_lex.item_list; List<Item> *fields= &lex->select_lex.item_list;
...@@ -630,13 +637,16 @@ bool mysql_multi_update_prepare(THD *thd) ...@@ -630,13 +637,16 @@ bool mysql_multi_update_prepare(THD *thd)
uint table_count= lex->table_count; uint table_count= lex->table_count;
const bool using_lock_tables= thd->locked_tables != 0; const bool using_lock_tables= thd->locked_tables != 0;
bool original_multiupdate= (thd->lex->sql_command == SQLCOM_UPDATE_MULTI); bool original_multiupdate= (thd->lex->sql_command == SQLCOM_UPDATE_MULTI);
bool need_reopen= FALSE;
DBUG_ENTER("mysql_multi_update_prepare"); DBUG_ENTER("mysql_multi_update_prepare");
/* following need for prepared statements, to run next time multi-update */ /* following need for prepared statements, to run next time multi-update */
thd->lex->sql_command= SQLCOM_UPDATE_MULTI; thd->lex->sql_command= SQLCOM_UPDATE_MULTI;
reopen_tables:
/* open tables and create derived ones, but do not lock and fill them */ /* open tables and create derived ones, but do not lock and fill them */
if ((original_multiupdate && if (((original_multiupdate || need_reopen) &&
open_tables(thd, &table_list, &table_count, 0)) || open_tables(thd, &table_list, &table_count, 0)) ||
mysql_handle_derived(lex, &mysql_derived_prepare)) mysql_handle_derived(lex, &mysql_derived_prepare))
DBUG_RETURN(TRUE); DBUG_RETURN(TRUE);
...@@ -741,20 +751,17 @@ bool mysql_multi_update_prepare(THD *thd) ...@@ -741,20 +751,17 @@ bool mysql_multi_update_prepare(THD *thd)
} }
} }
opened_tables= thd->status_var.opened_tables;
/* now lock and fill tables */ /* now lock and fill tables */
if (lock_tables(thd, table_list, table_count)) if (lock_tables(thd, table_list, table_count, &need_reopen))
DBUG_RETURN(TRUE);
/*
we have to re-call fixfields for fixed items, because lock maybe
reopened tables
*/
if (opened_tables != thd->status_var.opened_tables)
{ {
if (!need_reopen)
DBUG_RETURN(TRUE);
/* /*
Fields items cleanup(). There are only Item_fields in the list, so we We have to reopen tables since some of them were altered or dropped
do not do Item tree walking during lock_tables() or something was done with their triggers.
Let us do some cleanups to be able do setup_table() and setup_fields()
once again.
*/ */
List_iterator_fast<Item> it(*fields); List_iterator_fast<Item> it(*fields);
Item *item; Item *item;
...@@ -765,12 +772,8 @@ bool mysql_multi_update_prepare(THD *thd) ...@@ -765,12 +772,8 @@ bool mysql_multi_update_prepare(THD *thd)
for (TABLE_LIST *tbl= table_list; tbl; tbl= tbl->next_global) for (TABLE_LIST *tbl= table_list; tbl; tbl= tbl->next_global)
tbl->cleanup_items(); tbl->cleanup_items();
if (setup_tables(thd, &lex->select_lex.context, close_tables_for_reopen(thd, table_list);
&lex->select_lex.top_join_list, goto reopen_tables;
table_list, &lex->select_lex.where,
&lex->select_lex.leaf_tables, FALSE) ||
setup_fields_with_no_wrap(thd, 0, *fields, 1, 0, 0))
DBUG_RETURN(TRUE);
} }
/* /*
......
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