1
0
mirror of https://github.com/MariaDB/server.git synced 2025-08-01 03:47:19 +03:00

[SHOW] EXPLAIN UPDATE/DELETE - Merge with 10.0-base

This commit is contained in:
Sergey Petrunya
2013-05-27 09:31:41 +04:00
17 changed files with 1015 additions and 112 deletions

View File

@ -0,0 +1,104 @@
drop table if exists t0;
create table t0 (a int) engine=myisam;
insert into t0 values (1),(2),(3),(4),(5),(6),(7),(8);
#
# Tests for single-table DELETE
#
explain select * from t0 where a=3;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t0 ALL NULL NULL NULL NULL 8 Using where
explain delete from t0 where a=3;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t0 ALL NULL NULL NULL NULL 8 Using where
# DELETE without WHERE is a special case:
explain delete from t0;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE NULL NULL NULL NULL NULL NULL NULL Deleting all rows
create table t1 (a int, b int, filler char(100), key(a), key(b));
insert into t1
select A.a+10*B.a + 10*C.a, A.a+10*B.a + 10*C.a, 'filler'
from t0 A, t0 B, t0 C;
# This should use an index, possible_keys=NULL because there is no WHERE
explain delete from t1 order by a limit 2;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 index NULL a NULL NULL 512
# This should use range, possible_keys={a,b}
explain delete from t1 where a<20 and b < 10;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 range a,b a 5 NULL 1 Using where
# This should use ALL + filesort
explain delete from t1 order by a+1 limit 2;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 ALL NULL NULL NULL NULL 512 Using filesort
# This should use range + using filesort
explain delete from t1 where a<20 order by b limit 2;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 range a a 5 NULL 1 Using where; Using filesort
# Try some subqueries:
explain delete from t1 where a < (select max(a) from t0);
id select_type table type possible_keys key key_len ref rows Extra
1 PRIMARY t1 range a a 5 NULL 1 Using where
2 SUBQUERY t0 ALL NULL NULL NULL NULL 8
explain delete from t1 where a < (select max(a) from t0 where a < t1.b);
id select_type table type possible_keys key key_len ref rows Extra
1 PRIMARY t1 ALL NULL NULL NULL NULL 512 Using where
2 DEPENDENT SUBQUERY t0 ALL NULL NULL NULL NULL 8 Using where
#
# Tests for multi-table DELETE
#
explain delete t1 from t0, t1 where t0.a = t1.a;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t0 ALL NULL NULL NULL NULL 8 Using where
1 SIMPLE t1 ref a a 5 test.t0.a 4 Using index
drop table t0, t1;
# ###################################################################
# ## EXPLAIN UPDATE tests
# ###################################################################
create table t0 (a int) engine=myisam;
insert into t0 values (1),(2),(3),(4),(5),(6),(7),(8);
explain update t0 set a=3 where a=4;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t0 ALL NULL NULL NULL NULL 8 Using where
create table t1 (a int, b int, filler char(100), key(a), key(b));
insert into t1
select A.a+10*B.a + 10*C.a, A.a+10*B.a + 10*C.a, 'filler'
from t0 A, t0 B, t0 C;
explain update t1 set a=a+1 where 3>4;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE NULL NULL NULL NULL NULL NULL NULL Impossible where
explain update t1 set a=a+1 where a=3 and a=4;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE NULL NULL NULL NULL NULL NULL NULL Impossible where
# This should use an index, possible_keys=NULL because there is no WHERE
explain update t1 set a=a+1 order by a limit 2;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 ALL NULL NULL NULL NULL 512
# This should use range, possible_keys={a,b}
explain update t1 set filler='fooo' where a<20 and b < 10;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 range a,b a 5 NULL 1 Using where
# This should use ALL + filesort
explain update t1 set filler='fooo' order by a+1 limit 2;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 ALL NULL NULL NULL NULL 512
# This should use range + using filesort
explain update t1 set filler='fooo' where a<20 order by b limit 2;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 range a a 5 NULL 1 Using where
# Try some subqueries:
explain update t1 set filler='fooo' where a < (select max(a) from t0);
id select_type table type possible_keys key key_len ref rows Extra
1 PRIMARY t1 range a a 5 NULL 1 Using where
2 SUBQUERY t0 ALL NULL NULL NULL NULL 8
explain update t1 set filler='fooo' where a < (select max(a) from t0 where a < t1.b);
id select_type table type possible_keys key key_len ref rows Extra
1 PRIMARY t1 ALL NULL NULL NULL NULL 512 Using where
2 DEPENDENT SUBQUERY t0 ALL NULL NULL NULL NULL 8 Using where
#
# Tests for multi-table UPDATE
#
explain update t0, t1 set t1.a=t1.a+1 where t0.a = t1.a;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t0 ALL NULL NULL NULL NULL 8 Using where
1 SIMPLE t1 ref a a 5 test.t0.a 4 Using index
drop table t0, t1;

View File

@ -186,22 +186,38 @@ set @show_explain_probe_select_id=2;
set debug_dbug='+d,show_explain_probe_join_exec_start'; set debug_dbug='+d,show_explain_probe_join_exec_start';
update t2 set dummy=0 where (select max(a) from t0 where t2.a + t0.a <3) >3 ; update t2 set dummy=0 where (select max(a) from t0 where t2.a + t0.a <3) >3 ;
show explain for $thr2; show explain for $thr2;
ERROR HY000: Target is not running an EXPLAINable command id select_type table type possible_keys key key_len ref rows Extra
1 PRIMARY t2 ALL NULL NULL NULL NULL 2 Using where
2 DEPENDENT SUBQUERY t0 ALL NULL NULL NULL NULL 10 Using where
Warnings:
Note 1003 update t2 set dummy=0 where (select max(a) from t0 where t2.a + t0.a <3) >3
show explain for $thr2; show explain for $thr2;
ERROR HY000: Target is not running an EXPLAINable command id select_type table type possible_keys key key_len ref rows Extra
1 PRIMARY t2 ALL NULL NULL NULL NULL 2 Using where
2 DEPENDENT SUBQUERY t0 ALL NULL NULL NULL NULL 10 Using where
Warnings:
Note 1003 update t2 set dummy=0 where (select max(a) from t0 where t2.a + t0.a <3) >3
drop table t2; drop table t2;
set debug_dbug=@old_debug; set debug_dbug=@old_debug;
# #
# Attempt SHOW EXPLAIN for a DELETE # Attempt SHOW EXPLAIN for a DELETE (UPD: now works)
# #
create table t2 as select a as a, a as dummy from t0 limit 2; create table t2 as select a as a, a as dummy from t0 limit 2;
set @show_explain_probe_select_id=2; set @show_explain_probe_select_id=2;
set debug_dbug='+d,show_explain_probe_join_exec_start'; set debug_dbug='+d,show_explain_probe_join_exec_start';
delete from t2 where (select max(a) from t0 where t2.a + t0.a <3) >3 ; delete from t2 where (select max(a) from t0 where t2.a + t0.a <3) >3 ;
show explain for $thr2; show explain for $thr2;
ERROR HY000: Target is not running an EXPLAINable command id select_type table type possible_keys key key_len ref rows Extra
1 PRIMARY t2 ALL NULL NULL NULL NULL 2 Using where
2 DEPENDENT SUBQUERY t0 ALL NULL NULL NULL NULL 10 Using where
Warnings:
Note 1003 delete from t2 where (select max(a) from t0 where t2.a + t0.a <3) >3
show explain for $thr2; show explain for $thr2;
ERROR HY000: Target is not running an EXPLAINable command id select_type table type possible_keys key key_len ref rows Extra
1 PRIMARY t2 ALL NULL NULL NULL NULL 2 Using where
2 DEPENDENT SUBQUERY t0 ALL NULL NULL NULL NULL 10 Using where
Warnings:
Note 1003 delete from t2 where (select max(a) from t0 where t2.a + t0.a <3) >3
drop table t2; drop table t2;
set debug_dbug=@old_debug; set debug_dbug=@old_debug;
# #
@ -220,13 +236,13 @@ Note 1003 select t2.a, ((select max(a) from t0 where t2.a + t0.a <3) >3) as SUBQ
show explain for $thr2; show explain for $thr2;
id select_type table type possible_keys key key_len ref rows Extra id select_type table type possible_keys key key_len ref rows Extra
1 PRIMARY t2 ALL NULL NULL NULL NULL 3 1 PRIMARY t2 ALL NULL NULL NULL NULL 3
2 DEPENDENT SUBQUERY NULL NULL NULL NULL NULL NULL NULL Query plan already deleted 2 DEPENDENT SUBQUERY t0 ALL NULL NULL NULL NULL 10 Using where
Warnings: Warnings:
Note 1003 select t2.a, ((select max(a) from t0 where t2.a + t0.a <3) >3) as SUBQ from t2 Note 1003 select t2.a, ((select max(a) from t0 where t2.a + t0.a <3) >3) as SUBQ from t2
show explain for $thr2; show explain for $thr2;
id select_type table type possible_keys key key_len ref rows Extra id select_type table type possible_keys key key_len ref rows Extra
1 PRIMARY t2 ALL NULL NULL NULL NULL 3 1 PRIMARY t2 ALL NULL NULL NULL NULL 3
2 DEPENDENT SUBQUERY NULL NULL NULL NULL NULL NULL NULL Query plan already deleted 2 DEPENDENT SUBQUERY t0 ALL NULL NULL NULL NULL 10 Using where
Warnings: Warnings:
Note 1003 select t2.a, ((select max(a) from t0 where t2.a + t0.a <3) >3) as SUBQ from t2 Note 1003 select t2.a, ((select max(a) from t0 where t2.a + t0.a <3) >3) as SUBQ from t2
a SUBQ a SUBQ

View File

@ -0,0 +1,44 @@
drop table if exists t0, t1;
SET @old_debug= @@session.debug;
set debug_sync='RESET';
create table t0 (a int) engine=myisam;
insert into t0 values (1),(2),(3),(4),(5),(6),(7),(8);
create table t1 (a int, b int, filler char(100), key(a), key(b));
insert into t1
select A.a+10*B.a + 10*C.a, A.a+10*B.a + 10*C.a, 'filler'
from t0 A, t0 B, t0 C;
#
# Test SHOW EXPLAIN for single-table DELETE
#
set debug_dbug='+d,show_explain_probe_delete_exec_start';
delete from t1 where a<10 and b+1>1000;
show explain for $thr2;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 range a a 5 NULL 1 Using where
Warnings:
Note 1003 delete from t1 where a<10 and b+1>1000
#
# Test SHOW EXPLAIN for multi-table DELETE
#
set @show_explain_probe_select_id=1;
set debug_dbug='+d,show_explain_probe_do_select';
delete t1 from t1, t0 where t0.a=t1.a and t1.b +1 > 1000;
show explain for $thr2;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t0 ALL NULL NULL NULL NULL 8 Using where
1 SIMPLE t1 ref a a 5 test.t0.a 4 Using where
Warnings:
Note 1003 delete t1 from t1, t0 where t0.a=t1.a and t1.b +1 > 1000
#
# Test SHOW EXPLAIN for single-table UPDATE
#
set debug_dbug='+d,show_explain_probe_update_exec_start';
update t1 set filler='filler-data-2' where a<10 and b+1>1000;
show explain for $thr2;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 range a a 5 NULL 1 Using where
Warnings:
Note 1003 update t1 set filler='filler-data-2' where a<10 and b+1>1000
drop table t0,t1;
set debug_dbug=@old_debug;
set debug_sync='RESET';

View File

@ -0,0 +1,86 @@
#
# MariaDB tests for EXPLAIN UPDATE/DELETE.
#
--disable_warnings
drop table if exists t0;
--enable_warnings
create table t0 (a int) engine=myisam;
insert into t0 values (1),(2),(3),(4),(5),(6),(7),(8);
--echo #
--echo # Tests for single-table DELETE
--echo #
explain select * from t0 where a=3;
explain delete from t0 where a=3;
--echo # DELETE without WHERE is a special case:
explain delete from t0;
create table t1 (a int, b int, filler char(100), key(a), key(b));
insert into t1
select A.a+10*B.a + 10*C.a, A.a+10*B.a + 10*C.a, 'filler'
from t0 A, t0 B, t0 C;
--echo # This should use an index, possible_keys=NULL because there is no WHERE
explain delete from t1 order by a limit 2;
--echo # This should use range, possible_keys={a,b}
explain delete from t1 where a<20 and b < 10;
--echo # This should use ALL + filesort
explain delete from t1 order by a+1 limit 2;
--echo # This should use range + using filesort
explain delete from t1 where a<20 order by b limit 2;
--echo # Try some subqueries:
explain delete from t1 where a < (select max(a) from t0);
explain delete from t1 where a < (select max(a) from t0 where a < t1.b);
--echo #
--echo # Tests for multi-table DELETE
--echo #
explain delete t1 from t0, t1 where t0.a = t1.a;
drop table t0, t1;
--echo # ###################################################################
--echo # ## EXPLAIN UPDATE tests
--echo # ###################################################################
create table t0 (a int) engine=myisam;
insert into t0 values (1),(2),(3),(4),(5),(6),(7),(8);
explain update t0 set a=3 where a=4;
create table t1 (a int, b int, filler char(100), key(a), key(b));
insert into t1
select A.a+10*B.a + 10*C.a, A.a+10*B.a + 10*C.a, 'filler'
from t0 A, t0 B, t0 C;
explain update t1 set a=a+1 where 3>4;
explain update t1 set a=a+1 where a=3 and a=4;
--echo # This should use an index, possible_keys=NULL because there is no WHERE
explain update t1 set a=a+1 order by a limit 2;
--echo # This should use range, possible_keys={a,b}
explain update t1 set filler='fooo' where a<20 and b < 10;
--echo # This should use ALL + filesort
explain update t1 set filler='fooo' order by a+1 limit 2;
--echo # This should use range + using filesort
explain update t1 set filler='fooo' where a<20 order by b limit 2;
--echo # Try some subqueries:
explain update t1 set filler='fooo' where a < (select max(a) from t0);
explain update t1 set filler='fooo' where a < (select max(a) from t0 where a < t1.b);
--echo #
--echo # Tests for multi-table UPDATE
--echo #
explain update t0, t1 set t1.a=t1.a+1 where t0.a = t1.a;
drop table t0, t1;

View File

@ -238,10 +238,10 @@ set debug_dbug='+d,show_explain_probe_join_exec_start';
send update t2 set dummy=0 where (select max(a) from t0 where t2.a + t0.a <3) >3 ; send update t2 set dummy=0 where (select max(a) from t0 where t2.a + t0.a <3) >3 ;
connection default; connection default;
--source include/wait_condition.inc --source include/wait_condition.inc
--error ER_TARGET_NOT_EXPLAINABLE #--error ER_TARGET_NOT_EXPLAINABLE
evalp show explain for $thr2; evalp show explain for $thr2;
--source include/wait_condition.inc --source include/wait_condition.inc
--error ER_TARGET_NOT_EXPLAINABLE #--error ER_TARGET_NOT_EXPLAINABLE
evalp show explain for $thr2; evalp show explain for $thr2;
connection con1; connection con1;
reap; reap;
@ -249,7 +249,7 @@ drop table t2;
set debug_dbug=@old_debug; set debug_dbug=@old_debug;
--echo # --echo #
--echo # Attempt SHOW EXPLAIN for a DELETE --echo # Attempt SHOW EXPLAIN for a DELETE (UPD: now works)
--echo # --echo #
create table t2 as select a as a, a as dummy from t0 limit 2; create table t2 as select a as a, a as dummy from t0 limit 2;
set @show_explain_probe_select_id=2; set @show_explain_probe_select_id=2;
@ -257,10 +257,10 @@ set debug_dbug='+d,show_explain_probe_join_exec_start';
send delete from t2 where (select max(a) from t0 where t2.a + t0.a <3) >3 ; send delete from t2 where (select max(a) from t0 where t2.a + t0.a <3) >3 ;
connection default; connection default;
--source include/wait_condition.inc --source include/wait_condition.inc
--error ER_TARGET_NOT_EXPLAINABLE #--error ER_TARGET_NOT_EXPLAINABLE
evalp show explain for $thr2; evalp show explain for $thr2;
--source include/wait_condition.inc --source include/wait_condition.inc
--error ER_TARGET_NOT_EXPLAINABLE #--error ER_TARGET_NOT_EXPLAINABLE
evalp show explain for $thr2; evalp show explain for $thr2;
connection con1; connection con1;
reap; reap;

View File

@ -0,0 +1,78 @@
#
# SHOW EXPLAIN tests for non-select subqueries
#
#--source include/have_debug.inc
#--source include/have_innodb.inc
#--source include/not_embedded.inc
--disable_warnings
drop table if exists t0, t1;
--enable_warnings
SET @old_debug= @@session.debug;
set debug_sync='RESET';
#
# Setup two threads and their ids
#
let $thr1=`select connection_id()`;
connect (con2, localhost, root,,);
connection con2;
let $thr2=`select connection_id()`;
connection default;
#
# Create tables
#
create table t0 (a int) engine=myisam;
insert into t0 values (1),(2),(3),(4),(5),(6),(7),(8);
create table t1 (a int, b int, filler char(100), key(a), key(b));
insert into t1
select A.a+10*B.a + 10*C.a, A.a+10*B.a + 10*C.a, 'filler'
from t0 A, t0 B, t0 C;
let $wait_condition= select State='show_explain_trap' from information_schema.processlist where id=$thr2;
--echo #
--echo # Test SHOW EXPLAIN for single-table DELETE
--echo #
connection con2;
set debug_dbug='+d,show_explain_probe_delete_exec_start';
send delete from t1 where a<10 and b+1>1000;
connection default;
--source include/wait_condition.inc
evalp show explain for $thr2;
connection con2;
reap;
--echo #
--echo # Test SHOW EXPLAIN for multi-table DELETE
--echo #
set @show_explain_probe_select_id=1;
set debug_dbug='+d,show_explain_probe_do_select';
send delete t1 from t1, t0 where t0.a=t1.a and t1.b +1 > 1000;
connection default;
--source include/wait_condition.inc
evalp show explain for $thr2;
connection con2;
reap;
--echo #
--echo # Test SHOW EXPLAIN for single-table UPDATE
--echo #
connection con2;
set debug_dbug='+d,show_explain_probe_update_exec_start';
send update t1 set filler='filler-data-2' where a<10 and b+1>1000;
connection default;
--source include/wait_condition.inc
evalp show explain for $thr2;
connection con2;
reap;
drop table t0,t1;
set debug_dbug=@old_debug;
set debug_sync='RESET';

View File

@ -64,6 +64,8 @@ public:
{ {
return test(apc_calls); return test(apc_calls);
} }
inline bool is_enabled() { return enabled; }
/* Functor class for calls you can schedule */ /* Functor class for calls you can schedule */
class Apc_call class Apc_call

View File

@ -40,6 +40,147 @@
#include "records.h" // init_read_record, #include "records.h" // init_read_record,
#include "sql_derived.h" // mysql_handle_list_of_derived #include "sql_derived.h" // mysql_handle_list_of_derived
// end_read_record // end_read_record
/*
@brief
Print query plan of a single-table DELETE command
@detail
This function is used by EXPLAIN DELETE and by SHOW EXPLAIN when it is
invoked on a running DELETE statement.
*/
int Delete_plan::print_explain(select_result_sink *output, uint8 explain_flags,
bool *printed_anything)
{
if (deleting_all_rows)
{
const char *msg= "Deleting all rows";
if (print_explain_message_line(output, explain_flags, 1/*select number*/,
"SIMPLE", msg))
{
return 1;
}
*printed_anything= true;
return 0;
}
return Update_plan::print_explain(output, explain_flags, printed_anything);
}
int Update_plan::print_explain(select_result_sink *output, uint8 explain_flags,
bool *printed_anything)
{
if (impossible_where)
{
const char *msg= "Impossible where";
if (print_explain_message_line(output, explain_flags, 1/*select number*/,
"SIMPLE", msg))
{
return 1;
}
*printed_anything= true;
return 0;
}
select_lex->set_explain_type(FALSE);
/*
Print an EXPLAIN line. We dont have join, so we can't directly use
JOIN::print_explain.
We do have a SELECT_LEX (TODO but how is it useful? it has select_type..
and that's it?)
*/
enum join_type jtype;
if (select && select->quick)
{
int quick_type= select->quick->get_type();
if ((quick_type == QUICK_SELECT_I::QS_TYPE_INDEX_MERGE) ||
(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_UNION))
jtype= JT_INDEX_MERGE;
else
jtype= JT_RANGE;
}
else
{
if (index == MAX_KEY)
jtype= JT_ALL;
else
jtype= JT_NEXT;
}
StringBuffer<128> possible_keys_line;
make_possible_keys_line(table, possible_keys, &possible_keys_line);
const char *key_name;
const char *key_len;
StringBuffer<128> key_str;
StringBuffer<128> key_len_str;
StringBuffer<128> extra_str;
/* Calculate key_len */
if (select && select->quick)
{
select->quick->add_keys_and_lengths(&key_str, &key_len_str);
key_name= key_str.c_ptr();
key_len= key_len_str.c_ptr();
}
else
{
key_name= (index == MAX_KEY)? NULL : table->key_info[index].name;
key_len= NULL;
}
if (select && select->cond)
extra_str.append(STRING_WITH_LEN("Using where"));
if (select && select->quick &&
select->quick->get_type() == QUICK_SELECT_I::QS_TYPE_RANGE)
{
explain_append_mrr_info((QUICK_RANGE_SELECT*)select->quick, &extra_str);
}
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,
jtype,
possible_keys_line.length()? possible_keys_line.c_ptr(): NULL,
key_name,
key_len,
NULL, /* 'ref' is always NULL in single-table EXPLAIN DELETE */
select ? select->records : table_rows,
extra_str.c_ptr());
*printed_anything= true;
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.
@ -61,12 +202,16 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds,
bool const_cond_result; bool const_cond_result;
ha_rows deleted= 0; ha_rows deleted= 0;
bool reverse= FALSE; bool reverse= FALSE;
bool err= true;
ORDER *order= (ORDER *) ((order_list && order_list->elements) ? ORDER *order= (ORDER *) ((order_list && order_list->elements) ?
order_list->first : NULL); order_list->first : NULL);
uint usable_index= MAX_KEY;
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;
query_plan.index= MAX_KEY;
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))
@ -90,6 +235,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.table= table;
if (mysql_prepare_delete(thd, table_list, &conds)) if (mysql_prepare_delete(thd, table_list, &conds))
DBUG_RETURN(TRUE); DBUG_RETURN(TRUE);
@ -163,6 +310,11 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds,
table->file->info(HA_STATUS_VARIABLE | HA_STATUS_NO_LOCK); table->file->info(HA_STATUS_VARIABLE | HA_STATUS_NO_LOCK);
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);
if (thd->lex->describe)
goto exit_without_my_ok;
if (!(error=table->file->ha_delete_all_rows())) if (!(error=table->file->ha_delete_all_rows()))
{ {
/* /*
@ -187,7 +339,12 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds,
Item::cond_result result; Item::cond_result result;
conds= remove_eq_conds(thd, conds, &result); conds= remove_eq_conds(thd, conds, &result);
if (result == Item::COND_FALSE) // Impossible where if (result == Item::COND_FALSE) // Impossible where
{
limit= 0; limit= 0;
query_plan.set_impossible_where();
if (thd->lex->describe)
goto exit_without_my_ok;
}
} }
#ifdef WITH_PARTITION_STORAGE_ENGINE #ifdef WITH_PARTITION_STORAGE_ENGINE
@ -195,6 +352,7 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds,
{ {
free_underlaid_joins(thd, select_lex); free_underlaid_joins(thd, select_lex);
// No matching record // No matching record
//psergey-explain-todo: No-partitions used EXPLAIN here..
my_ok(thd, 0); my_ok(thd, 0);
DBUG_RETURN(0); DBUG_RETURN(0);
} }
@ -211,6 +369,10 @@ 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();
if (thd->lex->describe)
goto exit_without_my_ok;
delete select; delete select;
free_underlaid_joins(thd, select_lex); free_underlaid_joins(thd, select_lex);
/* /*
@ -243,26 +405,46 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds,
if (order) if (order)
{ {
uint length= 0;
SORT_FIELD *sortorder;
ha_rows examined_rows;
ha_rows found_rows;
table->update_const_key_parts(conds); table->update_const_key_parts(conds);
order= simple_remove_const(order, conds); order= simple_remove_const(order, conds);
bool need_sort;
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")
need_sort= FALSE; query_plan.using_filesort= FALSE;
usable_index= MAX_KEY; query_plan.index= MAX_KEY;
} }
else else
usable_index= get_index_for_order(order, table, select, limit, query_plan.index= get_index_for_order(order, table, select, limit,
&need_sort, &reverse); &query_plan.using_filesort,
if (need_sort) &reverse);
}
query_plan.select= select;
query_plan.possible_keys= table->quick_keys;
query_plan.table_rows= table->file->stats.records;
thd->lex->upd_del_plan= &query_plan;
/*
Ok, we have generated a query plan for the DELETE.
- if we're running EXPLAIN DELETE, goto produce explain output
- otherwise, execute the query plan
*/
if (thd->lex->describe)
goto exit_without_my_ok;
thd->apc_target.enable();
DBUG_EXECUTE_IF("show_explain_probe_delete_exec_start",
dbug_serve_apcs(thd, 1););
if (query_plan.using_filesort)
{
ha_rows examined_rows;
ha_rows found_rows;
uint length= 0;
SORT_FIELD *sortorder;
{ {
DBUG_ASSERT(usable_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));
@ -276,6 +458,7 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds,
{ {
delete select; delete select;
free_underlaid_joins(thd, &thd->lex->select_lex); free_underlaid_joins(thd, &thd->lex->select_lex);
thd->apc_target.disable();
DBUG_RETURN(TRUE); DBUG_RETURN(TRUE);
} }
thd->examined_row_count+= examined_rows; thd->examined_row_count+= examined_rows;
@ -294,19 +477,21 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds,
{ {
delete select; delete select;
free_underlaid_joins(thd, select_lex); free_underlaid_joins(thd, select_lex);
thd->apc_target.disable();
DBUG_RETURN(TRUE); DBUG_RETURN(TRUE);
} }
if (usable_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))
{ {
delete select; delete select;
free_underlaid_joins(thd, select_lex); free_underlaid_joins(thd, select_lex);
thd->apc_target.disable();
DBUG_RETURN(TRUE); DBUG_RETURN(TRUE);
} }
} }
else else
init_read_record_idx(&info, thd, table, 1, usable_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");
@ -398,6 +583,7 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds,
if (options & OPTION_QUICK) if (options & OPTION_QUICK)
(void) table->file->extra(HA_EXTRA_NORMAL); (void) table->file->extra(HA_EXTRA_NORMAL);
thd->apc_target.disable();
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
@ -458,6 +644,29 @@ cleanup:
DBUG_PRINT("info",("%ld records deleted",(long) deleted)); DBUG_PRINT("info",("%ld records deleted",(long) deleted));
} }
DBUG_RETURN(error >= 0 || thd->is_error()); DBUG_RETURN(error >= 0 || thd->is_error());
/* Special exits */
exit_without_my_ok:
thd->lex->upd_del_plan= &query_plan;
select_send *result;
bool printed_anything;
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->print_explain(result, 0 /* explain flags*/, &printed_anything);
if (err2)
result->abort_result_set();
else
result->send_eof();
delete select;
free_underlaid_joins(thd, select_lex);
//table->set_keyread(false);
DBUG_RETURN((err || thd->is_error() || thd->killed) ? 1 : 0);
} }

View File

@ -448,6 +448,7 @@ void lex_start(THD *thd)
lex->thd= lex->unit.thd= thd; lex->thd= lex->unit.thd= thd;
lex->upd_del_plan= NULL;
lex->context_stack.empty(); lex->context_stack.empty();
lex->unit.init_query(); lex->unit.init_query();
lex->unit.init_select(); lex->unit.init_select();
@ -2560,6 +2561,7 @@ LEX::LEX()
INITIAL_LEX_PLUGIN_LIST_SIZE, 0); INITIAL_LEX_PLUGIN_LIST_SIZE, 0);
reset_query_tables_list(TRUE); reset_query_tables_list(TRUE);
mi.init(); mi.init();
upd_del_plan= NULL;
} }
@ -4170,12 +4172,17 @@ bool st_select_lex::is_merged_child_of(st_select_lex *ancestor)
return all_merged; return all_merged;
} }
int LEX::print_explain(select_result_sink *output, uint8 explain_flags,
int print_explain_message_line(select_result_sink *result, bool *printed_anything)
SELECT_LEX *select_lex, {
bool on_the_fly, if (upd_del_plan)
uint8 options, {
const char *message); upd_del_plan->print_explain(output, explain_flags, printed_anything);
return 0;
}
int res= unit.print_explain(output, explain_flags, printed_anything);
return res;
}
int st_select_lex::print_explain(select_result_sink *output, int st_select_lex::print_explain(select_result_sink *output,
@ -4239,8 +4246,9 @@ int st_select_lex::print_explain(select_result_sink *output,
DBUG_ASSERT(join->have_query_plan == JOIN::QEP_DELETED); DBUG_ASSERT(join->have_query_plan == JOIN::QEP_DELETED);
msg= "Query plan already deleted"; msg= "Query plan already deleted";
} }
res= print_explain_message_line(output, this, TRUE /* on_the_fly */, set_explain_type(TRUE/* on_the_fly */);
0, msg); res= print_explain_message_line(output, 0/*options*/, select_number, type,
msg);
} }
err: err:
return res; return res;
@ -4260,9 +4268,10 @@ int st_select_lex_unit::print_explain(select_result_sink *output,
EXPLAIN state" error. EXPLAIN state" error.
*/ */
const char *msg="Query plan already deleted"; const char *msg="Query plan already deleted";
res= print_explain_message_line(output, first, TRUE /* on_the_fly */, first->set_explain_type(TRUE/* on_the_fly */);
0, msg); res= print_explain_message_line(output, 0/*options*/, first->select_number,
return 0; first->type, msg);
return res;
} }
for (SELECT_LEX *sl= first; sl; sl= sl->next_select()) for (SELECT_LEX *sl= first; sl; sl= sl->next_select())

View File

@ -2347,6 +2347,63 @@ protected:
LEX *m_lex; LEX *m_lex;
}; };
class Delete_plan;
class SQL_SELECT;
/*
Query plan of a single-table UPDATE.
(This is actually a plan for single-table DELETE also)
*/
class Update_plan
{
protected:
bool impossible_where;
public:
TABLE *table;
SQL_SELECT *select;
uint index;
ha_rows table_rows; /* Use if select==NULL */
/*
Top-level select_lex. Most of its fields are not used, we need it only to
get to the subqueries.
*/
SELECT_LEX *select_lex;
key_map possible_keys;
bool using_filesort;
/* Set this plan to be a plan to do nothing because of impossible WHRE*/
void set_impossible_where() { impossible_where= true; }
virtual int print_explain(select_result_sink *output, uint8 explain_flags,
bool *printed_anything);
virtual ~Update_plan() {}
Update_plan() : impossible_where(false), using_filesort(false) {}
};
/* Query plan of a single-table DELETE */
class Delete_plan : public Update_plan
{
bool deleting_all_rows;
public:
/* Construction functions */
Delete_plan() :
deleting_all_rows(false) {}
/* Set this query plan to be a plan to make a call to h->delete_all_rows() */
void set_delete_all_rows(ha_rows rows_arg)
{
deleting_all_rows= true;
table_rows= rows_arg;
}
int print_explain(select_result_sink *output, uint8 explain_flags,
bool *printed_anything);
};
/* 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
@ -2358,6 +2415,9 @@ struct LEX: public Query_tables_list
/* 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 */
Update_plan *upd_del_plan;
char *length,*dec,*change; char *length,*dec,*change;
LEX_STRING name; LEX_STRING name;
char *help_arg; char *help_arg;
@ -2773,6 +2833,9 @@ struct LEX: public Query_tables_list
} }
return FALSE; return FALSE;
} }
int print_explain(select_result_sink *output, uint8 explain_flags,
bool *printed_anything);
}; };

View File

@ -3229,7 +3229,8 @@ end_with_restore_list:
{ {
DBUG_ASSERT(first_table == all_tables && first_table != 0); DBUG_ASSERT(first_table == all_tables && first_table != 0);
TABLE_LIST *aux_tables= thd->lex->auxiliary_table_list.first; TABLE_LIST *aux_tables= thd->lex->auxiliary_table_list.first;
multi_delete *del_result; bool explain= test(lex->describe);
select_result *result;
if ((res= multi_delete_precheck(thd, all_tables))) if ((res= multi_delete_precheck(thd, all_tables)))
break; break;
@ -3244,37 +3245,72 @@ end_with_restore_list:
if ((res= open_and_lock_tables(thd, all_tables, TRUE, 0))) if ((res= open_and_lock_tables(thd, all_tables, TRUE, 0)))
break; break;
MYSQL_MULTI_DELETE_START(thd->query()); if (!explain)
{
MYSQL_MULTI_DELETE_START(thd->query());
}
if ((res= mysql_multi_delete_prepare(thd))) if ((res= mysql_multi_delete_prepare(thd)))
{ {
MYSQL_MULTI_DELETE_DONE(1, 0); if (!explain)
{
MYSQL_MULTI_DELETE_DONE(1, 0);
}
goto error; goto error;
} }
if (!thd->is_fatal_error && if (!thd->is_fatal_error)
(del_result= new multi_delete(aux_tables, lex->table_count)))
{ {
res= mysql_select(thd, &select_lex->ref_pointer_array, if (explain)
select_lex->get_table_list(), {
select_lex->with_wild, result= new select_send();
select_lex->item_list, if (thd->send_explain_fields(result))
select_lex->where, {
0, (ORDER *)NULL, (ORDER *)NULL, (Item *)NULL, delete result;
(ORDER *)NULL, result= NULL;
(select_lex->options | thd->variables.option_bits | }
SELECT_NO_JOIN_CACHE | SELECT_NO_UNLOCK | select_lex->set_explain_type(FALSE);
OPTION_SETUP_TABLES_DONE) & ~OPTION_BUFFER_RESULT, }
del_result, unit, select_lex); else
res|= thd->is_error(); result= new multi_delete(aux_tables, lex->table_count);
MYSQL_MULTI_DELETE_DONE(res, del_result->num_deleted());
if (res) if (result)
del_result->abort_result_set(); {
delete del_result; res= mysql_select(thd, &select_lex->ref_pointer_array,
select_lex->get_table_list(),
select_lex->with_wild,
select_lex->item_list,
select_lex->where,
0, (ORDER *)NULL, (ORDER *)NULL, (Item *)NULL,
(ORDER *)NULL,
(select_lex->options | thd->variables.option_bits |
SELECT_NO_JOIN_CACHE | SELECT_NO_UNLOCK |
OPTION_SETUP_TABLES_DONE) & ~OPTION_BUFFER_RESULT,
result, unit, select_lex);
res|= thd->is_error();
if (!explain)
{
MYSQL_MULTI_DELETE_DONE(res, del_result->num_deleted());
}
if (res)
result->abort_result_set(); /* for both DELETE and EXPLAIN DELETE */
else
{
if (explain)
result->send_eof();
}
delete result;
}
} }
else else
{ {
res= TRUE; // Error res= TRUE; // Error
MYSQL_MULTI_DELETE_DONE(1, 0); if (!explain)
{
MYSQL_MULTI_DELETE_DONE(1, 0);
}
} }
break; break;
} }

View File

@ -11098,7 +11098,8 @@ void JOIN::cleanup(bool full)
DBUG_ENTER("JOIN::cleanup"); DBUG_ENTER("JOIN::cleanup");
DBUG_PRINT("enter", ("full %u", (uint) full)); DBUG_PRINT("enter", ("full %u", (uint) full));
have_query_plan= QEP_DELETED; if (full)
have_query_plan= QEP_DELETED; //psergey: this is a problem!
if (table) if (table)
{ {
@ -22087,23 +22088,20 @@ void JOIN::clear()
/* /*
Print an EXPLAIN line with all NULLs and given message in the 'Extra' column Print an EXPLAIN line with all NULLs and given message in the 'Extra' column
*/ */
int print_explain_message_line(select_result_sink *result, int print_explain_message_line(select_result_sink *result,
SELECT_LEX *select_lex,
bool on_the_fly,
uint8 options, uint8 options,
uint select_number,
const char *select_type,
const char *message) const char *message)
{ {
const CHARSET_INFO *cs= system_charset_info; const CHARSET_INFO *cs= system_charset_info;
Item *item_null= new Item_null(); Item *item_null= new Item_null();
List<Item> item_list; List<Item> item_list;
if (on_the_fly) item_list.push_back(new Item_int((int32) select_number));
select_lex->set_explain_type(on_the_fly); item_list.push_back(new Item_string(select_type,
strlen(select_type), cs));
item_list.push_back(new Item_int((int32)
select_lex->select_number));
item_list.push_back(new Item_string(select_lex->type,
strlen(select_lex->type), cs));
for (uint i=0 ; i < 7; i++) for (uint i=0 ; i < 7; i++)
item_list.push_back(item_null); item_list.push_back(item_null);
if (options & DESCRIBE_PARTITIONS) if (options & DESCRIBE_PARTITIONS)
@ -22119,6 +22117,104 @@ int print_explain_message_line(select_result_sink *result,
} }
/*
Make a comma-separated list of possible_keys names and add it into the string
*/
void make_possible_keys_line(TABLE *table, key_map possible_keys, String *line)
{
if (!possible_keys.is_clear_all())
{
uint j;
for (j=0 ; j < table->s->keys ; j++)
{
if (possible_keys.is_set(j))
{
if (line->length())
line->append(',');
line->append(table->key_info[j].name,
strlen(table->key_info[j].name),
system_charset_info);
}
}
}
}
/*
Print an EXPLAIN output row, based on information provided in the parameters
@note
Parameters that may have NULL value in EXPLAIN output, should be passed
(char*)NULL.
@return
0 - OK
1 - OOM Error
*/
int print_explain_row(select_result_sink *result,
uint8 options,
uint select_number,
const char *select_type,
const char *table_name,
//const char *partitions, (todo)
enum join_type jtype,
const char *possible_keys,
const char *index,
const char *key_len,
const char *ref,
ha_rows rows,
const char *extra)
{
const CHARSET_INFO *cs= system_charset_info;
Item *item_null= new Item_null();
List<Item> item_list;
Item *item;
item_list.push_back(new Item_int((int32) select_number));
item_list.push_back(new Item_string(select_type,
strlen(select_type), cs));
item_list.push_back(new Item_string(table_name,
strlen(table_name), cs));
if (options & DESCRIBE_PARTITIONS)
item_list.push_back(item_null); // psergey-todo: produce proper value
const char *jtype_str= join_type_str[jtype];
item_list.push_back(new Item_string(jtype_str,
strlen(jtype_str), cs));
item= possible_keys? new Item_string(possible_keys, strlen(possible_keys),
cs) : item_null;
item_list.push_back(item);
/* 'index */
item= index ? new Item_string(index, strlen(index), cs) : item_null;
item_list.push_back(item);
/* 'key_len */
item= key_len ? new Item_string(key_len, strlen(key_len), cs) : item_null;
item_list.push_back(item);
/* 'ref' */
item= ref ? new Item_string(ref, strlen(ref), cs) : item_null;
item_list.push_back(item);
/* 'rows' */
item_list.push_back(new Item_int(rows,
MY_INT64_NUM_DECIMAL_DIGITS));
/* 'filtered' */
if (options & DESCRIBE_EXTENDED)
item_list.push_back(item_null);
/* 'Extra' */
item_list.push_back(new Item_string(extra, strlen(extra), cs));
if (result->send_data(item_list))
return 1;
return 0;
}
int print_fake_select_lex_join(select_result_sink *result, bool on_the_fly, int print_fake_select_lex_join(select_result_sink *result, bool on_the_fly,
SELECT_LEX *select_lex, uint8 explain_flags) SELECT_LEX *select_lex, uint8 explain_flags)
{ {
@ -22198,6 +22294,26 @@ int print_fake_select_lex_join(select_result_sink *result, bool on_the_fly,
} }
/*
Append MRR information from quick select to the given string
*/
void explain_append_mrr_info(QUICK_RANGE_SELECT *quick, String *res)
{
char mrr_str_buf[128];
mrr_str_buf[0]=0;
int len;
handler *h= quick->head->file;
len= h->multi_range_read_explain_info(quick->mrr_flags, mrr_str_buf,
sizeof(mrr_str_buf));
if (len > 0)
{
res->append(STRING_WITH_LEN("; "));
res->append(mrr_str_buf, len);
}
}
/** /**
EXPLAIN handling. EXPLAIN handling.
@ -22239,8 +22355,12 @@ int JOIN::print_explain(select_result_sink *result, uint8 explain_flags,
*/ */
if (message) if (message)
{ {
if (print_explain_message_line(result, join->select_lex, on_the_fly, if (on_the_fly)
explain_flags, message)) join->select_lex->set_explain_type(on_the_fly);
if (print_explain_message_line(result, explain_flags,
join->select_lex->select_number,
join->select_lex->type, message))
error= 1; error= 1;
} }
@ -22255,7 +22375,7 @@ int JOIN::print_explain(select_result_sink *result, uint8 explain_flags,
join->select_lex->master_unit()->derived->is_materialized_derived()) join->select_lex->master_unit()->derived->is_materialized_derived())
{ {
table_map used_tables=0; table_map used_tables=0;
//if (!join->select_lex->type)
if (on_the_fly) if (on_the_fly)
join->select_lex->set_explain_type(on_the_fly); join->select_lex->set_explain_type(on_the_fly);
@ -22670,19 +22790,8 @@ int JOIN::print_explain(select_result_sink *result, uint8 explain_flags,
*/ */
if (quick_type == QUICK_SELECT_I::QS_TYPE_RANGE) if (quick_type == QUICK_SELECT_I::QS_TYPE_RANGE)
{ {
char mrr_str_buf[128]; explain_append_mrr_info((QUICK_RANGE_SELECT*)(tab->select->quick),
mrr_str_buf[0]=0; &extra);
int len;
uint mrr_flags=
((QUICK_RANGE_SELECT*)(tab->select->quick))->mrr_flags;
len= table->file->multi_range_read_explain_info(mrr_flags,
mrr_str_buf,
sizeof(mrr_str_buf));
if (len > 0)
{
extra.append(STRING_WITH_LEN("; "));
extra.append(mrr_str_buf, len);
}
} }
if (need_tmp_table) if (need_tmp_table)

View File

@ -1828,6 +1828,28 @@ void push_index_cond(JOIN_TAB *tab, uint keyno);
#define OPT_LINK_EQUAL_FIELDS 1 #define OPT_LINK_EQUAL_FIELDS 1
/* EXPLAIN-related utility functions */
int print_explain_message_line(select_result_sink *result,
uint8 options,
uint select_number,
const char *select_type,
const char *message);
void explain_append_mrr_info(QUICK_RANGE_SELECT *quick, String *res);
int print_explain_row(select_result_sink *result,
uint8 options,
uint select_number,
const char *select_type,
const char *table_name,
//const char *partitions, (todo)
enum join_type jtype,
const char *possible_keys,
const char *index,
const char *key_len,
const char *ref,
ha_rows rows,
const char *extra);
void make_possible_keys_line(TABLE *table, key_map possible_keys, String *line);
/**************************************************************************** /****************************************************************************
Temporary table support for SQL Runtime Temporary table support for SQL Runtime
***************************************************************************/ ***************************************************************************/

View File

@ -2337,8 +2337,8 @@ void Show_explain_request::call_in_target_thread()
DBUG_ASSERT(current_thd == target_thd); DBUG_ASSERT(current_thd == target_thd);
set_current_thd(request_thd); set_current_thd(request_thd);
if (target_thd->lex->unit.print_explain(explain_buf, 0 /* explain flags*/, if (target_thd->lex->print_explain(explain_buf, 0 /* explain flags*/,
&printed_anything)) &printed_anything))
{ {
failed_to_produce= TRUE; failed_to_produce= TRUE;
} }

View File

@ -500,6 +500,38 @@ public:
} }
}; };
// The following class is a backport from MySQL 5.6:
/**
String class wrapper with a preallocated buffer of size buff_sz
This class allows to replace sequences of:
char buff[12345];
String str(buff, sizeof(buff));
str.length(0);
with a simple equivalent declaration:
StringBuffer<12345> str;
*/
template<size_t buff_sz>
class StringBuffer : public String
{
char buff[buff_sz];
public:
StringBuffer() : String(buff, buff_sz, &my_charset_bin) { length(0); }
explicit StringBuffer(const CHARSET_INFO *cs) : String(buff, buff_sz, cs)
{
length(0);
}
StringBuffer(const char *str, size_t length, const CHARSET_INFO *cs)
: String(buff, buff_sz, cs)
{
set(str, length, cs);
}
};
static inline bool check_if_only_end_space(CHARSET_INFO *cs, static inline bool check_if_only_end_space(CHARSET_INFO *cs,
const char *str, const char *str,
const char *end) const char *end)

View File

@ -260,7 +260,7 @@ int mysql_update(THD *thd,
bool can_compare_record; bool can_compare_record;
int res; int res;
int error, loc_error; int error, loc_error;
uint used_index, dup_key_found; uint dup_key_found;
bool need_sort= TRUE; bool need_sort= TRUE;
bool reverse= FALSE; bool reverse= FALSE;
#ifndef NO_EMBEDDED_ACCESS_CHECKS #ifndef NO_EMBEDDED_ACCESS_CHECKS
@ -270,12 +270,15 @@ int mysql_update(THD *thd,
ha_rows updated, found; ha_rows updated, found;
key_map old_covering_keys; key_map old_covering_keys;
TABLE *table; TABLE *table;
SQL_SELECT *select; SQL_SELECT *select= NULL;
READ_RECORD info; READ_RECORD info;
SELECT_LEX *select_lex= &thd->lex->select_lex; SELECT_LEX *select_lex= &thd->lex->select_lex;
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;
query_plan.index= MAX_KEY;
query_plan.using_filesort= FALSE;
DBUG_ENTER("mysql_update"); DBUG_ENTER("mysql_update");
if (open_tables(thd, &table_list, &table_count, 0)) if (open_tables(thd, &table_list, &table_count, 0))
@ -314,6 +317,8 @@ int mysql_update(THD *thd,
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.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 :
@ -370,7 +375,12 @@ int mysql_update(THD *thd,
Item::cond_result cond_value; Item::cond_result cond_value;
conds= remove_eq_conds(thd, conds, &cond_value); conds= remove_eq_conds(thd, conds, &cond_value);
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();
if (thd->lex->describe)
goto exit_without_my_ok;
}
} }
/* /*
@ -400,6 +410,10 @@ 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();
if (thd->lex->describe)
goto exit_without_my_ok;
delete select; delete select;
free_underlaid_joins(thd, select_lex); free_underlaid_joins(thd, select_lex);
/* /*
@ -438,16 +452,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;
used_index= MAX_KEY; query_plan.index= MAX_KEY;
used_key_is_modified= FALSE; used_key_is_modified= FALSE;
} }
else else
{ {
used_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 || used_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));
} }
@ -455,14 +469,37 @@ 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:
used_index= table->file->key_used_on_scan; query_plan.index= table->file->key_used_on_scan;
} }
if (used_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, used_index, table->write_set); used_key_is_modified= is_key_used(table, query_plan.index, table->write_set);
} }
} }
} }
/*
Query optimization is finished at this point.
- Save the decisions in the query plan
- if we're running EXPLAIN UPDATE, get out
*/
query_plan.select= select;
query_plan.possible_keys= table->quick_keys;
query_plan.table_rows= table->file->stats.records;
thd->lex->upd_del_plan= &query_plan;
/*
Ok, we have generated a query plan for the UPDATE.
- if we're running EXPLAIN UPDATE, goto produce explain output
- otherwise, execute the query plan
*/
if (thd->lex->describe)
goto exit_without_my_ok;
thd->apc_target.enable();
DBUG_EXECUTE_IF("show_explain_probe_update_exec_start",
dbug_serve_apcs(thd, 1););
if (used_key_is_modified || order || if (used_key_is_modified || order ||
partition_key_modified(table, table->write_set)) partition_key_modified(table, table->write_set))
@ -476,8 +513,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 (used_index < MAX_KEY && old_covering_keys.is_set(used_index)) if (query_plan.index < MAX_KEY && old_covering_keys.is_set(query_plan.index))
table->add_read_columns_used_by_index(used_index); table->add_read_columns_used_by_index(query_plan.index);
else else
table->use_all_columns(); table->use_all_columns();
@ -534,22 +571,22 @@ int mysql_update(THD *thd,
/* /*
When we get here, we have one of the following options: When we get here, we have one of the following options:
A. used_index == MAX_KEY A. query_plan.index == MAX_KEY
This means we should use full table scan, and start it with This means we should use full table scan, and start it with
init_read_record call init_read_record call
B. used_index != MAX_KEY B. query_plan.index != MAX_KEY
B.1 quick select is used, start the scan with init_read_record B.1 quick select is used, start the scan with init_read_record
B.2 quick select is not used, this is full index scan (with LIMIT) B.2 quick select is not used, this is full index scan (with LIMIT)
Full index scan must be started with init_read_record_idx Full index scan must be started with init_read_record_idx
*/ */
if (used_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, used_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;
@ -610,6 +647,7 @@ int mysql_update(THD *thd,
select= new SQL_SELECT; select= new SQL_SELECT;
select->head=table; select->head=table;
} }
//psergey-todo: disable SHOW EXPLAIN because the plan was deleted?
if (reinit_io_cache(&tempfile,READ_CACHE,0L,0,0)) if (reinit_io_cache(&tempfile,READ_CACHE,0L,0,0))
error=1; /* purecov: inspected */ error=1; /* purecov: inspected */
select->file=tempfile; // Read row ptrs from this file select->file=tempfile; // Read row ptrs from this file
@ -884,6 +922,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.
end_read_record(&info); end_read_record(&info);
delete select; delete select;
thd_proc_info(thd, "end"); thd_proc_info(thd, "end");
@ -962,6 +1001,27 @@ err:
table->disable_keyread(); table->disable_keyread();
thd->abort_on_warning= 0; thd->abort_on_warning= 0;
DBUG_RETURN(1); DBUG_RETURN(1);
exit_without_my_ok:
thd->lex->upd_del_plan= &query_plan;
select_send *result;
bool printed_anything;
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->print_explain(result, 0 /* explain flags*/, &printed_anything);
if (err2)
result->abort_result_set();
else
result->send_eof();
delete select;
free_underlaid_joins(thd, select_lex);
DBUG_RETURN((error >= 0 || thd->is_error()) ? 1 : 0);
} }
/* /*
@ -1381,20 +1441,37 @@ bool mysql_multi_update(THD *thd,
multi_update **result) multi_update **result)
{ {
bool res; bool res;
select_result *output;
bool explain= test(thd->lex->describe);
DBUG_ENTER("mysql_multi_update"); DBUG_ENTER("mysql_multi_update");
if (!(*result= new multi_update(table_list, if (explain)
&thd->lex->select_lex.leaf_tables,
fields, values,
handle_duplicates, ignore)))
{ {
DBUG_RETURN(TRUE); /* Handle EXPLAIN UPDATE */
if (!(output= new select_send()) ||
thd->send_explain_fields(output))
{
delete output;
DBUG_RETURN(TRUE);
}
select_lex->set_explain_type(FALSE);
*result= NULL; /* no multi_update object */
}
else
{
if (!(*result= new multi_update(table_list,
&thd->lex->select_lex.leaf_tables,
fields, values,
handle_duplicates, ignore)))
{
DBUG_RETURN(TRUE);
}
output= *result;
} }
thd->abort_on_warning= test(thd->variables.sql_mode & thd->abort_on_warning= test(thd->variables.sql_mode &
(MODE_STRICT_TRANS_TABLES | (MODE_STRICT_TRANS_TABLES |
MODE_STRICT_ALL_TABLES)); MODE_STRICT_ALL_TABLES));
List<Item> total_list; List<Item> total_list;
res= mysql_select(thd, &select_lex->ref_pointer_array, res= mysql_select(thd, &select_lex->ref_pointer_array,
@ -1404,12 +1481,20 @@ bool mysql_multi_update(THD *thd,
(ORDER *)NULL, (ORDER *)NULL,
options | SELECT_NO_JOIN_CACHE | SELECT_NO_UNLOCK | options | SELECT_NO_JOIN_CACHE | SELECT_NO_UNLOCK |
OPTION_SETUP_TABLES_DONE, OPTION_SETUP_TABLES_DONE,
*result, unit, select_lex); output, unit, select_lex);
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 (unlikely(res)) if (unlikely(res))
(*result)->abort_result_set(); (*result)->abort_result_set();
else
{
if (explain)
{
output->send_eof();
delete output;
}
}
thd->abort_on_warning= 0; thd->abort_on_warning= 0;
DBUG_RETURN(res); DBUG_RETURN(res);
} }

View File

@ -11913,13 +11913,21 @@ describe:
opt_describe_column {} opt_describe_column {}
| describe_command opt_extended_describe | describe_command opt_extended_describe
{ Lex->describe|= DESCRIBE_NORMAL; } { Lex->describe|= DESCRIBE_NORMAL; }
select explanable_command
{ {
LEX *lex=Lex; LEX *lex=Lex;
lex->select_lex.options|= SELECT_DESCRIBE; lex->select_lex.options|= SELECT_DESCRIBE;
} }
; ;
explanable_command:
select
| insert
| replace
| update
| delete
;
describe_command: describe_command:
DESC DESC
| DESCRIBE | DESCRIBE