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:
104
mysql-test/r/explain_non_select.result
Normal file
104
mysql-test/r/explain_non_select.result
Normal 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;
|
@ -186,22 +186,38 @@ set @show_explain_probe_select_id=2;
|
||||
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 ;
|
||||
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;
|
||||
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;
|
||||
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;
|
||||
set @show_explain_probe_select_id=2;
|
||||
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 ;
|
||||
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;
|
||||
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;
|
||||
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;
|
||||
id select_type table type possible_keys key key_len ref rows Extra
|
||||
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:
|
||||
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;
|
||||
id select_type table type possible_keys key key_len ref rows Extra
|
||||
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:
|
||||
Note 1003 select t2.a, ((select max(a) from t0 where t2.a + t0.a <3) >3) as SUBQ from t2
|
||||
a SUBQ
|
||||
|
44
mysql-test/r/show_explain_non_select.result
Normal file
44
mysql-test/r/show_explain_non_select.result
Normal 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';
|
86
mysql-test/t/explain_non_select.test
Normal file
86
mysql-test/t/explain_non_select.test
Normal 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;
|
@ -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 ;
|
||||
connection default;
|
||||
--source include/wait_condition.inc
|
||||
--error ER_TARGET_NOT_EXPLAINABLE
|
||||
#--error ER_TARGET_NOT_EXPLAINABLE
|
||||
evalp show explain for $thr2;
|
||||
--source include/wait_condition.inc
|
||||
--error ER_TARGET_NOT_EXPLAINABLE
|
||||
#--error ER_TARGET_NOT_EXPLAINABLE
|
||||
evalp show explain for $thr2;
|
||||
connection con1;
|
||||
reap;
|
||||
@ -249,7 +249,7 @@ drop table t2;
|
||||
set debug_dbug=@old_debug;
|
||||
|
||||
--echo #
|
||||
--echo # Attempt SHOW EXPLAIN for a DELETE
|
||||
--echo # Attempt SHOW EXPLAIN for a DELETE (UPD: now works)
|
||||
--echo #
|
||||
create table t2 as select a as a, a as dummy from t0 limit 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 ;
|
||||
connection default;
|
||||
--source include/wait_condition.inc
|
||||
--error ER_TARGET_NOT_EXPLAINABLE
|
||||
#--error ER_TARGET_NOT_EXPLAINABLE
|
||||
evalp show explain for $thr2;
|
||||
--source include/wait_condition.inc
|
||||
--error ER_TARGET_NOT_EXPLAINABLE
|
||||
#--error ER_TARGET_NOT_EXPLAINABLE
|
||||
evalp show explain for $thr2;
|
||||
connection con1;
|
||||
reap;
|
||||
|
78
mysql-test/t/show_explain_non_select.test
Normal file
78
mysql-test/t/show_explain_non_select.test
Normal 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';
|
@ -64,6 +64,8 @@ public:
|
||||
{
|
||||
return test(apc_calls);
|
||||
}
|
||||
|
||||
inline bool is_enabled() { return enabled; }
|
||||
|
||||
/* Functor class for calls you can schedule */
|
||||
class Apc_call
|
||||
|
@ -40,6 +40,147 @@
|
||||
#include "records.h" // init_read_record,
|
||||
#include "sql_derived.h" // mysql_handle_list_of_derived
|
||||
// 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.
|
||||
|
||||
@ -61,12 +202,16 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds,
|
||||
bool const_cond_result;
|
||||
ha_rows deleted= 0;
|
||||
bool reverse= FALSE;
|
||||
bool err= true;
|
||||
ORDER *order= (ORDER *) ((order_list && order_list->elements) ?
|
||||
order_list->first : NULL);
|
||||
uint usable_index= MAX_KEY;
|
||||
SELECT_LEX *select_lex= &thd->lex->select_lex;
|
||||
killed_state killed_status= NOT_KILLED;
|
||||
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");
|
||||
|
||||
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");
|
||||
table->map=1;
|
||||
query_plan.select_lex= &thd->lex->select_lex;
|
||||
query_plan.table= table;
|
||||
|
||||
if (mysql_prepare_delete(thd, table_list, &conds))
|
||||
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);
|
||||
ha_rows const maybe_deleted= table->file->stats.records;
|
||||
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()))
|
||||
{
|
||||
/*
|
||||
@ -187,7 +339,12 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds,
|
||||
Item::cond_result result;
|
||||
conds= remove_eq_conds(thd, conds, &result);
|
||||
if (result == Item::COND_FALSE) // Impossible where
|
||||
{
|
||||
limit= 0;
|
||||
query_plan.set_impossible_where();
|
||||
if (thd->lex->describe)
|
||||
goto exit_without_my_ok;
|
||||
}
|
||||
}
|
||||
|
||||
#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);
|
||||
// No matching record
|
||||
//psergey-explain-todo: No-partitions used EXPLAIN here..
|
||||
my_ok(thd, 0);
|
||||
DBUG_RETURN(0);
|
||||
}
|
||||
@ -211,6 +369,10 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds,
|
||||
DBUG_RETURN(TRUE);
|
||||
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;
|
||||
free_underlaid_joins(thd, select_lex);
|
||||
/*
|
||||
@ -243,26 +405,46 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds,
|
||||
|
||||
if (order)
|
||||
{
|
||||
uint length= 0;
|
||||
SORT_FIELD *sortorder;
|
||||
ha_rows examined_rows;
|
||||
ha_rows found_rows;
|
||||
|
||||
table->update_const_key_parts(conds);
|
||||
order= simple_remove_const(order, conds);
|
||||
|
||||
bool need_sort;
|
||||
if (select && select->quick && select->quick->unique_key_range())
|
||||
{ // Single row select (always "ordered")
|
||||
need_sort= FALSE;
|
||||
usable_index= MAX_KEY;
|
||||
query_plan.using_filesort= FALSE;
|
||||
query_plan.index= MAX_KEY;
|
||||
}
|
||||
else
|
||||
usable_index= get_index_for_order(order, table, select, limit,
|
||||
&need_sort, &reverse);
|
||||
if (need_sort)
|
||||
query_plan.index= get_index_for_order(order, table, select, limit,
|
||||
&query_plan.using_filesort,
|
||||
&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),
|
||||
MYF(MY_FAE | MY_ZEROFILL |
|
||||
MY_THREAD_SPECIFIC));
|
||||
@ -276,6 +458,7 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds,
|
||||
{
|
||||
delete select;
|
||||
free_underlaid_joins(thd, &thd->lex->select_lex);
|
||||
thd->apc_target.disable();
|
||||
DBUG_RETURN(TRUE);
|
||||
}
|
||||
thd->examined_row_count+= examined_rows;
|
||||
@ -294,19 +477,21 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds,
|
||||
{
|
||||
delete select;
|
||||
free_underlaid_joins(thd, select_lex);
|
||||
thd->apc_target.disable();
|
||||
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))
|
||||
{
|
||||
delete select;
|
||||
free_underlaid_joins(thd, select_lex);
|
||||
thd->apc_target.disable();
|
||||
DBUG_RETURN(TRUE);
|
||||
}
|
||||
}
|
||||
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);
|
||||
thd_proc_info(thd, "updating");
|
||||
@ -398,6 +583,7 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds,
|
||||
if (options & OPTION_QUICK)
|
||||
(void) table->file->extra(HA_EXTRA_NORMAL);
|
||||
|
||||
thd->apc_target.disable();
|
||||
cleanup:
|
||||
/*
|
||||
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_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);
|
||||
}
|
||||
|
||||
|
||||
|
@ -448,6 +448,7 @@ void lex_start(THD *thd)
|
||||
|
||||
lex->thd= lex->unit.thd= thd;
|
||||
|
||||
lex->upd_del_plan= NULL;
|
||||
lex->context_stack.empty();
|
||||
lex->unit.init_query();
|
||||
lex->unit.init_select();
|
||||
@ -2560,6 +2561,7 @@ LEX::LEX()
|
||||
INITIAL_LEX_PLUGIN_LIST_SIZE, 0);
|
||||
reset_query_tables_list(TRUE);
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
int print_explain_message_line(select_result_sink *result,
|
||||
SELECT_LEX *select_lex,
|
||||
bool on_the_fly,
|
||||
uint8 options,
|
||||
const char *message);
|
||||
int LEX::print_explain(select_result_sink *output, uint8 explain_flags,
|
||||
bool *printed_anything)
|
||||
{
|
||||
if (upd_del_plan)
|
||||
{
|
||||
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,
|
||||
@ -4239,8 +4246,9 @@ int st_select_lex::print_explain(select_result_sink *output,
|
||||
DBUG_ASSERT(join->have_query_plan == JOIN::QEP_DELETED);
|
||||
msg= "Query plan already deleted";
|
||||
}
|
||||
res= print_explain_message_line(output, this, TRUE /* on_the_fly */,
|
||||
0, msg);
|
||||
set_explain_type(TRUE/* on_the_fly */);
|
||||
res= print_explain_message_line(output, 0/*options*/, select_number, type,
|
||||
msg);
|
||||
}
|
||||
err:
|
||||
return res;
|
||||
@ -4260,9 +4268,10 @@ int st_select_lex_unit::print_explain(select_result_sink *output,
|
||||
EXPLAIN state" error.
|
||||
*/
|
||||
const char *msg="Query plan already deleted";
|
||||
res= print_explain_message_line(output, first, TRUE /* on_the_fly */,
|
||||
0, msg);
|
||||
return 0;
|
||||
first->set_explain_type(TRUE/* on_the_fly */);
|
||||
res= print_explain_message_line(output, 0/*options*/, first->select_number,
|
||||
first->type, msg);
|
||||
return res;
|
||||
}
|
||||
|
||||
for (SELECT_LEX *sl= first; sl; sl= sl->next_select())
|
||||
|
@ -2347,6 +2347,63 @@ protected:
|
||||
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 */
|
||||
|
||||
struct LEX: public Query_tables_list
|
||||
@ -2358,6 +2415,9 @@ struct LEX: public Query_tables_list
|
||||
/* list of all SELECT_LEX */
|
||||
SELECT_LEX *all_selects_list;
|
||||
|
||||
/* For single-table DELETE: its query plan */
|
||||
Update_plan *upd_del_plan;
|
||||
|
||||
char *length,*dec,*change;
|
||||
LEX_STRING name;
|
||||
char *help_arg;
|
||||
@ -2773,6 +2833,9 @@ struct LEX: public Query_tables_list
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
int print_explain(select_result_sink *output, uint8 explain_flags,
|
||||
bool *printed_anything);
|
||||
};
|
||||
|
||||
|
||||
|
@ -3229,7 +3229,8 @@ end_with_restore_list:
|
||||
{
|
||||
DBUG_ASSERT(first_table == all_tables && first_table != 0);
|
||||
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)))
|
||||
break;
|
||||
@ -3244,37 +3245,72 @@ end_with_restore_list:
|
||||
if ((res= open_and_lock_tables(thd, all_tables, TRUE, 0)))
|
||||
break;
|
||||
|
||||
MYSQL_MULTI_DELETE_START(thd->query());
|
||||
if (!explain)
|
||||
{
|
||||
MYSQL_MULTI_DELETE_START(thd->query());
|
||||
}
|
||||
|
||||
if ((res= mysql_multi_delete_prepare(thd)))
|
||||
{
|
||||
MYSQL_MULTI_DELETE_DONE(1, 0);
|
||||
if (!explain)
|
||||
{
|
||||
MYSQL_MULTI_DELETE_DONE(1, 0);
|
||||
}
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (!thd->is_fatal_error &&
|
||||
(del_result= new multi_delete(aux_tables, lex->table_count)))
|
||||
if (!thd->is_fatal_error)
|
||||
{
|
||||
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,
|
||||
del_result, unit, select_lex);
|
||||
res|= thd->is_error();
|
||||
MYSQL_MULTI_DELETE_DONE(res, del_result->num_deleted());
|
||||
if (res)
|
||||
del_result->abort_result_set();
|
||||
delete del_result;
|
||||
if (explain)
|
||||
{
|
||||
result= new select_send();
|
||||
if (thd->send_explain_fields(result))
|
||||
{
|
||||
delete result;
|
||||
result= NULL;
|
||||
}
|
||||
select_lex->set_explain_type(FALSE);
|
||||
}
|
||||
else
|
||||
result= new multi_delete(aux_tables, lex->table_count);
|
||||
|
||||
if (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
|
||||
{
|
||||
res= TRUE; // Error
|
||||
MYSQL_MULTI_DELETE_DONE(1, 0);
|
||||
if (!explain)
|
||||
{
|
||||
MYSQL_MULTI_DELETE_DONE(1, 0);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -11098,7 +11098,8 @@ void JOIN::cleanup(bool full)
|
||||
DBUG_ENTER("JOIN::cleanup");
|
||||
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)
|
||||
{
|
||||
@ -22087,23 +22088,20 @@ void JOIN::clear()
|
||||
/*
|
||||
Print an EXPLAIN line with all NULLs and given message in the 'Extra' column
|
||||
*/
|
||||
|
||||
int print_explain_message_line(select_result_sink *result,
|
||||
SELECT_LEX *select_lex,
|
||||
bool on_the_fly,
|
||||
uint8 options,
|
||||
uint select_number,
|
||||
const char *select_type,
|
||||
const char *message)
|
||||
{
|
||||
const CHARSET_INFO *cs= system_charset_info;
|
||||
Item *item_null= new Item_null();
|
||||
List<Item> item_list;
|
||||
|
||||
if (on_the_fly)
|
||||
select_lex->set_explain_type(on_the_fly);
|
||||
|
||||
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));
|
||||
item_list.push_back(new Item_int((int32) select_number));
|
||||
item_list.push_back(new Item_string(select_type,
|
||||
strlen(select_type), cs));
|
||||
for (uint i=0 ; i < 7; i++)
|
||||
item_list.push_back(item_null);
|
||||
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,
|
||||
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.
|
||||
|
||||
@ -22239,8 +22355,12 @@ int JOIN::print_explain(select_result_sink *result, uint8 explain_flags,
|
||||
*/
|
||||
if (message)
|
||||
{
|
||||
if (print_explain_message_line(result, join->select_lex, on_the_fly,
|
||||
explain_flags, message))
|
||||
if (on_the_fly)
|
||||
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;
|
||||
|
||||
}
|
||||
@ -22255,7 +22375,7 @@ int JOIN::print_explain(select_result_sink *result, uint8 explain_flags,
|
||||
join->select_lex->master_unit()->derived->is_materialized_derived())
|
||||
{
|
||||
table_map used_tables=0;
|
||||
//if (!join->select_lex->type)
|
||||
|
||||
if (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)
|
||||
{
|
||||
char mrr_str_buf[128];
|
||||
mrr_str_buf[0]=0;
|
||||
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);
|
||||
}
|
||||
explain_append_mrr_info((QUICK_RANGE_SELECT*)(tab->select->quick),
|
||||
&extra);
|
||||
}
|
||||
|
||||
if (need_tmp_table)
|
||||
|
@ -1828,6 +1828,28 @@ void push_index_cond(JOIN_TAB *tab, uint keyno);
|
||||
|
||||
#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
|
||||
***************************************************************************/
|
||||
|
@ -2337,8 +2337,8 @@ void Show_explain_request::call_in_target_thread()
|
||||
|
||||
DBUG_ASSERT(current_thd == target_thd);
|
||||
set_current_thd(request_thd);
|
||||
if (target_thd->lex->unit.print_explain(explain_buf, 0 /* explain flags*/,
|
||||
&printed_anything))
|
||||
if (target_thd->lex->print_explain(explain_buf, 0 /* explain flags*/,
|
||||
&printed_anything))
|
||||
{
|
||||
failed_to_produce= TRUE;
|
||||
}
|
||||
|
@ -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,
|
||||
const char *str,
|
||||
const char *end)
|
||||
|
@ -260,7 +260,7 @@ int mysql_update(THD *thd,
|
||||
bool can_compare_record;
|
||||
int res;
|
||||
int error, loc_error;
|
||||
uint used_index, dup_key_found;
|
||||
uint dup_key_found;
|
||||
bool need_sort= TRUE;
|
||||
bool reverse= FALSE;
|
||||
#ifndef NO_EMBEDDED_ACCESS_CHECKS
|
||||
@ -270,12 +270,15 @@ int mysql_update(THD *thd,
|
||||
ha_rows updated, found;
|
||||
key_map old_covering_keys;
|
||||
TABLE *table;
|
||||
SQL_SELECT *select;
|
||||
SQL_SELECT *select= NULL;
|
||||
READ_RECORD info;
|
||||
SELECT_LEX *select_lex= &thd->lex->select_lex;
|
||||
ulonglong id;
|
||||
List<Item> all_fields;
|
||||
killed_state killed_status= NOT_KILLED;
|
||||
Update_plan query_plan;
|
||||
query_plan.index= MAX_KEY;
|
||||
query_plan.using_filesort= FALSE;
|
||||
DBUG_ENTER("mysql_update");
|
||||
|
||||
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->quick_keys.clear_all();
|
||||
|
||||
query_plan.select_lex= &thd->lex->select_lex;
|
||||
query_plan.table= table;
|
||||
#ifndef NO_EMBEDDED_ACCESS_CHECKS
|
||||
/* Force privilege re-checking for views after they have been opened. */
|
||||
want_privilege= (table_list->view ? UPDATE_ACL :
|
||||
@ -370,7 +375,12 @@ int mysql_update(THD *thd,
|
||||
Item::cond_result cond_value;
|
||||
conds= remove_eq_conds(thd, conds, &cond_value);
|
||||
if (cond_value == Item::COND_FALSE)
|
||||
{
|
||||
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() ||
|
||||
(select && select->check_quick(thd, safe_update, limit)))
|
||||
{
|
||||
query_plan.set_impossible_where();
|
||||
if (thd->lex->describe)
|
||||
goto exit_without_my_ok;
|
||||
|
||||
delete select;
|
||||
free_underlaid_joins(thd, select_lex);
|
||||
/*
|
||||
@ -438,16 +452,16 @@ int mysql_update(THD *thd,
|
||||
if (select && select->quick && select->quick->unique_key_range())
|
||||
{ // Single row select (always "ordered"): Ok to use with key field UPDATE
|
||||
need_sort= FALSE;
|
||||
used_index= MAX_KEY;
|
||||
query_plan.index= MAX_KEY;
|
||||
used_key_is_modified= FALSE;
|
||||
}
|
||||
else
|
||||
{
|
||||
used_index= get_index_for_order(order, table, select, limit,
|
||||
&need_sort, &reverse);
|
||||
query_plan.index= get_index_for_order(order, table, select, limit,
|
||||
&need_sort, &reverse);
|
||||
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() &&
|
||||
select->quick->is_keys_used(table->write_set));
|
||||
}
|
||||
@ -455,14 +469,37 @@ int mysql_update(THD *thd,
|
||||
{
|
||||
if (need_sort)
|
||||
{ // 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:
|
||||
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 ||
|
||||
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->write_set == &table->def_write_set);
|
||||
|
||||
if (used_index < MAX_KEY && old_covering_keys.is_set(used_index))
|
||||
table->add_read_columns_used_by_index(used_index);
|
||||
if (query_plan.index < MAX_KEY && old_covering_keys.is_set(query_plan.index))
|
||||
table->add_read_columns_used_by_index(query_plan.index);
|
||||
else
|
||||
table->use_all_columns();
|
||||
|
||||
@ -534,22 +571,22 @@ int mysql_update(THD *thd,
|
||||
|
||||
/*
|
||||
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
|
||||
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.2 quick select is not used, this is full index scan (with LIMIT)
|
||||
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))
|
||||
goto err;
|
||||
}
|
||||
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");
|
||||
ha_rows tmp_limit= limit;
|
||||
@ -610,6 +647,7 @@ int mysql_update(THD *thd,
|
||||
select= new SQL_SELECT;
|
||||
select->head=table;
|
||||
}
|
||||
//psergey-todo: disable SHOW EXPLAIN because the plan was deleted?
|
||||
if (reinit_io_cache(&tempfile,READ_CACHE,0L,0,0))
|
||||
error=1; /* purecov: inspected */
|
||||
select->file=tempfile; // Read row ptrs from this file
|
||||
@ -884,6 +922,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.
|
||||
end_read_record(&info);
|
||||
delete select;
|
||||
thd_proc_info(thd, "end");
|
||||
@ -962,6 +1001,27 @@ err:
|
||||
table->disable_keyread();
|
||||
thd->abort_on_warning= 0;
|
||||
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)
|
||||
{
|
||||
bool res;
|
||||
select_result *output;
|
||||
bool explain= test(thd->lex->describe);
|
||||
DBUG_ENTER("mysql_multi_update");
|
||||
|
||||
if (!(*result= new multi_update(table_list,
|
||||
&thd->lex->select_lex.leaf_tables,
|
||||
fields, values,
|
||||
handle_duplicates, ignore)))
|
||||
|
||||
if (explain)
|
||||
{
|
||||
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 &
|
||||
(MODE_STRICT_TRANS_TABLES |
|
||||
MODE_STRICT_ALL_TABLES));
|
||||
|
||||
List<Item> total_list;
|
||||
|
||||
res= mysql_select(thd, &select_lex->ref_pointer_array,
|
||||
@ -1404,12 +1481,20 @@ bool mysql_multi_update(THD *thd,
|
||||
(ORDER *)NULL,
|
||||
options | SELECT_NO_JOIN_CACHE | SELECT_NO_UNLOCK |
|
||||
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()));
|
||||
res|= thd->is_error();
|
||||
if (unlikely(res))
|
||||
(*result)->abort_result_set();
|
||||
else
|
||||
{
|
||||
if (explain)
|
||||
{
|
||||
output->send_eof();
|
||||
delete output;
|
||||
}
|
||||
}
|
||||
thd->abort_on_warning= 0;
|
||||
DBUG_RETURN(res);
|
||||
}
|
||||
|
@ -11913,13 +11913,21 @@ describe:
|
||||
opt_describe_column {}
|
||||
| describe_command opt_extended_describe
|
||||
{ Lex->describe|= DESCRIBE_NORMAL; }
|
||||
select
|
||||
explanable_command
|
||||
{
|
||||
LEX *lex=Lex;
|
||||
lex->select_lex.options|= SELECT_DESCRIBE;
|
||||
}
|
||||
;
|
||||
|
||||
explanable_command:
|
||||
select
|
||||
| insert
|
||||
| replace
|
||||
| update
|
||||
| delete
|
||||
;
|
||||
|
||||
describe_command:
|
||||
DESC
|
||||
| DESCRIBE
|
||||
|
Reference in New Issue
Block a user