From eaba1ba4a575c5280d41eaa3deac890dd25d82e4 Mon Sep 17 00:00:00 2001 From: Sergei Petrunia Date: Tue, 27 May 2014 20:13:17 +0400 Subject: [PATCH] Re-commit in git: MDEV-406: ANALYZE $stmt - Ported the old patch to new explain code - New SQL syntax (ANALYZE $stmt) - ANALYZE UPDATE/DELETE is now supported (because EXPLAIN UPDATE/DELETE is supported) - Basic counters are calculated for basic kinds of queries (still need to see what happens with join buffer, ORDER BY...LIMIT queries, etc) --- sql/sql_class.cc | 19 +++++++- sql/sql_class.h | 12 +++++ sql/sql_delete.cc | 9 ++++ sql/sql_explain.cc | 110 ++++++++++++++++++++++++++++++++------------- sql/sql_explain.h | 43 ++++++++++++++---- sql/sql_lex.cc | 5 ++- sql/sql_lex.h | 4 +- sql/sql_parse.cc | 28 ++++++++++-- sql/sql_select.cc | 34 ++++++++++++-- sql/sql_select.h | 9 ++-- sql/sql_show.cc | 2 +- sql/sql_update.cc | 11 ++++- sql/sql_yacc.yy | 11 ++++- 13 files changed, 239 insertions(+), 58 deletions(-) diff --git a/sql/sql_class.cc b/sql/sql_class.cc index a48ebd450bb..8d6ddc0bb08 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -2282,6 +2282,9 @@ int THD::send_explain_fields(select_result *result) /* Populate the provided field_list with EXPLAIN output columns. this->lex->describe has the EXPLAIN flags + + The set/order of columns must be kept in sync with + Explain_query::print_explain and co. */ void THD::make_explain_field_list(List &field_list) @@ -2317,11 +2320,25 @@ void THD::make_explain_field_list(List &field_list) item->maybe_null=1; field_list.push_back(item= new Item_return_int("rows", 10, MYSQL_TYPE_LONGLONG)); - if (lex->describe & DESCRIBE_EXTENDED) + if (lex->analyze_stmt) + { + field_list.push_back(item= new Item_return_int("r_rows", 10, + MYSQL_TYPE_LONGLONG)); + item->maybe_null=1; + } + + if (lex->analyze_stmt || lex->describe & DESCRIBE_EXTENDED) { field_list.push_back(item= new Item_float("filtered", 0.1234, 2, 4)); item->maybe_null=1; } + + if (lex->analyze_stmt) + { + field_list.push_back(item= new Item_float("r_filtered", 0.1234, 2, 4)); + item->maybe_null=1; + } + item->maybe_null= 1; field_list.push_back(new Item_empty_string("Extra", 255, cs)); } diff --git a/sql/sql_class.h b/sql/sql_class.h index 5898d9e2cf8..a7991952a79 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -3959,6 +3959,18 @@ public: virtual void cleanup(); }; +class select_send_analyze : public select_send +{ +bool discard_data; + bool send_result_set_metadata(List &list, uint flags) { return 0; } + /* + ANALYZE-todo: we should call val_int() (or val_str() or whatever) to + compute the columns. If we don't, it's not full execution. + */ + int send_data(List &items) { return 0; } + bool send_eof() { return 0; } + void abort_result_set() {} +}; class select_to_file :public select_result_interceptor { protected: diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc index b3a8eb2a97b..9b811c8cea8 100644 --- a/sql/sql_delete.cc +++ b/sql/sql_delete.cc @@ -223,6 +223,7 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, killed_state killed_status= NOT_KILLED; THD::enum_binlog_query_type query_type= THD::ROW_QUERY_TYPE; bool with_select= !select_lex->item_list.is_empty(); + Explain_delete *explain; Delete_plan query_plan(thd->mem_root); query_plan.index= MAX_KEY; query_plan.using_filesort= FALSE; @@ -538,9 +539,11 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, goto cleanup; } + explain= (Explain_delete*)thd->lex->explain->get_upd_del_plan(); while (!(error=info.read_record(&info)) && !thd->killed && ! thd->is_error()) { + explain->on_record_read(); if (table->vfield) update_virtual_fields(thd, table, table->triggers ? VCOL_UPDATE_ALL : @@ -549,6 +552,7 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, // thd->is_error() is tested to disallow delete row on error if (!select || select->skip_record(thd) > 0) { + explain->on_record_after_where(); if (table->triggers && table->triggers->process_triggers(thd, TRG_EVENT_DELETE, TRG_ACTION_BEFORE, FALSE)) @@ -666,6 +670,11 @@ cleanup: } DBUG_ASSERT(transactional_table || !deleted || thd->transaction.stmt.modified_non_trans_table); free_underlaid_joins(thd, select_lex); + if (thd->lex->analyze_stmt) + { + error= thd->lex->explain->send_explain(thd); + } + else if (error < 0 || (thd->lex->ignore && !thd->is_error() && !thd->is_fatal_error)) { diff --git a/sql/sql_explain.cc b/sql/sql_explain.cc index 84ff8759d96..47581898e42 100644 --- a/sql/sql_explain.cc +++ b/sql/sql_explain.cc @@ -140,7 +140,7 @@ int Explain_query::send_explain(THD *thd) return 1; int res; - if ((res= print_explain(result, lex->describe))) + if ((res= print_explain(result, lex->describe, lex->analyze_stmt))) result->abort_result_set(); else result->send_eof(); @@ -154,16 +154,16 @@ int Explain_query::send_explain(THD *thd) */ int Explain_query::print_explain(select_result_sink *output, - uint8 explain_flags) + uint8 explain_flags, bool is_analyze) { if (upd_del_plan) { - upd_del_plan->print_explain(this, output, explain_flags); + upd_del_plan->print_explain(this, output, explain_flags, is_analyze); return 0; } else if (insert_plan) { - insert_plan->print_explain(this, output, explain_flags); + insert_plan->print_explain(this, output, explain_flags, is_analyze); return 0; } else @@ -172,14 +172,14 @@ int Explain_query::print_explain(select_result_sink *output, Explain_node *node= get_node(1); if (!node) return 1; /* No query plan */ - return node->print_explain(this, output, explain_flags); + return node->print_explain(this, output, explain_flags, is_analyze); } } bool print_explain_query(LEX *lex, THD *thd, String *str) { - return lex->explain->print_explain_str(thd, str); + return lex->explain->print_explain_str(thd, str, false); } @@ -187,14 +187,14 @@ bool print_explain_query(LEX *lex, THD *thd, String *str) Return tabular EXPLAIN output as a text string */ -bool Explain_query::print_explain_str(THD *thd, String *out_str) +bool Explain_query::print_explain_str(THD *thd, String *out_str, bool is_analyze) { List fields; thd->make_explain_field_list(fields); select_result_text_buffer output_buf(thd); output_buf.send_result_set_metadata(fields, thd->lex->describe); - if (print_explain(&output_buf, thd->lex->describe)) + if (print_explain(&output_buf, thd->lex->describe, is_analyze)) return true; output_buf.save_to(out_str); return false; @@ -217,7 +217,8 @@ static void push_string(List *item_list, String *str) int Explain_union::print_explain(Explain_query *query, select_result_sink *output, - uint8 explain_flags) + uint8 explain_flags, + bool is_analyze) { char table_name_buffer[SAFE_NAME_LEN]; @@ -225,7 +226,7 @@ int Explain_union::print_explain(Explain_query *query, for (int i= 0; i < (int) union_members.elements(); i++) { Explain_select *sel= query->get_select(union_members.at(i)); - sel->print_explain(query, output, explain_flags); + sel->print_explain(query, output, explain_flags, is_analyze); } /* Print a line with "UNION RESULT" */ @@ -287,9 +288,17 @@ int Explain_union::print_explain(Explain_query *query, /* `rows` */ item_list.push_back(item_null); + + /* `r_rows` */ + if (is_analyze) + item_list.push_back(item_null); /* `filtered` */ - if (explain_flags & DESCRIBE_EXTENDED) + if (explain_flags & DESCRIBE_EXTENDED || is_analyze) + item_list.push_back(item_null); + + /* `r_filtered` */ + if (is_analyze) item_list.push_back(item_null); /* `Extra` */ @@ -309,7 +318,7 @@ int Explain_union::print_explain(Explain_query *query, Print all subquery children (UNION children have already been printed at the start of this function) */ - return print_explain_for_children(query, output, explain_flags); + return print_explain_for_children(query, output, explain_flags, is_analyze); } @@ -319,12 +328,13 @@ int Explain_union::print_explain(Explain_query *query, int Explain_node::print_explain_for_children(Explain_query *query, select_result_sink *output, - uint8 explain_flags) + uint8 explain_flags, + bool is_analyze) { for (int i= 0; i < (int) children.elements(); i++) { Explain_node *node= query->get_node(children.at(i)); - if (node->print_explain(query, output, explain_flags)) + if (node->print_explain(query, output, explain_flags, is_analyze)) return 1; } return 0; @@ -344,7 +354,7 @@ Explain_select::~Explain_select() int Explain_select::print_explain(Explain_query *query, select_result_sink *output, - uint8 explain_flags) + uint8 explain_flags, bool is_analyze) { if (message) { @@ -359,8 +369,17 @@ int Explain_select::print_explain(Explain_query *query, item_list.push_back(item_null); if (explain_flags & DESCRIBE_PARTITIONS) item_list.push_back(item_null); - if (explain_flags & DESCRIBE_EXTENDED) + + /* filtered */ + if (is_analyze || explain_flags & DESCRIBE_EXTENDED) item_list.push_back(item_null); + + if (is_analyze) + { + /* r_rows, r_filtered */ + item_list.push_back(item_null); + item_list.push_back(item_null); + } item_list.push_back(new Item_string(message,strlen(message),cs)); @@ -373,7 +392,7 @@ int Explain_select::print_explain(Explain_query *query, bool using_fs= using_filesort; for (uint i=0; i< n_join_tabs; i++) { - join_tabs[i]->print_explain(output, explain_flags, select_id, + join_tabs[i]->print_explain(output, explain_flags, is_analyze, select_id, select_type, using_tmp, using_fs); if (i == 0) { @@ -387,7 +406,7 @@ int Explain_select::print_explain(Explain_query *query, } } - return print_explain_for_children(query, output, explain_flags); + return print_explain_for_children(query, output, explain_flags, is_analyze); } @@ -398,8 +417,9 @@ void Explain_table_access::push_extra(enum explain_extra_tag extra_tag) int Explain_table_access::print_explain(select_result_sink *output, uint8 explain_flags, - uint select_id, const char *select_type, - bool using_temporary, bool using_filesort) + bool is_analyze, + uint select_id, const char *select_type, + bool using_temporary, bool using_filesort) { const CHARSET_INFO *cs= system_charset_info; const char *hash_key_prefix= "#hash#"; @@ -519,8 +539,16 @@ int Explain_table_access::print_explain(select_result_sink *output, uint8 explai else item_list.push_back(item_null); + /* `r_rows` */ + if (is_analyze) + { + ha_rows avg_rows= r_scans ? round((double) r_rows / r_scans): 0; + item_list.push_back(new Item_int((longlong) (ulonglong) avg_rows, + MY_INT64_NUM_DECIMAL_DIGITS)); + } + /* `filtered` */ - if (explain_flags & DESCRIBE_EXTENDED) + if (explain_flags & DESCRIBE_EXTENDED || is_analyze) { if (filtered_set) { @@ -530,6 +558,17 @@ int Explain_table_access::print_explain(select_result_sink *output, uint8 explai item_list.push_back(item_null); } + /* `r_filtered` */ + if (is_analyze) + { + double r_filtered; + if (r_rows > 0) + r_filtered= 100.0 * (double)r_rows_after_table_cond / r_rows; + else + r_filtered= 100.0; + item_list.push_back(new Item_float(r_filtered, 2)); + } + /* `Extra` */ StringBuffer<256> extra_buf; bool first= true; @@ -802,12 +841,13 @@ void Explain_quick_select::print_key_len(String *str) int Explain_delete::print_explain(Explain_query *query, select_result_sink *output, - uint8 explain_flags) + uint8 explain_flags, + bool is_analyze) { if (deleting_all_rows) { const char *msg= "Deleting all rows"; - int res= print_explain_message_line(output, explain_flags, + int res= print_explain_message_line(output, explain_flags, is_analyze, 1 /*select number*/, select_type, &rows, msg); return res; @@ -815,14 +855,16 @@ int Explain_delete::print_explain(Explain_query *query, } else { - return Explain_update::print_explain(query, output, explain_flags); + return Explain_update::print_explain(query, output, explain_flags, + is_analyze); } } int Explain_update::print_explain(Explain_query *query, select_result_sink *output, - uint8 explain_flags) + uint8 explain_flags, + bool is_analyze) { StringBuffer<64> key_buf; StringBuffer<64> key_len_buf; @@ -832,7 +874,7 @@ int Explain_update::print_explain(Explain_query *query, const char *msg= impossible_where ? "Impossible WHERE" : "No matching rows after partition pruning"; - int res= print_explain_message_line(output, explain_flags, + int res= print_explain_message_line(output, explain_flags, is_analyze, 1 /*select number*/, select_type, NULL, /* rows */ @@ -892,8 +934,9 @@ int Explain_update::print_explain(Explain_query *query, Single-table DELETE commands do not do "Using temporary". "Using index condition" is also not possible (which is an unjustified limitation) */ + double r_filtered= 100 * (r_rows?((double)r_rows_after_where/r_rows):1.0); - print_explain_row(output, explain_flags, + print_explain_row(output, explain_flags, is_analyze, 1, /* id */ select_type, table_name.c_ptr(), @@ -904,18 +947,21 @@ int Explain_update::print_explain(Explain_query *query, key_len_buf.length() ? key_len_buf.c_ptr() : NULL, NULL, /* 'ref' is always NULL in single-table EXPLAIN DELETE */ &rows, + &r_rows, + r_filtered, extra_str.c_ptr_safe()); - return print_explain_for_children(query, output, explain_flags); + return print_explain_for_children(query, output, explain_flags, is_analyze); } int Explain_insert::print_explain(Explain_query *query, select_result_sink *output, - uint8 explain_flags) + uint8 explain_flags, + bool is_analyze) { const char *select_type="INSERT"; - print_explain_row(output, explain_flags, + print_explain_row(output, explain_flags, is_analyze, 1, /* id */ select_type, table_name.c_ptr(), @@ -926,9 +972,11 @@ int Explain_insert::print_explain(Explain_query *query, NULL, // key_len NULL, // ref NULL, // rows + NULL, // r_rows + 100.0, // r_filtered NULL); - return print_explain_for_children(query, output, explain_flags); + return print_explain_for_children(query, output, explain_flags, is_analyze); } diff --git a/sql/sql_explain.h b/sql/sql_explain.h index b9f381b867b..a6b41414c5d 100644 --- a/sql/sql_explain.h +++ b/sql/sql_explain.h @@ -60,10 +60,10 @@ public: } virtual int print_explain(Explain_query *query, select_result_sink *output, - uint8 explain_flags)=0; + uint8 explain_flags, bool is_analyze)=0; int print_explain_for_children(Explain_query *query, select_result_sink *output, - uint8 explain_flags); + uint8 explain_flags, bool is_analyze); virtual ~Explain_node(){} }; @@ -134,7 +134,7 @@ public: bool using_filesort; int print_explain(Explain_query *query, select_result_sink *output, - uint8 explain_flags); + uint8 explain_flags, bool is_analyze); }; @@ -172,7 +172,7 @@ public: union_members.append(select_no); } int print_explain(Explain_query *query, select_result_sink *output, - uint8 explain_flags); + uint8 explain_flags, bool is_analyze); const char *fake_select_type; bool using_filesort; @@ -238,13 +238,14 @@ public: Explain_union *get_union(uint select_id); /* Produce a tabular EXPLAIN output */ - int print_explain(select_result_sink *output, uint8 explain_flags); + int print_explain(select_result_sink *output, uint8 explain_flags, + bool is_analyze); /* Send tabular EXPLAIN to the client */ int send_explain(THD *thd); /* Return tabular EXPLAIN output as a text string */ - bool print_explain_str(THD *thd, String *out_str); + bool print_explain_str(THD *thd, String *out_str, bool is_analyze); /* If true, at least part of EXPLAIN can be printed */ bool have_query_plan() { return insert_plan || upd_del_plan|| get_node(1) != NULL; } @@ -252,6 +253,8 @@ public: void query_plan_ready(); MEM_ROOT *mem_root; + + Explain_update *get_upd_del_plan() { return upd_del_plan; } private: /* Explain_delete inherits from Explain_update */ Explain_update *upd_del_plan; @@ -459,8 +462,21 @@ public: StringBuffer<32> firstmatch_table_name; int print_explain(select_result_sink *output, uint8 explain_flags, + bool is_analyze, uint select_id, const char *select_type, bool using_temporary, bool using_filesort); + + /* ANALYZE members*/ + ha_rows r_scans; /* How many scans were ran on this join_tab */ + ha_rows r_rows; /* How many rows we've got after that */ + ha_rows r_rows_after_table_cond; /* Rows after applying the table condition */ + ha_rows r_rows_after_where; /* Rows after applying attached part of WHERE */ + + Explain_table_access(): + r_scans(0), r_rows(0), r_rows_after_table_cond(0), + r_rows_after_where(0) + {} + private: void append_tag_name(String *str, enum explain_extra_tag tag); }; @@ -502,8 +518,17 @@ public: bool using_filesort; bool using_io_buffer; + /* ANALYZE members and methods */ + ha_rows r_rows; + ha_rows r_rows_after_where; + inline void on_record_read() { r_rows++; } + inline void on_record_after_where() { r_rows_after_where++; } + + Explain_update() : + r_rows(0), r_rows_after_where(0) + {} virtual int print_explain(Explain_query *query, select_result_sink *output, - uint8 explain_flags); + uint8 explain_flags, bool is_analyze); }; @@ -523,7 +548,7 @@ public: int get_select_id() { return 1; /* always root */ } int print_explain(Explain_query *query, select_result_sink *output, - uint8 explain_flags); + uint8 explain_flags, bool is_analyze); }; @@ -544,7 +569,7 @@ public: virtual int get_select_id() { return 1; /* always root */ } virtual int print_explain(Explain_query *query, select_result_sink *output, - uint8 explain_flags); + uint8 explain_flags, bool is_analyze); }; diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index c549a7be2cc..cd9f9238f71 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -483,6 +483,7 @@ void lex_start(THD *thd) if (lex->select_lex.group_list_ptrs) lex->select_lex.group_list_ptrs->clear(); lex->describe= 0; + lex->analyze_stmt= 0; lex->subqueries= FALSE; lex->context_analysis_only= 0; lex->derived_tables= 0; @@ -4181,12 +4182,12 @@ 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) + bool is_analyze, bool *printed_anything) { int res; if (explain && explain->have_query_plan()) { - res= explain->print_explain(output, explain_flags); + res= explain->print_explain(output, explain_flags, is_analyze); *printed_anything= true; } else diff --git a/sql/sql_lex.h b/sql/sql_lex.h index 76288e94c75..70a793541af 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -2268,6 +2268,7 @@ public: void save_explain_data(Explain_query *query); void save_explain_data_intern(Explain_query *query, Explain_update *eu); + virtual ~Update_plan() {} Update_plan(MEM_ROOT *mem_root_arg) : @@ -2459,6 +2460,7 @@ struct LEX: public Query_tables_list */ uint table_count; uint8 describe; + bool analyze_stmt; /* TRUE<=> this is "ANALYZE $stmt" */ /* A flag that indicates what kinds of derived tables are present in the query (0 if no derived tables, otherwise a combination of flags @@ -2735,7 +2737,7 @@ struct LEX: public Query_tables_list } int print_explain(select_result_sink *output, uint8 explain_flags, - bool *printed_anything); + bool is_analyze, bool *printed_anything); }; diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 922f0d73165..42a3045cb1d 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -5233,7 +5233,8 @@ static bool execute_sqlcom_select(THD *thd, TABLE_LIST *all_tables) top-level LIMIT */ result->reset_offset_limit(); - thd->lex->explain->print_explain(result, thd->lex->describe); + thd->lex->explain->print_explain(result, thd->lex->describe, + thd->lex->analyze_stmt); if (lex->describe & DESCRIBE_EXTENDED) { char buff[1024]; @@ -5257,12 +5258,33 @@ static bool execute_sqlcom_select(THD *thd, TABLE_LIST *all_tables) } else { - if (!result && !(result= new select_send())) - return 1; /* purecov: inspected */ + //psergey-todo: ANALYZE should hook in here... + select_result *save_result; + if (lex->analyze_stmt) + { + save_result= result; + result= new select_send_analyze(); + } + else + { + if (!result && !(result= new select_send())) + return 1; /* purecov: inspected */ + } query_cache_store_query(thd, all_tables); res= handle_select(thd, lex, result, 0); if (result != lex->result) delete result; + + if (lex->analyze_stmt) + { + result= save_result; + if (!result && !(result= new select_send())) + return 1; + thd->lex->explain->send_explain(thd); + + if (result != lex->result) + delete result; + } } } /* Count number of empty select queries */ diff --git a/sql/sql_select.cc b/sql/sql_select.cc index b88aed1f1bb..6b8fda7bd82 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -17532,6 +17532,8 @@ sub_select(JOIN *join,JOIN_TAB *join_tab,bool end_of_records) (*join_tab->next_select)(join,join_tab+1,end_of_records); DBUG_RETURN(nls); } + join_tab->explain->r_scans++; + int error; enum_nested_loop_state rc= NESTED_LOOP_OK; READ_RECORD *info= &join_tab->read_record; @@ -17667,6 +17669,8 @@ evaluate_join_record(JOIN *join, JOIN_TAB *join_tab, DBUG_RETURN(NESTED_LOOP_KILLED); /* purecov: inspected */ } + join_tab->explain->r_rows++; + if (join_tab->table->vfield) update_virtual_fields(join->thd, join_tab->table); @@ -17685,6 +17689,7 @@ evaluate_join_record(JOIN *join, JOIN_TAB *join_tab, There is no select condition or the attached pushed down condition is true => a match is found. */ + join_tab->explain->r_rows_after_table_cond++; bool found= 1; while (join_tab->first_unmatched && found) { @@ -22908,10 +22913,11 @@ void JOIN::clear() /* Print an EXPLAIN line with all NULLs and given message in the 'Extra' column + TODO: is_analyze */ int print_explain_message_line(select_result_sink *result, - uint8 options, + uint8 options, bool is_analyze, uint select_number, const char *select_type, ha_rows *rows, @@ -22944,8 +22950,16 @@ int print_explain_message_line(select_result_sink *result, else item_list.push_back(item_null); + /* `r_rows` */ + if (is_analyze) + item_list.push_back(item_null); + /* `filtered` */ - if (options & DESCRIBE_EXTENDED) + if (is_analyze || options & DESCRIBE_EXTENDED) + item_list.push_back(item_null); + + /* `r_filtered` */ + if (is_analyze) item_list.push_back(item_null); /* `Extra` */ @@ -22996,7 +23010,7 @@ void make_possible_keys_line(TABLE *table, key_map possible_keys, String *line) */ int print_explain_row(select_result_sink *result, - uint8 options, + uint8 options, bool is_analyze, uint select_number, const char *select_type, const char *table_name, @@ -23007,6 +23021,8 @@ int print_explain_row(select_result_sink *result, const char *key_len, const char *ref, ha_rows *rows, + ha_rows *r_rows, + double r_filtered, const char *extra) { const CHARSET_INFO *cs= system_charset_info; @@ -23058,12 +23074,20 @@ int print_explain_row(select_result_sink *result, } else item_list.push_back(item_null); + + /* 'r_rows' */ + if (is_analyze) + item_list.push_back(item_null); /* 'filtered' */ const double filtered=100.0; - if (options & DESCRIBE_EXTENDED) + if (options & DESCRIBE_EXTENDED || is_analyze) item_list.push_back(new Item_float(filtered, 2)); + /* 'r_filtered' */ + if (is_analyze) + item_list.push_back(new Item_float(r_filtered, 2)); + /* 'Extra' */ if (extra) item_list.push_back(new Item_string(extra, strlen(extra), cs)); @@ -23291,6 +23315,8 @@ int JOIN::save_explain_data_intern(Explain_query *output, bool need_tmp_table, eta->key.set(thd->mem_root, NULL, (uint)-1); eta->quick_info= NULL; + tab->explain= eta; + /* id */ if (tab->bush_root_tab) eta->sjm_nest_select_id= select_id; diff --git a/sql/sql_select.h b/sql/sql_select.h index 271199e3d51..66d7b007bbb 100644 --- a/sql/sql_select.h +++ b/sql/sql_select.h @@ -250,7 +250,8 @@ typedef struct st_join_table { /* Special content for EXPLAIN 'Extra' column or NULL if none */ enum explain_extra_tag info; - + + Explain_table_access *explain; /* Bitmap of TAB_INFO_* bits that encodes special line for EXPLAIN 'Extra' column, or 0 if there is no info. @@ -1854,14 +1855,14 @@ void push_index_cond(JOIN_TAB *tab, uint keyno); /* EXPLAIN-related utility functions */ int print_explain_message_line(select_result_sink *result, - uint8 options, + uint8 options, bool is_analyze, uint select_number, const char *select_type, ha_rows *rows, const char *message); void explain_append_mrr_info(QUICK_RANGE_SELECT *quick, String *res); int print_explain_row(select_result_sink *result, - uint8 options, + uint8 options, bool is_analyze, uint select_number, const char *select_type, const char *table_name, @@ -1872,6 +1873,8 @@ int print_explain_row(select_result_sink *result, const char *key_len, const char *ref, ha_rows *rows, + ha_rows *r_rows, + double r_filtered, const char *extra); void make_possible_keys_line(TABLE *table, key_map possible_keys, String *line); diff --git a/sql/sql_show.cc b/sql/sql_show.cc index 535b113db45..375be489867 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -2366,7 +2366,7 @@ void Show_explain_request::call_in_target_thread() DBUG_ASSERT(current_thd == target_thd); set_current_thd(request_thd); if (target_thd->lex->print_explain(explain_buf, 0 /* explain flags*/, - &printed_anything)) + false /*TODO: analyze? */, &printed_anything)) { failed_to_produce= TRUE; } diff --git a/sql/sql_update.cc b/sql/sql_update.cc index 63e45ac1fec..eb0d108855d 100644 --- a/sql/sql_update.cc +++ b/sql/sql_update.cc @@ -277,6 +277,7 @@ int mysql_update(THD *thd, List all_fields; killed_state killed_status= NOT_KILLED; Update_plan query_plan(thd->mem_root); + Explain_update *explain; query_plan.index= MAX_KEY; query_plan.using_filesort= FALSE; DBUG_ENTER("mysql_update"); @@ -717,15 +718,16 @@ int mysql_update(THD *thd, if (table->file->ha_table_flags() & HA_PARTIAL_COLUMN_READ) table->prepare_for_position(); + explain= thd->lex->explain->get_upd_del_plan(); /* We can use compare_record() to optimize away updates if the table handler is returning all columns OR if if all updated columns are read */ can_compare_record= records_are_comparable(table); - while (!(error=info.read_record(&info)) && !thd->killed) { + explain->on_record_read(); if (table->vfield) update_virtual_fields(thd, table, table->triggers ? VCOL_UPDATE_ALL : @@ -736,6 +738,7 @@ int mysql_update(THD *thd, if (table->file->was_semi_consistent_read()) continue; /* repeat the read of the same row if it still exists */ + explain->on_record_after_where(); store_record(table,record[1]); if (fill_record_n_invoke_before_triggers(thd, table, fields, values, 0, TRG_EVENT_UPDATE)) @@ -993,7 +996,11 @@ int mysql_update(THD *thd, id= thd->arg_of_last_insert_id_function ? thd->first_successful_insert_id_in_prev_stmt : 0; - if (error < 0) + if (thd->lex->analyze_stmt) + { + error= thd->lex->explain->send_explain(thd); + } + else if (error < 0) { char buff[MYSQL_ERRMSG_SIZE]; my_snprintf(buff, sizeof(buff), ER(ER_UPDATE_INFO), (ulong) found, diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index d6b3fa41c78..68d4347de43 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -982,7 +982,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); This makes the code grep-able, and helps maintenance. */ - + %token ABORT_SYM /* INTERNAL (used in lex) */ %token ACCESSIBLE_SYM %token ACTION /* SQL-2003-N */ @@ -1804,6 +1804,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %type dyncall_create_list %type + analyze_stmt_command query verb_clause create change select do drop insert replace insert2 insert_values update delete truncate rename show describe load alter optimize keycache preload flush @@ -1990,6 +1991,7 @@ verb_clause: statement: alter | analyze + | analyze_stmt_command | binlog_base64_event | call | change @@ -12765,6 +12767,13 @@ describe_command: | DESCRIBE ; +analyze_stmt_command: + ANALYZE_SYM explainable_command + { + Lex->analyze_stmt= true; + } + ; + opt_extended_describe: /* empty */ {} | EXTENDED_SYM { Lex->describe|= DESCRIBE_EXTENDED; }