diff options
author | Konstantin Osipov <kostja@sun.com> | 2009-12-09 19:00:46 +0300 |
---|---|---|
committer | Konstantin Osipov <kostja@sun.com> | 2009-12-09 19:00:46 +0300 |
commit | b9d2f55a9d98c2a8fe4d67de51e7e7b84d2b7440 (patch) | |
tree | dc2ce09da5a4bdc6df0682821cabff45fcb7658e /sql | |
parent | e001a9f0d076b50dd35eb21d5f03619dffc14410 (diff) | |
download | mariadb-git-b9d2f55a9d98c2a8fe4d67de51e7e7b84d2b7440.tar.gz |
------------------------------------------------------------
revno: 2617.68.23
committer: Dmitry Lenev <dlenev@mysql.com>
branch nick: mysql-next-bg-pre1
timestamp: Wed 2009-09-16 09:34:42 +0400
message:
Pre-requisite patch for fixing bug #30977 "Concurrent statement
using stored function and DROP FUNCTION breaks SBR".
CREATE TABLE SELECT statements take exclusive metadata lock on table
being created. Invariant of metadata locking subsystem states that
such lock should be taken before taking any kind of shared locks.
Once metadata locks on stored routines are introduced statements like
"CREATE TABLE ... SELECT f1()" will break this invariant by taking
shared locks on routines before exclusive lock on target table.
To avoid this, open_tables() is reworked to process tables which are
directly used by the statement before stored routines are processed.
sql/sql_base.cc:
Refactored open_tables() implementation to process stored routines
only after tables which are directly used by statement were processed.
To achieve this moved handling of routines in open_tables() out of
loop which iterates over tables to a new separate loop. And in its
turn this allowed to split handling of particular table or view to
an auxiliary function, which made code in open_tables() simpler and
more easy to understand.
Diffstat (limited to 'sql')
-rw-r--r-- | sql/sql_base.cc | 714 |
1 files changed, 375 insertions, 339 deletions
diff --git a/sql/sql_base.cc b/sql/sql_base.cc index ad618eb59fe..20353f0c9f8 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -3753,16 +3753,13 @@ thr_lock_type read_lock_type_for_table(THD *thd, TABLE *table) /* - Perform steps of prelocking algorithm for elements of the - prelocking set other than tables. E.g. cache routines and, if - prelocking strategy prescribes so, extend the prelocking set with - tables and routines used by them. + Handle element of prelocking set other than table. E.g. cache routine + and, if prelocking strategy prescribes so, extend the prelocking set + with tables and routines used by it. @param[in] thd Thread context. @param[in] prelocking_ctx Prelocking context. - @param[in] start First element in the list representing - subset of the prelocking set to be - processed. + @param[in] rt Element of prelocking set to be processed. @param[in] prelocking_strategy Strategy which specifies how the prelocking set should be extended when one of its elements is processed. @@ -3775,47 +3772,294 @@ thr_lock_type read_lock_type_for_table(THD *thd, TABLE *table) */ static bool -open_routines(THD *thd, Query_tables_list *prelocking_ctx, - Sroutine_hash_entry *start, - Prelocking_strategy *prelocking_strategy, - bool *need_prelocking) +open_and_process_routine(THD *thd, Query_tables_list *prelocking_ctx, + Sroutine_hash_entry *rt, + Prelocking_strategy *prelocking_strategy, + bool *need_prelocking) { - DBUG_ENTER("open_routines"); + int type= rt->key.str[0]; - for (Sroutine_hash_entry *rt= start; rt; rt= rt->next) - { - int type= rt->key.str[0]; + DBUG_ENTER("open_and_process_routine"); - switch (type) + switch (type) + { + case TYPE_ENUM_FUNCTION: + case TYPE_ENUM_PROCEDURE: { - case TYPE_ENUM_FUNCTION: - case TYPE_ENUM_PROCEDURE: - { - sp_name name(thd, rt->key.str, rt->key.length); - sp_head *sp; + sp_name name(thd, rt->key.str, rt->key.length); + sp_head *sp; - if (sp_cache_routine(thd, type, &name, &sp)) - DBUG_RETURN(TRUE); + if (sp_cache_routine(thd, type, &name, &sp)) + DBUG_RETURN(TRUE); - if (sp) - { - prelocking_strategy->handle_routine(thd, prelocking_ctx, rt, sp, - need_prelocking); - } + if (sp) + { + prelocking_strategy->handle_routine(thd, prelocking_ctx, rt, sp, + need_prelocking); } - break; - case TYPE_ENUM_TRIGGER: - break; - default: - /* Impossible type value. */ - DBUG_ASSERT(0); } + break; + case TYPE_ENUM_TRIGGER: + break; + default: + /* Impossible type value. */ + DBUG_ASSERT(0); } DBUG_RETURN(FALSE); } /** + Handle table list element by obtaining metadata lock, opening table or view + and, if prelocking strategy prescribes so, extending the prelocking set with + tables and routines used by it. + + @param[in] thd Thread context. + @param[in] lex LEX structure for statement. + @param[in] tables Table list element to be processed. + @param[in,out] counter Number of tables which are open. + @param[in] flags Bitmap of flags to modify how the tables + will be open, see open_table() description + for details. + @param[in] prelocking_strategy Strategy which specifies how the + prelocking set should be extended + when table or view is processed. + @param[in] has_prelocking_list Indicates that prelocking set/list for + this statement has already been built. + @param[in] ot_ctx Context used to recover from a failed + open_table() attempt. + @param[in] new_frm_mem Temporary MEM_ROOT to be used for + parsing .FRMs for views. + + @retval FALSE Success. + @retval TRUE Error, reported unless there is a chance to recover from it. +*/ + +static bool +open_and_process_table(THD *thd, LEX *lex, TABLE_LIST *tables, + uint *counter, uint flags, + Prelocking_strategy *prelocking_strategy, + bool has_prelocking_list, + Open_table_context *ot_ctx, + MEM_ROOT *new_frm_mem) +{ + bool error= FALSE; + bool safe_to_ignore_table= FALSE; + DBUG_ENTER("open_and_process_table"); + + /* + Ignore placeholders for derived tables. After derived tables + 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->view) + goto end; + /* + We restore view's name and database wiped out by derived tables + processing and fall back to standard open process in order to + obtain proper metadata locks and do other necessary steps like + stored routine processing. + */ + tables->db= tables->view_db.str; + tables->db_length= tables->view_db.length; + tables->table_name= tables->view_name.str; + tables->table_name_length= tables->view_name.length; + } + /* + If this TABLE_LIST object is a placeholder for an information_schema + table, create a temporary table to represent the information_schema + table in the query. Do not fill it yet - will be filled during + execution. + */ + if (tables->schema_table) + { + /* + If this information_schema table is merged into a mergeable + view, ignore it for now -- it will be filled when its respective + TABLE_LIST is processed. This code works only during re-execution. + */ + if (tables->view) + goto process_view_routines; + if (!mysql_schema_table(thd, lex, tables) && + !check_and_update_table_version(thd, tables, tables->table->s)) + { + goto end; + } + error= TRUE; + goto end; + } + DBUG_PRINT("tcache", ("opening table: '%s'.'%s' item: %p", + tables->db, tables->table_name, tables)); //psergey: invalid read of size 1 here + (*counter)++; + + /* Not a placeholder: must be a base table or a view. Let us open it. */ + DBUG_ASSERT(!tables->table); + + if (tables->prelocking_placeholder) + { + /* + For the tables added by the pre-locking code, attempt to open + the table but fail silently if the table does not exist. + The real failure will occur when/if a statement attempts to use + that table. + */ + Prelock_error_handler prelock_handler; + thd->push_internal_handler(& prelock_handler); + error= open_table(thd, tables, new_frm_mem, ot_ctx, flags); + thd->pop_internal_handler(); + safe_to_ignore_table= prelock_handler.safely_trapped_errors(); + } + else + error= open_table(thd, tables, new_frm_mem, ot_ctx, flags); + + free_root(new_frm_mem, MYF(MY_KEEP_PREALLOC)); + + if (error) + { + if (! ot_ctx->can_recover_from_failed_open_table() && safe_to_ignore_table) + { + DBUG_PRINT("info", ("open_table: ignoring table '%s'.'%s'", + tables->db, tables->alias)); + error= FALSE; + } + goto end; + } + + /* + We can't rely on simple check for TABLE_LIST::view to determine + that this is a view since during re-execution we might reopen + ordinary table in place of view and thus have TABLE_LIST::view + set from repvious execution and TABLE_LIST::table set from + current. + */ + if (!tables->table && tables->view) + { + /* VIEW placeholder */ + (*counter)--; + + /* + tables->next_global list consists of two parts: + 1) Query tables and underlying tables of views. + 2) Tables used by all stored routines that this statement invokes on + execution. + We need to know where the bound between these two parts is. If we've + just opened a view, which was the last table in part #1, and it + has added its base tables after itself, adjust the boundary pointer + accordingly. + */ + if (lex->query_tables_own_last == &(tables->next_global) && + tables->view->query_tables) + lex->query_tables_own_last= tables->view->query_tables_last; + /* + Let us free memory used by 'sroutines' hash here since we never + call destructor for this LEX. + */ + my_hash_free(&tables->view->sroutines); + goto process_view_routines; + } + + /* + Special types of open can succeed but still don't set + TABLE_LIST::table to anything. + */ + if (tables->open_strategy && !tables->table) + goto end; + + /* + If we are not already in prelocked mode and extended table list is not + yet built we might have to build the prelocking set for this statement. + + Since currently no prelocking strategy prescribes doing anything for + tables which are only read, we do below checks only if table is going + to be changed. + */ + if (thd->locked_tables_mode <= LTM_LOCK_TABLES && + ! has_prelocking_list && + tables->lock_type >= TL_WRITE_ALLOW_WRITE) + { + bool need_prelocking= FALSE; + TABLE_LIST **save_query_tables_last= lex->query_tables_last; + /* + Extend statement's table list and the prelocking set with + tables and routines according to the current prelocking + strategy. + + For example, for DML statements we need to add tables and routines + used by triggers which are going to be invoked for this element of + table list and also add tables required for handling of foreign keys. + */ + error= prelocking_strategy->handle_table(thd, lex, tables, + &need_prelocking); + + if (need_prelocking && ! lex->requires_prelocking()) + lex->mark_as_requiring_prelocking(save_query_tables_last); + + if (error) + goto end; + } + + if (tables->lock_type != TL_UNLOCK && ! thd->locked_tables_mode) + { + if (tables->lock_type == TL_WRITE_DEFAULT) + tables->table->reginfo.lock_type= thd->update_lock_default; + else if (tables->lock_type == TL_READ_DEFAULT) + tables->table->reginfo.lock_type= + read_lock_type_for_table(thd, tables->table); + else + tables->table->reginfo.lock_type= tables->lock_type; + } + tables->table->grant= tables->grant; + + /* Check and update metadata version of a base table. */ + error= check_and_update_table_version(thd, tables, tables->table->s); + + if (error) + goto end; + /* + After opening a MERGE table add the children to the query list of + tables, so that they are opened too. + Note that placeholders don't have the handler open. + */ + /* MERGE tables need to access parent and child TABLE_LISTs. */ + DBUG_ASSERT(tables->table->pos_in_table_list == tables); + /* Non-MERGE tables ignore this call. */ + if (tables->table->file->extra(HA_EXTRA_ADD_CHILDREN_LIST)) + { + error= TRUE; + goto end; + } + +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->locked_tables_mode <= LTM_LOCK_TABLES && + ! has_prelocking_list) + { + bool need_prelocking= FALSE; + TABLE_LIST **save_query_tables_last= lex->query_tables_last; + + error= prelocking_strategy->handle_view(thd, lex, tables, + &need_prelocking); + + if (need_prelocking && ! lex->requires_prelocking()) + lex->mark_as_requiring_prelocking(save_query_tables_last); + + if (error) + goto end; + } + +end: + DBUG_RETURN(error); +} + + +/** Open all tables in list @param[in] thd Thread context. @@ -3847,27 +4091,22 @@ open_routines(THD *thd, Query_tables_list *prelocking_ctx, bool open_tables(THD *thd, TABLE_LIST **start, uint *counter, uint flags, Prelocking_strategy *prelocking_strategy) { - TABLE_LIST *tables= NULL; + /* + We use pointers to "next_global" member in the last processed TABLE_LIST + element and to the "next" member in the last processed Sroutine_hash_entry + element as iterators over, correspondingly, the table list and stored routines + list which stay valid and allow to continue iteration when new elements are + added to the tail of the lists. + */ + TABLE_LIST **table_to_open; + Sroutine_hash_entry **sroutine_to_open; + TABLE_LIST *tables; Open_table_context ot_ctx(thd); bool error= FALSE; MEM_ROOT new_frm_mem; - /* Also used for indicating that prelocking is need */ - TABLE_LIST **query_tables_last_own; - bool safe_to_ignore_table; + bool has_prelocking_list= thd->lex->requires_prelocking(); DBUG_ENTER("open_tables"); /* - temporary mem_root for new .frm parsing. - TODO: variables for size - */ - init_sql_alloc(&new_frm_mem, 8024, 8024); - - thd->current_tablenr= 0; - restart: - *counter= 0; - query_tables_last_own= 0; - thd_proc_info(thd, "Opening tables"); - - /* Close HANDLER tables which are marked for flush or against which there are pending exclusive metadata locks. Note that we do this not to avoid deadlocks (calls to mysql_ha_flush() in mdl_wait_for_locks() and @@ -3880,316 +4119,116 @@ bool open_tables(THD *thd, TABLE_LIST **start, uint *counter, uint flags, mysql_ha_flush(thd); /* - If we are not already executing prelocked statement and don't have - statement for which table list for prelocking is already built, let - us cache routines and try to build such table list. + temporary mem_root for new .frm parsing. + TODO: variables for size */ + init_sql_alloc(&new_frm_mem, 8024, 8024); - if (thd->locked_tables_mode <= LTM_LOCK_TABLES && - !thd->lex->requires_prelocking() && - thd->lex->uses_stored_routines()) - { - bool need_prelocking= FALSE; - TABLE_LIST **save_query_tables_last= thd->lex->query_tables_last; - - DBUG_ASSERT(thd->lex->query_tables == *start); - - error= open_routines(thd, thd->lex, - (Sroutine_hash_entry *)thd->lex->sroutines_list.first, - prelocking_strategy, &need_prelocking); - if (error) - { - /* - Serious error during reading stored routines from mysql.proc table. - Something's wrong with the table or its contents, and an error has - been emitted; we must abort. - */ - goto err; - } - else if (need_prelocking) - { - query_tables_last_own= save_query_tables_last; - *start= thd->lex->query_tables; - } - } + thd->current_tablenr= 0; + restart: + table_to_open= start; + sroutine_to_open= (Sroutine_hash_entry**) &thd->lex->sroutines_list.first; + *counter= 0; + thd_proc_info(thd, "Opening tables"); /* - For every table in the list of tables to open, try to find or open - a table. + Perform steps of prelocking algorithm until there are unprocessed + elements in prelocking list/set. */ - for (tables= *start; tables ;tables= tables->next_global) + while (*table_to_open || + (thd->locked_tables_mode <= LTM_LOCK_TABLES && + ! has_prelocking_list && + *sroutine_to_open)) { - safe_to_ignore_table= FALSE; - - /* - Ignore placeholders for derived tables. After derived tables - 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->view) - continue; - /* - We restore view's name and database wiped out by derived tables - processing and fall back to standard open process in order to - obtain proper metadata locks and do other necessary steps like - stored routine processing. - */ - tables->db= tables->view_db.str; - tables->db_length= tables->view_db.length; - tables->table_name= tables->view_name.str; - tables->table_name_length= tables->view_name.length; - } /* - If this TABLE_LIST object is a placeholder for an information_schema - table, create a temporary table to represent the information_schema - table in the query. Do not fill it yet - will be filled during - execution. + For every table in the list of tables to open, try to find or open + a table. */ - if (tables->schema_table) + for (tables= *table_to_open; tables; + table_to_open= &tables->next_global, tables= tables->next_global) { - /* - If this information_schema table is merged into a mergeable - view, ignore it for now -- it will be filled when its respective - TABLE_LIST is processed. This code works only during re-execution. - */ - if (tables->view) - goto process_view_routines; - if (!mysql_schema_table(thd, thd->lex, tables) && - !check_and_update_table_version(thd, tables, tables->table->s)) - { - continue; - } - DBUG_RETURN(TRUE); - } - DBUG_PRINT("tcache", ("opening table: '%s'.'%s' item: 0x%lx", - tables->db, tables->table_name, (long) tables)); - (*counter)++; - - /* Not a placeholder: must be a base table or a view. Let us open it. */ - DBUG_ASSERT(!tables->table); + error= open_and_process_table(thd, thd->lex, tables, counter, + flags, prelocking_strategy, + has_prelocking_list, &ot_ctx, + &new_frm_mem); - if (tables->prelocking_placeholder) - { - /* - For the tables added by the pre-locking code, attempt to open - the table but fail silently if the table does not exist. - The real failure will occur when/if a statement attempts to use - that table. - */ - Prelock_error_handler prelock_handler; - thd->push_internal_handler(& prelock_handler); - error= open_table(thd, tables, &new_frm_mem, &ot_ctx, flags); - thd->pop_internal_handler(); - safe_to_ignore_table= prelock_handler.safely_trapped_errors(); - } - else - error= open_table(thd, tables, &new_frm_mem, &ot_ctx, flags); - - free_root(&new_frm_mem, MYF(MY_KEEP_PREALLOC)); - - if (error) - { - if (ot_ctx.can_recover_from_failed_open_table()) + if (error) { - /* - We have met exclusive metadata lock or old version of table. Now we - have to close all tables which are not up to date/release metadata - locks. We also have to throw away set of prelocked tables (and thus - close tables from this set that were open by now) since it possible - that one of tables which determined its content was changed. - - Instead of implementing complex/non-robust logic mentioned - above we simply close and then reopen all tables. - - In order to prepare for recalculation of set of prelocked tables - we pretend that we have finished calculation which we were doing - currently. - */ - if (query_tables_last_own) - thd->lex->mark_as_requiring_prelocking(query_tables_last_own); - close_tables_for_reopen(thd, start); - /* - Here we rely on the fact that 'tables' still points to the valid - TABLE_LIST element. Altough currently this assumption is valid - it may change in future. - */ - if (ot_ctx.recover_from_failed_open_table_attempt(thd, tables)) - goto err; - - error= FALSE; - goto restart; - } + if (ot_ctx.can_recover_from_failed_open_table()) + { + /* + We have met exclusive metadata lock or old version of table. + Now we have to close all tables and release metadata locks. + We also have to throw away set of prelocked tables (and thus + close tables from this set that were open by now) since it + is possible that one of tables which determined its content + was changed. + + Instead of implementing complex/non-robust logic mentioned + above we simply close and then reopen all tables. + + We have to save pointer to table list element for table which we + have failed to open since closing tables can trigger removal of + elements from the table list (if MERGE tables are involved), + */ + TABLE_LIST *failed_table= *table_to_open; + close_tables_for_reopen(thd, start); + /* + Here we rely on the fact that 'tables' still points to the valid + TABLE_LIST element. Altough currently this assumption is valid + it may change in future. + */ + if (ot_ctx.recover_from_failed_open_table_attempt(thd, failed_table)) + goto err; - if (safe_to_ignore_table) - { - DBUG_PRINT("info", ("open_table: ignoring table '%s'.'%s'", - tables->db, tables->alias)); - error= FALSE; - continue; + error= FALSE; + goto restart; + } + goto err; } - goto err; } /* - We can't rely on simple check for TABLE_LIST::view to determine - that this is a view since during re-execution we might reopen - ordinary table in place of view and thus have TABLE_LIST::view - set from repvious execution and TABLE_LIST::table set from - current. + If we are not already in prelocked mode and extended table list is + not yet built for our statement we need to cache routines it uses + and build the prelocking list for it. */ - if (!tables->table && tables->view) - { - /* VIEW placeholder */ - (*counter)--; - - /* - tables->next_global list consists of two parts: - 1) Query tables and underlying tables of views. - 2) Tables used by all stored routines that this statement invokes on - execution. - We need to know where the bound between these two parts is. If we've - just opened a view, which was the last table in part #1, and it - has added its base tables after itself, adjust the boundary pointer - accordingly. - */ - if (query_tables_last_own == &(tables->next_global) && - tables->view->query_tables) - query_tables_last_own= tables->view->query_tables_last; - /* - Let us free memory used by 'sroutines' hash here since we never - call destructor for this LEX. - */ - my_hash_free(&tables->view->sroutines); - goto process_view_routines; - } - - /* - Special types of open can succeed but still don't set - TABLE_LIST::table to anything. - */ - if (tables->open_strategy && !tables->table) - continue; - - /* - If we are not already in prelocked mode and extended table list is not - yet built we might have to build the prelocking set for this statement. - - Since currently no prelocking strategy prescribes doing anything for - tables which are only read, we do below checks only if table is going - to be changed. - */ - if (thd->locked_tables_mode <= LTM_LOCK_TABLES && - !thd->lex->requires_prelocking() && - tables->lock_type >= TL_WRITE_ALLOW_WRITE) + if (thd->locked_tables_mode <= LTM_LOCK_TABLES && ! has_prelocking_list) { bool need_prelocking= FALSE; - bool not_used; TABLE_LIST **save_query_tables_last= thd->lex->query_tables_last; - Sroutine_hash_entry **sroutines_next= - (Sroutine_hash_entry **)thd->lex->sroutines_list.next; - /* - Extend statement's table list and the prelocking set with - tables and routines according to the current prelocking - strategy. + Process elements of the prelocking set which are present there + since parsing stage or were added to it by invocations of + Prelocking_strategy methods in the above loop over tables. - For example, for DML statements we need to add tables and routines - used by triggers which are going to be invoked for this element of - table list and also add tables required for handling of foreign keys. + For example, if element is a routine, cache it and then, + if prelocking strategy prescribes so, add tables it uses to the + table list and routines it might invoke to the prelocking set. */ - error= prelocking_strategy->handle_table(thd, thd->lex, tables, - &need_prelocking); - - if (need_prelocking && ! query_tables_last_own) - query_tables_last_own= save_query_tables_last; - - if (error) - goto err; - - /* - Process elements of the prelocking set which were added - by the above invocation of Prelocking_strategy method. - - For example, if new element is a routine, cache it and then, if - prelocking strategy prescribes so, add tables it uses to the table - list and routines it might invoke to the prelocking set. - */ - error= open_routines(thd, thd->lex, *sroutines_next, prelocking_strategy, - ¬_used); - if (error) - goto err; - } - - if (tables->lock_type != TL_UNLOCK && ! thd->locked_tables_mode) - { - if (tables->lock_type == TL_WRITE_DEFAULT) - tables->table->reginfo.lock_type= thd->update_lock_default; - else if (tables->lock_type == TL_READ_DEFAULT) - tables->table->reginfo.lock_type= - read_lock_type_for_table(thd, tables->table); - else - tables->table->reginfo.lock_type= tables->lock_type; - } - tables->table->grant= tables->grant; - - /* Check and update metadata version of a base table. */ - error= check_and_update_table_version(thd, tables, tables->table->s); - - if (error) - goto err; - /* - After opening a MERGE table add the children to the query list of - tables, so that they are opened too. - Note that placeholders don't have the handler open. - */ - /* MERGE tables need to access parent and child TABLE_LISTs. */ - DBUG_ASSERT(tables->table->pos_in_table_list == tables); - /* Non-MERGE tables ignore this call. */ - if (tables->table->file->extra(HA_EXTRA_ADD_CHILDREN_LIST)) - { - error= TRUE; - goto err; - } - -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->locked_tables_mode <= LTM_LOCK_TABLES && - !thd->lex->requires_prelocking()) - { - bool need_prelocking= FALSE; - bool not_used; - TABLE_LIST **save_query_tables_last= thd->lex->query_tables_last; - Sroutine_hash_entry **sroutines_next= - (Sroutine_hash_entry **)thd->lex->sroutines_list.next; - - error= prelocking_strategy->handle_view(thd, thd->lex, tables, - &need_prelocking); - - if (need_prelocking && ! query_tables_last_own) - query_tables_last_own= save_query_tables_last; + for (Sroutine_hash_entry *rt= *sroutine_to_open; rt; + sroutine_to_open= &rt->next, rt= rt->next) + { + error= open_and_process_routine(thd, thd->lex, rt, + prelocking_strategy, + &need_prelocking); - if (error) - goto err; + if (error) + { + /* + Serious error during reading stored routines from mysql.proc table. + Something is wrong with the table or its contents, and an error has + been emitted; we must abort. + */ + goto err; + } + } - error= open_routines(thd, thd->lex, *sroutines_next, prelocking_strategy, - ¬_used); + if (need_prelocking && ! thd->lex->requires_prelocking()) + thd->lex->mark_as_requiring_prelocking(save_query_tables_last); - if (error) - { - /* - Serious error during reading stored routines from mysql.proc table. - Something is wrong with the table or its contents, and an error has - been emitted; we must abort. - */ - goto err; - } + if (need_prelocking && ! *start) + *start= thd->lex->query_tables; } } @@ -4220,12 +4259,9 @@ err: thd_proc_info(thd, 0); free_root(&new_frm_mem, MYF(0)); // Free pre-alloced block - if (query_tables_last_own) - thd->lex->mark_as_requiring_prelocking(query_tables_last_own); - - if (error && tables) + if (error && *table_to_open) { - tables->table= NULL; + (*table_to_open)->table= NULL; } DBUG_PRINT("open_tables", ("returning: %d", (int) error)); DBUG_RETURN(error); |