[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)
This commit is contained in:
Sergey Petrunya 2013-06-20 20:58:26 +04:00
parent 0a560289aa
commit 52cfa54c1d
7 changed files with 64 additions and 17 deletions

View File

@ -1,4 +1,4 @@
drop table if exists t0; drop table if exists t0, t1;
create table t0 (a int) engine=myisam; create table t0 (a int) engine=myisam;
insert into t0 values (1),(2),(3),(4),(5),(6),(7),(8); insert into t0 values (1),(2),(3),(4),(5),(6),(7),(8);
# #

View File

@ -49,6 +49,7 @@ void QPF_query::add_node(QPF_node *node)
if (node->get_type() == QPF_node::QPF_UNION) if (node->get_type() == QPF_node::QPF_UNION)
{ {
QPF_union *u= (QPF_union*)node; QPF_union *u= (QPF_union*)node;
DBUG_ASSERT(!unions[u->get_select_id()]);
unions[u->get_select_id()]= u; unions[u->get_select_id()]= u;
} }
else else
@ -60,7 +61,10 @@ void QPF_query::add_node(QPF_node *node)
DBUG_ASSERT(0); DBUG_ASSERT(0);
} }
else else
{
DBUG_ASSERT(!selects[sel->select_id]);
selects[sel->select_id] = sel; 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"; const char *msg= "Deleting all rows";
int res= print_explain_message_line(output, explain_flags, int res= print_explain_message_line(output, explain_flags,
1 /*select number*/, 1 /*select number*/,
"SIMPLE", msg); select_type, msg);
return res; return res;
} }
@ -513,7 +517,7 @@ int QPF_update::print_explain(QPF_query *query, select_result_sink *output,
const char *msg= "Impossible where"; const char *msg= "Impossible where";
int res= print_explain_message_line(output, explain_flags, int res= print_explain_message_line(output, explain_flags,
1 /*select number*/, 1 /*select number*/,
"SIMPLE", msg); select_type, msg);
return res; return res;
} }
@ -541,7 +545,7 @@ int QPF_update::print_explain(QPF_query *query, select_result_sink *output,
print_explain_row(output, explain_flags, print_explain_row(output, explain_flags,
1, /* id */ 1, /* id */
"SIMPLE", select_type,
table_name.c_ptr(), table_name.c_ptr(),
// partitions, // partitions,
jtype, jtype,

View File

@ -350,6 +350,8 @@ public:
virtual enum qpf_node_type get_type() { return QPF_UPDATE; } virtual enum qpf_node_type get_type() { return QPF_UPDATE; }
virtual int get_select_id() { return 1; /* always root */ } virtual int get_select_id() { return 1; /* always root */ }
const char *select_type;
bool impossible_where; bool impossible_where;
StringBuffer<64> table_name; StringBuffer<64> table_name;

View File

@ -58,10 +58,12 @@ void Delete_plan::save_query_plan_footprint(QPF_query *query)
if (deleting_all_rows) if (deleting_all_rows)
{ {
qpf->deleting_all_rows= true; qpf->deleting_all_rows= true;
qpf->select_type= "SIMPLE";
} }
else 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; 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) void Update_plan::save_query_plan_footprint(QPF_query *query)
{ {
QPF_update* qpf= new QPF_update; QPF_update* qpf= new QPF_update;
save_query_plan_footprint_intern(qpf); save_query_plan_footprint_intern(query, qpf);
query->upd_del_plan= 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); qpf->table_name.append(table->pos_in_table_list->alias);
if (impossible_where) if (impossible_where)
{ {
@ -85,8 +88,10 @@ void Update_plan::save_query_plan_footprint_intern(QPF_update *qpf)
return; return;
} }
// TODO: do we need the following: select_type qpf->impossible_where= false;
//select_lex->set_explain_type(TRUE);
select_lex->set_explain_type(TRUE);
qpf->select_type= select_lex->type;
/* Set jtype */ /* Set jtype */
if (select && select->quick) 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); 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.select_lex= &thd->lex->select_lex;
query_plan.table= table; 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)) if (mysql_prepare_delete(thd, table_list, &conds))
DBUG_RETURN(TRUE); DBUG_RETURN(TRUE);
@ -612,7 +642,7 @@ exit_without_my_ok:
List<Item> dummy; /* note: looked in 5.6 and they too use a dummy list like this */ List<Item> dummy; /* note: looked in 5.6 and they too use a dummy list like this */
result->prepare(dummy, &thd->lex->unit); result->prepare(dummy, &thd->lex->unit);
thd->send_explain_fields(result); 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) if (err2)
result->abort_result_set(); result->abort_result_set();

View File

@ -4338,6 +4338,13 @@ int st_select_lex_unit::save_qpf(QPF_query *output)
//int res= 0; //int res= 0;
SELECT_LEX *first= first_select(); 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; QPF_union *qpfu= new QPF_union;
/* /*
TODO: The following code should be eliminated. If we have a capability to TODO: The following code should be eliminated. If we have a capability to

View File

@ -730,10 +730,7 @@ public:
friend int subselect_union_engine::exec(); friend int subselect_union_engine::exec();
List<Item> *get_unit_column_types(); List<Item> *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); int save_qpf(QPF_query *output);
}; };
@ -2379,6 +2376,7 @@ class Update_plan
protected: protected:
bool impossible_where; bool impossible_where;
public: public:
bool updating_a_view;
TABLE *table; TABLE *table;
SQL_SELECT *select; SQL_SELECT *select;
uint index; uint index;
@ -2396,7 +2394,7 @@ public:
void set_impossible_where() { impossible_where= true; } void set_impossible_where() { impossible_where= true; }
void save_query_plan_footprint(QPF_query *query); 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() {} virtual ~Update_plan() {}
Update_plan() : impossible_where(false), using_filesort(false) {} Update_plan() : impossible_where(false), using_filesort(false) {}

View File

@ -314,6 +314,10 @@ int mysql_update(THD *thd,
my_error(ER_NON_UPDATABLE_TABLE, MYF(0), table_list->alias, "UPDATE"); my_error(ER_NON_UPDATABLE_TABLE, MYF(0), table_list->alias, "UPDATE");
DBUG_RETURN(1); 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 */ /* Calculate "table->covering_keys" based on the WHERE */
table->covering_keys= table->s->keys_in_use; table->covering_keys= table->s->keys_in_use;
table->quick_keys.clear_all(); table->quick_keys.clear_all();
@ -1013,13 +1017,14 @@ exit_without_my_ok:
query_plan.save_query_plan_footprint(thd->lex->query_plan_footprint); query_plan.save_query_plan_footprint(thd->lex->query_plan_footprint);
select_send *result; select_send *result;
bool printed_anything; //bool printed_anything;
if (!(result= new select_send())) if (!(result= new select_send()))
return 1; /* purecov: inspected */ return 1; /* purecov: inspected */
List<Item> dummy; /* note: looked in 5.6 and they too use a dummy list like this */ List<Item> dummy; /* note: looked in 5.6 and they too use a dummy list like this */
result->prepare(dummy, &thd->lex->unit); result->prepare(dummy, &thd->lex->unit);
thd->send_explain_fields(result); 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) if (err2)
result->abort_result_set(); result->abort_result_set();
@ -1498,6 +1503,7 @@ bool mysql_multi_update(THD *thd,
{ {
if (explain) if (explain)
{ {
thd->lex->query_plan_footprint->print_explain(output, 0);
output->send_eof(); output->send_eof();
delete output; delete output;
} }