diff --git a/mysql-test/r/show_explain.result b/mysql-test/r/show_explain.result index 73afa0920ce..55bc875fe59 100644 --- a/mysql-test/r/show_explain.result +++ b/mysql-test/r/show_explain.result @@ -1,4 +1,5 @@ -drop table if exists t0, t1; +drop table if exists t0, t1, t2, t3, t4; +drop view if exists v1; create table t0 (a int); insert into t0 values (0),(1),(2),(3),(4),(5),(6),(7),(8),(9); create table t1 (a int); @@ -20,12 +21,16 @@ select count(*) from t1 where a < 100000; show explain for $thr2; id select_type table type possible_keys key key_len ref rows Extra 1 SIMPLE t1 index a a 5 NULL 1000 Using where; Using index +Warnings: +Note 1003 select count(*) from t1 where a < 100000 count(*) 1000 select max(c) from t1 where a < 10; 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 10 Using index condition +Warnings: +Note 1003 select max(c) from t1 where a < 10 max(c) 9 # We can catch EXPLAIN, too. @@ -35,6 +40,8 @@ explain select max(c) from t1 where a < 10; 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 10 Using index condition; Rowid-ordered scan +Warnings: +Note 1003 explain select max(c) from t1 where a < 10 id select_type table type possible_keys key key_len ref rows Extra 1 SIMPLE t1 range a a 5 NULL 10 Using index condition; Rowid-ordered scan set optimizer_switch= @show_expl_tmp; @@ -47,6 +54,8 @@ id select_type table type possible_keys key key_len ref rows Extra 1 PRIMARY A ALL NULL NULL NULL NULL 10 2 UNION B ALL NULL NULL NULL NULL 10 NULL UNION RESULT ALL NULL NULL NULL NULL NULL +Warnings: +Note 1003 explain select a from t0 A union select a+1 from t0 B id select_type table type possible_keys key key_len ref rows Extra 1 PRIMARY A ALL NULL NULL NULL NULL 10 2 UNION B ALL NULL NULL NULL NULL 10 @@ -60,6 +69,8 @@ id select_type table type possible_keys key key_len ref rows Extra 1 PRIMARY A ALL NULL NULL NULL NULL 10 2 UNION B ALL NULL NULL NULL NULL 10 NULL UNION RESULT ALL NULL NULL NULL NULL NULL +Warnings: +Note 1003 explain select a from t0 A union select a+1 from t0 B id select_type table type possible_keys key key_len ref rows Extra 1 PRIMARY A ALL NULL NULL NULL NULL 10 2 UNION B ALL NULL NULL NULL NULL 10 @@ -72,6 +83,8 @@ show explain for $thr2; id select_type table type possible_keys key key_len ref rows Extra 1 PRIMARY A ALL NULL NULL NULL NULL 10 Using where 2 SUBQUERY B ALL NULL NULL NULL NULL 10 +Warnings: +Note 1003 select a, (select max(a) from t0 B) from t0 A where a<1 a (select max(a) from t0 B) 0 9 # Uncorrelated subquery, explain @@ -82,6 +95,8 @@ show explain for $thr2; id select_type table type possible_keys key key_len ref rows Extra 1 PRIMARY A ALL NULL NULL NULL NULL 10 Using where 2 SUBQUERY B ALL NULL NULL NULL NULL 10 +Warnings: +Note 1003 explain select a, (select max(a) from t0 B) from t0 A where a<1 id select_type table type possible_keys key key_len ref rows Extra 1 PRIMARY A ALL NULL NULL NULL NULL 10 Using where 2 SUBQUERY B ALL NULL NULL NULL NULL 10 @@ -93,6 +108,8 @@ show explain for $thr2; id select_type table type possible_keys key key_len ref rows Extra 1 PRIMARY a ALL NULL NULL NULL NULL 10 Using where 2 DEPENDENT SUBQUERY b ALL NULL NULL NULL NULL 10 Using where +Warnings: +Note 1003 select a, (select max(a) from t0 b where b.a+a.a<10) from t0 a where a<1 a (select max(a) from t0 b where b.a+a.a<10) 0 9 # correlated subquery, explain @@ -103,6 +120,8 @@ show explain for $thr2; id select_type table type possible_keys key key_len ref rows Extra 1 PRIMARY a ALL NULL NULL NULL NULL 10 Using where 2 DEPENDENT SUBQUERY b ALL NULL NULL NULL NULL 10 Using where +Warnings: +Note 1003 select a, (select max(a) from t0 b where b.a+a.a<10) from t0 a where a<1 a (select max(a) from t0 b where b.a+a.a<10) 0 9 # correlated subquery, select, while inside the subquery @@ -113,6 +132,8 @@ show explain for $thr2; id select_type table type possible_keys key key_len ref rows Extra 1 PRIMARY a ALL NULL NULL NULL NULL 10 Using where 2 DEPENDENT SUBQUERY b ALL NULL NULL NULL NULL 10 Using where +Warnings: +Note 1003 select a, (select max(a) from t0 b where b.a+a.a<10) from t0 a where a<1 a (select max(a) from t0 b where b.a+a.a<10) 0 9 # correlated subquery, explain, while inside the subquery @@ -123,6 +144,8 @@ show explain for $thr2; id select_type table type possible_keys key key_len ref rows Extra 1 PRIMARY a ALL NULL NULL NULL NULL 10 Using where 2 DEPENDENT SUBQUERY b ALL NULL NULL NULL NULL 10 Using where +Warnings: +Note 1003 select a, (select max(a) from t0 b where b.a+a.a<10) from t0 a where a<1 a (select max(a) from t0 b where b.a+a.a<10) 0 9 # correlated subquery, explain, while inside the subquery @@ -131,7 +154,365 @@ set debug_dbug='d,show_explain_probe_join_exec_end'; select a, (select max(a) from t0 b where b.a+a.a<10) from t0 a where a<1; show explain for $thr2; id select_type table type possible_keys key key_len ref rows Extra -1 PRIMARY NULL NULL NULL NULL NULL NULL NULL Query plan already deleted +1 PRIMARY a ALL NULL NULL NULL NULL 10 +2 DEPENDENT SUBQUERY b ALL NULL NULL NULL NULL 10 +Warnings: +Note 1003 select a, (select max(a) from t0 b where b.a+a.a<10) from t0 a where a<1 a (select max(a) from t0 b where b.a+a.a<10) 0 9 +# Try to do SHOW EXPLAIN for a query that runs a SET command: +# I've found experimentally that select_id==2 here... +# +set @show_explain_probe_select_id=2; +set debug_dbug='d,show_explain_probe_join_exec_start'; +set @foo= (select max(a) from t0 where sin(a) >0); +show explain for $thr2; +ERROR HY000: Error when executing command SHOW EXPLAIN: Target is not running EXPLAINable command +# +# Attempt SHOW EXPLAIN for an UPDATE +# +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'; +update t2 set dummy=0 where (select max(a) from t0 where t2.a + t0.a <3) >3 ; +show explain for $thr2; +ERROR HY000: Error when executing command SHOW EXPLAIN: Target is not running EXPLAINable command +show explain for $thr2; +ERROR HY000: Error when executing command SHOW EXPLAIN: Target is not running EXPLAINable command +drop table t2; +# +# Attempt SHOW EXPLAIN for a DELETE +# +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: Error when executing command SHOW EXPLAIN: Target is not running EXPLAINable command +show explain for $thr2; +ERROR HY000: Error when executing command SHOW EXPLAIN: Target is not running EXPLAINable command +drop table t2; +# +# Multiple SHOW EXPLAIN calls for one select +# +create table t2 as select a as a, a as dummy from t0 limit 3; +set @show_explain_probe_select_id=2; +set debug_dbug='d,show_explain_probe_join_exec_start'; +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 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 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 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 +0 0 +1 0 +2 0 +drop table t2; +# +# SHOW EXPLAIN for SELECT ... ORDER BY with "Using filesort" +# +explain select * from t0 order by a; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t0 ALL NULL NULL NULL NULL 10 Using filesort +set debug_dbug='d,show_explain_probe_join_exec_start'; +set @show_explain_probe_select_id=1; +select * from t0 order by a; +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 10 Using filesort +Warnings: +Note 1003 select * from t0 order by a +a +0 +1 +2 +3 +4 +5 +6 +7 +8 +9 +# +# SHOW EXPLAIN for SELECT ... with "Using temporary" +# +explain select distinct a from t0; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t0 ALL NULL NULL NULL NULL 10 Using temporary +set debug_dbug='d,show_explain_probe_join_exec_start'; +set @show_explain_probe_select_id=1; +select distinct a from t0; +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 10 Using temporary +Warnings: +Note 1003 select distinct a from t0 +a +0 +1 +2 +3 +4 +5 +6 +7 +8 +9 +# +# SHOW EXPLAIN for SELECT ... with "Using temporary; Using filesort" +# +explain select distinct a from t0; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t0 ALL NULL NULL NULL NULL 10 Using temporary +set debug_dbug='d,show_explain_probe_join_exec_start'; +set @show_explain_probe_select_id=1; +select distinct a from t0; +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 10 Using temporary +Warnings: +Note 1003 select distinct a from t0 +a +0 +1 +2 +3 +4 +5 +6 +7 +8 +9 +set debug_dbug=''; +# +# MDEV-238: SHOW EXPLAIN: Server crashes in JOIN::print_explain with FROM subquery and GROUP BY +# +CREATE TABLE t2 ( a INT ); +INSERT INTO t2 VALUES (1),(2),(1),(4),(2); +explain SELECT alias.a FROM t2, ( SELECT * FROM t2 ) AS alias GROUP BY alias.a; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t2 ALL NULL NULL NULL NULL 5 Using temporary; Using filesort +1 SIMPLE t2 ALL NULL NULL NULL NULL 5 Using join buffer (flat, BNL join) +set debug_dbug='d,show_explain_in_find_all_keys'; +SELECT alias.a FROM t2, ( SELECT * FROM t2 ) AS alias GROUP BY alias.a; +# NOTE: current code will not show "Using join buffer": +show explain for $thr2; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t2 ALL NULL NULL NULL NULL 5 Using temporary; Using filesort +1 SIMPLE t2 ALL NULL NULL NULL NULL 5 +Warnings: +Note 1003 SELECT alias.a FROM t2, ( SELECT * FROM t2 ) AS alias GROUP BY alias.a +a +1 +2 +4 +set debug_dbug=''; +DROP TABLE t2; +# +# MDEV-239: Assertion `field_types == 0 ... ' failed in Protocol_text::store(double, uint32, String*) with +# SHOW EXPLAIN over EXPLAIN EXTENDED +# +CREATE TABLE t2 (a INT); +INSERT INTO t2 VALUES (1),(2),(1),(4),(2); +EXPLAIN EXTENDED SELECT alias.a FROM t2, ( SELECT * FROM t2 ) AS alias GROUP BY alias.a ; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t2 ALL NULL NULL NULL NULL 5 100.00 Using temporary; Using filesort +1 SIMPLE t2 ALL NULL NULL NULL NULL 5 100.00 Using join buffer (flat, BNL join) +Warnings: +Note 1003 select `test`.`t2`.`a` AS `a` from `test`.`t2` join `test`.`t2` group by `test`.`t2`.`a` +set @show_explain_probe_select_id=1; +set debug_dbug='d,show_explain_probe_join_exec_end'; +EXPLAIN EXTENDED SELECT alias.a FROM t2, ( SELECT * FROM t2 ) AS alias GROUP BY alias.a ; +show explain for $thr2; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t2 ALL NULL NULL NULL NULL 5 Using temporary; Using filesort +1 SIMPLE t2 ALL NULL NULL NULL NULL 5 Using join buffer (flat, BNL join) +Warnings: +Note 1003 EXPLAIN EXTENDED SELECT alias.a FROM t2, ( SELECT * FROM t2 ) AS alias GROUP BY alias.a +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t2 ALL NULL NULL NULL NULL 5 100.00 Using temporary; Using filesort +1 SIMPLE t2 ALL NULL NULL NULL NULL 5 100.00 Using join buffer (flat, BNL join) +Warnings: +Note 1003 select `test`.`t2`.`a` AS `a` from `test`.`t2` join `test`.`t2` group by `test`.`t2`.`a` +set debug_dbug=''; +DROP TABLE t2; +# +# MDEV-240: SHOW EXPLAIN: Assertion `this->optimized == 2' failed in +# JOIN::print_explain on query with a JOIN, TEMPTABLE view, +# +CREATE TABLE t3 (a INT); +CREATE ALGORITHM=TEMPTABLE VIEW v1 AS SELECT * FROM t3; +INSERT INTO t3 VALUES (8); +CREATE TABLE t2 (b INT); +INSERT INTO t2 VALUES (4),(5),(6),(7),(8),(9); +explain SELECT * FROM v1, t2; +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY system NULL NULL NULL NULL 1 +1 PRIMARY t2 ALL NULL NULL NULL NULL 6 +2 DERIVED t3 system NULL NULL NULL NULL 1 +set @show_explain_probe_select_id=2; +set debug_dbug='d,show_explain_probe_join_exec_end'; +SELECT * FROM v1, t2; +show explain for $thr2; +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY NULL NULL NULL NULL NULL NULL NULL Not yet optimized +Warnings: +Note 1003 SELECT * FROM v1, t2 +a b +8 4 +8 5 +8 6 +8 7 +8 8 +8 9 +set debug_dbug=''; +DROP VIEW v1; +DROP TABLE t2, t3; +# +# MDEV-267: SHOW EXPLAIN: Server crashes in JOIN::print_explain on most of queries +# +set @show_explain_probe_select_id=1; +set debug_dbug='d,show_explain_probe_join_exec_end'; +select sleep(1); +show explain for $thr2; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE NULL NULL NULL NULL NULL NULL NULL No tables used +Warnings: +Note 1003 select sleep(1) +sleep(1) +0 +set debug_dbug=''; +# +# Same as above, but try another reason for JOIN to be degenerate +# +set @show_explain_probe_select_id=1; +set debug_dbug='d,show_explain_probe_join_exec_end'; +select * from t0 where 1>10; +show explain for $thr2; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE NULL NULL NULL NULL NULL NULL NULL Impossible WHERE +Warnings: +Note 1003 select * from t0 where 1>10 +a +set debug_dbug=''; +# +# Same as above, but try another reason for JOIN to be degenerate (2) +# +create table t3(a int primary key); +insert into t3 select a from t0; +set @show_explain_probe_select_id=1; +set debug_dbug='d,show_explain_probe_join_exec_end'; +select * from t0,t3 where t3.a=112233; +show explain for $thr2; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE NULL NULL NULL NULL NULL NULL NULL no matching row in const table +Warnings: +Note 1003 select * from t0,t3 where t3.a=112233 +a a +set debug_dbug=''; +drop table t3; +# +# MDEV-270: SHOW EXPLAIN: server crashes in JOIN::print_explain on a query with +# select tables optimized away +# +CREATE TABLE t2 (pk INT PRIMARY KEY, a INT ) ENGINE=MyISAM; +INSERT INTO t2 VALUES +(1,4),(2,62),(3,7),(4,1),(5,0),(6,7),(7,7),(8,1),(9,7),(10,1), +(11,5),(12,2),(13,0),(14,1),(15,8),(16,1),(17,1),(18,9),(19,1),(20,5) ; +explain SELECT * FROM t2 WHERE a = +(SELECT MAX(a) FROM t2 +WHERE pk= (SELECT MAX(pk) FROM t2 WHERE pk = 3) +); +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY t2 ALL NULL NULL NULL NULL 20 Using where +2 SUBQUERY t2 const PRIMARY PRIMARY 4 const 1 Using where +3 SUBQUERY NULL NULL NULL NULL NULL NULL NULL Select tables optimized away +set @show_explain_probe_select_id=2; +set debug_dbug='d,show_explain_probe_do_select'; +SELECT * FROM t2 WHERE a = +(SELECT MAX(a) FROM t2 +WHERE pk= (SELECT MAX(pk) FROM t2 WHERE pk = 3) +); +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 20 Using where +2 SUBQUERY t2 const PRIMARY PRIMARY 4 1 Using where +3 SUBQUERY NULL NULL NULL NULL NULL NULL NULL Select tables optimized away +Warnings: +Note 1003 SELECT * FROM t2 WHERE a = +(SELECT MAX(a) FROM t2 +WHERE pk= (SELECT MAX(pk) FROM t2 WHERE pk = 3) +) +pk a +3 7 +6 7 +7 7 +9 7 +set debug_dbug=''; +drop table t2; +# +# MDEV-273: SHOW EXPLAIN: server crashes in JOIN::print_explain on a query with impossible WHERE +# +CREATE TABLE t2 (a1 INT, KEY(a1)) ENGINE=MyISAM; +INSERT INTO t2 VALUES +(4),(6),(7),(1),(0),(7),(7),(1),(7),(1), +(5),(2),(0),(1),(8),(1),(1),(9),(1),(5); +CREATE TABLE t3 (b1 INT) ENGINE=MyISAM; +INSERT INTO t3 VALUES +(4),(5),(8),(4),(8),(2),(9),(6),(4),(8), +(3),(5),(9),(6),(8),(3),(2),(6),(3),(1), +(4),(3),(1),(7),(0),(0),(9),(5),(9),(0), +(2),(2),(5),(9),(1),(4),(8),(6),(5),(5), +(1),(7),(2),(8),(9),(3),(2),(6),(6),(5), +(4),(3),(2),(7),(4),(6),(0),(8),(5),(8), +(2),(9),(7),(5),(7),(0),(4),(3),(1),(0), +(6),(2),(8),(3),(7),(3),(5),(5),(1),(2), +(1),(7),(1),(9),(9),(8),(3); +CREATE TABLE t4 (c1 INT) ENGINE=MyISAM; +EXPLAIN +SELECT count(*) FROM t2, t3 +WHERE a1 < ALL ( +SELECT a1 FROM t2 +WHERE a1 IN ( SELECT a1 FROM t2, t4 ) +); +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY t2 index NULL a1 5 NULL 20 Using where; Using index +1 PRIMARY t3 ALL NULL NULL NULL NULL 87 Using join buffer (flat, BNL join) +2 SUBQUERY NULL NULL NULL NULL NULL NULL NULL Impossible WHERE noticed after reading const tables +set @show_explain_probe_select_id=1; +set debug_dbug='d,show_explain_probe_do_select'; +SELECT count(*) FROM t2, t3 +WHERE a1 < ALL ( +SELECT a1 FROM t2 +WHERE a1 IN ( SELECT a1 FROM t2, t4 ) +); +show explain for $thr2; +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY t2 index NULL a1 5 NULL 20 Using where; Using index +1 PRIMARY t3 ALL NULL NULL NULL NULL 87 Using join buffer (flat, BNL join) +2 SUBQUERY NULL NULL NULL NULL NULL NULL NULL no matching row in const table +Warnings: +Note 1003 SELECT count(*) FROM t2, t3 +WHERE a1 < ALL ( +SELECT a1 FROM t2 +WHERE a1 IN ( SELECT a1 FROM t2, t4 ) +) +count(*) +1740 +set debug_dbug=''; +drop table t2, t3, t4; drop table t0,t1; diff --git a/mysql-test/t/show_explain.test b/mysql-test/t/show_explain.test index 38fc8ff4c8e..01dd7b8801d 100644 --- a/mysql-test/t/show_explain.test +++ b/mysql-test/t/show_explain.test @@ -4,7 +4,8 @@ --source include/have_debug.inc --disable_warnings -drop table if exists t0, t1; +drop table if exists t0, t1, t2, t3, t4; +drop view if exists v1; --enable_warnings # @@ -196,12 +197,308 @@ reap; # TODO: explain in the parent subuqery when the un-correlated child has been # run (and have done irreversible cleanups) +# ^^ Is this at all possible after 5.3? +# Maybe, for 5.3 try this: +# - run before/after the parent has invoked child's optimization +# - run after materialization -# TODO: hit JOIN::optimize for non-select commands: UPDATE/DELETE, SET. +--echo # Try to do SHOW EXPLAIN for a query that runs a SET command: +--echo # I've found experimentally that select_id==2 here... +--echo # +set @show_explain_probe_select_id=2; +set debug_dbug='d,show_explain_probe_join_exec_start'; +send set @foo= (select max(a) from t0 where sin(a) >0); +connection default; +--source include/wait_condition.inc +--error ER_ERROR_WHEN_EXECUTING_COMMAND +evalp show explain for $thr2; +connection con1; +reap; + +--echo # +--echo # Attempt SHOW EXPLAIN for an UPDATE +--echo # +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'; +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_ERROR_WHEN_EXECUTING_COMMAND +evalp show explain for $thr2; +--error ER_ERROR_WHEN_EXECUTING_COMMAND +evalp show explain for $thr2; +connection con1; +reap; +drop table t2; + +--echo # +--echo # Attempt SHOW EXPLAIN for a DELETE +--echo # +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'; +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_ERROR_WHEN_EXECUTING_COMMAND +evalp show explain for $thr2; +--error ER_ERROR_WHEN_EXECUTING_COMMAND +evalp show explain for $thr2; +connection con1; +reap; +drop table t2; -## TODO: Test this: multiple SHOW EXPLAIN calls in course of running of one select -## +--echo # +--echo # Multiple SHOW EXPLAIN calls for one select +--echo # +create table t2 as select a as a, a as dummy from t0 limit 3; +set @show_explain_probe_select_id=2; +set debug_dbug='d,show_explain_probe_join_exec_start'; +send select t2.a, ((select max(a) from t0 where t2.a + t0.a <3) >3) as SUBQ from t2; +connection default; +--source include/wait_condition.inc +evalp show explain for $thr2; +evalp show explain for $thr2; +evalp show explain for $thr2; +connection con1; +reap; +drop table t2; + +--echo # +--echo # SHOW EXPLAIN for SELECT ... ORDER BY with "Using filesort" +--echo # +explain select * from t0 order by a; + +set debug_dbug='d,show_explain_probe_join_exec_start'; +set @show_explain_probe_select_id=1; +send select * from t0 order by a; +connection default; +--source include/wait_condition.inc +evalp show explain for $thr2; +connection con1; +reap; + +--echo # +--echo # SHOW EXPLAIN for SELECT ... with "Using temporary" +--echo # +connection default; +explain select distinct a from t0; +connection con1; + +set debug_dbug='d,show_explain_probe_join_exec_start'; +set @show_explain_probe_select_id=1; +send select distinct a from t0; +connection default; +--source include/wait_condition.inc +evalp show explain for $thr2; +connection con1; +reap; + +--echo # +--echo # SHOW EXPLAIN for SELECT ... with "Using temporary; Using filesort" +--echo # +connection default; +explain select distinct a from t0; +connection con1; + +set debug_dbug='d,show_explain_probe_join_exec_start'; +set @show_explain_probe_select_id=1; +send select distinct a from t0; +connection default; +--source include/wait_condition.inc +evalp show explain for $thr2; +connection con1; +reap; +set debug_dbug=''; + +--echo # +--echo # MDEV-238: SHOW EXPLAIN: Server crashes in JOIN::print_explain with FROM subquery and GROUP BY +--echo # +CREATE TABLE t2 ( a INT ); +INSERT INTO t2 VALUES (1),(2),(1),(4),(2); +explain SELECT alias.a FROM t2, ( SELECT * FROM t2 ) AS alias GROUP BY alias.a; + +set debug_dbug='d,show_explain_in_find_all_keys'; +send SELECT alias.a FROM t2, ( SELECT * FROM t2 ) AS alias GROUP BY alias.a; + +connection default; +--source include/wait_condition.inc +--echo # NOTE: current code will not show "Using join buffer": +evalp show explain for $thr2; +connection con1; +reap; +set debug_dbug=''; +DROP TABLE t2; + + +--echo # +--echo # MDEV-239: Assertion `field_types == 0 ... ' failed in Protocol_text::store(double, uint32, String*) with +--echo # SHOW EXPLAIN over EXPLAIN EXTENDED +--echo # + + +CREATE TABLE t2 (a INT); +INSERT INTO t2 VALUES (1),(2),(1),(4),(2); + +EXPLAIN EXTENDED SELECT alias.a FROM t2, ( SELECT * FROM t2 ) AS alias GROUP BY alias.a ; + +set @show_explain_probe_select_id=1; +set debug_dbug='d,show_explain_probe_join_exec_end'; +send EXPLAIN EXTENDED SELECT alias.a FROM t2, ( SELECT * FROM t2 ) AS alias GROUP BY alias.a ; + +connection default; +--source include/wait_condition.inc +evalp show explain for $thr2; +connection con1; +reap; +set debug_dbug=''; +DROP TABLE t2; + + +--echo # +--echo # MDEV-240: SHOW EXPLAIN: Assertion `this->optimized == 2' failed in +--echo # JOIN::print_explain on query with a JOIN, TEMPTABLE view, +--echo # +CREATE TABLE t3 (a INT); +CREATE ALGORITHM=TEMPTABLE VIEW v1 AS SELECT * FROM t3; +INSERT INTO t3 VALUES (8); +CREATE TABLE t2 (b INT); +INSERT INTO t2 VALUES (4),(5),(6),(7),(8),(9); +explain SELECT * FROM v1, t2; + +set @show_explain_probe_select_id=2; +set debug_dbug='d,show_explain_probe_join_exec_end'; +send SELECT * FROM v1, t2; + +connection default; +--source include/wait_condition.inc +evalp show explain for $thr2; +connection con1; +reap; +set debug_dbug=''; +DROP VIEW v1; +DROP TABLE t2, t3; + +--echo # +--echo # MDEV-267: SHOW EXPLAIN: Server crashes in JOIN::print_explain on most of queries +--echo # +set @show_explain_probe_select_id=1; +set debug_dbug='d,show_explain_probe_join_exec_end'; +send select sleep(1); +connection default; +--source include/wait_condition.inc +evalp show explain for $thr2; +connection con1; +reap; +set debug_dbug=''; + + +--echo # +--echo # Same as above, but try another reason for JOIN to be degenerate +--echo # +set @show_explain_probe_select_id=1; +set debug_dbug='d,show_explain_probe_join_exec_end'; +send select * from t0 where 1>10; +connection default; +--source include/wait_condition.inc +evalp show explain for $thr2; +connection con1; +reap; +set debug_dbug=''; + +--echo # +--echo # Same as above, but try another reason for JOIN to be degenerate (2) +--echo # +create table t3(a int primary key); +insert into t3 select a from t0; +set @show_explain_probe_select_id=1; +set debug_dbug='d,show_explain_probe_join_exec_end'; +send select * from t0,t3 where t3.a=112233; +connection default; +--source include/wait_condition.inc +evalp show explain for $thr2; +connection con1; +reap; +set debug_dbug=''; +drop table t3; + +--echo # +--echo # MDEV-270: SHOW EXPLAIN: server crashes in JOIN::print_explain on a query with +--echo # select tables optimized away +--echo # + +CREATE TABLE t2 (pk INT PRIMARY KEY, a INT ) ENGINE=MyISAM; +INSERT INTO t2 VALUES + (1,4),(2,62),(3,7),(4,1),(5,0),(6,7),(7,7),(8,1),(9,7),(10,1), + (11,5),(12,2),(13,0),(14,1),(15,8),(16,1),(17,1),(18,9),(19,1),(20,5) ; + +explain SELECT * FROM t2 WHERE a = + (SELECT MAX(a) FROM t2 + WHERE pk= (SELECT MAX(pk) FROM t2 WHERE pk = 3) + ); + +set @show_explain_probe_select_id=2; +set debug_dbug='d,show_explain_probe_do_select'; +send SELECT * FROM t2 WHERE a = + (SELECT MAX(a) FROM t2 + WHERE pk= (SELECT MAX(pk) FROM t2 WHERE pk = 3) + ); +connection default; +--source include/wait_condition.inc +evalp show explain for $thr2; +connection con1; +reap; +set debug_dbug=''; +drop table t2; + + +--echo # +--echo # MDEV-273: SHOW EXPLAIN: server crashes in JOIN::print_explain on a query with impossible WHERE +--echo # +CREATE TABLE t2 (a1 INT, KEY(a1)) ENGINE=MyISAM; +INSERT INTO t2 VALUES + (4),(6),(7),(1),(0),(7),(7),(1),(7),(1), + (5),(2),(0),(1),(8),(1),(1),(9),(1),(5); + +CREATE TABLE t3 (b1 INT) ENGINE=MyISAM; +INSERT INTO t3 VALUES + (4),(5),(8),(4),(8),(2),(9),(6),(4),(8), + (3),(5),(9),(6),(8),(3),(2),(6),(3),(1), + (4),(3),(1),(7),(0),(0),(9),(5),(9),(0), + (2),(2),(5),(9),(1),(4),(8),(6),(5),(5), + (1),(7),(2),(8),(9),(3),(2),(6),(6),(5), + (4),(3),(2),(7),(4),(6),(0),(8),(5),(8), + (2),(9),(7),(5),(7),(0),(4),(3),(1),(0), + (6),(2),(8),(3),(7),(3),(5),(5),(1),(2), + (1),(7),(1),(9),(9),(8),(3); +CREATE TABLE t4 (c1 INT) ENGINE=MyISAM; + +EXPLAIN +SELECT count(*) FROM t2, t3 +WHERE a1 < ALL ( + SELECT a1 FROM t2 + WHERE a1 IN ( SELECT a1 FROM t2, t4 ) +); + +set @show_explain_probe_select_id=1; +set debug_dbug='d,show_explain_probe_do_select'; +send +SELECT count(*) FROM t2, t3 +WHERE a1 < ALL ( + SELECT a1 FROM t2 + WHERE a1 IN ( SELECT a1 FROM t2, t4 ) +); + +connection default; +--source include/wait_condition.inc +evalp show explain for $thr2; +connection con1; +reap; +set debug_dbug=''; +drop table t2, t3, t4; + ## TODO: Test this: have several SHOW EXPLAIN requests be queued up for a ## thread and served together. diff --git a/sql/filesort.cc b/sql/filesort.cc index 03379f2738a..5220d80767d 100644 --- a/sql/filesort.cc +++ b/sql/filesort.cc @@ -502,7 +502,7 @@ static ha_rows find_all_keys(SORTPARAM *param, SQL_SELECT *select, my_off_t record; TABLE *sort_form; THD *thd= current_thd; - volatile killed_state *killed= &thd->killed; + //volatile killed_state *killed= &thd->killed; handler *file; MY_BITMAP *save_read_set, *save_write_set, *save_vcol_set; uchar *next_sort_key= sort_keys_buf; @@ -523,6 +523,11 @@ static ha_rows find_all_keys(SORTPARAM *param, SQL_SELECT *select, if (flag) ref_pos= &file->ref[0]; next_pos=ref_pos; + + DBUG_EXECUTE_IF("show_explain_in_find_all_keys", + dbug_serve_apcs(thd, 1); + ); + if (!quick_select) { next_pos=(uchar*) 0; /* Find records in sequence */ @@ -586,7 +591,7 @@ static ha_rows find_all_keys(SORTPARAM *param, SQL_SELECT *select, break; } - if (*killed) + if (thd->check_killed()) { DBUG_PRINT("info",("Sort killed by user")); if (!quick_select) @@ -1231,18 +1236,20 @@ int merge_buffers(SORTPARAM *param, IO_CACHE *from_file, void *first_cmp_arg; element_count dupl_count= 0; uchar *src; - killed_state not_killable; + /* killed_state not_killable; */ uchar *unique_buff= param->unique_buff; - volatile killed_state *killed= ¤t_thd->killed; + /* volatile killed_state *killed= ¤t_thd->killed; */ + const bool killable= !param->not_killable; + THD* const thd=current_thd; DBUG_ENTER("merge_buffers"); - status_var_increment(current_thd->status_var.filesort_merge_passes); - current_thd->query_plan_fsort_passes++; - if (param->not_killable) + status_var_increment(thd->status_var.filesort_merge_passes); + thd->query_plan_fsort_passes++; + /*if (param->not_killable) { killed= ¬_killable; not_killable= NOT_KILLED; - } + }*/ error=0; rec_length= param->rec_length; @@ -1320,7 +1327,7 @@ int merge_buffers(SORTPARAM *param, IO_CACHE *from_file, while (queue.elements > 1) { - if (*killed) + if (killable && thd->check_killed()) { error= 1; goto err; /* purecov: inspected */ } diff --git a/sql/item_subselect.cc b/sql/item_subselect.cc index 276f35fe301..7b1e4fc8b27 100644 --- a/sql/item_subselect.cc +++ b/sql/item_subselect.cc @@ -4725,6 +4725,7 @@ int subselect_hash_sj_engine::exec() thd->lex->current_select= materialize_engine->select_lex; /* The subquery should be optimized, and materialized only once. */ DBUG_ASSERT(materialize_join->optimized && !is_materialized); + materialize_join->exec(); if ((res= test(materialize_join->error || thd->is_fatal_error || thd->is_error()))) diff --git a/sql/my_apc.cc b/sql/my_apc.cc index 386e7d635bd..30adbaad1b6 100644 --- a/sql/my_apc.cc +++ b/sql/my_apc.cc @@ -25,6 +25,14 @@ */ +/* + Initialize the target. + + @note + Initialization must be done prior to enabling/disabling the target, or making + any call requests to it. + Initial state after initialization is 'disabled'. +*/ void Apc_target::init() { // todo: should use my_pthread_... functions instead? @@ -37,6 +45,9 @@ void Apc_target::init() } +/* + Destroy the target. The target must be disabled when this call is made. +*/ void Apc_target::destroy() { DBUG_ASSERT(!enabled); @@ -44,6 +55,9 @@ void Apc_target::destroy() } +/* + Enter ther state where the target is available for serving APC requests +*/ void Apc_target::enable() { pthread_mutex_lock(&LOCK_apc_queue); @@ -52,6 +66,13 @@ void Apc_target::enable() } +/* + Make the target unavailable for serving APC requests. + + @note + This call will serve all requests that were already enqueued +*/ + void Apc_target::disable() { bool process= FALSE; @@ -63,6 +84,9 @@ void Apc_target::disable() process_apc_requests(); } + +/* (internal) Put request into the request list */ + void Apc_target::enqueue_request(Call_request *qe) { //call_queue_size++; @@ -82,6 +106,13 @@ void Apc_target::enqueue_request(Call_request *qe) } } + +/* + (internal) Remove given request from the request queue. + + The request is not necessarily first in the queue. +*/ + void Apc_target::dequeue_request(Call_request *qe) { //call_queue_size--; @@ -100,8 +131,10 @@ void Apc_target::dequeue_request(Call_request *qe) /* - Make an apc call in another thread. The caller is responsible so - that we're not calling to ourselves. + Make an APC (Async Procedure Call) in another thread. + + The caller is responsible for making sure he's not calling an Apc_target + that is serviced by the same thread it is called from. psergey-todo: Should waits here be KILLable? (it seems one needs to use thd->enter_cond() calls to be killable) @@ -120,7 +153,7 @@ bool Apc_target::make_apc_call(apc_func_t func, void *func_arg, Call_request apc_request; apc_request.func= func; apc_request.func_arg= func_arg; - apc_request.done= FALSE; + apc_request.processed= FALSE; (void)pthread_cond_init(&apc_request.COND_request, NULL); (void)pthread_mutex_init(&apc_request.LOCK_request, MY_MUTEX_INIT_SLOW); pthread_mutex_lock(&apc_request.LOCK_request); @@ -134,19 +167,19 @@ bool Apc_target::make_apc_call(apc_func_t func, void *func_arg, int wait_res= 0; /* todo: how about processing other errors here? */ - while (!apc_request.done && (wait_res != ETIMEDOUT)) + while (!apc_request.processed && (wait_res != ETIMEDOUT)) { wait_res= pthread_cond_timedwait(&apc_request.COND_request, &apc_request.LOCK_request, &abstime); } - if (!apc_request.done) + if (!apc_request.processed) { - /* We timed out */ - apc_request.done= TRUE; + /* The wait has timed out. Remove the request from the queue */ + apc_request.processed= TRUE; *timed_out= TRUE; pthread_mutex_unlock(&apc_request.LOCK_request); - + //psergey-todo: "Whoa rare event" refers to this part, right? put a comment. pthread_mutex_lock(&LOCK_apc_queue); dequeue_request(&apc_request); pthread_mutex_unlock(&LOCK_apc_queue); @@ -172,7 +205,8 @@ bool Apc_target::make_apc_call(apc_func_t func, void *func_arg, /* - Process all APC requests + Process all APC requests. + This should be called periodically by the APC target thread. */ void Apc_target::process_apc_requests() @@ -191,7 +225,7 @@ void Apc_target::process_apc_requests() request->what="seen by process_apc_requests"; pthread_mutex_lock(&request->LOCK_request); - if (request->done) + if (request->processed) { /* We can get here when @@ -215,7 +249,7 @@ void Apc_target::process_apc_requests() */ request->what="dequeued by process_apc_requests"; dequeue_request(request); - request->done= TRUE; + request->processed= TRUE; pthread_mutex_unlock(&LOCK_apc_queue); diff --git a/sql/my_apc.h b/sql/my_apc.h index 3906aa24408..0698703ad40 100644 --- a/sql/my_apc.h +++ b/sql/my_apc.h @@ -3,9 +3,18 @@ */ /* - Design - - Mutex-guarded request queue (it belongs to the target), which can be enabled/ - disabled (when empty). + Interface + ~~~~~~~~~ + ( + - This is an APC request queue + - We assume there is a particular owner thread which periodically calls + process_apc_requests() to serve the call requests. + - Other threads can post call requests, and block until they are exectued. + ) + + Implementation + ~~~~~~~~~~~~~~ + - The target has a mutex-guarded request queue. - After the request has been put into queue, the requestor waits for request to be satisfied. The worker satisifes the request and signals the @@ -21,31 +30,11 @@ public: Apc_target() : enabled(0), apc_calls(NULL) /*, call_queue_size(0)*/ {} ~Apc_target() { DBUG_ASSERT(!enabled && !apc_calls);} - /* - Initialize the target. This must be called before anything else. Right - after initialization, the target is disabled. - */ void init(); - - /* - Destroy the target. The target must be disabled when this call is made. - */ void destroy(); - - /* - Enter into state where this target will be serving APC requests - */ void enable(); - - /* - Leave the state where we could serve APC requests (will serve all already - enqueued requests) - */ void disable(); - /* - This should be called periodically to serve observation requests. - */ void process_apc_requests(); typedef void (*apc_func_t)(void *arg); @@ -68,18 +57,32 @@ public: #endif private: class Call_request; + + /* + Non-zero value means we're enabled. It's an int, not bool, because one can + call enable() N times (and then needs to call disable() N times before the + target is really disabled) + */ int enabled; + /* + Circular, double-linked list of all enqueued call requests. + We use this structure, because we + - process requests sequentially + - a thread that has posted a request may time out (or be KILLed) and + cancel the request, which means we'll need to remove its request at + arbitrary point in time. + */ Call_request *apc_calls; - pthread_mutex_t LOCK_apc_queue; + pthread_mutex_t LOCK_apc_queue; class Call_request { public: - apc_func_t func; - void *func_arg; - bool done; + apc_func_t func; /* Function to call */ + void *func_arg; /* Argument to pass it */ + bool processed; pthread_mutex_t LOCK_request; pthread_cond_t COND_request; @@ -87,13 +90,15 @@ private: Call_request *next; Call_request *prev; - const char *what; + const char *what; /* State of the request */ }; void enqueue_request(Call_request *qe); void dequeue_request(Call_request *qe); + + /* return the first call request in queue, or NULL if there are none enqueued */ Call_request *get_first_in_queue() - { + { return apc_calls; } }; diff --git a/sql/sql_class.cc b/sql/sql_class.cc index b191adaca8a..2ad4e77405d 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -2015,6 +2015,11 @@ int THD::send_explain_fields(select_result *result) } +/* + Populate the provided field_list with EXPLAIN output columns. + this->lex->describe has the EXPLAIN flags +*/ + void THD::make_explain_field_list(List &field_list) { Item *item; @@ -3280,13 +3285,20 @@ void Show_explain_request::get_explain_data(void *arg) //TODO: change mem_root to point to request_thd->mem_root. // Actually, change the ARENA, because we're going to allocate items! Query_arena backup_arena; - req->target_thd->set_n_backup_active_arena((Query_arena*)req->request_thd, - &backup_arena); + THD *target_thd= req->target_thd; - req->target_thd->lex->unit.print_explain(req->explain_buf); - - req->target_thd->restore_active_arena((Query_arena*)req->request_thd, + target_thd->set_n_backup_active_arena((Query_arena*)req->request_thd, &backup_arena); + + req->query_str.copy(target_thd->query(), + target_thd->query_length(), + &my_charset_bin); + + if (target_thd->lex->unit.print_explain(req->explain_buf, 0 /* explain flags */)) + req->failed_to_produce= TRUE; + + target_thd->restore_active_arena((Query_arena*)req->request_thd, + &backup_arena); } diff --git a/sql/sql_class.h b/sql/sql_class.h index e82579678b9..6be477605f1 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -1528,8 +1528,12 @@ public: THD *target_thd; THD *request_thd; + bool failed_to_produce; + select_result_explain_buffer *explain_buf; + String query_str; + static void get_explain_data(void *arg); }; diff --git a/sql/sql_join_cache.cc b/sql/sql_join_cache.cc index f953cf4df57..d0c77496694 100644 --- a/sql/sql_join_cache.cc +++ b/sql/sql_join_cache.cc @@ -2236,7 +2236,7 @@ enum_nested_loop_state JOIN_CACHE::join_matching_records(bool skip_last) while (!(error= join_tab_scan->next())) { - if (join->thd->killed) + if (join->thd->check_killed()) { /* The user has aborted the execution of the query */ join->thd->send_kill_message(); @@ -2506,7 +2506,7 @@ enum_nested_loop_state JOIN_CACHE::join_null_complements(bool skip_last) for ( ; cnt; cnt--) { - if (join->thd->killed) + if (join->thd->check_killed()) { /* The user has aborted the execution of the query */ join->thd->send_kill_message(); @@ -3356,7 +3356,7 @@ int JOIN_TAB_SCAN::next() update_virtual_fields(thd, table); while (!err && select && (skip_rc= select->skip_record(thd)) <= 0) { - if (thd->killed || skip_rc < 0) + if (thd->check_killed() || skip_rc < 0) return 1; /* Move to the next record if the last retrieved record does not diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index 4c19217a99d..df0fcd24249 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -3831,6 +3831,7 @@ void SELECT_LEX::update_used_tables() /** Set the EXPLAIN type for this subquery. + psergey-todo: comments about */ void st_select_lex::set_explain_type(bool on_the_fly) @@ -4072,16 +4073,32 @@ int print_explain_message_line(select_result_sink *result, const char *message); -int st_select_lex::print_explain(select_result_sink *output) +int st_select_lex::print_explain(select_result_sink *output, + uint8 explain_flags) { int res; if (join && join->have_query_plan == JOIN::QEP_AVAILABLE) { - res= join->print_explain(output, TRUE, - FALSE, // need_tmp_table, - FALSE, // bool need_order, - FALSE, // bool distinct, - NULL); //const char *message + /* + There is a number of reasons join can be marked as degenerate, so all + three conditions below can happen simultaneously, or individually: + */ + if (!join->table_count || !join->tables_list || join->zero_result_cause) + { + /* It's a degenerate join */ + const char *cause= join->zero_result_cause ? join-> zero_result_cause : + "No tables used"; + res= join->print_explain(output, explain_flags, TRUE, FALSE, FALSE, + FALSE, cause); + } + else + { + res= join->print_explain(output, explain_flags, TRUE, + join->need_tmp, // need_tmp_table + (join->order != 0 && !join->skip_sort_order), // bool need_order + join->select_distinct, // bool distinct + NULL); //const char *message + } if (res) goto err; @@ -4095,7 +4112,7 @@ int st_select_lex::print_explain(select_result_sink *output) */ if (!(unit->item && unit->item->eliminated)) { - unit->print_explain(output); + unit->print_explain(output, explain_flags); } } } @@ -4119,14 +4136,24 @@ err: } -int st_select_lex_unit::print_explain(select_result_sink *output) +int st_select_lex_unit::print_explain(select_result_sink *output, + uint8 explain_flags) { int res= 0; SELECT_LEX *first= first_select(); + + if (first && !first->next_select() && !first->join) + { + /* + If there is only one child, 'first', and it has join==NULL, emit "not in + EXPLAIN state" error. + */ + return 1; + } for (SELECT_LEX *sl= first; sl; sl= sl->next_select()) { - if ((res= sl->print_explain(output))) + if ((res= sl->print_explain(output, explain_flags))) break; } @@ -4136,7 +4163,7 @@ int st_select_lex_unit::print_explain(select_result_sink *output) if (fake_select_lex && !fake_select_lex->join) { res= print_fake_select_lex_join(output, TRUE /* on the fly */, - fake_select_lex, 0 /* flags */); + fake_select_lex, explain_flags); } return res; } diff --git a/sql/sql_lex.h b/sql/sql_lex.h index 038b7e98d77..911a6b766f6 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -718,7 +718,7 @@ public: friend int subselect_union_engine::exec(); List *get_unit_column_types(); - int print_explain(select_result_sink *output); + int print_explain(select_result_sink *output, uint8 explain_flags); }; typedef class st_select_lex_unit SELECT_LEX_UNIT; @@ -1039,7 +1039,7 @@ public: bool save_prep_leaf_tables(THD *thd); bool is_merged_child_of(st_select_lex *ancestor); - int print_explain(select_result_sink *output); + int print_explain(select_result_sink *output, uint8 explain_flags); /* For MODE_ONLY_FULL_GROUP_BY we need to maintain two flags: - Non-aggregated fields are used in this select. diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 5cb4ac8d10c..4d7f66388d9 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -7281,28 +7281,36 @@ prev_record_reads(POSITION *positions, uint idx, table_map found_ref) return found; } +enum enum_exec_or_opt {WALK_OPTIMIZATION_TABS , WALK_EXECUTION_TABS}; /* Enumerate join tabs in breadth-first fashion, including const tables. */ -JOIN_TAB *first_breadth_first_tab(JOIN *join) +JOIN_TAB *first_breadth_first_tab(JOIN *join, enum enum_exec_or_opt tabs_kind) { - return join->join_tab; /* There's always one (i.e. first) table */ + /* There's always one (i.e. first) table */ + return (tabs_kind == WALK_EXECUTION_TABS)? join->join_tab: + join->table_access_tabs; } -JOIN_TAB *next_breadth_first_tab(JOIN *join, JOIN_TAB *tab) +JOIN_TAB *next_breadth_first_tab(JOIN *join, enum enum_exec_or_opt tabs_kind, + JOIN_TAB *tab) { + JOIN_TAB* const first_top_tab= first_breadth_first_tab(join, tabs_kind); + const uint n_top_tabs_count= (tabs_kind == WALK_EXECUTION_TABS)? + join->top_join_tab_count: + join->top_table_access_tabs_count; if (!tab->bush_root_tab) { /* We're at top level. Get the next top-level tab */ tab++; - if (tab < join->join_tab + join->top_join_tab_count) + if (tab < first_top_tab + n_top_tabs_count) return tab; /* No more top-level tabs. Switch to enumerating SJM nest children */ - tab= join->join_tab; + tab= first_top_tab; } else { @@ -7326,7 +7334,7 @@ JOIN_TAB *next_breadth_first_tab(JOIN *join, JOIN_TAB *tab) Ok, "tab" points to a top-level table, and we need to find the next SJM nest and enter it. */ - for (; tab < join->join_tab + join->top_join_tab_count; tab++) + for (; tab < first_top_tab + n_top_tabs_count; tab++) { if (tab->bush_children) return tab->bush_children->start; @@ -7350,7 +7358,7 @@ JOIN_TAB *first_top_level_tab(JOIN *join, enum enum_with_const_tables with_const JOIN_TAB *next_top_level_tab(JOIN *join, JOIN_TAB *tab) { - tab= next_breadth_first_tab(join, tab); + tab= next_breadth_first_tab(join, WALK_EXECUTION_TABS, tab); if (tab && tab->bush_root_tab) tab= NULL; return tab; @@ -7650,6 +7658,13 @@ get_best_combination(JOIN *join) join->top_join_tab_count= join->join_tab_ranges.head()->end - join->join_tab_ranges.head()->start; + /* + Save pointers to select join tabs for SHOW EXPLAIN + */ + join->table_access_tabs= join->join_tab; + join->top_table_access_tabs_count= join->top_join_tab_count; + + update_depend_map(join); DBUG_RETURN(0); } @@ -8079,6 +8094,7 @@ JOIN::make_simple_join(JOIN *parent, TABLE *temp_table) !(parent->join_tab_reexec= (JOIN_TAB*) thd->alloc(sizeof(JOIN_TAB)))) DBUG_RETURN(TRUE); /* purecov: inspected */ + // psergey-todo: here, save the pointer for original join_tabs. join_tab= parent->join_tab_reexec; table= &parent->table_reexec[0]; parent->table_reexec[0]= temp_table; table_count= top_join_tab_count= 1; @@ -10239,7 +10255,12 @@ void JOIN_TAB::cleanup() if (cache) { cache->free(); - cache= 0; + cache= 0; // psergey: this is why we don't see "Using join cache" in SHOW EXPLAIN + // when it is run for "Using temporary+filesort" queries while they + // are at reading-from-tmp-table phase. + // + // TODO ask igor if this can be just moved to later phase + // (JOIN_CACHE objects themselves are not big, arent they) } limit= 0; if (table) @@ -10546,7 +10567,10 @@ void JOIN::cleanup(bool full) DBUG_ENTER("JOIN::cleanup"); DBUG_PRINT("enter", ("full %u", (uint) full)); - have_query_plan= QEP_DELETED; + /* + psergey: let's try without this first: + have_query_plan= QEP_DELETED; + */ if (table) { JOIN_TAB *tab; @@ -21200,7 +21224,7 @@ int print_explain_message_line(select_result_sink *result, int print_fake_select_lex_join(select_result_sink *result, bool on_the_fly, - SELECT_LEX *select_lex, uint8 select_options) + SELECT_LEX *select_lex, uint8 explain_flags) { const CHARSET_INFO *cs= system_charset_info; Item *item_null= new Item_null(); @@ -21246,7 +21270,7 @@ int print_fake_select_lex_join(select_result_sink *result, bool on_the_fly, item_list.push_back(new Item_string(table_name_buffer, len, cs)); } /* partitions */ - if (/*join->thd->lex->describe*/ select_options & DESCRIBE_PARTITIONS) + if (explain_flags & DESCRIBE_PARTITIONS) item_list.push_back(item_null); /* type */ item_list.push_back(new Item_string(join_type_str[JT_ALL], @@ -21261,7 +21285,7 @@ int print_fake_select_lex_join(select_result_sink *result, bool on_the_fly, /* ref */ item_list.push_back(item_null); /* in_rows */ - if (select_options & DESCRIBE_EXTENDED) + if (explain_flags & DESCRIBE_EXTENDED) item_list.push_back(item_null); /* rows */ item_list.push_back(item_null); @@ -21287,7 +21311,8 @@ int print_fake_select_lex_join(select_result_sink *result, bool on_the_fly, modifications to any select's data structures */ -int JOIN::print_explain(select_result_sink *result, bool on_the_fly, +int JOIN::print_explain(select_result_sink *result, uint8 explain_flags, + bool on_the_fly, bool need_tmp_table, bool need_order, bool distinct, const char *message) { @@ -21303,7 +21328,7 @@ int JOIN::print_explain(select_result_sink *result, bool on_the_fly, DBUG_PRINT("info", ("Select 0x%lx, type %s, message %s", (ulong)join->select_lex, join->select_lex->type, message ? message : "NULL")); - DBUG_ASSERT(this->have_query_plan == QEP_AVAILABLE); + DBUG_ASSERT(have_query_plan == QEP_AVAILABLE); /* Don't log this into the slow query log */ if (!on_the_fly) @@ -21318,35 +21343,16 @@ int JOIN::print_explain(select_result_sink *result, bool on_the_fly, */ if (message) { - // join->thd->lex->describe <- as options -#if 0 - item_list.push_bace(new Item_int((int32) - join->select_lex->select_number)); - item_list.push_back(new Item_string(join->select_lex->type, - strlen(join->select_lex->type), cs)); - for (uint i=0 ; i < 7; i++) - item_list.push_back(item_null); - if (join->thd->lex->describe & DESCRIBE_PARTITIONS) - item_list.push_back(item_null); - if (join->thd->lex->describe & DESCRIBE_EXTENDED) - item_list.push_back(item_null); - - item_list.push_back(new Item_string(message,strlen(message),cs)); - if (result->send_data(item_list)) - error= 1; -#endif - //psergey-todo: is passing join->thd->lex->describe correct for SHOW EXPLAIN? if (print_explain_message_line(result, join->select_lex, on_the_fly, - join->thd->lex->describe, message)) + explain_flags, message)) error= 1; } else if (join->select_lex == join->unit->fake_select_lex) { - //psergey-todo: is passing join->thd->lex->describe correct for SHOW EXPLAIN? if (print_fake_select_lex_join(result, on_the_fly, join->select_lex, - join->thd->lex->describe)) + explain_flags)) error= 1; } else if (!join->select_lex->master_unit()->derived || @@ -21359,9 +21365,10 @@ int JOIN::print_explain(select_result_sink *result, bool on_the_fly, bool printing_materialize_nest= FALSE; uint select_id= join->select_lex->select_number; + JOIN_TAB* const first_top_tab= first_breadth_first_tab(join, WALK_OPTIMIZATION_TABS); - for (JOIN_TAB *tab= first_breadth_first_tab(join); tab; - tab= next_breadth_first_tab(join, tab)) + for (JOIN_TAB *tab= first_breadth_first_tab(join, WALK_OPTIMIZATION_TABS); tab; + tab= next_breadth_first_tab(join, WALK_OPTIMIZATION_TABS, tab)) { if (tab->bush_root_tab) { @@ -21450,7 +21457,7 @@ int JOIN::print_explain(select_result_sink *result, bool on_the_fly, cs)); } /* "partitions" column */ - if (join->thd->lex->describe & DESCRIBE_PARTITIONS) + if (explain_flags & DESCRIBE_PARTITIONS) { #ifdef WITH_PARTITION_STORAGE_ENGINE partition_info *part_info; @@ -21598,7 +21605,7 @@ int JOIN::print_explain(select_result_sink *result, bool on_the_fly, table_list->schema_table) { /* in_rows */ - if (join->thd->lex->describe & DESCRIBE_EXTENDED) + if (explain_flags & DESCRIBE_EXTENDED) item_list.push_back(item_null); /* rows */ item_list.push_back(item_null); @@ -21635,7 +21642,7 @@ int JOIN::print_explain(select_result_sink *result, bool on_the_fly, MY_INT64_NUM_DECIMAL_DIGITS)); /* Add "filtered" field to item_list. */ - if (join->thd->lex->describe & DESCRIBE_EXTENDED) + if (explain_flags & DESCRIBE_EXTENDED) { float f= 0.0; if (examined_rows) @@ -21719,7 +21726,7 @@ int JOIN::print_explain(select_result_sink *result, bool on_the_fly, { extra.append(STRING_WITH_LEN("; Using where with pushed " "condition")); - if (thd->lex->describe & DESCRIBE_EXTENDED) + if (explain_flags & DESCRIBE_EXTENDED) { extra.append(STRING_WITH_LEN(": ")); ((COND *)pushed_cond)->print(&extra, QT_ORDINARY); @@ -21812,7 +21819,7 @@ int JOIN::print_explain(select_result_sink *result, bool on_the_fly, extra.append(STRING_WITH_LEN("; End temporary")); else if (tab->do_firstmatch) { - if (tab->do_firstmatch == join->join_tab - 1) + if (tab->do_firstmatch == /*join->join_tab*/ first_top_tab - 1) extra.append(STRING_WITH_LEN("; FirstMatch")); else { @@ -21879,7 +21886,8 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order, THD *thd=join->thd; select_result *result=join->result; DBUG_ENTER("select_describe"); - join->error= join->print_explain(result, FALSE, /* Not on-the-fly */ + join->error= join->print_explain(result, thd->lex->describe, + FALSE, /* Not on-the-fly */ need_tmp_table, need_order, distinct, message); diff --git a/sql/sql_select.h b/sql/sql_select.h index e504eaf52b1..00af5e14607 100644 --- a/sql/sql_select.h +++ b/sql/sql_select.h @@ -896,6 +896,20 @@ protected: public: JOIN_TAB *join_tab, **best_ref; + + /* + For "Using temporary+Using filesort" queries, JOIN::join_tab can point to + either: + 1. array of join tabs describing how to run the select, or + 2. array of single join tab describing read from the temporary table. + + SHOW EXPLAIN code needs to read/show #1. This is why two next members are + there for saving it. + */ + JOIN_TAB *table_access_tabs; + uint top_table_access_tabs_count; + + JOIN_TAB **map2table; ///< mapping between table indexes and JOIN_TABs JOIN_TAB *join_tab_save; ///< saved join_tab for subquery reexecution @@ -1161,7 +1175,11 @@ public: const char *zero_result_cause; ///< not 0 if exec must return zero result bool union_part; ///< this subselect is part of union - bool optimized; ///< flag to avoid double optimization in EXPLAIN + + enum join_optimization_state { NOT_OPTIMIZED=0, + OPTIMIZATION_IN_PROGRESS=1, + OPTIMIZATION_DONE=2}; + bool optimized; ///< flag to avoid double optimization in EXPLAIN bool initialized; ///< flag to avoid double init_execution calls enum { QEP_NOT_PRESENT_YET, QEP_AVAILABLE, QEP_DELETED} have_query_plan; @@ -1393,7 +1411,8 @@ public: return (unit->item && unit->item->is_in_predicate()); } - int print_explain(select_result_sink *result, bool on_the_fly, + int print_explain(select_result_sink *result, uint8 explain_flags, + bool on_the_fly, bool need_tmp_table, bool need_order, bool distinct,const char *message); private: diff --git a/sql/sql_show.cc b/sql/sql_show.cc index 09100c78b4b..87f46abb23c 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -2062,16 +2062,33 @@ void mysqld_show_explain(THD *thd, ulong thread_id) explain_req.explain_buf= explain_buf; explain_req.target_thd= tmp; explain_req.request_thd= thd; + explain_req.failed_to_produce= FALSE; bres= tmp->apc_target.make_apc_call(Show_explain_request::get_explain_data, (void*)&explain_req, timeout_sec, &timed_out); - if (bres) + + if (bres || explain_req.failed_to_produce) { /* TODO not enabled or time out */ - my_error(ER_ERROR_WHEN_EXECUTING_COMMAND, MYF(0), - "SHOW EXPLAIN", - "Target is not running EXPLAINable command"); + if (timed_out) + { + my_error(ER_ERROR_WHEN_EXECUTING_COMMAND, MYF(0), + "SHOW EXPLAIN", + "Timeout"); + } + else + { + my_error(ER_ERROR_WHEN_EXECUTING_COMMAND, MYF(0), + "SHOW EXPLAIN", + "Target is not running EXPLAINable command"); + } + bres= TRUE; + } + else + { + push_warning(thd, MYSQL_ERROR::WARN_LEVEL_NOTE, + ER_YES, explain_req.query_str.c_ptr_safe()); } mysql_mutex_unlock(&tmp->LOCK_thd_data); if (!bres) diff --git a/sql/table.h b/sql/table.h index f3f9d5ac036..bd149b10adf 100644 --- a/sql/table.h +++ b/sql/table.h @@ -1106,7 +1106,12 @@ public: See TABLE_LIST::process_index_hints(). */ bool force_index_group; - bool distinct,const_table,no_rows, used_for_duplicate_elimination; + /* + TRUE<=> this table was created with create_tmp_table(... distinct=TRUE..) + call + */ + bool distinct; + bool const_table,no_rows, used_for_duplicate_elimination; /** If set, the optimizer has found that row retrieval should access index