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);
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
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);
explain partitions update t1 set b=12345 where a in (32,33);
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)
}
case SQLCOM_UPDATE:
case SQLCOM_UPDATE_MULTI:
case SQLCOM_INSERT:
case SQLCOM_REPLACE:
case SQLCOM_REPLACE_SELECT:
case SQLCOM_INSERT_SELECT:
{
if (!lex->describe)
flags= 0;
......
......@@ -680,20 +680,8 @@ cleanup:
/* Special exits */
exit_without_my_ok:
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;
free_underlaid_joins(thd, select_lex);
//table->set_keyread(false);
......
......@@ -22,9 +22,8 @@
#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;
}
......@@ -32,6 +31,7 @@ Explain_query::Explain_query()
Explain_query::~Explain_query()
{
delete upd_del_plan;
delete insert_plan;
uint i;
for (i= 0 ; i < unions.elements(); 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
*/
int Explain_query::print_explain(select_result_sink *output,
uint8 explain_flags)
uint8 explain_flags)
{
if (upd_del_plan)
{
upd_del_plan->print_explain(this, output, explain_flags);
return 0;
}
else if (insert_plan)
{
insert_plan->print_explain(this, output, explain_flags);
return 0;
}
else
{
/* Start printing from node with id=1 */
......@@ -681,7 +709,7 @@ const char * Explain_quick_select::get_name_by_type()
return "sort_intersect";
default:
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_len_str.length() ? key_len_str.c_ptr() : NULL,
NULL, /* 'ref' is always NULL in single-table EXPLAIN DELETE */
rows,
&rows,
extra_str.c_ptr());
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)
{
delete lex->explain;
......
......@@ -37,10 +37,16 @@ class Explain_query;
class Explain_node : public Sql_alloc
{
public:
enum explain_node_type {EXPLAIN_UNION, EXPLAIN_SELECT, EXPLAIN_UPDATE, EXPLAIN_DELETE };
virtual enum explain_node_type get_type()= 0;
enum explain_node_type
{
EXPLAIN_UNION,
EXPLAIN_SELECT,
EXPLAIN_UPDATE,
EXPLAIN_DELETE,
EXPLAIN_INSERT
};
virtual enum explain_node_type get_type()= 0;
virtual int get_select_id()= 0;
/*
......@@ -172,8 +178,10 @@ public:
bool using_filesort;
};
class Explain_delete;
class Explain_update;
class Explain_delete;
class Explain_insert;
/*
Explain structure for a query (i.e. a statement).
......@@ -229,14 +237,20 @@ public:
/* Explain_delete inherits from Explain_update */
Explain_update *upd_del_plan;
/* Query "plan" for INSERTs */
Explain_insert *insert_plan;
/* Produce a tabular EXPLAIN output */
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 */
bool print_explain_str(THD *thd, String *out_str);
/* 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;
private:
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.
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
......
......@@ -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
......@@ -660,6 +690,7 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list,
enum_duplicates duplic,
bool ignore)
{
bool retval= true;
int error, res;
bool transactional_table, joins_freed= FALSE;
bool changed;
......@@ -780,6 +811,17 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list,
/* Restore the current context. */
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
......@@ -1128,16 +1170,19 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list,
DBUG_RETURN(FALSE);
abort:
exit_without_my_ok:
#ifndef EMBEDDED_LIBRARY
if (lock_type == TL_WRITE_DELAYED)
end_delayed_insert(thd);
#endif
if (table != NULL)
table->file->ha_release_auto_increment();
retval= thd->lex->explain->send_explain(thd);
if (!joins_freed)
free_underlaid_joins(thd, &thd->lex->select_lex);
thd->abort_on_warning= 0;
DBUG_RETURN(TRUE);
DBUG_RETURN(retval);
}
......
......@@ -3245,14 +3245,7 @@ end_with_restore_list:
}
if (!res && explain)
{
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;
}
res= thd->lex->explain->send_explain(thd);
/* revert changes for SP */
MYSQL_INSERT_SELECT_DONE(res, (ulong) thd->get_row_count_func());
......@@ -3341,14 +3334,7 @@ end_with_restore_list:
else
{
if (explain)
{
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;
}
res= thd->lex->explain->send_explain(thd);
}
delete result;
}
......
......@@ -22238,7 +22238,10 @@ int print_explain_message_line(select_result_sink *result,
if (options & DESCRIBE_EXTENDED)
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))
return 1;
......@@ -22292,7 +22295,7 @@ int print_explain_row(select_result_sink *result,
const char *index,
const char *key_len,
const char *ref,
ha_rows rows,
ha_rows *rows,
const char *extra)
{
const CHARSET_INFO *cs= system_charset_info;
......@@ -22337,15 +22340,24 @@ int print_explain_row(select_result_sink *result,
item_list.push_back(item);
/* 'rows' */
item_list.push_back(new Item_int(rows,
MY_INT64_NUM_DECIMAL_DIGITS));
if (rows)
{
item_list.push_back(new Item_int(*rows,
MY_INT64_NUM_DECIMAL_DIGITS));
}
else
item_list.push_back(item_null);
/* 'filtered' */
const double filtered=100.0;
if (options & DESCRIBE_EXTENDED)
item_list.push_back(new Item_float(filtered, 2));
/* '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))
return 1;
......
......@@ -1865,7 +1865,7 @@ int print_explain_row(select_result_sink *result,
const char *index,
const char *key_len,
const char *ref,
ha_rows rows,
ha_rows *rows,
const char *extra);
void make_possible_keys_line(TABLE *table, key_map possible_keys, String *line);
......
......@@ -948,7 +948,7 @@ int mysql_update(THD *thd,
if (!transactional_table && updated > 0)
thd->transaction.stmt.modified_non_trans_table= TRUE;
thd->apc_target.disable(); //psergey-todo.
thd->apc_target.disable();
apc_target_enabled= false;
end_read_record(&info);
delete select;
......@@ -1035,19 +1035,8 @@ err:
exit_without_my_ok:
DBUG_ASSERT(!apc_target_enabled);
query_plan.save_explain_data(thd->lex->explain);
select_send *result;
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();
int err2= thd->lex->explain->send_explain(thd);
delete select;
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