diff --git a/sql/opt_qpf.cc b/sql/opt_qpf.cc index b3d15a74984..819c0e6cc22 100644 --- a/sql/opt_qpf.cc +++ b/sql/opt_qpf.cc @@ -74,7 +74,7 @@ int QPF_query::print_explain(select_result_sink *output, { if (upd_del_plan) { - upd_del_plan->print_explain(output, explain_flags); + upd_del_plan->print_explain(this, output, explain_flags); return 0; } else @@ -196,12 +196,20 @@ int QPF_union::print_explain(QPF_query *query, select_result_sink *output, if (output->send_data(item_list)) return 1; + return print_explain_for_children(query, output, explain_flags); +} + + +int QPF_node::print_explain_for_children(QPF_query *query, + select_result_sink *output, + uint8 explain_flags) +{ for (int i= 0; i < (int) children.elements(); i++) { QPF_node *node= query->get_node(children.at(i)); - node->print_explain(query, output, explain_flags); + if (node->print_explain(query, output, explain_flags)) + return 1; } - return 0; } @@ -263,12 +271,7 @@ int QPF_select::print_explain(QPF_query *query, select_result_sink *output, } } - for (int i= 0; i < (int) children.elements(); i++) - { - QPF_node *node= query->get_node(children.at(i)); - node->print_explain(query, output, explain_flags); - } - return 0; + return print_explain_for_children(query, output, explain_flags); } @@ -482,3 +485,78 @@ void QPF_table_access::append_tag_name(String *str, enum Extra_tag tag) } +int QPF_delete::print_explain(QPF_query *query, select_result_sink *output, + uint8 explain_flags) +{ + if (deleting_all_rows) + { + const char *msg= "Deleting all rows"; + int res= print_explain_message_line(output, explain_flags, + 1 /*select number*/, + "SIMPLE", msg); + return res; + + } + else + { + return QPF_update::print_explain(query, output, explain_flags); + } +} + + +int QPF_update::print_explain(QPF_query *query, select_result_sink *output, + uint8 explain_flags) +{ + StringBuffer<64> extra_str; + if (impossible_where) + { + const char *msg= "Impossible where"; + int res= print_explain_message_line(output, explain_flags, + 1 /*select number*/, + "SIMPLE", msg); + return res; + } + + if (using_where) + extra_str.append(STRING_WITH_LEN("Using where")); + + if (mrr_type.length() != 0) + { + if (extra_str.length() !=0) + extra_str.append(STRING_WITH_LEN("; ")); + extra_str.append(mrr_type); + } + + if (using_filesort) + { + if (extra_str.length() !=0) + extra_str.append(STRING_WITH_LEN("; ")); + extra_str.append(STRING_WITH_LEN("Using filesort")); + } + + /* + Single-table DELETE commands do not do "Using temporary". + "Using index condition" is also not possible (which is an unjustified limitation) + */ + + print_explain_row(output, explain_flags, + 1, /* id */ + "SIMPLE", + table_name.c_ptr(), + // partitions, + jtype, + possible_keys_line.length()? possible_keys_line.c_ptr(): NULL, + key_str.length()? key_str.c_ptr() : NULL, + key_len_str.length() ? key_len_str.c_ptr() : NULL, + NULL, /* 'ref' is always NULL in single-table EXPLAIN DELETE */ + rows, + extra_str.c_ptr()); + + return print_explain_for_children(query, output, explain_flags); +} + +void delete_qpf_query(QPF_query * query) +{ + delete query; +} + diff --git a/sql/opt_qpf.h b/sql/opt_qpf.h index d7e4caa1c97..6e0bb40c576 100644 --- a/sql/opt_qpf.h +++ b/sql/opt_qpf.h @@ -18,7 +18,7 @@ class QPF_query; class QPF_node : public Sql_alloc { public: - enum qpf_node_type {QPF_UNION, QPF_SELECT}; + enum qpf_node_type {QPF_UNION, QPF_SELECT, QPF_UPDATE, QPF_DELETE }; virtual enum qpf_node_type get_type()= 0; @@ -36,7 +36,9 @@ public: virtual int print_explain(QPF_query *query, select_result_sink *output, uint8 explain_flags)=0; - + + int print_explain_for_children(QPF_query *query, select_result_sink *output, + uint8 explain_flags); virtual ~QPF_node(){} }; @@ -155,9 +157,42 @@ public: bool using_filesort; }; +class QPF_delete; + /* - Query Plan Footprint for a query (i.e. a statement) + Query Plan Footprint (QPF) for a query (i.e. a statement). + + This should be able to survive when the query plan was deleted. Currently, + we do not intend for it survive until after query's MEM_ROOT is freed. It + does surivive freeing of query's items. + + For reference, the process of post-query cleanup is as follows: + + >dispatch_command + | >mysql_parse + | | ... + | | lex_end() + | | ... + | | >THD::cleanup_after_query + | | | ... + | | | free_items() + | | | ... + | | dispatch_command + + That is, the order of actions is: + - free query's Items + - write to slow query log + - free query's MEM_ROOT + */ class QPF_query : public Sql_alloc @@ -174,8 +209,8 @@ public: /* This will return a select (even if there is a union with this id) */ QPF_select *get_select(uint select_id); - /* Delete_plan inherits from Update_plan */ - Update_plan *upd_del_plan; + /* QPF_delete inherits from QPF_update */ + QPF_update *upd_del_plan; /* Produce a tabular EXPLAIN output */ int print_explain(select_result_sink *output, uint8 explain_flags); @@ -304,5 +339,50 @@ private: void append_tag_name(String *str, enum Extra_tag tag); }; -// TODO: should Update_plan inherit from QPF_table_access? + +/* + Query Plan Footprint for an UPDATE statement +*/ + +class QPF_update : public QPF_node +{ +public: + virtual enum qpf_node_type get_type() { return QPF_UPDATE; } + virtual int get_select_id() { return 1; /* always root */ } + + bool impossible_where; + StringBuffer<64> table_name; + + enum join_type jtype; + StringBuffer<128> possible_keys_line; + StringBuffer<128> key_str; + StringBuffer<128> key_len_str; + StringBuffer<64> mrr_type; + + bool using_where; + ha_rows rows; + + bool using_filesort; + + virtual int print_explain(QPF_query *query, select_result_sink *output, + uint8 explain_flags); +}; + + +/* + Query Plan Footprint for a DELETE statement +*/ + +class QPF_delete: public QPF_update +{ +public: + bool deleting_all_rows; + + virtual enum qpf_node_type get_type() { return QPF_DELETE; } + virtual int get_select_id() { return 1; /* always root */ } + + virtual int print_explain(QPF_query *query, select_result_sink *output, + uint8 explain_flags); +}; + diff --git a/sql/sp_head.cc b/sql/sp_head.cc index f0a87673857..3cc3a3893bb 100644 --- a/sql/sp_head.cc +++ b/sql/sp_head.cc @@ -3006,6 +3006,9 @@ sp_lex_keeper::reset_lex_and_exec_core(THD *thd, uint *nextp, else if (! thd->in_sub_stmt) thd->mdl_context.release_statement_locks(); } + + delete_qpf_query(m_lex->query_plan_footprint); + m_lex->query_plan_footprint= NULL; if (m_lex->query_tables_own_last) { diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc index f6719d3a91b..602831829d7 100644 --- a/sql/sql_delete.cc +++ b/sql/sql_delete.cc @@ -51,25 +51,44 @@ invoked on a running DELETE statement. */ -int Delete_plan::print_explain(select_result_sink *output, uint8 explain_flags) +void Delete_plan::save_query_plan_footprint(QPF_query *query) { + QPF_delete* qpf= new QPF_delete; + if (deleting_all_rows) { - const char *msg= "Deleting all rows"; - if (print_explain_message_line(output, explain_flags, 1/*select number*/, - "SIMPLE", msg)) - { - return 1; - } - return 0; + qpf->deleting_all_rows= true; } - return Update_plan::print_explain(output, explain_flags); + else + { + Update_plan::save_query_plan_footprint_intern(qpf); + } + + query->upd_del_plan= qpf; } -void Update_plan::save_query_plan_footprint() -{ - select_lex->set_explain_type(TRUE); +void Update_plan::save_query_plan_footprint(QPF_query *query) +{ + QPF_update* qpf= new QPF_update; + save_query_plan_footprint_intern(qpf); + query->upd_del_plan= qpf; +} + + +void Update_plan::save_query_plan_footprint_intern(QPF_update *qpf) +{ + qpf->table_name.append(table->pos_in_table_list->alias); + if (impossible_where) + { + qpf->impossible_where= true; + return; + } + + // TODO: do we need the following: select_type + //select_lex->set_explain_type(TRUE); + + /* Set jtype */ if (select && select->quick) { int quick_type= select->quick->get_type(); @@ -77,106 +96,46 @@ void Update_plan::save_query_plan_footprint() (quick_type == QUICK_SELECT_I::QS_TYPE_INDEX_INTERSECT) || (quick_type == QUICK_SELECT_I::QS_TYPE_ROR_INTERSECT) || (quick_type == QUICK_SELECT_I::QS_TYPE_ROR_UNION)) - jtype= JT_INDEX_MERGE; + qpf->jtype= JT_INDEX_MERGE; else - jtype= JT_RANGE; + qpf->jtype= JT_RANGE; } else { if (index == MAX_KEY) - jtype= JT_ALL; + qpf->jtype= JT_ALL; else - jtype= JT_NEXT; + qpf->jtype= JT_NEXT; } - using_where= test(select && select->cond); + qpf->using_where= test(select && select->cond); + qpf->using_filesort= using_filesort; + //using_filesort is already set - make_possible_keys_line(table, possible_keys, &possible_keys_line); + make_possible_keys_line(table, possible_keys, &qpf->possible_keys_line); /* Calculate key_len */ if (select && select->quick) { - select->quick->add_keys_and_lengths(&key_str, &key_len_str); + select->quick->add_keys_and_lengths(&qpf->key_str, &qpf->key_len_str); } else { if (index != MAX_KEY) { - key_str.append(table->key_info[index].name); + qpf->key_str.append(table->key_info[index].name); } // key_len stays NULL } + qpf->rows= select ? select->records : table_rows; if (select && select->quick && select->quick->get_type() == QUICK_SELECT_I::QS_TYPE_RANGE) { - explain_append_mrr_info((QUICK_RANGE_SELECT*)select->quick, &mrr_type); + explain_append_mrr_info((QUICK_RANGE_SELECT*)select->quick, &qpf->mrr_type); } } -int Update_plan::print_explain(select_result_sink *output, uint8 explain_flags) -{ - StringBuffer<64> extra_str; - if (impossible_where) - { - const char *msg= "Impossible where"; - if (print_explain_message_line(output, explain_flags, 1/*select number*/, - "SIMPLE", msg)) - { - return 1; - } - return 0; - } - - if (using_where) - extra_str.append(STRING_WITH_LEN("Using where")); - - if (mrr_type.length() != 0) - { - if (extra_str.length() !=0) - extra_str.append(STRING_WITH_LEN("; ")); - extra_str.append(mrr_type); - } - - if (using_filesort) - { - if (extra_str.length() !=0) - extra_str.append(STRING_WITH_LEN("; ")); - extra_str.append(STRING_WITH_LEN("Using filesort")); - } - - /* - Single-table DELETE commands do not do "Using temporary". - "Using index condition" is also not possible (which is an unjustified limitation) - */ - - print_explain_row(output, explain_flags, - 1, /* id */ - select_lex->type, - table->pos_in_table_list->alias, - // partitions, - (enum join_type) jtype, - possible_keys_line.length()? possible_keys_line.c_ptr(): NULL, - key_str.length()? key_str.c_ptr() : NULL, - key_len_str.length() ? key_len_str.c_ptr() : NULL, - NULL, /* 'ref' is always NULL in single-table EXPLAIN DELETE */ - select ? select->records : table_rows, - extra_str.c_ptr()); - - /* - psergey-todo: handle all this through saving QPF. - - for (SELECT_LEX_UNIT *unit= select_lex->first_inner_unit(); - unit; - unit= unit->next_unit()) - { - if (unit->print_explain(output, explain_flags, printed_anything)) - return 1; - } - */ - return 0; -} - /** Implement DELETE SQL word. @@ -205,10 +164,10 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, SELECT_LEX *select_lex= &thd->lex->select_lex; killed_state killed_status= NOT_KILLED; THD::enum_binlog_query_type query_type= THD::ROW_QUERY_TYPE; - - Delete_plan *query_plan = new Delete_plan; - query_plan->index= MAX_KEY; - query_plan->using_filesort= FALSE; + + Delete_plan query_plan; + query_plan.index= MAX_KEY; + query_plan.using_filesort= FALSE; DBUG_ENTER("mysql_delete"); if (open_and_lock_tables(thd, table_list, TRUE, 0)) @@ -232,8 +191,8 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, } thd_proc_info(thd, "init"); table->map=1; - query_plan->select_lex= &thd->lex->select_lex; - query_plan->table= table; + query_plan.select_lex= &thd->lex->select_lex; + query_plan.table= table; if (mysql_prepare_delete(thd, table_list, &conds)) DBUG_RETURN(TRUE); @@ -308,7 +267,7 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, ha_rows const maybe_deleted= table->file->stats.records; DBUG_PRINT("debug", ("Trying to use delete_all_rows()")); - query_plan->set_delete_all_rows(maybe_deleted); + query_plan.set_delete_all_rows(maybe_deleted); if (thd->lex->describe) goto exit_without_my_ok; @@ -338,7 +297,7 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, if (result == Item::COND_FALSE) // Impossible where { limit= 0; - query_plan->set_impossible_where(); + query_plan.set_impossible_where(); if (thd->lex->describe) goto exit_without_my_ok; } @@ -366,7 +325,7 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, DBUG_RETURN(TRUE); if ((select && select->check_quick(thd, safe_update, limit)) || !limit) { - query_plan->set_impossible_where(); + query_plan.set_impossible_where(); if (thd->lex->describe) goto exit_without_my_ok; @@ -407,22 +366,19 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, if (select && select->quick && select->quick->unique_key_range()) { // Single row select (always "ordered") - query_plan->using_filesort= FALSE; - query_plan->index= MAX_KEY; + query_plan.using_filesort= FALSE; + query_plan.index= MAX_KEY; } else - query_plan->index= get_index_for_order(order, table, select, limit, - &query_plan->using_filesort, - &reverse); + query_plan.index= get_index_for_order(order, table, select, limit, + &query_plan.using_filesort, + &reverse); } - query_plan->select= select; - query_plan->possible_keys= table->quick_keys; - query_plan->table_rows= table->file->stats.records; + query_plan.select= select; + query_plan.possible_keys= table->quick_keys; + query_plan.table_rows= table->file->stats.records; - thd->lex->query_plan_footprint= new QPF_query; - thd->lex->query_plan_footprint->upd_del_plan= query_plan; - /* Ok, we have generated a query plan for the DELETE. - if we're running EXPLAIN DELETE, goto produce explain output @@ -431,12 +387,13 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, if (thd->lex->describe) goto exit_without_my_ok; - query_plan->save_query_plan_footprint(); + query_plan.save_query_plan_footprint(thd->lex->query_plan_footprint); thd->apc_target.enable(); + DBUG_EXECUTE_IF("show_explain_probe_delete_exec_start", dbug_serve_apcs(thd, 1);); - if (query_plan->using_filesort) + if (query_plan.using_filesort) { ha_rows examined_rows; ha_rows found_rows; @@ -444,7 +401,7 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, SORT_FIELD *sortorder; { - DBUG_ASSERT(query_plan->index == MAX_KEY); + DBUG_ASSERT(query_plan.index == MAX_KEY); table->sort.io_cache= (IO_CACHE *) my_malloc(sizeof(IO_CACHE), MYF(MY_FAE | MY_ZEROFILL | MY_THREAD_SPECIFIC)); @@ -480,7 +437,7 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, thd->apc_target.disable(); DBUG_RETURN(TRUE); } - if (query_plan->index == MAX_KEY || (select && select->quick)) + if (query_plan.index == MAX_KEY || (select && select->quick)) { if (init_read_record(&info, thd, table, select, 1, 1, FALSE)) { @@ -491,7 +448,7 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, } } else - init_read_record_idx(&info, thd, table, 1, query_plan->index, reverse); + init_read_record_idx(&info, thd, table, 1, query_plan.index, reverse); init_ftfuncs(thd, select_lex, 1); thd_proc_info(thd, "updating"); @@ -584,11 +541,6 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, (void) table->file->extra(HA_EXTRA_NORMAL); thd->apc_target.disable(); - if (thd->lex->query_plan_footprint) - { - delete thd->lex->query_plan_footprint; - thd->lex->query_plan_footprint= NULL; - } cleanup: /* Invalidate the table in the query cache if something changed. This must @@ -652,9 +604,7 @@ cleanup: /* Special exits */ exit_without_my_ok: - query_plan->save_query_plan_footprint(); - thd->lex->query_plan_footprint->upd_del_plan= query_plan; - + query_plan.save_query_plan_footprint(thd->lex->query_plan_footprint); select_send *result; if (!(result= new select_send())) @@ -669,9 +619,6 @@ exit_without_my_ok: else result->send_eof(); - delete thd->lex->query_plan_footprint; - thd->lex->query_plan_footprint= NULL; - delete select; free_underlaid_joins(thd, select_lex); //table->set_keyread(false); diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index bc16f61b77e..6ad7b288a4b 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -447,6 +447,8 @@ void lex_start(THD *thd) DBUG_ENTER("lex_start"); lex->thd= lex->unit.thd= thd; + + lex->query_plan_footprint= NULL; lex->context_stack.empty(); lex->unit.init_query(); diff --git a/sql/sql_lex.h b/sql/sql_lex.h index db944ab83ec..47d01f2e137 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -619,6 +619,8 @@ class select_union; class Procedure; class QPF_query; +void delete_qpf_query(QPF_query * query); + class st_select_lex_unit: public st_select_lex_node { protected: TABLE_LIST result_table_list; @@ -2360,15 +2362,18 @@ protected: LEX *m_lex; }; + class Delete_plan; class SQL_SELECT; +class QPF_query; +class QPF_update; + /* Query plan of a single-table UPDATE. (This is actually a plan for single-table DELETE also) - - TODO: this should be a query plan footprint, not a query plan. */ + class Update_plan { protected: @@ -2378,10 +2383,9 @@ public: SQL_SELECT *select; uint index; ha_rows table_rows; /* Use if select==NULL */ - - /* + /* Top-level select_lex. Most of its fields are not used, we need it only to - get to the subqueries. + get to the subqueries. */ SELECT_LEX *select_lex; @@ -2391,20 +2395,11 @@ public: /* Set this plan to be a plan to do nothing because of impossible WHRE*/ void set_impossible_where() { impossible_where= true; } - virtual int print_explain(select_result_sink *output, uint8 explain_flags); + void save_query_plan_footprint(QPF_query *query); + void save_query_plan_footprint_intern(QPF_update *qpf); virtual ~Update_plan() {} Update_plan() : impossible_where(false), using_filesort(false) {} - - void save_query_plan_footprint(); - /* Query Plan Footprint fields */ - // cant use it here: enum join_type - int jtype; - bool using_where; - StringBuffer<128> possible_keys_line; - StringBuffer<128> key_str; - StringBuffer<128> key_len_str; - StringBuffer<64> mrr_type; }; @@ -2424,11 +2419,11 @@ public: deleting_all_rows= true; table_rows= rows_arg; } - int print_explain(select_result_sink *output, uint8 explain_flags); + + void save_query_plan_footprint(QPF_query *query); }; -class QPF_query; /* The state of the lex parsing. This is saved in the THD struct */ struct LEX: public Query_tables_list @@ -2439,8 +2434,8 @@ struct LEX: public Query_tables_list SELECT_LEX *current_select; /* list of all SELECT_LEX */ SELECT_LEX *all_selects_list; - - /* For single-table DELETE: its query plan */ + + /* Query Plan Footprint of a currently running select */ QPF_query *query_plan_footprint; char *length,*dec,*change; diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 1513ce61886..474d3889b53 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -598,6 +598,10 @@ static void handle_bootstrap_impl(THD *thd) #if defined(ENABLED_PROFILING) thd->profiling.finish_current_query(); #endif + // + delete thd->lex->query_plan_footprint; + thd->lex->query_plan_footprint= NULL; + // if (bootstrap_error) break; @@ -1484,6 +1488,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd, thd->update_all_stats(); log_slow_statement(thd); + /* psergey-todo: this is the place we could print EXPLAIN to slow query log */ thd_proc_info(thd, "cleaning up"); thd->reset_query(); @@ -1518,6 +1523,9 @@ void log_slow_statement(THD *thd) { DBUG_ENTER("log_slow_statement"); + delete thd->lex->query_plan_footprint; + thd->lex->query_plan_footprint= NULL; + /* The following should never be true with our current code base, but better to keep this here so we don't accidently try to log a @@ -1526,6 +1534,7 @@ void log_slow_statement(THD *thd) if (unlikely(thd->in_sub_stmt)) DBUG_VOID_RETURN; // Don't set time for sub stmt + /* Follow the slow log filter configuration. */ if (!thd->enable_slow_log || !(thd->variables.log_slow_filter & thd->query_plan_flags)) @@ -2178,6 +2187,9 @@ mysql_execute_command(THD *thd) /* Release metadata locks acquired in this transaction. */ thd->mdl_context.release_transactional_locks(); } + + DBUG_ASSERT(!thd->lex->query_plan_footprint); + thd->lex->query_plan_footprint= new QPF_query; #ifndef DBUG_OFF if (lex->sql_command != SQLCOM_SET_OPTION) @@ -3273,7 +3285,7 @@ end_with_restore_list: result= NULL; } select_lex->set_explain_type(FALSE); - thd->lex->query_plan_footprint= new QPF_query; + //thd->lex->query_plan_footprint= new QPF_query; } else result= new multi_delete(aux_tables, lex->table_count); @@ -3301,8 +3313,8 @@ end_with_restore_list: { result->reset_offset_limit(); thd->lex->query_plan_footprint->print_explain(result, thd->lex->describe); - delete thd->lex->query_plan_footprint; - thd->lex->query_plan_footprint= NULL; + //delete thd->lex->query_plan_footprint; + //thd->lex->query_plan_footprint= NULL; } if (res) @@ -4819,7 +4831,7 @@ static bool execute_sqlcom_select(THD *thd, TABLE_LIST *all_tables) if (!(result= new select_send())) return 1; /* purecov: inspected */ thd->send_explain_fields(result); - thd->lex->query_plan_footprint= new QPF_query; + //thd->lex->query_plan_footprint= new QPF_query; res= mysql_explain_union(thd, &thd->lex->unit, result); if (!res) @@ -4831,8 +4843,8 @@ static bool execute_sqlcom_select(THD *thd, TABLE_LIST *all_tables) result->reset_offset_limit(); thd->lex->query_plan_footprint->print_explain(result, thd->lex->describe); } - delete thd->lex->query_plan_footprint; - thd->lex->query_plan_footprint= NULL; + //delete thd->lex->query_plan_footprint; + //thd->lex->query_plan_footprint= NULL; //psergey-todo: here, produce the EXPLAIN output. // mysql_explain_union() itself is only responsible for calling diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc index 71b4a0cf817..d81070b7406 100644 --- a/sql/sql_prepare.cc +++ b/sql/sql_prepare.cc @@ -2486,6 +2486,7 @@ void reinit_stmt_before_use(THD *thd, LEX *lex) object and because of this can be used in different threads. */ lex->thd= thd; + DBUG_ASSERT(!lex->query_plan_footprint); if (lex->empty_field_list_on_rset) { @@ -3925,6 +3926,9 @@ bool Prepared_statement::execute(String *expanded_query, bool open_cursor) if (! cursor) cleanup_stmt(); + //psergey: TODO the "EXECUTE problem" is here + delete_qpf_query(thd->lex->query_plan_footprint); + thd->lex->query_plan_footprint= NULL; thd->set_statement(&stmt_backup); thd->stmt_arena= old_stmt_arena; diff --git a/sql/sql_update.cc b/sql/sql_update.cc index a694eb3d360..ce56a725567 100644 --- a/sql/sql_update.cc +++ b/sql/sql_update.cc @@ -276,7 +276,9 @@ int mysql_update(THD *thd, ulonglong id; List all_fields; killed_state killed_status= NOT_KILLED; - Update_plan *query_plan; + Update_plan query_plan; + query_plan.index= MAX_KEY; + query_plan.using_filesort= FALSE; bool apc_target_enabled= false; // means was enabled *by code this function* DBUG_ENTER("mysql_update"); @@ -315,12 +317,9 @@ int mysql_update(THD *thd, /* Calculate "table->covering_keys" based on the WHERE */ table->covering_keys= table->s->keys_in_use; table->quick_keys.clear_all(); - - query_plan= new Update_plan; - query_plan->index= MAX_KEY; - query_plan->using_filesort= FALSE; - query_plan->select_lex= &thd->lex->select_lex; - query_plan->table= table; + + query_plan.select_lex= &thd->lex->select_lex; + query_plan.table= table; #ifndef NO_EMBEDDED_ACCESS_CHECKS /* Force privilege re-checking for views after they have been opened. */ want_privilege= (table_list->view ? UPDATE_ACL : @@ -379,7 +378,7 @@ int mysql_update(THD *thd, if (cond_value == Item::COND_FALSE) { limit= 0; // Impossible WHERE - query_plan->set_impossible_where(); + query_plan.set_impossible_where(); if (thd->lex->describe) goto exit_without_my_ok; } @@ -412,7 +411,7 @@ int mysql_update(THD *thd, if (error || !limit || thd->is_error() || (select && select->check_quick(thd, safe_update, limit))) { - query_plan->set_impossible_where(); + query_plan.set_impossible_where(); if (thd->lex->describe) goto exit_without_my_ok; @@ -454,16 +453,16 @@ int mysql_update(THD *thd, if (select && select->quick && select->quick->unique_key_range()) { // Single row select (always "ordered"): Ok to use with key field UPDATE need_sort= FALSE; - query_plan->index= MAX_KEY; + query_plan.index= MAX_KEY; used_key_is_modified= FALSE; } else { - query_plan->index= get_index_for_order(order, table, select, limit, - &need_sort, &reverse); + query_plan.index= get_index_for_order(order, table, select, limit, + &need_sort, &reverse); if (select && select->quick) { - DBUG_ASSERT(need_sort || query_plan->index == select->quick->index); + DBUG_ASSERT(need_sort || query_plan.index == select->quick->index); used_key_is_modified= (!select->quick->unique_key_range() && select->quick->is_keys_used(table->write_set)); } @@ -471,11 +470,11 @@ int mysql_update(THD *thd, { if (need_sort) { // Assign table scan index to check below for modified key fields: - query_plan->index= table->file->key_used_on_scan; + query_plan.index= table->file->key_used_on_scan; } - if (query_plan->index != MAX_KEY) + if (query_plan.index != MAX_KEY) { // Check if we are modifying a key that we are used to search with: - used_key_is_modified= is_key_used(table, query_plan->index, table->write_set); + used_key_is_modified= is_key_used(table, query_plan.index, table->write_set); } } } @@ -485,11 +484,9 @@ int mysql_update(THD *thd, - Save the decisions in the query plan - if we're running EXPLAIN UPDATE, get out */ - query_plan->select= select; - query_plan->possible_keys= table->quick_keys; - query_plan->table_rows= table->file->stats.records; - thd->lex->query_plan_footprint= new QPF_query; - thd->lex->query_plan_footprint->upd_del_plan= query_plan; + query_plan.select= select; + query_plan.possible_keys= table->quick_keys; + query_plan.table_rows= table->file->stats.records; /* Ok, we have generated a query plan for the UPDATE. @@ -498,8 +495,8 @@ int mysql_update(THD *thd, */ if (thd->lex->describe) goto exit_without_my_ok; - - query_plan->save_query_plan_footprint(); + + query_plan.save_query_plan_footprint(thd->lex->query_plan_footprint); thd->apc_target.enable(); apc_target_enabled= true; DBUG_EXECUTE_IF("show_explain_probe_update_exec_start", @@ -518,8 +515,8 @@ int mysql_update(THD *thd, DBUG_ASSERT(table->read_set == &table->def_read_set); DBUG_ASSERT(table->write_set == &table->def_write_set); - if (query_plan->index < MAX_KEY && old_covering_keys.is_set(query_plan->index)) - table->add_read_columns_used_by_index(query_plan->index); + if (query_plan.index < MAX_KEY && old_covering_keys.is_set(query_plan.index)) + table->add_read_columns_used_by_index(query_plan.index); else table->use_all_columns(); @@ -585,13 +582,13 @@ int mysql_update(THD *thd, Full index scan must be started with init_read_record_idx */ - if (query_plan->index == MAX_KEY || (select && select->quick)) + if (query_plan.index == MAX_KEY || (select && select->quick)) { if (init_read_record(&info, thd, table, select, 0, 1, FALSE)) goto err; } else - init_read_record_idx(&info, thd, table, 1, query_plan->index, reverse); + init_read_record_idx(&info, thd, table, 1, query_plan.index, reverse); thd_proc_info(thd, "Searching rows for update"); ha_rows tmp_limit= limit; @@ -1005,12 +1002,6 @@ err: if (apc_target_enabled) thd->apc_target.disable(); - if (thd->lex->query_plan_footprint) - { - delete thd->lex->query_plan_footprint; - thd->lex->query_plan_footprint= NULL; - } - delete select; free_underlaid_joins(thd, select_lex); table->disable_keyread(); @@ -1019,25 +1010,22 @@ err: exit_without_my_ok: DBUG_ASSERT(!apc_target_enabled); - query_plan->save_query_plan_footprint(); - thd->lex->query_plan_footprint->upd_del_plan= query_plan; - + query_plan.save_query_plan_footprint(thd->lex->query_plan_footprint); + select_send *result; + bool printed_anything; if (!(result= new select_send())) return 1; /* purecov: inspected */ List dummy; /* note: looked in 5.6 and they too use a dummy list like this */ result->prepare(dummy, &thd->lex->unit); thd->send_explain_fields(result); - int err2= thd->lex->query_plan_footprint->print_explain(result, 0); + int err2= thd->lex->print_explain(result, 0 /* explain flags*/, &printed_anything); if (err2) result->abort_result_set(); else result->send_eof(); - delete thd->lex->query_plan_footprint; - thd->lex->query_plan_footprint= NULL; - delete select; free_underlaid_joins(thd, select_lex); DBUG_RETURN((error >= 0 || thd->is_error()) ? 1 : 0); @@ -1475,8 +1463,6 @@ bool mysql_multi_update(THD *thd, } select_lex->set_explain_type(FALSE); *result= NULL; /* no multi_update object */ - - thd->lex->query_plan_footprint= new QPF_query; } else { @@ -1506,15 +1492,6 @@ bool mysql_multi_update(THD *thd, DBUG_PRINT("info",("res: %d report_error: %d", res, (int) thd->is_error())); res|= thd->is_error(); - - if (explain) - { - //result->reset_offset_limit(); - thd->lex->query_plan_footprint->print_explain(output, thd->lex->describe); - delete thd->lex->query_plan_footprint; - thd->lex->query_plan_footprint= NULL; - } - if (unlikely(res)) (*result)->abort_result_set(); else