Commit 0a560289 authored by Sergey Petrunya's avatar Sergey Petrunya

[SHOW] EXPLAIN UPDATE/DELETE, code re-structuring

- Introduce back QueryPlan/QueryPlanFootprint separation for 
  single-table UPDATEs/DELETEs
- Create an empty QueryPlanFootprint for all kinds of queries
parent 6efa1d8c
...@@ -74,7 +74,7 @@ int QPF_query::print_explain(select_result_sink *output, ...@@ -74,7 +74,7 @@ int QPF_query::print_explain(select_result_sink *output,
{ {
if (upd_del_plan) if (upd_del_plan)
{ {
upd_del_plan->print_explain(output, explain_flags); upd_del_plan->print_explain(this, output, explain_flags);
return 0; return 0;
} }
else else
...@@ -196,12 +196,20 @@ int QPF_union::print_explain(QPF_query *query, select_result_sink *output, ...@@ -196,12 +196,20 @@ int QPF_union::print_explain(QPF_query *query, select_result_sink *output,
if (output->send_data(item_list)) if (output->send_data(item_list))
return 1; 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++) for (int i= 0; i < (int) children.elements(); i++)
{ {
QPF_node *node= query->get_node(children.at(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; return 0;
} }
...@@ -263,12 +271,7 @@ int QPF_select::print_explain(QPF_query *query, select_result_sink *output, ...@@ -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++) return print_explain_for_children(query, output, explain_flags);
{
QPF_node *node= query->get_node(children.at(i));
node->print_explain(query, output, explain_flags);
}
return 0;
} }
...@@ -482,3 +485,78 @@ void QPF_table_access::append_tag_name(String *str, enum Extra_tag tag) ...@@ -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;
}
...@@ -18,7 +18,7 @@ class QPF_query; ...@@ -18,7 +18,7 @@ class QPF_query;
class QPF_node : public Sql_alloc class QPF_node : public Sql_alloc
{ {
public: 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; virtual enum qpf_node_type get_type()= 0;
...@@ -36,7 +36,9 @@ public: ...@@ -36,7 +36,9 @@ public:
virtual int print_explain(QPF_query *query, select_result_sink *output, virtual int print_explain(QPF_query *query, select_result_sink *output,
uint8 explain_flags)=0; uint8 explain_flags)=0;
int print_explain_for_children(QPF_query *query, select_result_sink *output,
uint8 explain_flags);
virtual ~QPF_node(){} virtual ~QPF_node(){}
}; };
...@@ -155,9 +157,42 @@ public: ...@@ -155,9 +157,42 @@ public:
bool using_filesort; 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()
| | | ...
| | <THD::cleanup_after_query
| |
| <mysql_parse
|
| log_slow_statement()
|
| free_root()
|
>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 class QPF_query : public Sql_alloc
...@@ -174,8 +209,8 @@ public: ...@@ -174,8 +209,8 @@ public:
/* This will return a select (even if there is a union with this id) */ /* This will return a select (even if there is a union with this id) */
QPF_select *get_select(uint select_id); QPF_select *get_select(uint select_id);
/* Delete_plan inherits from Update_plan */ /* QPF_delete inherits from QPF_update */
Update_plan *upd_del_plan; QPF_update *upd_del_plan;
/* Produce a tabular EXPLAIN output */ /* 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);
...@@ -304,5 +339,50 @@ private: ...@@ -304,5 +339,50 @@ private:
void append_tag_name(String *str, enum Extra_tag tag); 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);
};
...@@ -3006,6 +3006,9 @@ sp_lex_keeper::reset_lex_and_exec_core(THD *thd, uint *nextp, ...@@ -3006,6 +3006,9 @@ sp_lex_keeper::reset_lex_and_exec_core(THD *thd, uint *nextp,
else if (! thd->in_sub_stmt) else if (! thd->in_sub_stmt)
thd->mdl_context.release_statement_locks(); 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) if (m_lex->query_tables_own_last)
{ {
......
...@@ -51,25 +51,44 @@ ...@@ -51,25 +51,44 @@
invoked on a running DELETE statement. 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) if (deleting_all_rows)
{ {
const char *msg= "Deleting all rows"; qpf->deleting_all_rows= true;
if (print_explain_message_line(output, explain_flags, 1/*select number*/,
"SIMPLE", msg))
{
return 1;
}
return 0;
} }
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()
void Update_plan::save_query_plan_footprint(QPF_query *query)
{ {
select_lex->set_explain_type(TRUE); 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) if (select && select->quick)
{ {
int quick_type= select->quick->get_type(); int quick_type= select->quick->get_type();
...@@ -77,106 +96,46 @@ void Update_plan::save_query_plan_footprint() ...@@ -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_INDEX_INTERSECT) ||
(quick_type == QUICK_SELECT_I::QS_TYPE_ROR_INTERSECT) || (quick_type == QUICK_SELECT_I::QS_TYPE_ROR_INTERSECT) ||
(quick_type == QUICK_SELECT_I::QS_TYPE_ROR_UNION)) (quick_type == QUICK_SELECT_I::QS_TYPE_ROR_UNION))
jtype= JT_INDEX_MERGE; qpf->jtype= JT_INDEX_MERGE;
else else
jtype= JT_RANGE; qpf->jtype= JT_RANGE;
} }
else else
{ {
if (index == MAX_KEY) if (index == MAX_KEY)
jtype= JT_ALL; qpf->jtype= JT_ALL;
else 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 //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 */ /* Calculate key_len */
if (select && select->quick) 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 else
{ {
if (index != MAX_KEY) 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 // key_len stays NULL
} }
qpf->rows= select ? select->records : table_rows;
if (select && select->quick && if (select && select->quick &&
select->quick->get_type() == QUICK_SELECT_I::QS_TYPE_RANGE) 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. Implement DELETE SQL word.
...@@ -205,10 +164,10 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, ...@@ -205,10 +164,10 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds,
SELECT_LEX *select_lex= &thd->lex->select_lex; SELECT_LEX *select_lex= &thd->lex->select_lex;
killed_state killed_status= NOT_KILLED; killed_state killed_status= NOT_KILLED;
THD::enum_binlog_query_type query_type= THD::ROW_QUERY_TYPE; THD::enum_binlog_query_type query_type= THD::ROW_QUERY_TYPE;
Delete_plan *query_plan = new Delete_plan; Delete_plan query_plan;
query_plan->index= MAX_KEY; query_plan.index= MAX_KEY;
query_plan->using_filesort= FALSE; query_plan.using_filesort= FALSE;
DBUG_ENTER("mysql_delete"); DBUG_ENTER("mysql_delete");
if (open_and_lock_tables(thd, table_list, TRUE, 0)) 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, ...@@ -232,8 +191,8 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds,
} }
thd_proc_info(thd, "init"); thd_proc_info(thd, "init");
table->map=1; table->map=1;
query_plan->select_lex= &thd->lex->select_lex; query_plan.select_lex= &thd->lex->select_lex;
query_plan->table= table; query_plan.table= table;
if (mysql_prepare_delete(thd, table_list, &conds)) if (mysql_prepare_delete(thd, table_list, &conds))
DBUG_RETURN(TRUE); DBUG_RETURN(TRUE);
...@@ -308,7 +267,7 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, ...@@ -308,7 +267,7 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds,
ha_rows const maybe_deleted= table->file->stats.records; ha_rows const maybe_deleted= table->file->stats.records;
DBUG_PRINT("debug", ("Trying to use delete_all_rows()")); 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) if (thd->lex->describe)
goto exit_without_my_ok; goto exit_without_my_ok;
...@@ -338,7 +297,7 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, ...@@ -338,7 +297,7 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds,
if (result == Item::COND_FALSE) // Impossible where if (result == Item::COND_FALSE) // Impossible where
{ {
limit= 0; limit= 0;
query_plan->set_impossible_where(); query_plan.set_impossible_where();
if (thd->lex->describe) if (thd->lex->describe)
goto exit_without_my_ok; goto exit_without_my_ok;
} }
...@@ -366,7 +325,7 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, ...@@ -366,7 +325,7 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds,
DBUG_RETURN(TRUE); DBUG_RETURN(TRUE);
if ((select && select->check_quick(thd, safe_update, limit)) || !limit) if ((select && select->check_quick(thd, safe_update, limit)) || !limit)
{ {
query_plan->set_impossible_where(); query_plan.set_impossible_where();
if (thd->lex->describe) if (thd->lex->describe)
goto exit_without_my_ok; goto exit_without_my_ok;
...@@ -407,22 +366,19 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, ...@@ -407,22 +366,19 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds,
if (select && select->quick && select->quick->unique_key_range()) if (select && select->quick && select->quick->unique_key_range())
{ // Single row select (always "ordered") { // Single row select (always "ordered")
query_plan->using_filesort= FALSE; query_plan.using_filesort= FALSE;
query_plan->index= MAX_KEY; query_plan.index= MAX_KEY;
} }
else else
query_plan->index= get_index_for_order(order, table, select, limit, query_plan.index= get_index_for_order(order, table, select, limit,
&query_plan->using_filesort, &query_plan.using_filesort,
&reverse); &reverse);
} }
query_plan->select= select; query_plan.select= select;
query_plan->possible_keys= table->quick_keys; query_plan.possible_keys= table->quick_keys;
query_plan->table_rows= table->file->stats.records; 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. Ok, we have generated a query plan for the DELETE.
- if we're running EXPLAIN DELETE, goto produce explain output - 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, ...@@ -431,12 +387,13 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds,
if (thd->lex->describe) if (thd->lex->describe)
goto exit_without_my_ok; 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(); thd->apc_target.enable();
DBUG_EXECUTE_IF("show_explain_probe_delete_exec_start", DBUG_EXECUTE_IF("show_explain_probe_delete_exec_start",
dbug_serve_apcs(thd, 1);); dbug_serve_apcs(thd, 1););
if (query_plan->using_filesort) if (query_plan.using_filesort)
{ {
ha_rows examined_rows; ha_rows examined_rows;
ha_rows found_rows; ha_rows found_rows;
...@@ -444,7 +401,7 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, ...@@ -444,7 +401,7 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds,
SORT_FIELD *sortorder; 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), table->sort.io_cache= (IO_CACHE *) my_malloc(sizeof(IO_CACHE),
MYF(MY_FAE | MY_ZEROFILL | MYF(MY_FAE | MY_ZEROFILL |
MY_THREAD_SPECIFIC)); MY_THREAD_SPECIFIC));
...@@ -480,7 +437,7 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, ...@@ -480,7 +437,7 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds,
thd->apc_target.disable(); thd->apc_target.disable();
DBUG_RETURN(TRUE); 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)) 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, ...@@ -491,7 +448,7 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds,
} }
} }
else 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); init_ftfuncs(thd, select_lex, 1);
thd_proc_info(thd, "updating"); thd_proc_info(thd, "updating");
...@@ -584,11 +541,6 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, ...@@ -584,11 +541,6 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds,
(void) table->file->extra(HA_EXTRA_NORMAL); (void) table->file->extra(HA_EXTRA_NORMAL);
thd->apc_target.disable(); thd->apc_target.disable();
if (thd->lex->query_plan_footprint)
{
delete thd->lex->query_plan_footprint;
thd->lex->query_plan_footprint= NULL;
}
cleanup: cleanup:
/* /*
Invalidate the table in the query cache if something changed. This must Invalidate the table in the query cache if something changed. This must
...@@ -652,9 +604,7 @@ cleanup: ...@@ -652,9 +604,7 @@ cleanup:
/* Special exits */ /* Special exits */
exit_without_my_ok: exit_without_my_ok:
query_plan->save_query_plan_footprint(); query_plan.save_query_plan_footprint(thd->lex->query_plan_footprint);
thd->lex->query_plan_footprint->upd_del_plan= query_plan;
select_send *result; select_send *result;
if (!(result= new select_send())) if (!(result= new select_send()))
...@@ -669,9 +619,6 @@ exit_without_my_ok: ...@@ -669,9 +619,6 @@ exit_without_my_ok:
else else
result->send_eof(); result->send_eof();
delete thd->lex->query_plan_footprint;
thd->lex->query_plan_footprint= NULL;
delete select; delete select;
free_underlaid_joins(thd, select_lex); free_underlaid_joins(thd, select_lex);
//table->set_keyread(false); //table->set_keyread(false);
......
...@@ -447,6 +447,8 @@ void lex_start(THD *thd) ...@@ -447,6 +447,8 @@ void lex_start(THD *thd)
DBUG_ENTER("lex_start"); DBUG_ENTER("lex_start");
lex->thd= lex->unit.thd= thd; lex->thd= lex->unit.thd= thd;
lex->query_plan_footprint= NULL;
lex->context_stack.empty(); lex->context_stack.empty();
lex->unit.init_query(); lex->unit.init_query();
......
...@@ -619,6 +619,8 @@ class select_union; ...@@ -619,6 +619,8 @@ class select_union;
class Procedure; class Procedure;
class QPF_query; class QPF_query;
void delete_qpf_query(QPF_query * query);
class st_select_lex_unit: public st_select_lex_node { class st_select_lex_unit: public st_select_lex_node {
protected: protected:
TABLE_LIST result_table_list; TABLE_LIST result_table_list;
...@@ -2360,15 +2362,18 @@ protected: ...@@ -2360,15 +2362,18 @@ protected:
LEX *m_lex; LEX *m_lex;
}; };
class Delete_plan; class Delete_plan;
class SQL_SELECT; class SQL_SELECT;
class QPF_query;
class QPF_update;
/* /*
Query plan of a single-table UPDATE. Query plan of a single-table UPDATE.
(This is actually a plan for single-table DELETE also) (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 class Update_plan
{ {
protected: protected:
...@@ -2378,10 +2383,9 @@ public: ...@@ -2378,10 +2383,9 @@ public:
SQL_SELECT *select; SQL_SELECT *select;
uint index; uint index;
ha_rows table_rows; /* Use if select==NULL */ ha_rows table_rows; /* Use if select==NULL */
/*
/*
Top-level select_lex. Most of its fields are not used, we need it only to 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; SELECT_LEX *select_lex;
...@@ -2391,20 +2395,11 @@ public: ...@@ -2391,20 +2395,11 @@ public:
/* Set this plan to be a plan to do nothing because of impossible WHRE*/ /* Set this plan to be a plan to do nothing because of impossible WHRE*/
void set_impossible_where() { impossible_where= true; } 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() {} virtual ~Update_plan() {}
Update_plan() : impossible_where(false), using_filesort(false) {} 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: ...@@ -2424,11 +2419,11 @@ public:
deleting_all_rows= true; deleting_all_rows= true;
table_rows= rows_arg; 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 */ /* The state of the lex parsing. This is saved in the THD struct */
struct LEX: public Query_tables_list struct LEX: public Query_tables_list
...@@ -2439,8 +2434,8 @@ struct LEX: public Query_tables_list ...@@ -2439,8 +2434,8 @@ struct LEX: public Query_tables_list
SELECT_LEX *current_select; SELECT_LEX *current_select;
/* list of all SELECT_LEX */ /* list of all SELECT_LEX */
SELECT_LEX *all_selects_list; 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; QPF_query *query_plan_footprint;
char *length,*dec,*change; char *length,*dec,*change;
......
...@@ -598,6 +598,10 @@ static void handle_bootstrap_impl(THD *thd) ...@@ -598,6 +598,10 @@ static void handle_bootstrap_impl(THD *thd)
#if defined(ENABLED_PROFILING) #if defined(ENABLED_PROFILING)
thd->profiling.finish_current_query(); thd->profiling.finish_current_query();
#endif #endif
//
delete thd->lex->query_plan_footprint;
thd->lex->query_plan_footprint= NULL;
//
if (bootstrap_error) if (bootstrap_error)
break; break;
...@@ -1484,6 +1488,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd, ...@@ -1484,6 +1488,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
thd->update_all_stats(); thd->update_all_stats();
log_slow_statement(thd); 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_proc_info(thd, "cleaning up");
thd->reset_query(); thd->reset_query();
...@@ -1518,6 +1523,9 @@ void log_slow_statement(THD *thd) ...@@ -1518,6 +1523,9 @@ void log_slow_statement(THD *thd)
{ {
DBUG_ENTER("log_slow_statement"); 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, 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 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) ...@@ -1526,6 +1534,7 @@ void log_slow_statement(THD *thd)
if (unlikely(thd->in_sub_stmt)) if (unlikely(thd->in_sub_stmt))
DBUG_VOID_RETURN; // Don't set time for sub stmt DBUG_VOID_RETURN; // Don't set time for sub stmt
/* Follow the slow log filter configuration. */ /* Follow the slow log filter configuration. */
if (!thd->enable_slow_log || if (!thd->enable_slow_log ||
!(thd->variables.log_slow_filter & thd->query_plan_flags)) !(thd->variables.log_slow_filter & thd->query_plan_flags))
...@@ -2178,6 +2187,9 @@ mysql_execute_command(THD *thd) ...@@ -2178,6 +2187,9 @@ mysql_execute_command(THD *thd)
/* Release metadata locks acquired in this transaction. */ /* Release metadata locks acquired in this transaction. */
thd->mdl_context.release_transactional_locks(); thd->mdl_context.release_transactional_locks();
} }
DBUG_ASSERT(!thd->lex->query_plan_footprint);
thd->lex->query_plan_footprint= new QPF_query;
#ifndef DBUG_OFF #ifndef DBUG_OFF
if (lex->sql_command != SQLCOM_SET_OPTION) if (lex->sql_command != SQLCOM_SET_OPTION)
...@@ -3273,7 +3285,7 @@ end_with_restore_list: ...@@ -3273,7 +3285,7 @@ end_with_restore_list:
result= NULL; result= NULL;
} }
select_lex->set_explain_type(FALSE); select_lex->set_explain_type(FALSE);
thd->lex->query_plan_footprint= new QPF_query; //thd->lex->query_plan_footprint= new QPF_query;
} }
else else
result= new multi_delete(aux_tables, lex->table_count); result= new multi_delete(aux_tables, lex->table_count);
...@@ -3301,8 +3313,8 @@ end_with_restore_list: ...@@ -3301,8 +3313,8 @@ end_with_restore_list:
{ {
result->reset_offset_limit(); result->reset_offset_limit();
thd->lex->query_plan_footprint->print_explain(result, thd->lex->describe); thd->lex->query_plan_footprint->print_explain(result, thd->lex->describe);
delete thd->lex->query_plan_footprint; //delete thd->lex->query_plan_footprint;
thd->lex->query_plan_footprint= NULL; //thd->lex->query_plan_footprint= NULL;
} }
if (res) if (res)
...@@ -4819,7 +4831,7 @@ static bool execute_sqlcom_select(THD *thd, TABLE_LIST *all_tables) ...@@ -4819,7 +4831,7 @@ static bool execute_sqlcom_select(THD *thd, TABLE_LIST *all_tables)
if (!(result= new select_send())) if (!(result= new select_send()))
return 1; /* purecov: inspected */ return 1; /* purecov: inspected */
thd->send_explain_fields(result); 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); res= mysql_explain_union(thd, &thd->lex->unit, result);
if (!res) if (!res)
...@@ -4831,8 +4843,8 @@ static bool execute_sqlcom_select(THD *thd, TABLE_LIST *all_tables) ...@@ -4831,8 +4843,8 @@ static bool execute_sqlcom_select(THD *thd, TABLE_LIST *all_tables)
result->reset_offset_limit(); result->reset_offset_limit();
thd->lex->query_plan_footprint->print_explain(result, thd->lex->describe); thd->lex->query_plan_footprint->print_explain(result, thd->lex->describe);
} }
delete thd->lex->query_plan_footprint; //delete thd->lex->query_plan_footprint;
thd->lex->query_plan_footprint= NULL; //thd->lex->query_plan_footprint= NULL;
//psergey-todo: here, produce the EXPLAIN output. //psergey-todo: here, produce the EXPLAIN output.
// mysql_explain_union() itself is only responsible for calling // mysql_explain_union() itself is only responsible for calling
......
...@@ -2486,6 +2486,7 @@ void reinit_stmt_before_use(THD *thd, LEX *lex) ...@@ -2486,6 +2486,7 @@ void reinit_stmt_before_use(THD *thd, LEX *lex)
object and because of this can be used in different threads. object and because of this can be used in different threads.
*/ */
lex->thd= thd; lex->thd= thd;
DBUG_ASSERT(!lex->query_plan_footprint);
if (lex->empty_field_list_on_rset) if (lex->empty_field_list_on_rset)
{ {
...@@ -3925,6 +3926,9 @@ bool Prepared_statement::execute(String *expanded_query, bool open_cursor) ...@@ -3925,6 +3926,9 @@ bool Prepared_statement::execute(String *expanded_query, bool open_cursor)
if (! cursor) if (! cursor)
cleanup_stmt(); 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->set_statement(&stmt_backup);
thd->stmt_arena= old_stmt_arena; thd->stmt_arena= old_stmt_arena;
......
...@@ -276,7 +276,9 @@ int mysql_update(THD *thd, ...@@ -276,7 +276,9 @@ int mysql_update(THD *thd,
ulonglong id; ulonglong id;
List<Item> all_fields; List<Item> all_fields;
killed_state killed_status= NOT_KILLED; 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* bool apc_target_enabled= false; // means was enabled *by code this function*
DBUG_ENTER("mysql_update"); DBUG_ENTER("mysql_update");
...@@ -315,12 +317,9 @@ int mysql_update(THD *thd, ...@@ -315,12 +317,9 @@ int mysql_update(THD *thd,
/* 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();
query_plan= new Update_plan; query_plan.select_lex= &thd->lex->select_lex;
query_plan->index= MAX_KEY; query_plan.table= table;
query_plan->using_filesort= FALSE;
query_plan->select_lex= &thd->lex->select_lex;
query_plan->table= table;
#ifndef NO_EMBEDDED_ACCESS_CHECKS #ifndef NO_EMBEDDED_ACCESS_CHECKS
/* Force privilege re-checking for views after they have been opened. */ /* Force privilege re-checking for views after they have been opened. */
want_privilege= (table_list->view ? UPDATE_ACL : want_privilege= (table_list->view ? UPDATE_ACL :
...@@ -379,7 +378,7 @@ int mysql_update(THD *thd, ...@@ -379,7 +378,7 @@ int mysql_update(THD *thd,
if (cond_value == Item::COND_FALSE) if (cond_value == Item::COND_FALSE)
{ {
limit= 0; // Impossible WHERE limit= 0; // Impossible WHERE
query_plan->set_impossible_where(); query_plan.set_impossible_where();
if (thd->lex->describe) if (thd->lex->describe)
goto exit_without_my_ok; goto exit_without_my_ok;
} }
...@@ -412,7 +411,7 @@ int mysql_update(THD *thd, ...@@ -412,7 +411,7 @@ int mysql_update(THD *thd,
if (error || !limit || thd->is_error() || if (error || !limit || thd->is_error() ||
(select && select->check_quick(thd, safe_update, limit))) (select && select->check_quick(thd, safe_update, limit)))
{ {
query_plan->set_impossible_where(); query_plan.set_impossible_where();
if (thd->lex->describe) if (thd->lex->describe)
goto exit_without_my_ok; goto exit_without_my_ok;
...@@ -454,16 +453,16 @@ int mysql_update(THD *thd, ...@@ -454,16 +453,16 @@ int mysql_update(THD *thd,
if (select && select->quick && select->quick->unique_key_range()) if (select && select->quick && select->quick->unique_key_range())
{ // Single row select (always "ordered"): Ok to use with key field UPDATE { // Single row select (always "ordered"): Ok to use with key field UPDATE
need_sort= FALSE; need_sort= FALSE;
query_plan->index= MAX_KEY; query_plan.index= MAX_KEY;
used_key_is_modified= FALSE; used_key_is_modified= FALSE;
} }
else else
{ {
query_plan->index= get_index_for_order(order, table, select, limit, query_plan.index= get_index_for_order(order, table, select, limit,
&need_sort, &reverse); &need_sort, &reverse);
if (select && select->quick) 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() && used_key_is_modified= (!select->quick->unique_key_range() &&
select->quick->is_keys_used(table->write_set)); select->quick->is_keys_used(table->write_set));
} }
...@@ -471,11 +470,11 @@ int mysql_update(THD *thd, ...@@ -471,11 +470,11 @@ int mysql_update(THD *thd,
{ {
if (need_sort) if (need_sort)
{ // Assign table scan index to check below for modified key fields: { // 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: { // 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, ...@@ -485,11 +484,9 @@ int mysql_update(THD *thd,
- Save the decisions in the query plan - Save the decisions in the query plan
- if we're running EXPLAIN UPDATE, get out - if we're running EXPLAIN UPDATE, get out
*/ */
query_plan->select= select; query_plan.select= select;
query_plan->possible_keys= table->quick_keys; query_plan.possible_keys= table->quick_keys;
query_plan->table_rows= table->file->stats.records; 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. Ok, we have generated a query plan for the UPDATE.
...@@ -498,8 +495,8 @@ int mysql_update(THD *thd, ...@@ -498,8 +495,8 @@ int mysql_update(THD *thd,
*/ */
if (thd->lex->describe) if (thd->lex->describe)
goto exit_without_my_ok; 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(); thd->apc_target.enable();
apc_target_enabled= true; apc_target_enabled= true;
DBUG_EXECUTE_IF("show_explain_probe_update_exec_start", DBUG_EXECUTE_IF("show_explain_probe_update_exec_start",
...@@ -518,8 +515,8 @@ int mysql_update(THD *thd, ...@@ -518,8 +515,8 @@ int mysql_update(THD *thd,
DBUG_ASSERT(table->read_set == &table->def_read_set); DBUG_ASSERT(table->read_set == &table->def_read_set);
DBUG_ASSERT(table->write_set == &table->def_write_set); DBUG_ASSERT(table->write_set == &table->def_write_set);
if (query_plan->index < MAX_KEY && old_covering_keys.is_set(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); table->add_read_columns_used_by_index(query_plan.index);
else else
table->use_all_columns(); table->use_all_columns();
...@@ -585,13 +582,13 @@ int mysql_update(THD *thd, ...@@ -585,13 +582,13 @@ int mysql_update(THD *thd,
Full index scan must be started with init_read_record_idx 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)) if (init_read_record(&info, thd, table, select, 0, 1, FALSE))
goto err; goto err;
} }
else 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"); thd_proc_info(thd, "Searching rows for update");
ha_rows tmp_limit= limit; ha_rows tmp_limit= limit;
...@@ -1005,12 +1002,6 @@ err: ...@@ -1005,12 +1002,6 @@ err:
if (apc_target_enabled) if (apc_target_enabled)
thd->apc_target.disable(); thd->apc_target.disable();
if (thd->lex->query_plan_footprint)
{
delete thd->lex->query_plan_footprint;
thd->lex->query_plan_footprint= NULL;
}
delete select; delete select;
free_underlaid_joins(thd, select_lex); free_underlaid_joins(thd, select_lex);
table->disable_keyread(); table->disable_keyread();
...@@ -1019,25 +1010,22 @@ err: ...@@ -1019,25 +1010,22 @@ err:
exit_without_my_ok: exit_without_my_ok:
DBUG_ASSERT(!apc_target_enabled); DBUG_ASSERT(!apc_target_enabled);
query_plan->save_query_plan_footprint(); query_plan.save_query_plan_footprint(thd->lex->query_plan_footprint);
thd->lex->query_plan_footprint->upd_del_plan= query_plan;
select_send *result; select_send *result;
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->query_plan_footprint->print_explain(result, 0); int err2= thd->lex->print_explain(result, 0 /* explain flags*/, &printed_anything);
if (err2) if (err2)
result->abort_result_set(); result->abort_result_set();
else else
result->send_eof(); result->send_eof();
delete thd->lex->query_plan_footprint;
thd->lex->query_plan_footprint= NULL;
delete select; delete select;
free_underlaid_joins(thd, select_lex); free_underlaid_joins(thd, select_lex);
DBUG_RETURN((error >= 0 || thd->is_error()) ? 1 : 0); DBUG_RETURN((error >= 0 || thd->is_error()) ? 1 : 0);
...@@ -1475,8 +1463,6 @@ bool mysql_multi_update(THD *thd, ...@@ -1475,8 +1463,6 @@ bool mysql_multi_update(THD *thd,
} }
select_lex->set_explain_type(FALSE); select_lex->set_explain_type(FALSE);
*result= NULL; /* no multi_update object */ *result= NULL; /* no multi_update object */
thd->lex->query_plan_footprint= new QPF_query;
} }
else else
{ {
...@@ -1506,15 +1492,6 @@ bool mysql_multi_update(THD *thd, ...@@ -1506,15 +1492,6 @@ bool mysql_multi_update(THD *thd,
DBUG_PRINT("info",("res: %d report_error: %d", res, (int) thd->is_error())); DBUG_PRINT("info",("res: %d report_error: %d", res, (int) thd->is_error()));
res|= 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)) if (unlikely(res))
(*result)->abort_result_set(); (*result)->abort_result_set();
else else
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment