From: Mattias Jonsson Date: April 7 2012 11:18pm Subject: bzr push into mysql-trunk branch (mattias.jonsson:3875 to 3876) WL#4443 List-Archive: http://lists.mysql.com/commits/143423 Message-Id: <201204072318.q37NIDGw008071@acsmt356.oracle.com> MIME-Version: 1.0 Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit 3876 Mattias Jonsson 2012-04-08 WL#4443 - Prune partition locks. Updated with dlenev's review patch chunk 1. modified: sql/opt_explain.cc sql/rpl_info_table_access.cc sql/sp_head.cc sql/sql_acl.cc sql/sql_base.cc sql/sql_base.h sql/sql_delete.cc sql/sql_insert.cc sql/sql_lex.cc sql/sql_lex.h sql/sql_load.cc sql/sql_parse.cc sql/sql_parse.h sql/sql_prepare.cc sql/sql_select.cc sql/sql_union.cc sql/sql_update.cc sql/sql_view.cc 3875 Mattias Jonsson 2012-03-30 WL#4443: Updated some result files. removed debug runs from push. added fix for preventing stored functions to execute during prepare (to avoid using tables before open/lock). modified: mysql-test/collections/mysql-trunk-wl4443.push mysql-test/r/innodb_explain_json_non_select_all.result mysql-test/r/innodb_explain_json_non_select_none.result mysql-test/r/myisam_explain_json_non_select_all.result mysql-test/r/myisam_explain_json_non_select_none.result mysql-test/suite/rpl/r/rpl_parallel_change_master.result mysql-test/t/partition_locking.test sql/item.cc === modified file 'sql/opt_explain.cc' --- a/sql/opt_explain.cc revid:mattias.jonsson@stripped +++ b/sql/opt_explain.cc revid:mattias.jonsson@stripped @@ -21,7 +21,7 @@ #include "sql_partition.h" // for make_used_partitions_str() #include "sql_join_buffer.h" // JOIN_CACHE #include "opt_explain_format.h" -#include "sql_base.h" // open_query_tables, lock_query_tables +#include "sql_base.h" // lock_tables typedef qep_row::extra extra; @@ -1963,17 +1963,23 @@ bool mysql_explain_unit(THD *thd, SELECT unit->fake_select_lex->select_number= UINT_MAX; // just for initialization unit->fake_select_lex->options|= SELECT_DESCRIBE; - if (open_query_tables(thd)) - DBUG_RETURN(true); - res= unit->prepare(thd, result, SELECT_NO_UNLOCK | SELECT_DESCRIBE); if (res) DBUG_RETURN(res); - if (lock_query_tables(thd)) + /* + If tables are not locked at this point, it means that we have delayed + this step until after prepare stage (now), in order to do better + partition pruning. + + We need to lock tables now in order to proceed with the remaning + stages of query optimization. + */ + if (! thd->lex->is_query_tables_locked() && + lock_tables(thd, thd->lex->query_tables, thd->lex->table_count, 0)) DBUG_RETURN(true); - + res= unit->optimize(); if (!res) === modified file 'sql/rpl_info_table_access.cc' --- a/sql/rpl_info_table_access.cc revid:mattias.jonsson@stripped +++ b/sql/rpl_info_table_access.cc revid:mattias.jonsson@stripped @@ -52,6 +52,7 @@ bool Rpl_info_table_access::open_table(T Open_tables_backup* backup) { TABLE_LIST tables; + Query_tables_list query_tables_list_backup; uint flags= (MYSQL_OPEN_IGNORE_GLOBAL_READ_LOCK | MYSQL_LOCK_IGNORE_GLOBAL_READ_ONLY | @@ -71,6 +72,15 @@ bool Rpl_info_table_access::open_table(T mysql_reset_thd_for_next_command(thd); } + /* + We need to use new Open_tables_state in order not to be affected + by LOCK TABLES/prelocked mode. + Also in order not to break execution of current statement we also + have to backup/reset/restore Query_tables_list part of LEX, which + is accessed and updated in the process of opening and locking + tables. + */ + thd->lex->reset_n_backup_query_tables_list(&query_tables_list_backup); thd->reset_n_backup_open_tables_state(backup); tables.init_one_table(dbstr.str, dbstr.length, tbstr.str, tbstr.length, @@ -80,6 +90,7 @@ bool Rpl_info_table_access::open_table(T { close_thread_tables(thd); thd->restore_backup_open_tables_state(backup); + thd->lex->restore_backup_query_tables_list(&query_tables_list_backup); my_error(ER_NO_SUCH_TABLE, MYF(0), dbstr.str, tbstr.str); DBUG_RETURN(TRUE); } @@ -95,12 +106,15 @@ bool Rpl_info_table_access::open_table(T ha_rollback_trans(thd, FALSE); close_thread_tables(thd); thd->restore_backup_open_tables_state(backup); + thd->lex->restore_backup_query_tables_list(&query_tables_list_backup); my_error(ER_COL_COUNT_DOESNT_MATCH_CORRUPTED_V2, MYF(0), tables.table->s->db.str, tables.table->s->table_name.str, max_num_field, tables.table->s->fields); DBUG_RETURN(TRUE); } + thd->lex->restore_backup_query_tables_list(&query_tables_list_backup); + *table= tables.table; tables.table->use_all_columns(); DBUG_RETURN(FALSE); @@ -132,6 +146,8 @@ bool Rpl_info_table_access::close_table( Open_tables_backup *backup, bool error) { + Query_tables_list query_tables_list_backup; + DBUG_ENTER("Rpl_info_table_access::close_table"); if (table) @@ -148,7 +164,14 @@ bool Rpl_info_table_access::close_table( else ha_commit_trans(thd, TRUE); } + /* + In order not to break execution of current statement we have to + backup/reset/restore Query_tables_list part of LEX, which is + accessed and updated in the process of closing tables. + */ + thd->lex->reset_n_backup_query_tables_list(&query_tables_list_backup); close_thread_tables(thd); + thd->lex->restore_backup_query_tables_list(&query_tables_list_backup); thd->restore_backup_open_tables_state(backup); } === modified file 'sql/sp_head.cc' --- a/sql/sp_head.cc revid:mattias.jonsson@stripped +++ b/sql/sp_head.cc revid:mattias.jonsson@stripped @@ -2952,7 +2952,7 @@ sp_lex_keeper::reset_lex_and_exec_core(T UINT_MAX, false)); if (!res) - res= open_query_tables(thd) || lock_query_tables(thd); + res= open_and_lock_tables(thd, m_lex->query_tables, true, 0); if (!res) { === modified file 'sql/sql_acl.cc' --- a/sql/sql_acl.cc revid:mattias.jonsson@stripped +++ b/sql/sql_acl.cc revid:mattias.jonsson@stripped @@ -3556,7 +3556,7 @@ int mysql_table_grant(THD *thd, TABLE_LI class LEX_COLUMN *column; List_iterator column_iter(columns); - if (open_query_tables(thd)) + if (open_normal_and_derived_tables(thd, table_list, 0)) DBUG_RETURN(TRUE); while ((column = column_iter++)) === modified file 'sql/sql_base.cc' --- a/sql/sql_base.cc revid:mattias.jonsson@stripped +++ b/sql/sql_base.cc revid:mattias.jonsson@stripped @@ -1567,8 +1567,13 @@ void close_thread_tables(THD *thd) /* Ensure we are calling ha_reset() for all used tables */ mark_used_tables_as_free_for_reuse(thd, thd->open_tables); - /* Set the tables_state to be able to reopen/reuse the tables */ - thd->lex->tables_state= Query_tables_list::TABLES_STATE_REUSABLE; + /* + Mark this statement as one that has "unlocked" its tables. + For purposes of Query_tables_list::lock_tables_state we treat + any statement which passed through close_thread_tables() as + such. + */ + thd->lex->lock_tables_state= Query_tables_list::LTS_NOT_LOCKED; /* We are under simple LOCK TABLES or we're inside a sub-statement @@ -1611,9 +1616,11 @@ void close_thread_tables(THD *thd) */ (void)thd->binlog_flush_pending_rows_event(TRUE); mysql_unlock_tables(thd, thd->lock); - thd->lex->tables_state= Query_tables_list::TABLES_STATE_UNLOCKED; thd->lock=0; } + + thd->lex->lock_tables_state= Query_tables_list::LTS_NOT_LOCKED; + /* Closing a MERGE child before the parent would be fatal if the other thread tries to abort the MERGE lock in between. @@ -1621,8 +1628,6 @@ void close_thread_tables(THD *thd) if (thd->open_tables) close_open_tables(thd); - thd->lex->tables_state= Query_tables_list::TABLES_STATE_CLOSED; - DBUG_VOID_RETURN; } @@ -5662,101 +5667,6 @@ void open_and_lock_tables_cleanup(THD *t /** - Cleanup failed open or lock tables. - - @param thd Thread context. - @param mdl_savepoint Savepoint for rollback. -*/ - -void open_and_lock_query_tables_cleanup(THD *thd) -{ - if (thd->lex->is_query_tables_opened()) - open_and_lock_tables_cleanup(thd, thd->lex->mdl_open_savepoint); - thd->lex->tables_state= Query_tables_list::TABLES_STATE_CLOSED; -} - - -/** - Open query tables, open derived tables and prepares them. - - @param thd Thread context. - - @return Operation status - @retval false OK - @retval true Error -*/ - -bool open_query_tables(THD *thd) -{ - DBUG_ENTER("open_query_tables"); - - if (thd->lex->is_query_tables_opened()) - { - DBUG_PRINT("info", ("Query tables already opened")); - DBUG_RETURN(false); - } - - - thd->lex->mdl_open_savepoint= thd->mdl_context.mdl_savepoint(); - - /* if prepare, then we must use shared mdl. */ - if (open_tables(thd, - &thd->lex->query_tables, - &thd->lex->tables_lock_count, - (thd->stmt_arena->is_stmt_prepare() - ? MYSQL_OPEN_FORCE_SHARED_MDL : 0))) - DBUG_RETURN(true); - - thd->lex->tables_state= Query_tables_list::TABLES_STATE_OPENED; - - if (mysql_handle_derived(thd->lex, &mysql_derived_prepare)) - { - open_and_lock_query_tables_cleanup(thd); - DBUG_RETURN(true); - } - - thd->lex->tables_state= Query_tables_list::TABLES_STATE_DERIVED_PREPARED; - - DBUG_RETURN(false); -} - - -/** - Lock query tables. - - @param thd Thread context. - - @return Operation status - @retval false OK - @retval true Error -*/ - -bool lock_query_tables(THD *thd) -{ - DBUG_ENTER("lock_query_tables"); - - if (thd->lex->is_query_tables_locked()) - { - DBUG_PRINT("info", ("Query tables already locked")); - DBUG_RETURN(false); - } - - DBUG_ASSERT(thd->lex->tables_state >= - Query_tables_list::TABLES_STATE_OPENED); - - if (lock_tables(thd, - thd->lex->query_tables, - thd->lex->tables_lock_count, 0)) - { - open_and_lock_query_tables_cleanup(thd); - DBUG_RETURN(true); - } - thd->lex->tables_state= Query_tables_list::TABLES_STATE_LOCKED; - DBUG_RETURN(false); -} - - -/** Open all tables in list, locks them and optionally process derived tables. @param thd Thread context. @@ -5808,34 +5718,33 @@ err: } -/* +/** Open all tables in list and process derived tables - SYNOPSIS - open_normal_and_derived_tables - thd - thread handler - tables - list of tables for open - flags - bitmap of flags to modify how the tables will be open: - MYSQL_LOCK_IGNORE_FLUSH - open table even if someone has - done a flush on it. + @param thd thread handler + @param tables list of tables for open + @param flags bitmap of flags to modify how the tables will be open: + MYSQL_LOCK_IGNORE_FLUSH - open table even if someone has + done a flush on it. - RETURN - FALSE - ok - TRUE - error + @retval FALSE - ok + @retval TRUE - error - NOTE + @note This is to be used on prepare stage when you don't read any data from the tables. + + @note + Updates Query_tables_list::table_count as side-effect. */ bool open_normal_and_derived_tables(THD *thd, TABLE_LIST *tables, uint flags) { DML_prelocking_strategy prelocking_strategy; - uint counter; MDL_savepoint mdl_savepoint= thd->mdl_context.mdl_savepoint(); DBUG_ENTER("open_normal_and_derived_tables"); - DBUG_ASSERT(!thd->fill_derived_tables()); - if (open_tables(thd, &tables, &counter, flags, &prelocking_strategy) || + if (open_tables(thd, &tables, &thd->lex->table_count, flags, + &prelocking_strategy) || mysql_handle_derived(thd->lex, &mysql_derived_prepare)) goto end; @@ -5925,8 +5834,23 @@ bool lock_tables(THD *thd, TABLE_LIST *t DBUG_ASSERT(thd->locked_tables_mode <= LTM_LOCK_TABLES || !thd->lex->requires_prelocking()); + /* + lock_tables() should not be called if this statement has + already locked its tables. + */ + DBUG_ASSERT(thd->lex->lock_tables_state == Query_tables_list::LTS_NOT_LOCKED); + if (!tables && !thd->lex->requires_prelocking()) + { + /* + Even though we are not really locking any tables mark this + statement as one that has locked its tables, so we won't + call this function second time for the same execution of + the same statement. + */ + thd->lex->lock_tables_state= Query_tables_list::LTS_LOCKED; DBUG_RETURN(thd->decide_logging_format(tables)); + } /* Check for thd->locked_tables_mode to avoid a redundant @@ -6079,6 +6003,13 @@ bool lock_tables(THD *thd, TABLE_LIST *t } } + /* + Mark the statement as having tables locked. For purposes + of Query_tables_list::lock_tables_state we treat any + statement which passes through lock_tables() as such. + */ + thd->lex->lock_tables_state= Query_tables_list::LTS_LOCKED; + DBUG_RETURN(thd->decide_logging_format(tables)); } @@ -9568,7 +9499,6 @@ open_system_tables_for_read(THD *thd, TA we also have to backup and reset/and then restore part of LEX which is accessed by open_tables() in order to determine if prelocking is needed and what tables should be added for it. - close_system_tables() doesn't require such treatment. */ lex->reset_n_backup_query_tables_list(&query_tables_list_backup); thd->reset_n_backup_open_tables_state(backup); @@ -9607,7 +9537,16 @@ open_system_tables_for_read(THD *thd, TA void close_system_tables(THD *thd, Open_tables_backup *backup) { + Query_tables_list query_tables_list_backup; + + /* + In order not affect execution of current statement we have to + backup/reset/restore Query_tables_list part of LEX, which is + accessed and updated in the process of closing tables. + */ + thd->lex->reset_n_backup_query_tables_list(&query_tables_list_backup); close_thread_tables(thd); + thd->lex->restore_backup_query_tables_list(&query_tables_list_backup); thd->restore_backup_open_tables_state(backup); } === modified file 'sql/sql_base.h' --- a/sql/sql_base.h revid:mattias.jonsson@stripped +++ b/sql/sql_base.h revid:mattias.jonsson@stripped @@ -244,8 +244,6 @@ bool open_tables(THD *thd, TABLE_LIST ** Prelocking_strategy *prelocking_strategy); void open_and_lock_tables_cleanup(THD *thd, const MDL_savepoint &mdl_savepoint); -void open_and_lock_query_tables_cleanup(THD *thd); -bool open_query_tables(THD *thd); /* open_and_lock_tables with optional derived handling */ bool open_and_lock_tables(THD *thd, TABLE_LIST *tables, bool derived, uint flags, @@ -256,7 +254,6 @@ TABLE *open_n_lock_single_table(THD *thd Prelocking_strategy *prelocking_strategy); bool open_normal_and_derived_tables(THD *thd, TABLE_LIST *tables, uint flags); bool lock_tables(THD *thd, TABLE_LIST *tables, uint counter, uint flags); -bool lock_query_tables(THD *thd); void free_io_cache(TABLE *entry); void intern_close_table(TABLE *entry); bool close_thread_table(THD *thd, TABLE **table_ptr); === modified file 'sql/sql_delete.cc' --- a/sql/sql_delete.cc revid:mattias.jonsson@stripped +++ b/sql/sql_delete.cc revid:mattias.jonsson@stripped @@ -71,7 +71,7 @@ bool mysql_delete(THD *thd, TABLE_LIST * THD::enum_binlog_query_type query_type= THD::ROW_QUERY_TYPE; DBUG_ENTER("mysql_delete"); - if (open_query_tables(thd)) + if (open_normal_and_derived_tables(thd, table_list, 0)) DBUG_RETURN(TRUE); if (!(table= table_list->table)) @@ -118,7 +118,7 @@ bool mysql_delete(THD *thd, TABLE_LIST * goto exit_all_parts_pruned_away; #endif - if (lock_query_tables(thd)) + if (lock_tables(thd, table_list, thd->lex->table_count, 0)) DBUG_RETURN(true); const_cond= (!conds || conds->const_item()); @@ -556,19 +556,17 @@ extern "C" int refpos_order_cmp(const vo return file->cmp_ref((const uchar*)a, (const uchar*)b); } -/* - make delete specific preparation and checks after opening tables +/** + Make delete specific preparation and checks after opening tables. - SYNOPSIS - mysql_multi_delete_prepare() - thd thread handler + @param thd Thread context. + @param[out] table_count Number of tables to be deleted from. - RETURN - FALSE OK - TRUE Error + @retval FALST - success. + @retval TRUE - error. */ -int mysql_multi_delete_prepare(THD *thd) +int mysql_multi_delete_prepare(THD *thd, uint *table_count) { LEX *lex= thd->lex; TABLE_LIST *aux_tables= lex->auxiliary_table_list.first; @@ -588,6 +586,7 @@ int mysql_multi_delete_prepare(THD *thd) DELETE_ACL, SELECT_ACL)) DBUG_RETURN(TRUE); + *table_count= 0; /* Multi-delete can't be constructed over-union => we always have @@ -599,6 +598,8 @@ int mysql_multi_delete_prepare(THD *thd) target_tbl; target_tbl= target_tbl->next_local) { + ++(*table_count); + if (!(target_tbl->table= target_tbl->correspondent_table->table)) { DBUG_ASSERT(target_tbl->correspondent_table->view && === modified file 'sql/sql_insert.cc' --- a/sql/sql_insert.cc revid:mattias.jonsson@stripped +++ b/sql/sql_insert.cc revid:mattias.jonsson@stripped @@ -743,7 +743,7 @@ bool mysql_insert(THD *thd,TABLE_LIST *t } else { - if (open_query_tables(thd)) + if (open_normal_and_derived_tables(thd, table_list, 0)) DBUG_RETURN(true); } @@ -912,7 +912,7 @@ bool mysql_insert(THD *thd,TABLE_LIST *t /* Lock the tables now if not delayed/already locked. */ if (!is_locked && - lock_query_tables(thd)) + lock_tables(thd, table_list, thd->lex->table_count, 0)) DBUG_RETURN(true); /* === modified file 'sql/sql_lex.cc' --- a/sql/sql_lex.cc revid:mattias.jonsson@stripped +++ b/sql/sql_lex.cc revid:mattias.jonsson@stripped @@ -2778,8 +2778,8 @@ void Query_tables_list::reset_query_tabl sroutines_list_own_elements= 0; binlog_stmt_flags= 0; stmt_accessed_table_flag= 0; - tables_state= TABLES_STATE_NONE; - tables_lock_count= 0; + lock_tables_state= LTS_NOT_LOCKED; + table_count= 0; } === modified file 'sql/sql_lex.h' --- a/sql/sql_lex.h revid:mattias.jonsson@stripped +++ b/sql/sql_lex.h revid:mattias.jonsson@stripped @@ -46,7 +46,6 @@ class sys_var; class Item_func_match; class File_parser; class Key_part_spec; -class MDL_savepoint; #ifdef MYSQL_SERVER /* @@ -1069,50 +1068,36 @@ public: uint sroutines_list_own_elements; /** - Enum to track the state of the tables used by the query. - */ - enum enum_tables_state { - /** The tables is not yet opened. */ - TABLES_STATE_NONE = 0, - /** - The tables are already opened and locked in thd->open_tables, - but not marked as used. - */ - TABLES_STATE_REUSABLE, - /** Base tables are opened. */ - TABLES_STATE_OPENED, - /** Derived tables are prepared. */ - TABLES_STATE_DERIVED_PREPARED, - /** Base tables are locked. */ - TABLES_STATE_LOCKED, - /** Derived tables are optimized. */ - TABLES_STATE_DERIVED_OPTIMIZED, - /** Derived tables are created for materialization. */ - TABLES_STATE_DERIVED_CREATED, - /** Derived tables are materialized. */ - TABLES_STATE_DERIVED_MATERIALIZED, - /** Base tables are unlocked. */ - TABLES_STATE_UNLOCKED, - /** Base tables are closed or marked for reuse. */ - TABLES_STATE_CLOSED + Locking state of tables in this particular statement. + + If we under LOCK TABLES or in prelocked mode we consider tables + for the statement to be "locked" if there was a call to lock_tables() + (which called handler::start_stmt()) for tables of this statement + and there was no matching close_thread_tables() call. + + As result this state may differ significantly from one represented + by Open_tables_state::lock/locked_tables_mode more, which are always + "on" under LOCK TABLES or in prelocked mode. + */ + enum enum_lock_tables_state { + LTS_NOT_LOCKED = 0, + LTS_LOCKED }; - enum_tables_state tables_state; - bool is_query_tables_opened() - { - if (tables_state >= TABLES_STATE_OPENED && - tables_state < TABLES_STATE_CLOSED) - return true; - return false; - } + enum_lock_tables_state lock_tables_state; bool is_query_tables_locked() { - if (tables_state >= TABLES_STATE_LOCKED && - tables_state < TABLES_STATE_UNLOCKED) - return true; - return false; + return (lock_tables_state == LTS_LOCKED); } - uint tables_lock_count; - MDL_savepoint mdl_open_savepoint; + + /** + Number of tables which were open by open_tables() and to be locked + by lock_tables(). + Note that we set this member only in some cases, when this value + needs to be passed from open_tables() to lock_tables() which are + separated by some amount of code. + */ + uint table_count; + /* These constructor and destructor serve for creation/destruction of Query_tables_list instances which are used as backup storage. === modified file 'sql/sql_load.cc' --- a/sql/sql_load.cc revid:mattias.jonsson@stripped +++ b/sql/sql_load.cc revid:mattias.jonsson@stripped @@ -228,9 +228,8 @@ int mysql_load(THD *thd,sql_exchange *ex ER(WARN_NON_ASCII_SEPARATOR_NOT_IMPLEMENTED)); } - if (open_query_tables(thd) || lock_query_tables(thd)) + if (open_and_lock_tables(thd, table_list, TRUE, 0)) DBUG_RETURN(TRUE); - if (setup_tables_and_check_access(thd, &thd->lex->select_lex.context, &thd->lex->select_lex.top_join_list, table_list, === modified file 'sql/sql_parse.cc' --- a/sql/sql_parse.cc revid:mattias.jonsson@stripped +++ b/sql/sql_parse.cc revid:mattias.jonsson@stripped @@ -2461,8 +2461,7 @@ case SQLCOM_PREPARE: } case SQLCOM_DO: if (check_table_access(thd, SELECT_ACL, all_tables, FALSE, UINT_MAX, FALSE) - || open_query_tables(thd) - || lock_query_tables(thd)) + || open_and_lock_tables(thd, all_tables, TRUE, 0)) goto error; res= mysql_do(thd, *lex->insert_list); @@ -2777,7 +2776,7 @@ case SQLCOM_PREPARE: goto end_with_restore_list; } - if (!(res= open_query_tables(thd))) + if (!(res= open_normal_and_derived_tables(thd, all_tables, 0))) { /* The table already exists */ if (create_table->table || create_table->view) @@ -3291,7 +3290,7 @@ end_with_restore_list: unit->set_limit(select_lex); - if (!(res= open_query_tables(thd))) + if (!(res= open_normal_and_derived_tables(thd, all_tables, 0))) { MYSQL_INSERT_SELECT_START(thd->query()); /* Skip first table, which is the table we are inserting in */ @@ -3367,6 +3366,7 @@ end_with_restore_list: { DBUG_ASSERT(first_table == all_tables && first_table != 0); TABLE_LIST *aux_tables= thd->lex->auxiliary_table_list.first; + uint del_table_count; multi_delete *del_result; if ((res= multi_delete_precheck(thd, all_tables))) @@ -3379,18 +3379,18 @@ end_with_restore_list: goto error; THD_STAGE_INFO(thd, stage_init); - if ((res= open_query_tables(thd))) + if ((res= open_normal_and_derived_tables(thd, all_tables, 0))) break; MYSQL_MULTI_DELETE_START(thd->query()); - if ((res= mysql_multi_delete_prepare(thd))) + if ((res= mysql_multi_delete_prepare(thd, &del_table_count))) { MYSQL_MULTI_DELETE_DONE(1, 0); goto error; } if (!thd->is_fatal_error && - (del_result= new multi_delete(aux_tables, lex->tables_lock_count))) + (del_result= new multi_delete(aux_tables, del_table_count))) { if (lex->describe) res= explain_multi_table_modification(thd, del_result); @@ -3506,9 +3506,8 @@ end_with_restore_list: { List *lex_var_list= &lex->var_list; - if (check_table_access(thd, SELECT_ACL, all_tables, FALSE, UINT_MAX, FALSE) - || open_query_tables(thd) - || lock_query_tables(thd)) + if ((check_table_access(thd, SELECT_ACL, all_tables, FALSE, UINT_MAX, FALSE) + || open_and_lock_tables(thd, all_tables, TRUE, 0))) goto error; if (!(res= sql_set_variables(thd, lex_var_list))) { @@ -4293,8 +4292,7 @@ create_sp_error: */ if (check_table_access(thd, SELECT_ACL, all_tables, FALSE, UINT_MAX, FALSE) || - open_query_tables(thd) || - lock_query_tables(thd)) + open_and_lock_tables(thd, all_tables, TRUE, 0)) goto error; /* @@ -4865,7 +4863,7 @@ static bool execute_sqlcom_select(THD *t param->select_limit= new Item_int((ulonglong) thd->variables.select_limit); } - if (!(res= open_query_tables(thd))) + if (!(res= open_normal_and_derived_tables(thd, all_tables, 0))) { if (lex->describe) { === modified file 'sql/sql_parse.h' --- a/sql/sql_parse.h revid:mattias.jonsson@stripped +++ b/sql/sql_parse.h revid:mattias.jonsson@stripped @@ -42,7 +42,7 @@ bool select_precheck(THD *thd, LEX *lex, bool multi_update_precheck(THD *thd, TABLE_LIST *tables); bool multi_delete_precheck(THD *thd, TABLE_LIST *tables); int mysql_multi_update_prepare(THD *thd); -int mysql_multi_delete_prepare(THD *thd); +int mysql_multi_delete_prepare(THD *thd, uint *table_count); bool mysql_insert_select_prepare(THD *thd); bool update_precheck(THD *thd, TABLE_LIST *tables); bool delete_precheck(THD *thd, TABLE_LIST *tables); === modified file 'sql/sql_prepare.cc' --- a/sql/sql_prepare.cc revid:mattias.jonsson@stripped +++ b/sql/sql_prepare.cc revid:mattias.jonsson@stripped @@ -90,7 +90,7 @@ When one supplies long data for a placeh #include "set_var.h" #include "sql_prepare.h" #include "sql_parse.h" // insert_precheck, update_precheck, delete_precheck -#include "sql_base.h" // open_query_tables +#include "sql_base.h" // open_normal_and_derived_tables #include "sql_cache.h" // query_cache_* #include "sql_view.h" // create_view_precheck #include "sql_delete.h" // mysql_prepare_delete @@ -1269,7 +1269,8 @@ static bool mysql_test_insert(Prepared_s If we would use locks, then we have to ensure we are not using TL_WRITE_DELAYED as having two such locks can cause table corruption. */ - if (open_query_tables(thd)) + if (open_normal_and_derived_tables(thd, table_list, + MYSQL_OPEN_FORCE_SHARED_MDL)) goto error; if ((values= its++)) @@ -1349,7 +1350,8 @@ static int mysql_test_update(Prepared_st DBUG_ENTER("mysql_test_update"); if (update_precheck(thd, table_list) || - open_query_tables(thd)) + open_normal_and_derived_tables(thd, table_list, + MYSQL_OPEN_FORCE_SHARED_MDL)) goto error; if (table_list->multitable_view) @@ -1426,7 +1428,8 @@ static bool mysql_test_delete(Prepared_s DBUG_ASSERT(stmt->is_stmt_prepare()); if (delete_precheck(thd, table_list) || - open_query_tables(thd)) + open_normal_and_derived_tables(thd, table_list, + MYSQL_OPEN_FORCE_SHARED_MDL)) goto error; if (!table_list->table) @@ -1478,7 +1481,7 @@ static int mysql_test_select(Prepared_st goto error; } - if (open_query_tables(thd)) + if (open_normal_and_derived_tables(thd, tables, MYSQL_OPEN_FORCE_SHARED_MDL)) goto error; thd->lex->used_tables= 0; // Updated by setup_fields @@ -1540,7 +1543,7 @@ static bool mysql_test_do_fields(Prepare UINT_MAX, FALSE)) DBUG_RETURN(TRUE); - if (open_query_tables(thd)) + if (open_normal_and_derived_tables(thd, tables, MYSQL_OPEN_FORCE_SHARED_MDL)) DBUG_RETURN(TRUE); DBUG_RETURN(setup_fields(thd, Ref_ptr_array(), *values, MARK_COLUMNS_NONE, 0, 0)); @@ -1572,7 +1575,7 @@ static bool mysql_test_set_fields(Prepar if ((tables && check_table_access(thd, SELECT_ACL, tables, FALSE, UINT_MAX, FALSE)) || - open_query_tables(thd)) + open_normal_and_derived_tables(thd, tables, MYSQL_OPEN_FORCE_SHARED_MDL)) goto error; while ((var= it++)) @@ -1609,7 +1612,7 @@ static bool mysql_test_call_fields(Prepa if ((tables && check_table_access(thd, SELECT_ACL, tables, FALSE, UINT_MAX, FALSE)) || - open_query_tables(thd)) + open_normal_and_derived_tables(thd, tables, MYSQL_OPEN_FORCE_SHARED_MDL)) goto err; while ((item= it++)) @@ -1689,11 +1692,12 @@ select_like_stmt_test_with_open(Prepared /* We should not call LEX::unit.cleanup() after this - open_query_tables() call because we don't allow + open_normal_and_derived_tables() call because we don't allow prepared EXPLAIN yet so derived tables will clean up after themself. */ - if (open_query_tables(stmt->thd)) + if (open_normal_and_derived_tables(stmt->thd, tables, + MYSQL_OPEN_FORCE_SHARED_MDL)) DBUG_RETURN(TRUE); DBUG_RETURN(select_like_stmt_test(stmt, specific_prepare, @@ -1734,7 +1738,8 @@ static bool mysql_test_create_table(Prep if (!(lex->create_info.options & HA_LEX_CREATE_TMP_TABLE)) create_table->open_type= OT_BASE_ONLY; - if (open_query_tables(stmt->thd)) + if (open_normal_and_derived_tables(stmt->thd, lex->query_tables, + MYSQL_OPEN_FORCE_SHARED_MDL)) DBUG_RETURN(TRUE); select_lex->context.resolve_in_select_list= TRUE; @@ -1753,7 +1758,8 @@ static bool mysql_test_create_table(Prep we validate metadata of all CREATE TABLE statements, which keeps metadata validation code simple. */ - if (open_query_tables(stmt->thd)) + if (open_normal_and_derived_tables(stmt->thd, lex->query_tables, + MYSQL_OPEN_FORCE_SHARED_MDL)) DBUG_RETURN(TRUE); } @@ -1794,7 +1800,7 @@ static bool mysql_test_create_view(Prepa if (open_temporary_tables(thd, tables)) goto err; - if (open_query_tables(thd)) + if (open_normal_and_derived_tables(thd, tables, MYSQL_OPEN_FORCE_SHARED_MDL)) goto err; lex->context_analysis_only|= CONTEXT_ANALYSIS_ONLY_VIEW; @@ -1834,6 +1840,18 @@ static bool mysql_test_multiupdate(Prepa /** + Wrapper for mysql_multi_delete_prepare() function which makes + it compatible with select_like_stmt_test_with_open(). +*/ + +static int mysql_multi_delete_prepare_tester(THD *thd) +{ + uint table_count; + return mysql_multi_delete_prepare(thd, &table_count); +} + + +/** Validate and prepare for execution a multi delete statement. @param stmt prepared statement @@ -1857,7 +1875,7 @@ static bool mysql_test_multidelete(Prepa if (multi_delete_precheck(stmt->thd, tables) || select_like_stmt_test_with_open(stmt, tables, - &mysql_multi_delete_prepare, + &mysql_multi_delete_prepare_tester, OPTION_SETUP_TABLES_DONE)) goto error; if (!tables->table) @@ -1874,13 +1892,13 @@ error: /** Wrapper for mysql_insert_select_prepare, to make change of local tables - after open_query_tables() call. + after open_normal_and_derived_tables() call. @param thd thread handle @note We need to remove the first local table after - open_query_tables(), because mysql_handle_derived + open_normal_and_derived_tables(), because mysql_handle_derived uses local tables lists. */ === modified file 'sql/sql_select.cc' --- a/sql/sql_select.cc revid:mattias.jonsson@stripped +++ b/sql/sql_select.cc revid:mattias.jonsson@stripped @@ -1166,16 +1166,12 @@ mysql_select(THD *thd, SELECT_LEX *select_lex) { bool free_join= 1; - bool store_in_query_cache= false; uint og_num= 0; ORDER *first_order= NULL; ORDER *first_group= NULL; JOIN *join= NULL; DBUG_ENTER("mysql_select"); - if (open_query_tables(thd)) - DBUG_RETURN(true); - if (order) { og_num= order->elements; @@ -1187,10 +1183,6 @@ mysql_select(THD *thd, first_group= group->first; } - /* Only register the query if it was opened above. */ - if (thd->lex->tables_state < Query_tables_list::TABLES_STATE_LOCKED) - store_in_query_cache= true; - if (mysql_prepare_select(thd, tables, wild_num, fields, conds, og_num, first_order, first_group, having, proc_param, select_options, result, unit, @@ -1204,23 +1196,36 @@ mysql_select(THD *thd, DBUG_RETURN(true); } - if (lock_query_tables(thd)) + if (! thd->lex->is_query_tables_locked()) { - if (free_join) + /* + If tables are not locked at this point, it means that we have delayed + this step until after prepare stage (i.e. this moment). This allows to + do better partition pruning and avoid locking unused partitions. + As a consequence, in such a case, prepare stage can rely only on + metadata about tables used and not data from them. + We need to lock tables now in order to proceed with the remaning + stages of query optimization and execution. + */ + if (lock_tables(thd, thd->lex->query_tables, thd->lex->table_count, 0)) { - thd_proc_info(thd, "end"); - (void) select_lex->cleanup(); + if (free_join) + { + thd_proc_info(thd, "end"); + (void) select_lex->cleanup(); + } + DBUG_RETURN(true); } - DBUG_RETURN(true); - } - /* - Tables must be locked before storing the query in the query cache. - Transactional engines must been signalled that the statement started, - which external_lock signals. - */ - if (store_in_query_cache) + /* + Only register query in cache if it tables were locked above. + + Tables must be locked before storing the query in the query cache. + Transactional engines must been signalled that the statement started, + which external_lock signals. + */ query_cache_store_query(thd, thd->lex->query_tables); + } if (mysql_execute_select(thd, select_lex, free_join, join)) DBUG_RETURN(true); === modified file 'sql/sql_union.cc' --- a/sql/sql_union.cc revid:mattias.jonsson@stripped +++ b/sql/sql_union.cc revid:mattias.jonsson@stripped @@ -34,31 +34,36 @@ bool mysql_union(THD *thd, LEX *lex, sel SELECT_LEX_UNIT *unit, ulong setup_tables_done_option) { bool res; - bool store_in_query_cache= false; DBUG_ENTER("mysql_union"); - if (open_query_tables(thd)) - DBUG_RETURN(true); - - /* Only register the query if it was opened above */ - if (thd->lex->tables_state < Query_tables_list::TABLES_STATE_LOCKED) - store_in_query_cache= true; - res= unit->prepare(thd, result, SELECT_NO_UNLOCK | setup_tables_done_option); if (res) goto err; - if (lock_query_tables(thd)) - goto err; + if (! thd->lex->is_query_tables_locked()) + { + /* + If tables are not locked at this point, it means that we have delayed + this step until after prepare stage (i.e. this moment). This allows to + do better partition pruning and avoid locking unused partitions. + As a consequence, in such a case, prepare stage can rely only on + metadata about tables used and not data from them. + We need to lock tables now in order to proceed with the remaning + stages of query optimization and execution. + */ + if (lock_tables(thd, lex->query_tables, lex->table_count, 0)) + goto err; - /* - Tables must be locked before storing the query in the query cache. - Transactional engines must been signalled that the statement started, - which external_lock signals. - */ - if (store_in_query_cache) + /* + Only register query in cache if it tables were locked above. + + Tables must be locked before storing the query in the query cache. + Transactional engines must been signalled that the statement started, + which external_lock signals. + */ query_cache_store_query(thd, thd->lex->query_tables); + } res= unit->optimize() || unit->exec(); res|= unit->cleanup(); === modified file 'sql/sql_update.cc' --- a/sql/sql_update.cc revid:mattias.jonsson@stripped +++ b/sql/sql_update.cc revid:mattias.jonsson@stripped @@ -288,7 +288,7 @@ int mysql_update(THD *thd, DBUG_ENTER("mysql_update"); - if (open_query_tables(thd)) + if (open_normal_and_derived_tables(thd, table_list, 0)) DBUG_RETURN(1); if (table_list->multitable_view) @@ -438,7 +438,7 @@ int mysql_update(THD *thd, bitmap_copy(&table->part_info->lock_partitions, &lock_partitions); } #endif - if (lock_query_tables(thd)) + if (lock_tables(thd, table_list, thd->lex->table_count, 0)) DBUG_RETURN(1); /* Update the table->file->stats.records number */ @@ -1294,7 +1294,10 @@ int mysql_multi_update_prepare(THD *thd) keep prepare of multi-UPDATE compatible with concurrent LOCK TABLES WRITE and global read lock. */ - if (original_multiupdate && open_query_tables(thd)) + if (original_multiupdate && + open_normal_and_derived_tables(thd, table_list, + (thd->stmt_arena->is_stmt_prepare() ? + MYSQL_OPEN_FORCE_SHARED_MDL : 0))) DBUG_RETURN(TRUE); /* setup_tables() need for VIEWs. JOIN::prepare() will call setup_tables() === modified file 'sql/sql_view.cc' --- a/sql/sql_view.cc revid:mattias.jonsson@stripped +++ b/sql/sql_view.cc revid:mattias.jonsson@stripped @@ -449,7 +449,7 @@ bool mysql_create_view(THD *thd, TABLE_L } /* Not required to lock any tables. */ - if (open_query_tables(thd)) + if (open_normal_and_derived_tables(thd, lex->query_tables, 0)) { view= lex->unlink_first_table(&link_to_local); res= TRUE; No bundle (reason: useless for push emails).