diff options
author | Sergey Petrunya <psergey@askmonty.org> | 2013-06-18 21:08:34 +0400 |
---|---|---|
committer | Sergey Petrunya <psergey@askmonty.org> | 2013-06-18 21:08:34 +0400 |
commit | 1951d40a88e3b3fc0d2fac2ba3358d887e738485 (patch) | |
tree | 7d429c705ddd7f9f2ea5cc7cc3d69a7dbcccc809 | |
parent | 1c6fc3f6b9f7e79331053b5675793d70d0e70af0 (diff) | |
download | mariadb-git-1951d40a88e3b3fc0d2fac2ba3358d887e738485.tar.gz |
[SHOW] EXPLAIN UPDATE/DELETE, code re-structuring
- Make EXPLAIN UPDATE/DELETE use "Query Plan Footprints", too.
-rw-r--r-- | sql/opt_qpf.cc | 16 | ||||
-rw-r--r-- | sql/opt_qpf.h | 7 | ||||
-rw-r--r-- | sql/sql_delete.cc | 152 | ||||
-rw-r--r-- | sql/sql_lex.cc | 6 | ||||
-rw-r--r-- | sql/sql_lex.h | 18 | ||||
-rw-r--r-- | sql/sql_update.cc | 68 |
6 files changed, 152 insertions, 115 deletions
diff --git a/sql/opt_qpf.cc b/sql/opt_qpf.cc index 8e8ca06ed24..b3d15a74984 100644 --- a/sql/opt_qpf.cc +++ b/sql/opt_qpf.cc @@ -12,6 +12,7 @@ QPF_query::QPF_query() { + upd_del_plan= NULL; memset(&unions, 0, sizeof(unions)); memset(&selects, 0, sizeof(selects)); } @@ -19,6 +20,7 @@ QPF_query::QPF_query() QPF_query::~QPF_query() { + delete upd_del_plan; uint i; for (i=0 ; i < MAX_TABLES; i++) delete unions[i]; @@ -70,9 +72,17 @@ void QPF_query::add_node(QPF_node *node) int QPF_query::print_explain(select_result_sink *output, uint8 explain_flags) { - // Start with id=1 - QPF_node *node= get_node(1); - return node->print_explain(this, output, explain_flags); + if (upd_del_plan) + { + upd_del_plan->print_explain(output, explain_flags); + return 0; + } + else + { + // Start with id=1 + QPF_node *node= get_node(1); + return node->print_explain(this, output, explain_flags); + } } diff --git a/sql/opt_qpf.h b/sql/opt_qpf.h index 9fb2324bef6..82d305261e7 100644 --- a/sql/opt_qpf.h +++ b/sql/opt_qpf.h @@ -161,6 +161,9 @@ 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; private: QPF_union *unions[MAX_TABLES]; @@ -269,10 +272,10 @@ public: key_map range_checked_map; // valid with ET_USING_MRR - StringBuffer <64> mrr_type; + StringBuffer<64> mrr_type; // valid with ET_USING_JOIN_BUFFER - StringBuffer <64> join_buffer_type; + StringBuffer<64> join_buffer_type; //TABLE *firstmatch_table; StringBuffer<64> firstmatch_table_name; diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc index 390d9e3945a..f6719d3a91b 100644 --- a/sql/sql_delete.cc +++ b/sql/sql_delete.cc @@ -51,8 +51,7 @@ invoked on a running DELETE statement. */ -int Delete_plan::print_explain(select_result_sink *output, uint8 explain_flags, - bool *printed_anything) +int Delete_plan::print_explain(select_result_sink *output, uint8 explain_flags) { if (deleting_all_rows) { @@ -62,37 +61,15 @@ int Delete_plan::print_explain(select_result_sink *output, uint8 explain_flags, { return 1; } - *printed_anything= true; return 0; } - return Update_plan::print_explain(output, explain_flags, printed_anything); + return Update_plan::print_explain(output, explain_flags); } - -int Update_plan::print_explain(select_result_sink *output, uint8 explain_flags, - bool *printed_anything) +void Update_plan::save_query_plan_footprint() { - if (impossible_where) - { - const char *msg= "Impossible where"; - if (print_explain_message_line(output, explain_flags, 1/*select number*/, - "SIMPLE", msg)) - { - return 1; - } - *printed_anything= true; - return 0; - } + select_lex->set_explain_type(TRUE); - select_lex->set_explain_type(FALSE); - /* - Print an EXPLAIN line. We dont have join, so we can't directly use - JOIN::print_explain. - We do have a SELECT_LEX (TODO but how is it useful? it has select_type.. - and that's it?) - */ - - enum join_type jtype; if (select && select->quick) { int quick_type= select->quick->get_type(); @@ -112,35 +89,53 @@ int Update_plan::print_explain(select_result_sink *output, uint8 explain_flags, jtype= JT_NEXT; } - StringBuffer<128> possible_keys_line; + using_where= test(select && select->cond); + //using_filesort is already set make_possible_keys_line(table, possible_keys, &possible_keys_line); - const char *key_name; - const char *key_len; - - StringBuffer<128> key_str; - StringBuffer<128> key_len_str; - StringBuffer<128> extra_str; - /* Calculate key_len */ if (select && select->quick) { select->quick->add_keys_and_lengths(&key_str, &key_len_str); - key_name= key_str.c_ptr(); - key_len= key_len_str.c_ptr(); } else { - key_name= (index == MAX_KEY)? NULL : table->key_info[index].name; - key_len= NULL; + if (index != MAX_KEY) + { + key_str.append(table->key_info[index].name); + } + // key_len stays NULL } - - if (select && select->cond) - extra_str.append(STRING_WITH_LEN("Using where")); + if (select && select->quick && select->quick->get_type() == QUICK_SELECT_I::QS_TYPE_RANGE) { - explain_append_mrr_info((QUICK_RANGE_SELECT*)select->quick, &extra_str); + explain_append_mrr_info((QUICK_RANGE_SELECT*)select->quick, &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) @@ -160,15 +155,14 @@ int Update_plan::print_explain(select_result_sink *output, uint8 explain_flags, select_lex->type, table->pos_in_table_list->alias, // partitions, - jtype, + (enum join_type) jtype, possible_keys_line.length()? possible_keys_line.c_ptr(): NULL, - key_name, - key_len, + 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()); - *printed_anything= true; /* psergey-todo: handle all this through saving QPF. @@ -211,10 +205,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; - query_plan.index= MAX_KEY; - query_plan.using_filesort= FALSE; + + Delete_plan *query_plan = new Delete_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)) @@ -238,8 +232,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); @@ -314,7 +308,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; @@ -344,7 +338,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; } @@ -372,7 +366,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; @@ -413,20 +407,22 @@ 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; - thd->lex->upd_del_plan= &query_plan; + 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 @@ -435,11 +431,12 @@ 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(); 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; @@ -447,7 +444,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)); @@ -483,7 +480,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)) { @@ -494,7 +491,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"); @@ -587,6 +584,11 @@ 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 @@ -650,21 +652,25 @@ cleanup: /* Special exits */ exit_without_my_ok: - thd->lex->upd_del_plan= &query_plan; - + query_plan->save_query_plan_footprint(); + thd->lex->query_plan_footprint->upd_del_plan= query_plan; + + select_send *result; - bool printed_anything; if (!(result= new select_send())) return 1; /* purecov: inspected */ List<Item> 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->print_explain(result, 0 /* explain flags*/, &printed_anything); + int err2= thd->lex->query_plan_footprint->print_explain(result, 0); 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); diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index b2d4ca13823..bc16f61b77e 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -448,7 +448,6 @@ void lex_start(THD *thd) lex->thd= lex->unit.thd= thd; - lex->upd_del_plan= NULL; lex->context_stack.empty(); lex->unit.init_query(); lex->unit.init_select(); @@ -2561,7 +2560,6 @@ LEX::LEX() INITIAL_LEX_PLUGIN_LIST_SIZE, 0); reset_query_tables_list(TRUE); mi.init(); - upd_del_plan= NULL; } @@ -4176,11 +4174,11 @@ bool st_select_lex::is_merged_child_of(st_select_lex *ancestor) int LEX::print_explain(select_result_sink *output, uint8 explain_flags, bool *printed_anything) { - if (upd_del_plan) + /* if (upd_del_plan) { upd_del_plan->print_explain(output, explain_flags, printed_anything); return 0; - } + }*/ //int res= unit.print_explain(output, explain_flags, printed_anything); //psergey-todo: here, we should make Query Plan Footprint, and then produce diff --git a/sql/sql_lex.h b/sql/sql_lex.h index d2a8b59a593..db944ab83ec 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -2378,6 +2378,7 @@ 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. @@ -2390,11 +2391,20 @@ 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, - bool *printed_anything); + virtual int print_explain(select_result_sink *output, uint8 explain_flags); 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; }; @@ -2414,8 +2424,7 @@ public: deleting_all_rows= true; table_rows= rows_arg; } - int print_explain(select_result_sink *output, uint8 explain_flags, - bool *printed_anything); + int print_explain(select_result_sink *output, uint8 explain_flags); }; @@ -2432,7 +2441,6 @@ struct LEX: public Query_tables_list SELECT_LEX *all_selects_list; /* For single-table DELETE: its query plan */ - Update_plan *upd_del_plan; QPF_query *query_plan_footprint; char *length,*dec,*change; diff --git a/sql/sql_update.cc b/sql/sql_update.cc index c6bf984c70c..a694eb3d360 100644 --- a/sql/sql_update.cc +++ b/sql/sql_update.cc @@ -276,9 +276,7 @@ int mysql_update(THD *thd, ulonglong id; List<Item> all_fields; killed_state killed_status= NOT_KILLED; - Update_plan query_plan; - query_plan.index= MAX_KEY; - query_plan.using_filesort= FALSE; + Update_plan *query_plan; bool apc_target_enabled= false; // means was enabled *by code this function* DBUG_ENTER("mysql_update"); @@ -317,9 +315,12 @@ 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.select_lex= &thd->lex->select_lex; - query_plan.table= table; + + 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; #ifndef NO_EMBEDDED_ACCESS_CHECKS /* Force privilege re-checking for views after they have been opened. */ want_privilege= (table_list->view ? UPDATE_ACL : @@ -378,7 +379,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; } @@ -411,7 +412,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; @@ -453,16 +454,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)); } @@ -470,11 +471,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); } } } @@ -484,10 +485,11 @@ 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->upd_del_plan= &query_plan; + 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 UPDATE. @@ -496,7 +498,8 @@ int mysql_update(THD *thd, */ if (thd->lex->describe) goto exit_without_my_ok; - + + query_plan->save_query_plan_footprint(); thd->apc_target.enable(); apc_target_enabled= true; DBUG_EXECUTE_IF("show_explain_probe_update_exec_start", @@ -515,8 +518,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(); @@ -582,13 +585,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; @@ -1002,6 +1005,12 @@ 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(); @@ -1010,22 +1019,25 @@ err: exit_without_my_ok: DBUG_ASSERT(!apc_target_enabled); - thd->lex->upd_del_plan= &query_plan; - + query_plan->save_query_plan_footprint(); + thd->lex->query_plan_footprint->upd_del_plan= query_plan; + select_send *result; - bool printed_anything; if (!(result= new select_send())) return 1; /* purecov: inspected */ List<Item> 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->print_explain(result, 0 /* explain flags*/, &printed_anything); + int err2= thd->lex->query_plan_footprint->print_explain(result, 0); 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); |