[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
This commit is contained in:
parent
6efa1d8c24
commit
0a560289aa
@ -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,
|
|||||||
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,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
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;
|
|||||||
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:
|
|||||||
|
|
||||||
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:
|
|||||||
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:
|
|||||||
/* 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:
|
|||||||
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,
|
|||||||
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 @@
|
|||||||
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()
|
|
||||||
{
|
|
||||||
select_lex->set_explain_type(TRUE);
|
|
||||||
|
|
||||||
|
void Update_plan::save_query_plan_footprint(QPF_query *query)
|
||||||
|
{
|
||||||
|
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()
|
|||||||
(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,
|
|||||||
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,
|
|||||||
}
|
}
|
||||||
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,
|
|||||||
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,
|
|||||||
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,
|
|||||||
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,
|
|||||||
|
|
||||||
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,
|
|||||||
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,
|
|||||||
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,
|
|||||||
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,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
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,
|
|||||||
(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:
|
|||||||
|
|
||||||
/* 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:
|
|||||||
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)
|
|||||||
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;
|
|||||||
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:
|
|||||||
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:
|
|||||||
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:
|
|||||||
/* 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:
|
|||||||
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
|
|||||||
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)
|
|||||||
#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,
|
|||||||
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)
|
|||||||
{
|
{
|
||||||
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)
|
|||||||
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)
|
|||||||
/* 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:
|
|||||||
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:
|
|||||||
{
|
{
|
||||||
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)
|
|||||||
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)
|
|||||||
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)
|
|||||||
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)
|
|||||||
|
|
||||||
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,
|
|||||||
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,
|
|||||||
/* 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,
|
|||||||
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,
|
|||||||
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,
|
|||||||
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,
|
|||||||
{
|
{
|
||||||
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,
|
|||||||
- 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,
|
|||||||
*/
|
*/
|
||||||
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,
|
|||||||
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,
|
|||||||
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:
|
|||||||
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:
|
|||||||
|
|
||||||
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,
|
|||||||
}
|
}
|
||||||
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,
|
|||||||
|
|
||||||
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
|
||||||
|
Loading…
x
Reference in New Issue
Block a user