1
0
mirror of https://github.com/MariaDB/server.git synced 2025-08-08 11:22:35 +03:00

MDEV-3798: EXPLAIN UPDATE/DELETE

- Add support for EXPLAIN INSERT.
This commit is contained in:
Sergey Petrunya
2013-10-07 17:29:51 +04:00
parent 69393db3d1
commit 98a8642fe8
11 changed files with 201 additions and 62 deletions

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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);

View File

@@ -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()
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)
} }
/*
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()
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,
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;

View File

@@ -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
{
EXPLAIN_UNION,
EXPLAIN_SELECT,
EXPLAIN_UPDATE,
EXPLAIN_DELETE,
EXPLAIN_INSERT
};
virtual enum explain_node_type get_type()= 0; 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:
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:
/* 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:
/* /*
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:
}; };
/*
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

View File

@@ -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,
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,
/* 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,
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);
} }

View File

@@ -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:
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;
} }

View File

@@ -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,
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,
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;

View File

@@ -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);

View File

@@ -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:
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);