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:
@@ -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 };
|
||||
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);
|
||||
|
Reference in New Issue
Block a user