Commit 98a8642f authored by Sergey Petrunya's avatar Sergey Petrunya

MDEV-3798: EXPLAIN UPDATE/DELETE

- Add support for EXPLAIN INSERT.
parent 69393db3
...@@ -181,3 +181,20 @@ explain partitions update t1 set b=12345 where a in (32,33); ...@@ -181,3 +181,20 @@ explain partitions update t1 set b=12345 where a in (32,33);
id select_type table partitions type possible_keys key key_len ref rows Extra id select_type table partitions type possible_keys key key_len ref rows Extra
1 SIMPLE NULL NULL NULL NULL NULL NULL NULL NULL No matching rows after partition pruning 1 SIMPLE NULL NULL NULL NULL NULL NULL NULL NULL No matching rows after partition pruning
drop table t1; drop table t1;
#
# Tests for EXPLAIN INSERT ... VALUES
#
create table t1 (a int, key(a));
explain insert into t1 values (1),(2),(3);
id select_type table type possible_keys key key_len ref rows Extra
1 INSERT t1 ALL NULL NULL NULL NULL NULL NULL
insert into t1 values (1),(2),(3);
create table t2 (a int, b int);
explain insert into t2 values
(10, 1+(select max(a) from t1)),
(11, 1+(select max(a+1) from t1));
id select_type table type possible_keys key key_len ref rows Extra
1 INSERT t2 ALL NULL NULL NULL NULL NULL NULL
3 SUBQUERY t1 index NULL a 5 NULL 3 Using index
2 SUBQUERY NULL NULL NULL NULL NULL NULL NULL Select tables optimized away
drop table t1,t2;
...@@ -155,3 +155,18 @@ explain partitions delete from t1 where a in (32,33); ...@@ -155,3 +155,18 @@ explain partitions delete from t1 where a in (32,33);
explain partitions update t1 set b=12345 where a in (32,33); explain partitions update t1 set b=12345 where a in (32,33);
drop table t1; drop table t1;
--echo #
--echo # Tests for EXPLAIN INSERT ... VALUES
--echo #
create table t1 (a int, key(a));
explain insert into t1 values (1),(2),(3);
insert into t1 values (1),(2),(3);
create table t2 (a int, b int);
explain insert into t2 values
(10, 1+(select max(a) from t1)),
(11, 1+(select max(a+1) from t1));
drop table t1,t2;
...@@ -328,6 +328,10 @@ sp_get_flags_for_command(LEX *lex) ...@@ -328,6 +328,10 @@ sp_get_flags_for_command(LEX *lex)
} }
case SQLCOM_UPDATE: case SQLCOM_UPDATE:
case SQLCOM_UPDATE_MULTI: case SQLCOM_UPDATE_MULTI:
case SQLCOM_INSERT:
case SQLCOM_REPLACE:
case SQLCOM_REPLACE_SELECT:
case SQLCOM_INSERT_SELECT:
{ {
if (!lex->describe) if (!lex->describe)
flags= 0; flags= 0;
......
...@@ -680,20 +680,8 @@ cleanup: ...@@ -680,20 +680,8 @@ cleanup:
/* Special exits */ /* Special exits */
exit_without_my_ok: exit_without_my_ok:
query_plan.save_explain_data(thd->lex->explain); query_plan.save_explain_data(thd->lex->explain);
int err2= thd->lex->explain->send_explain(thd);
select_send *result2;
if (!(result2= new select_send()))
return 1; /* purecov: inspected */
List<Item> dummy; /* note: looked in 5.6 and they too use a dummy list like this */
result2->prepare(dummy, &thd->lex->unit);
thd->send_explain_fields(result2);
int err2= thd->lex->explain->print_explain(result2, thd->lex->describe);
if (err2)
result2->abort_result_set();
else
result2->send_eof();
delete select; delete select;
free_underlaid_joins(thd, select_lex); free_underlaid_joins(thd, select_lex);
//table->set_keyread(false); //table->set_keyread(false);
......
...@@ -22,9 +22,8 @@ ...@@ -22,9 +22,8 @@
#include "sql_select.h" #include "sql_select.h"
Explain_query::Explain_query() Explain_query::Explain_query() : upd_del_plan(NULL), insert_plan(NULL)
{ {
upd_del_plan= NULL;
operations= 0; operations= 0;
} }
...@@ -32,6 +31,7 @@ Explain_query::Explain_query() ...@@ -32,6 +31,7 @@ Explain_query::Explain_query()
Explain_query::~Explain_query() Explain_query::~Explain_query()
{ {
delete upd_del_plan; delete upd_del_plan;
delete insert_plan;
uint i; uint i;
for (i= 0 ; i < unions.elements(); i++) for (i= 0 ; i < unions.elements(); i++)
delete unions.at(i); delete unions.at(i);
...@@ -100,18 +100,46 @@ void Explain_query::add_node(Explain_node *node) ...@@ -100,18 +100,46 @@ void Explain_query::add_node(Explain_node *node)
} }
/*
Send EXPLAIN output to the client.
*/
int Explain_query::send_explain(THD *thd)
{
select_result *result;
LEX *lex= thd->lex;
if (!(result= new select_send()) ||
thd->send_explain_fields(result))
return 1;
int res;
if ((res= print_explain(result, lex->describe)))
result->abort_result_set();
else
result->send_eof();
return res;
}
/* /*
The main entry point to print EXPLAIN of the entire query The main entry point to print EXPLAIN of the entire query
*/ */
int Explain_query::print_explain(select_result_sink *output, int Explain_query::print_explain(select_result_sink *output,
uint8 explain_flags) uint8 explain_flags)
{ {
if (upd_del_plan) if (upd_del_plan)
{ {
upd_del_plan->print_explain(this, output, explain_flags); upd_del_plan->print_explain(this, output, explain_flags);
return 0; return 0;
} }
else if (insert_plan)
{
insert_plan->print_explain(this, output, explain_flags);
return 0;
}
else else
{ {
/* Start printing from node with id=1 */ /* Start printing from node with id=1 */
...@@ -681,7 +709,7 @@ const char * Explain_quick_select::get_name_by_type() ...@@ -681,7 +709,7 @@ const char * Explain_quick_select::get_name_by_type()
return "sort_intersect"; return "sort_intersect";
default: default:
DBUG_ASSERT(0); DBUG_ASSERT(0);
return "Oops"; return "unknown quick select type";
} }
} }
...@@ -809,13 +837,34 @@ int Explain_update::print_explain(Explain_query *query, ...@@ -809,13 +837,34 @@ int Explain_update::print_explain(Explain_query *query,
key_str.length()? key_str.c_ptr() : NULL, key_str.length()? key_str.c_ptr() : NULL,
key_len_str.length() ? key_len_str.c_ptr() : NULL, key_len_str.length() ? key_len_str.c_ptr() : NULL,
NULL, /* 'ref' is always NULL in single-table EXPLAIN DELETE */ NULL, /* 'ref' is always NULL in single-table EXPLAIN DELETE */
rows, &rows,
extra_str.c_ptr()); extra_str.c_ptr());
return print_explain_for_children(query, output, explain_flags); return print_explain_for_children(query, output, explain_flags);
} }
int Explain_insert::print_explain(Explain_query *query, select_result_sink *output,
uint8 explain_flags)
{
const char *select_type="INSERT";
print_explain_row(output, explain_flags,
1, /* id */
select_type,
table_name.c_ptr(),
NULL, // partitions
JT_ALL,
NULL, // possible_keys
NULL, // key
NULL, // key_len
NULL, // ref
NULL, // rows
NULL);
return print_explain_for_children(query, output, explain_flags);
}
void delete_explain_query(LEX *lex) void delete_explain_query(LEX *lex)
{ {
delete lex->explain; delete lex->explain;
......
...@@ -37,10 +37,16 @@ class Explain_query; ...@@ -37,10 +37,16 @@ class Explain_query;
class Explain_node : public Sql_alloc class Explain_node : public Sql_alloc
{ {
public: public:
enum explain_node_type {EXPLAIN_UNION, EXPLAIN_SELECT, EXPLAIN_UPDATE, EXPLAIN_DELETE }; enum explain_node_type
virtual enum explain_node_type get_type()= 0; {
EXPLAIN_UNION,
EXPLAIN_SELECT,
EXPLAIN_UPDATE,
EXPLAIN_DELETE,
EXPLAIN_INSERT
};
virtual enum explain_node_type get_type()= 0;
virtual int get_select_id()= 0; virtual int get_select_id()= 0;
/* /*
...@@ -172,8 +178,10 @@ public: ...@@ -172,8 +178,10 @@ public:
bool using_filesort; bool using_filesort;
}; };
class Explain_delete;
class Explain_update;
class Explain_delete;
class Explain_insert;
/* /*
Explain structure for a query (i.e. a statement). Explain structure for a query (i.e. a statement).
...@@ -229,14 +237,20 @@ public: ...@@ -229,14 +237,20 @@ public:
/* Explain_delete inherits from Explain_update */ /* Explain_delete inherits from Explain_update */
Explain_update *upd_del_plan; Explain_update *upd_del_plan;
/* Query "plan" for INSERTs */
Explain_insert *insert_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);
/* Send tabular EXPLAIN to the client */
int send_explain(THD *thd);
/* Return tabular EXPLAIN output as a text string */ /* Return tabular EXPLAIN output as a text string */
bool print_explain_str(THD *thd, String *out_str); bool print_explain_str(THD *thd, String *out_str);
/* If true, at least part of EXPLAIN can be printed */ /* If true, at least part of EXPLAIN can be printed */
bool have_query_plan() { return upd_del_plan!= NULL || get_node(1) != NULL; } bool have_query_plan() { return insert_plan || upd_del_plan|| get_node(1) != NULL; }
MEM_ROOT *mem_root; MEM_ROOT *mem_root;
private: private:
Dynamic_array<Explain_union*> unions; Dynamic_array<Explain_union*> unions;
...@@ -445,7 +459,7 @@ private: ...@@ -445,7 +459,7 @@ private:
/* /*
Query Plan Footprint for single-table UPDATE. EXPLAIN structure for single-table UPDATE.
This is similar to Explain_table_access, except that it is more restrictive. This is similar to Explain_table_access, except that it is more restrictive.
Also, it can have UPDATE operation options, but currently there aren't any. Also, it can have UPDATE operation options, but currently there aren't any.
...@@ -482,8 +496,28 @@ public: ...@@ -482,8 +496,28 @@ public:
}; };
/*
EXPLAIN data structure for an INSERT.
At the moment this doesn't do much as we don't really have any query plans
for INSERT statements.
*/
class Explain_insert : public Explain_node
{
public:
StringBuffer<64> table_name;
enum explain_node_type get_type() { return EXPLAIN_INSERT; }
int get_select_id() { return 1; /* always root */ }
int print_explain(Explain_query *query, select_result_sink *output,
uint8 explain_flags);
};
/* /*
Explain data of a single-table DELETE. EXPLAIN data of a single-table DELETE.
*/ */
class Explain_delete: public Explain_update class Explain_delete: public Explain_update
......
...@@ -644,6 +644,36 @@ create_insert_stmt_from_insert_delayed(THD *thd, String *buf) ...@@ -644,6 +644,36 @@ create_insert_stmt_from_insert_delayed(THD *thd, String *buf)
} }
static void save_insert_query_plan(THD* thd, TABLE_LIST *table_list)
{
Explain_insert* explain= new Explain_insert;
explain->table_name.append(table_list->table->alias);
thd->lex->explain->insert_plan= explain;
/* See Update_plan::updating_a_view for details */
bool skip= test(table_list->view);
/* Save subquery children */
for (SELECT_LEX_UNIT *unit= thd->lex->select_lex.first_inner_unit();
unit;
unit= unit->next_unit())
{
if (skip)
{
skip= false;
continue;
}
/*
Table elimination doesn't work for INSERTS, but let's still have this
here for consistency
*/
if (!(unit->item && unit->item->eliminated))
explain->add_child(unit->first_select()->select_number);
}
}
/** /**
INSERT statement implementation INSERT statement implementation
...@@ -660,6 +690,7 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list, ...@@ -660,6 +690,7 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list,
enum_duplicates duplic, enum_duplicates duplic,
bool ignore) bool ignore)
{ {
bool retval= true;
int error, res; int error, res;
bool transactional_table, joins_freed= FALSE; bool transactional_table, joins_freed= FALSE;
bool changed; bool changed;
...@@ -780,6 +811,17 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list, ...@@ -780,6 +811,17 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list,
/* Restore the current context. */ /* Restore the current context. */
ctx_state.restore_state(context, table_list); ctx_state.restore_state(context, table_list);
if (thd->lex->unit.first_select()->optimize_unflattened_subqueries(false))
{
goto abort;
}
save_insert_query_plan(thd, table_list);
if (thd->lex->describe)
{
retval= 0;
goto exit_without_my_ok;
}
/* /*
Fill in the given fields and dump it to the table file Fill in the given fields and dump it to the table file
...@@ -1128,16 +1170,19 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list, ...@@ -1128,16 +1170,19 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list,
DBUG_RETURN(FALSE); DBUG_RETURN(FALSE);
abort: abort:
exit_without_my_ok:
#ifndef EMBEDDED_LIBRARY #ifndef EMBEDDED_LIBRARY
if (lock_type == TL_WRITE_DELAYED) if (lock_type == TL_WRITE_DELAYED)
end_delayed_insert(thd); end_delayed_insert(thd);
#endif #endif
if (table != NULL) if (table != NULL)
table->file->ha_release_auto_increment(); table->file->ha_release_auto_increment();
retval= thd->lex->explain->send_explain(thd);
if (!joins_freed) if (!joins_freed)
free_underlaid_joins(thd, &thd->lex->select_lex); free_underlaid_joins(thd, &thd->lex->select_lex);
thd->abort_on_warning= 0; thd->abort_on_warning= 0;
DBUG_RETURN(TRUE); DBUG_RETURN(retval);
} }
......
...@@ -3245,14 +3245,7 @@ end_with_restore_list: ...@@ -3245,14 +3245,7 @@ end_with_restore_list:
} }
if (!res && explain) if (!res && explain)
{ res= thd->lex->explain->send_explain(thd);
select_result *result= new select_send();
LEX *lex= thd->lex;
if (thd->send_explain_fields(result) ||
lex->explain->print_explain(result, lex->describe) ||
result->send_eof())
res= 1;
}
/* revert changes for SP */ /* revert changes for SP */
MYSQL_INSERT_SELECT_DONE(res, (ulong) thd->get_row_count_func()); MYSQL_INSERT_SELECT_DONE(res, (ulong) thd->get_row_count_func());
...@@ -3341,14 +3334,7 @@ end_with_restore_list: ...@@ -3341,14 +3334,7 @@ end_with_restore_list:
else else
{ {
if (explain) if (explain)
{ res= thd->lex->explain->send_explain(thd);
select_result *result= new select_send();
LEX *lex= thd->lex;
if (thd->send_explain_fields(result) ||
lex->explain->print_explain(result, lex->describe) ||
result->send_eof())
res= 1;
}
} }
delete result; delete result;
} }
......
...@@ -22238,7 +22238,10 @@ int print_explain_message_line(select_result_sink *result, ...@@ -22238,7 +22238,10 @@ int print_explain_message_line(select_result_sink *result,
if (options & DESCRIBE_EXTENDED) if (options & DESCRIBE_EXTENDED)
item_list.push_back(item_null); item_list.push_back(item_null);
item_list.push_back(new Item_string(message,strlen(message),cs)); if (message)
item_list.push_back(new Item_string(message,strlen(message),cs));
else
item_list.push_back(item_null);
if (result->send_data(item_list)) if (result->send_data(item_list))
return 1; return 1;
...@@ -22292,7 +22295,7 @@ int print_explain_row(select_result_sink *result, ...@@ -22292,7 +22295,7 @@ int print_explain_row(select_result_sink *result,
const char *index, const char *index,
const char *key_len, const char *key_len,
const char *ref, const char *ref,
ha_rows rows, ha_rows *rows,
const char *extra) const char *extra)
{ {
const CHARSET_INFO *cs= system_charset_info; const CHARSET_INFO *cs= system_charset_info;
...@@ -22337,15 +22340,24 @@ int print_explain_row(select_result_sink *result, ...@@ -22337,15 +22340,24 @@ int print_explain_row(select_result_sink *result,
item_list.push_back(item); item_list.push_back(item);
/* 'rows' */ /* 'rows' */
item_list.push_back(new Item_int(rows, if (rows)
MY_INT64_NUM_DECIMAL_DIGITS)); {
item_list.push_back(new Item_int(*rows,
MY_INT64_NUM_DECIMAL_DIGITS));
}
else
item_list.push_back(item_null);
/* 'filtered' */ /* 'filtered' */
const double filtered=100.0; const double filtered=100.0;
if (options & DESCRIBE_EXTENDED) if (options & DESCRIBE_EXTENDED)
item_list.push_back(new Item_float(filtered, 2)); item_list.push_back(new Item_float(filtered, 2));
/* 'Extra' */ /* 'Extra' */
item_list.push_back(new Item_string(extra, strlen(extra), cs)); if (extra)
item_list.push_back(new Item_string(extra, strlen(extra), cs));
else
item_list.push_back(item_null);
if (result->send_data(item_list)) if (result->send_data(item_list))
return 1; return 1;
......
...@@ -1865,7 +1865,7 @@ int print_explain_row(select_result_sink *result, ...@@ -1865,7 +1865,7 @@ int print_explain_row(select_result_sink *result,
const char *index, const char *index,
const char *key_len, const char *key_len,
const char *ref, const char *ref,
ha_rows rows, ha_rows *rows,
const char *extra); const char *extra);
void make_possible_keys_line(TABLE *table, key_map possible_keys, String *line); void make_possible_keys_line(TABLE *table, key_map possible_keys, String *line);
......
...@@ -948,7 +948,7 @@ int mysql_update(THD *thd, ...@@ -948,7 +948,7 @@ int mysql_update(THD *thd,
if (!transactional_table && updated > 0) if (!transactional_table && updated > 0)
thd->transaction.stmt.modified_non_trans_table= TRUE; thd->transaction.stmt.modified_non_trans_table= TRUE;
thd->apc_target.disable(); //psergey-todo. thd->apc_target.disable();
apc_target_enabled= false; apc_target_enabled= false;
end_read_record(&info); end_read_record(&info);
delete select; delete select;
...@@ -1035,19 +1035,8 @@ err: ...@@ -1035,19 +1035,8 @@ err:
exit_without_my_ok: exit_without_my_ok:
DBUG_ASSERT(!apc_target_enabled); DBUG_ASSERT(!apc_target_enabled);
query_plan.save_explain_data(thd->lex->explain); query_plan.save_explain_data(thd->lex->explain);
select_send *result; int err2= thd->lex->explain->send_explain(thd);
if (!(result= new select_send()))
return 1; /* purecov: inspected */
List<Item> dummy; /* note: looked in 5.6 and they too use a dummy list like this */
result->prepare(dummy, &thd->lex->unit);
thd->send_explain_fields(result);
int err2= thd->lex->explain->print_explain(result,
thd->lex->describe);
if (err2)
result->abort_result_set();
else
result->send_eof();
delete select; delete select;
free_underlaid_joins(thd, select_lex); free_underlaid_joins(thd, select_lex);
......
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