From 52cfa54c1d211a17a9df7c38a4568ddc4d09e6d9 Mon Sep 17 00:00:00 2001 From: Sergey Petrunya Date: Thu, 20 Jun 2013 20:58:26 +0400 Subject: [PATCH] [SHOW] EXPLAIN UPDATE/DELETE, code re-structuring Single table UPDATE/DELETE - Correctly print type=SIMPLE vs type=PRIMARY - Handle UPDATE/DELETE of mergeable VIEWs: we get the VIEW's select as the first subquery. (MySQL 5.6 doesn't print it because it finds that the subquery is not attached to any select) --- mysql-test/r/explain_non_select.result | 2 +- sql/opt_qpf.cc | 10 ++++-- sql/opt_qpf.h | 2 ++ sql/sql_delete.cc | 42 ++++++++++++++++++++++---- sql/sql_lex.cc | 7 +++++ sql/sql_lex.h | 8 ++--- sql/sql_update.cc | 10 ++++-- 7 files changed, 64 insertions(+), 17 deletions(-) diff --git a/mysql-test/r/explain_non_select.result b/mysql-test/r/explain_non_select.result index 1c8e444c4b9..d664e7241e2 100644 --- a/mysql-test/r/explain_non_select.result +++ b/mysql-test/r/explain_non_select.result @@ -1,4 +1,4 @@ -drop table if exists t0; +drop table if exists t0, t1; create table t0 (a int) engine=myisam; insert into t0 values (1),(2),(3),(4),(5),(6),(7),(8); # diff --git a/sql/opt_qpf.cc b/sql/opt_qpf.cc index 819c0e6cc22..425823df9e1 100644 --- a/sql/opt_qpf.cc +++ b/sql/opt_qpf.cc @@ -49,6 +49,7 @@ void QPF_query::add_node(QPF_node *node) if (node->get_type() == QPF_node::QPF_UNION) { QPF_union *u= (QPF_union*)node; + DBUG_ASSERT(!unions[u->get_select_id()]); unions[u->get_select_id()]= u; } else @@ -60,7 +61,10 @@ void QPF_query::add_node(QPF_node *node) DBUG_ASSERT(0); } else + { + DBUG_ASSERT(!selects[sel->select_id]); selects[sel->select_id] = sel; + } } } @@ -493,7 +497,7 @@ int QPF_delete::print_explain(QPF_query *query, select_result_sink *output, const char *msg= "Deleting all rows"; int res= print_explain_message_line(output, explain_flags, 1 /*select number*/, - "SIMPLE", msg); + select_type, msg); return res; } @@ -513,7 +517,7 @@ int QPF_update::print_explain(QPF_query *query, select_result_sink *output, const char *msg= "Impossible where"; int res= print_explain_message_line(output, explain_flags, 1 /*select number*/, - "SIMPLE", msg); + select_type, msg); return res; } @@ -541,7 +545,7 @@ int QPF_update::print_explain(QPF_query *query, select_result_sink *output, print_explain_row(output, explain_flags, 1, /* id */ - "SIMPLE", + select_type, table_name.c_ptr(), // partitions, jtype, diff --git a/sql/opt_qpf.h b/sql/opt_qpf.h index 6e0bb40c576..53839ecbac8 100644 --- a/sql/opt_qpf.h +++ b/sql/opt_qpf.h @@ -350,6 +350,8 @@ public: virtual enum qpf_node_type get_type() { return QPF_UPDATE; } virtual int get_select_id() { return 1; /* always root */ } + const char *select_type; + bool impossible_where; StringBuffer<64> table_name; diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc index 602831829d7..30ae60ddc43 100644 --- a/sql/sql_delete.cc +++ b/sql/sql_delete.cc @@ -58,10 +58,12 @@ void Delete_plan::save_query_plan_footprint(QPF_query *query) if (deleting_all_rows) { qpf->deleting_all_rows= true; + qpf->select_type= "SIMPLE"; } else { - Update_plan::save_query_plan_footprint_intern(qpf); + qpf->deleting_all_rows= false; + Update_plan::save_query_plan_footprint_intern(query, qpf); } query->upd_del_plan= qpf; @@ -71,13 +73,14 @@ void Delete_plan::save_query_plan_footprint(QPF_query *query) void Update_plan::save_query_plan_footprint(QPF_query *query) { QPF_update* qpf= new QPF_update; - save_query_plan_footprint_intern(qpf); + save_query_plan_footprint_intern(query, qpf); query->upd_del_plan= qpf; } -void Update_plan::save_query_plan_footprint_intern(QPF_update *qpf) +void Update_plan::save_query_plan_footprint_intern(QPF_query *query, QPF_update *qpf) { + qpf->select_type= "SIMPLE"; qpf->table_name.append(table->pos_in_table_list->alias); if (impossible_where) { @@ -85,8 +88,10 @@ void Update_plan::save_query_plan_footprint_intern(QPF_update *qpf) return; } - // TODO: do we need the following: select_type - //select_lex->set_explain_type(TRUE); + qpf->impossible_where= false; + + select_lex->set_explain_type(TRUE); + qpf->select_type= select_lex->type; /* Set jtype */ if (select && select->quick) @@ -134,6 +139,28 @@ void Update_plan::save_query_plan_footprint_intern(QPF_update *qpf) { explain_append_mrr_info((QUICK_RANGE_SELECT*)select->quick, &qpf->mrr_type); } + + bool skip= updating_a_view; + /* Save subquery children */ + for (SELECT_LEX_UNIT *unit= select_lex->first_inner_unit(); + unit; + unit= unit->next_unit()) + { + if (skip) + { + skip= false; + continue; + } + /* + Display subqueries only if they are not parts of eliminated WHERE/ON + clauses. + */ + if (!(unit->item && unit->item->eliminated)) + qpf->add_child(unit->first_select()->select_number); + + //TODO: temporary?: + unit->save_qpf(query); + } } @@ -194,6 +221,9 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, query_plan.select_lex= &thd->lex->select_lex; query_plan.table= table; + //psergey-todo: Ugly, discuss with Sanja + query_plan.updating_a_view= test(table_list->view); + if (mysql_prepare_delete(thd, table_list, &conds)) DBUG_RETURN(TRUE); @@ -612,7 +642,7 @@ exit_without_my_ok: 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->query_plan_footprint->print_explain(result, 0 /* explain flags*/); if (err2) result->abort_result_set(); diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index 6ad7b288a4b..455ef55944d 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -4337,6 +4337,13 @@ int st_select_lex_unit::save_qpf(QPF_query *output) { //int res= 0; SELECT_LEX *first= first_select(); + + if (!first->next_select()) + { + /* This is a 1-way UNION, i.e. not really a UNION */ + first->save_qpf(output); + return 0; + } QPF_union *qpfu= new QPF_union; /* diff --git a/sql/sql_lex.h b/sql/sql_lex.h index 47d01f2e137..6c7a5ca4d13 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -730,10 +730,7 @@ public: friend int subselect_union_engine::exec(); List *get_unit_column_types(); -#if 0 - int print_explain(select_result_sink *output, uint8 explain_flags, - bool *printed_anything); -#endif + int save_qpf(QPF_query *output); }; @@ -2379,6 +2376,7 @@ class Update_plan protected: bool impossible_where; public: + bool updating_a_view; TABLE *table; SQL_SELECT *select; uint index; @@ -2396,7 +2394,7 @@ public: void set_impossible_where() { impossible_where= true; } void save_query_plan_footprint(QPF_query *query); - void save_query_plan_footprint_intern(QPF_update *qpf); + void save_query_plan_footprint_intern(QPF_query *query, QPF_update *qpf); virtual ~Update_plan() {} Update_plan() : impossible_where(false), using_filesort(false) {} diff --git a/sql/sql_update.cc b/sql/sql_update.cc index ce56a725567..96f49785a32 100644 --- a/sql/sql_update.cc +++ b/sql/sql_update.cc @@ -314,6 +314,10 @@ int mysql_update(THD *thd, my_error(ER_NON_UPDATABLE_TABLE, MYF(0), table_list->alias, "UPDATE"); DBUG_RETURN(1); } + + //psergey-todo: Ugly, discuss with Sanja + query_plan.updating_a_view= test(table_list->view); + /* Calculate "table->covering_keys" based on the WHERE */ table->covering_keys= table->s->keys_in_use; table->quick_keys.clear_all(); @@ -1013,13 +1017,14 @@ exit_without_my_ok: query_plan.save_query_plan_footprint(thd->lex->query_plan_footprint); select_send *result; - bool printed_anything; + //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->print_explain(result, 0 /* explain flags*/, &printed_anything); + //int err2= thd->lex->print_explain(result, 0 /* explain flags*/, &printed_anything); + int err2= thd->lex->query_plan_footprint->print_explain(result, 0 /* explain flags*/); if (err2) result->abort_result_set(); @@ -1498,6 +1503,7 @@ bool mysql_multi_update(THD *thd, { if (explain) { + thd->lex->query_plan_footprint->print_explain(output, 0); output->send_eof(); delete output; }