diff options
author | Sergey Petrunya <psergey@askmonty.org> | 2011-05-29 12:58:44 +0400 |
---|---|---|
committer | Sergey Petrunya <psergey@askmonty.org> | 2011-05-29 12:58:44 +0400 |
commit | 5cd18326c2167ddf1fe989154c519809cd26f175 (patch) | |
tree | 0166109cba0ae1b9b6bfa81d8c38ecd1a0fe2276 | |
parent | 8ce2e1bcbe1f00b6030b1c82ea62b3a64b1d0de9 (diff) | |
parent | 4efe046352f61cc7b8a44a604f96f8b68cf155c0 (diff) | |
download | mariadb-git-5cd18326c2167ddf1fe989154c519809cd26f175.tar.gz |
Merge 5.3->main -> 5.3-mwl90
40 files changed, 5348 insertions, 1929 deletions
diff --git a/mysql-test/r/subselect.result b/mysql-test/r/subselect.result index 492eb92e9c0..42b38136109 100644 --- a/mysql-test/r/subselect.result +++ b/mysql-test/r/subselect.result @@ -1364,7 +1364,7 @@ explain extended select * from t2 where t2.a in (select t1.a from t1,t3 where t1 id select_type table type possible_keys key key_len ref rows filtered Extra 1 PRIMARY t2 index a a 5 NULL 4 100.00 Using where; Using index 1 PRIMARY t3 index a a 5 NULL 3 100.00 Using where; Using index -1 PRIMARY t1 ref a a 10 test.t2.a,test.t3.a 116 100.61 Using index; FirstMatch(t2) +1 PRIMARY t1 ref a a 10 test.t2.a,test.t3.a 116 100.00 Using index; FirstMatch(t2) Warnings: Note 1003 select `test`.`t2`.`a` AS `a` from `test`.`t2` semi join (`test`.`t1` join `test`.`t3`) where ((`test`.`t1`.`a` = `test`.`t2`.`a`) and (`test`.`t1`.`b` = `test`.`t3`.`a`)) insert into t1 values (3,31); diff --git a/mysql-test/r/subselect4.result b/mysql-test/r/subselect4.result index a8102486f6b..0cf935418d4 100644 --- a/mysql-test/r/subselect4.result +++ b/mysql-test/r/subselect4.result @@ -1,3 +1,4 @@ +drop table if exists t1,t2,t3,t4,t5,t6; # # Bug #46791: Assertion failed:(table->key_read==0),function unknown # function,file sql_base.cc @@ -356,7 +357,7 @@ FROM t2 WHERE PTYPE = 'Design')); id select_type table type possible_keys key key_len ref rows Extra 1 PRIMARY t1 ALL t1_IDX NULL NULL NULL 5 -1 PRIMARY subselect2 eq_ref unique_key unique_key 3 func 1 +1 PRIMARY <subquery2> eq_ref distinct_key distinct_key 3 func 1 2 SUBQUERY t2 ALL NULL NULL NULL NULL 6 Using where 2 SUBQUERY t3 ALL NULL NULL NULL NULL 12 Using where; Using join buffer (flat, BNL join) PREPARE stmt FROM "EXPLAIN SELECT EMPNAME @@ -371,13 +372,13 @@ WHERE EMPNUM IN EXECUTE stmt; id select_type table type possible_keys key key_len ref rows Extra 1 PRIMARY t1 ALL t1_IDX NULL NULL NULL 5 -1 PRIMARY subselect2 eq_ref unique_key unique_key 3 func 1 +1 PRIMARY <subquery2> eq_ref distinct_key distinct_key 3 func 1 2 SUBQUERY t2 ALL NULL NULL NULL NULL 6 Using where 2 SUBQUERY t3 ALL NULL NULL NULL NULL 12 Using where; Using join buffer (flat, BNL join) EXECUTE stmt; id select_type table type possible_keys key key_len ref rows Extra 1 SIMPLE t1 ALL t1_IDX NULL NULL NULL 5 -1 SIMPLE subselect2 eq_ref unique_key unique_key 3 func 1 +1 SIMPLE <subquery2> eq_ref distinct_key distinct_key 3 func 1 2 SUBQUERY t2 ALL NULL NULL NULL NULL 6 Using where 2 SUBQUERY t3 ALL NULL NULL NULL NULL 12 Using where; Using join buffer (flat, BNL join) DEALLOCATE PREPARE stmt; @@ -394,7 +395,7 @@ FROM t2 WHERE PTYPE = 'Design')); id select_type table type possible_keys key key_len ref rows Extra 1 PRIMARY t1 ALL t1_IDX NULL NULL NULL 5 -1 PRIMARY subselect2 eq_ref unique_key unique_key 3 func 1 +1 PRIMARY <subquery2> eq_ref distinct_key distinct_key 3 func 1 2 SUBQUERY t2 ALL NULL NULL NULL NULL 6 Using where 2 SUBQUERY t3 ALL NULL NULL NULL NULL 12 Using where; Using join buffer (flat, BNL join) PREPARE stmt FROM "EXPLAIN SELECT EMPNAME @@ -409,13 +410,13 @@ WHERE EMPNUM IN EXECUTE stmt; id select_type table type possible_keys key key_len ref rows Extra 1 PRIMARY t1 ALL t1_IDX NULL NULL NULL 5 -1 PRIMARY subselect2 eq_ref unique_key unique_key 3 func 1 +1 PRIMARY <subquery2> eq_ref distinct_key distinct_key 3 func 1 2 SUBQUERY t2 ALL NULL NULL NULL NULL 6 Using where 2 SUBQUERY t3 ALL NULL NULL NULL NULL 12 Using where; Using join buffer (flat, BNL join) EXECUTE stmt; id select_type table type possible_keys key key_len ref rows Extra 1 SIMPLE t1 ALL t1_IDX NULL NULL NULL 5 -1 SIMPLE subselect2 eq_ref unique_key unique_key 3 func 1 +1 SIMPLE <subquery2> eq_ref distinct_key distinct_key 3 func 1 2 SUBQUERY t2 ALL NULL NULL NULL NULL 6 Using where 2 SUBQUERY t3 ALL NULL NULL NULL NULL 12 Using where; Using join buffer (flat, BNL join) DEALLOCATE PREPARE stmt; @@ -431,7 +432,7 @@ FROM t2 WHERE PTYPE = 'Design')); id select_type table type possible_keys key key_len ref rows Extra 1 PRIMARY t1 ALL NULL NULL NULL NULL 5 -1 PRIMARY subselect2 eq_ref unique_key unique_key 3 func 1 +1 PRIMARY <subquery2> eq_ref distinct_key distinct_key 3 func 1 2 SUBQUERY t2 ALL NULL NULL NULL NULL 6 Using where 2 SUBQUERY t3 ALL NULL NULL NULL NULL 12 Using where; Using join buffer (flat, BNL join) PREPARE stmt FROM "EXPLAIN SELECT EMPNAME @@ -446,13 +447,13 @@ WHERE EMPNUM IN EXECUTE stmt; id select_type table type possible_keys key key_len ref rows Extra 1 PRIMARY t1 ALL NULL NULL NULL NULL 5 -1 PRIMARY subselect2 eq_ref unique_key unique_key 3 func 1 +1 PRIMARY <subquery2> eq_ref distinct_key distinct_key 3 func 1 2 SUBQUERY t2 ALL NULL NULL NULL NULL 6 Using where 2 SUBQUERY t3 ALL NULL NULL NULL NULL 12 Using where; Using join buffer (flat, BNL join) EXECUTE stmt; id select_type table type possible_keys key key_len ref rows Extra 1 SIMPLE t1 ALL NULL NULL NULL NULL 5 -1 SIMPLE subselect2 eq_ref unique_key unique_key 3 func 1 +1 SIMPLE <subquery2> eq_ref distinct_key distinct_key 3 func 1 2 SUBQUERY t2 ALL NULL NULL NULL NULL 6 Using where 2 SUBQUERY t3 ALL NULL NULL NULL NULL 12 Using where; Using join buffer (flat, BNL join) DEALLOCATE PREPARE stmt; @@ -579,7 +580,7 @@ FROM t2 WHERE PTYPE = 'Design')); id select_type table type possible_keys key key_len ref rows Extra 1 PRIMARY t1 ALL t1_IDX NULL NULL NULL 5 -1 PRIMARY subselect2 eq_ref unique_key unique_key 3 func 1 +1 PRIMARY <subquery2> eq_ref distinct_key distinct_key 3 func 1 2 SUBQUERY t2 ALL NULL NULL NULL NULL 6 Using where 2 SUBQUERY t3 ALL NULL NULL NULL NULL 12 Using where; Using join buffer (flat, BNL join) PREPARE stmt FROM "EXPLAIN SELECT EMPNAME @@ -594,13 +595,13 @@ WHERE EMPNUM IN EXECUTE stmt; id select_type table type possible_keys key key_len ref rows Extra 1 PRIMARY t1 ALL t1_IDX NULL NULL NULL 5 -1 PRIMARY subselect2 eq_ref unique_key unique_key 3 func 1 +1 PRIMARY <subquery2> eq_ref distinct_key distinct_key 3 func 1 2 SUBQUERY t2 ALL NULL NULL NULL NULL 6 Using where 2 SUBQUERY t3 ALL NULL NULL NULL NULL 12 Using where; Using join buffer (flat, BNL join) EXECUTE stmt; id select_type table type possible_keys key key_len ref rows Extra 1 SIMPLE t1 ALL t1_IDX NULL NULL NULL 5 -1 SIMPLE subselect2 eq_ref unique_key unique_key 3 func 1 +1 SIMPLE <subquery2> eq_ref distinct_key distinct_key 3 func 1 2 SUBQUERY t2 ALL NULL NULL NULL NULL 6 Using where 2 SUBQUERY t3 ALL NULL NULL NULL NULL 12 Using where; Using join buffer (flat, BNL join) DEALLOCATE PREPARE stmt; @@ -617,7 +618,7 @@ FROM t2 WHERE PTYPE = 'Design')); id select_type table type possible_keys key key_len ref rows Extra 1 PRIMARY t1 ALL t1_IDX NULL NULL NULL 5 -1 PRIMARY subselect2 eq_ref unique_key unique_key 3 func 1 +1 PRIMARY <subquery2> eq_ref distinct_key distinct_key 3 func 1 2 SUBQUERY t2 ALL NULL NULL NULL NULL 6 Using where 2 SUBQUERY t3 ALL NULL NULL NULL NULL 12 Using where; Using join buffer (flat, BNL join) PREPARE stmt FROM "EXPLAIN SELECT EMPNAME @@ -632,13 +633,13 @@ WHERE EMPNUM IN EXECUTE stmt; id select_type table type possible_keys key key_len ref rows Extra 1 PRIMARY t1 ALL t1_IDX NULL NULL NULL 5 -1 PRIMARY subselect2 eq_ref unique_key unique_key 3 func 1 +1 PRIMARY <subquery2> eq_ref distinct_key distinct_key 3 func 1 2 SUBQUERY t2 ALL NULL NULL NULL NULL 6 Using where 2 SUBQUERY t3 ALL NULL NULL NULL NULL 12 Using where; Using join buffer (flat, BNL join) EXECUTE stmt; id select_type table type possible_keys key key_len ref rows Extra 1 SIMPLE t1 ALL t1_IDX NULL NULL NULL 5 -1 SIMPLE subselect2 eq_ref unique_key unique_key 3 func 1 +1 SIMPLE <subquery2> eq_ref distinct_key distinct_key 3 func 1 2 SUBQUERY t2 ALL NULL NULL NULL NULL 6 Using where 2 SUBQUERY t3 ALL NULL NULL NULL NULL 12 Using where; Using join buffer (flat, BNL join) DEALLOCATE PREPARE stmt; @@ -654,7 +655,7 @@ FROM t2 WHERE PTYPE = 'Design')); id select_type table type possible_keys key key_len ref rows Extra 1 PRIMARY t1 ALL NULL NULL NULL NULL 5 -1 PRIMARY subselect2 eq_ref unique_key unique_key 3 func 1 +1 PRIMARY <subquery2> eq_ref distinct_key distinct_key 3 func 1 2 SUBQUERY t2 ALL NULL NULL NULL NULL 6 Using where 2 SUBQUERY t3 ALL NULL NULL NULL NULL 12 Using where; Using join buffer (flat, BNL join) PREPARE stmt FROM "EXPLAIN SELECT EMPNAME @@ -669,13 +670,13 @@ WHERE EMPNUM IN EXECUTE stmt; id select_type table type possible_keys key key_len ref rows Extra 1 PRIMARY t1 ALL NULL NULL NULL NULL 5 -1 PRIMARY subselect2 eq_ref unique_key unique_key 3 func 1 +1 PRIMARY <subquery2> eq_ref distinct_key distinct_key 3 func 1 2 SUBQUERY t2 ALL NULL NULL NULL NULL 6 Using where 2 SUBQUERY t3 ALL NULL NULL NULL NULL 12 Using where; Using join buffer (flat, BNL join) EXECUTE stmt; id select_type table type possible_keys key key_len ref rows Extra 1 SIMPLE t1 ALL NULL NULL NULL NULL 5 -1 SIMPLE subselect2 eq_ref unique_key unique_key 3 func 1 +1 SIMPLE <subquery2> eq_ref distinct_key distinct_key 3 func 1 2 SUBQUERY t2 ALL NULL NULL NULL NULL 6 Using where 2 SUBQUERY t3 ALL NULL NULL NULL NULL 12 Using where; Using join buffer (flat, BNL join) DEALLOCATE PREPARE stmt; @@ -735,6 +736,7 @@ WHERE ( t1.f10 ) IN ( SELECT f11 FROM t2 GROUP BY f11 )); id select_type table type possible_keys key key_len ref rows Extra 1 PRIMARY NULL NULL NULL NULL NULL NULL NULL Impossible WHERE noticed after reading const tables 2 SUBQUERY t1 ALL NULL NULL NULL NULL 2 Using where +2 SUBQUERY <subquery3> eq_ref distinct_key distinct_key 5 test.t1.f10 1 3 SUBQUERY t2 ALL NULL NULL NULL NULL 2 Using temporary SELECT * FROM t1 WHERE f3 = ( @@ -749,6 +751,7 @@ WHERE ( f10, f10 ) IN ( SELECT f11, f11 FROM t2 GROUP BY f11 )); id select_type table type possible_keys key key_len ref rows Extra 1 PRIMARY NULL NULL NULL NULL NULL NULL NULL Impossible WHERE noticed after reading const tables 2 SUBQUERY t1 ALL NULL NULL NULL NULL 2 Using where +2 SUBQUERY <subquery3> eq_ref distinct_key distinct_key 10 test.t1.f10,test.t1.f10 1 3 SUBQUERY t2 ALL NULL NULL NULL NULL 2 Using temporary SELECT * FROM t1 WHERE f3 = ( diff --git a/mysql-test/r/subselect_mat.result b/mysql-test/r/subselect_mat.result index 144519878c1..95b4eeb5d04 100644 --- a/mysql-test/r/subselect_mat.result +++ b/mysql-test/r/subselect_mat.result @@ -1,4 +1,7 @@ +set @@optimizer_switch='materialization=on,in_to_exists=off,semijoin=off'; drop table if exists t1, t2, t3, t1i, t2i, t3i; +drop table if exists columns; +drop table if exists t1_16, t2_16, t3_16; drop view if exists v1, v2, v1m, v2m; create table t1 (a1 char(8), a2 char(8)); create table t2 (b1 char(8), b2 char(8)); @@ -30,7 +33,7 @@ create index it3i3 on t3i (c1, c2); insert into t1i select * from t1; insert into t2i select * from t2; insert into t3i select * from t3; -set @@optimizer_switch='materialization=on,in_to_exists=off,semijoin=off'; +set @@optimizer_switch='materialization=on,in_to_exists=off,firstmatch=off'; /****************************************************************************** * Simple tests. ******************************************************************************/ @@ -41,7 +44,7 @@ id select_type table type possible_keys key key_len ref rows filtered Extra 1 PRIMARY t1 ALL NULL NULL NULL NULL 3 100.00 Using where 2 SUBQUERY t2 ALL NULL NULL NULL NULL 5 100.00 Using where Warnings: -Note 1003 select `test`.`t1`.`a1` AS `a1`,`test`.`t1`.`a2` AS `a2` from `test`.`t1` where <expr_cache><`test`.`t1`.`a1`>(<in_optimizer>(`test`.`t1`.`a1`,`test`.`t1`.`a1` in ( <materialize> (select `test`.`t2`.`b1` from `test`.`t2` where (`test`.`t2`.`b1` > '0') ), <primary_index_lookup>(`test`.`t1`.`a1` in <temporary table> on distinct_key where ((`test`.`t1`.`a1` = `materialized subselect`.`b1`)))))) +Note 1003 select `test`.`t1`.`a1` AS `a1`,`test`.`t1`.`a2` AS `a2` from `test`.`t1` where <expr_cache><`test`.`t1`.`a1`>(<in_optimizer>(`test`.`t1`.`a1`,`test`.`t1`.`a1` in ( <materialize> (select `test`.`t2`.`b1` from `test`.`t2` where (`test`.`t2`.`b1` > '0') ), <primary_index_lookup>(`test`.`t1`.`a1` in <temporary table> on distinct_key where ((`test`.`t1`.`a1` = `<subquery2>`.`b1`)))))) select * from t1 where a1 in (select b1 from t2 where b1 > '0'); a1 a2 1 - 01 2 - 01 @@ -52,7 +55,7 @@ id select_type table type possible_keys key key_len ref rows filtered Extra 1 PRIMARY t1 ALL NULL NULL NULL NULL 3 100.00 Using where 2 SUBQUERY t2 ALL NULL NULL NULL NULL 5 100.00 Using where; Using temporary Warnings: -Note 1003 select `test`.`t1`.`a1` AS `a1`,`test`.`t1`.`a2` AS `a2` from `test`.`t1` where <expr_cache><`test`.`t1`.`a1`>(<in_optimizer>(`test`.`t1`.`a1`,`test`.`t1`.`a1` in ( <materialize> (select `test`.`t2`.`b1` from `test`.`t2` where (`test`.`t2`.`b1` > '0') group by `test`.`t2`.`b1` ), <primary_index_lookup>(`test`.`t1`.`a1` in <temporary table> on distinct_key where ((`test`.`t1`.`a1` = `materialized subselect`.`b1`)))))) +Note 1003 select `test`.`t1`.`a1` AS `a1`,`test`.`t1`.`a2` AS `a2` from `test`.`t1` where <expr_cache><`test`.`t1`.`a1`>(<in_optimizer>(`test`.`t1`.`a1`,`test`.`t1`.`a1` in ( <materialize> (select `test`.`t2`.`b1` from `test`.`t2` where (`test`.`t2`.`b1` > '0') group by `test`.`t2`.`b1` ), <primary_index_lookup>(`test`.`t1`.`a1` in <temporary table> on distinct_key where ((`test`.`t1`.`a1` = `<subquery2>`.`b1`)))))) select * from t1 where a1 in (select b1 from t2 where b1 > '0' group by b1); a1 a2 1 - 01 2 - 01 @@ -63,7 +66,7 @@ id select_type table type possible_keys key key_len ref rows filtered Extra 1 PRIMARY t1 ALL NULL NULL NULL NULL 3 100.00 Using where 2 SUBQUERY t2 ALL NULL NULL NULL NULL 5 100.00 Using where; Using temporary Warnings: -Note 1003 select `test`.`t1`.`a1` AS `a1`,`test`.`t1`.`a2` AS `a2` from `test`.`t1` where <expr_cache><`test`.`t1`.`a2`,`test`.`t1`.`a1`>(<in_optimizer>((`test`.`t1`.`a1`,`test`.`t1`.`a2`),(`test`.`t1`.`a1`,`test`.`t1`.`a2`) in ( <materialize> (select `test`.`t2`.`b1`,`test`.`t2`.`b2` from `test`.`t2` where (`test`.`t2`.`b1` > '0') group by `test`.`t2`.`b1`,`test`.`t2`.`b2` ), <primary_index_lookup>(`test`.`t1`.`a1` in <temporary table> on distinct_key where ((`test`.`t1`.`a1` = `materialized subselect`.`b1`) and (`test`.`t1`.`a2` = `materialized subselect`.`b2`)))))) +Note 1003 select `test`.`t1`.`a1` AS `a1`,`test`.`t1`.`a2` AS `a2` from `test`.`t1` where <expr_cache><`test`.`t1`.`a2`,`test`.`t1`.`a1`>(<in_optimizer>((`test`.`t1`.`a1`,`test`.`t1`.`a2`),(`test`.`t1`.`a1`,`test`.`t1`.`a2`) in ( <materialize> (select `test`.`t2`.`b1`,`test`.`t2`.`b2` from `test`.`t2` where (`test`.`t2`.`b1` > '0') group by `test`.`t2`.`b1`,`test`.`t2`.`b2` ), <primary_index_lookup>(`test`.`t1`.`a1` in <temporary table> on distinct_key where ((`test`.`t1`.`a1` = `<subquery2>`.`b1`) and (`test`.`t1`.`a2` = `<subquery2>`.`b2`)))))) select * from t1 where (a1, a2) in (select b1, b2 from t2 where b1 > '0' group by b1, b2); a1 a2 1 - 01 2 - 01 @@ -74,7 +77,7 @@ id select_type table type possible_keys key key_len ref rows filtered Extra 1 PRIMARY t1 ALL NULL NULL NULL NULL 3 100.00 Using where 2 SUBQUERY t2 ALL NULL NULL NULL NULL 5 100.00 Using where; Using temporary Warnings: -Note 1003 select `test`.`t1`.`a1` AS `a1`,`test`.`t1`.`a2` AS `a2` from `test`.`t1` where <expr_cache><`test`.`t1`.`a2`,`test`.`t1`.`a1`>(<in_optimizer>((`test`.`t1`.`a1`,`test`.`t1`.`a2`),(`test`.`t1`.`a1`,`test`.`t1`.`a2`) in ( <materialize> (select `test`.`t2`.`b1`,min(`test`.`t2`.`b2`) from `test`.`t2` where (`test`.`t2`.`b1` > '0') group by `test`.`t2`.`b1` ), <primary_index_lookup>(`test`.`t1`.`a1` in <temporary table> on distinct_key where ((`test`.`t1`.`a1` = `materialized subselect`.`b1`) and (`test`.`t1`.`a2` = `materialized subselect`.`min(b2)`)))))) +Note 1003 select `test`.`t1`.`a1` AS `a1`,`test`.`t1`.`a2` AS `a2` from `test`.`t1` where <expr_cache><`test`.`t1`.`a2`,`test`.`t1`.`a1`>(<in_optimizer>((`test`.`t1`.`a1`,`test`.`t1`.`a2`),(`test`.`t1`.`a1`,`test`.`t1`.`a2`) in ( <materialize> (select `test`.`t2`.`b1`,min(`test`.`t2`.`b2`) from `test`.`t2` where (`test`.`t2`.`b1` > '0') group by `test`.`t2`.`b1` ), <primary_index_lookup>(`test`.`t1`.`a1` in <temporary table> on distinct_key where ((`test`.`t1`.`a1` = `<subquery2>`.`b1`) and (`test`.`t1`.`a2` = `<subquery2>`.`min(b2)`)))))) select * from t1 where (a1, a2) in (select b1, min(b2) from t2 where b1 > '0' group by b1); a1 a2 1 - 01 2 - 01 @@ -82,10 +85,10 @@ a1 a2 explain extended select * from t1i where a1 in (select b1 from t2i where b1 > '0'); id select_type table type possible_keys key key_len ref rows filtered Extra -1 PRIMARY t1i index NULL it1i3 18 NULL 3 100.00 Using where; Using index -2 SUBQUERY t2i index it2i1,it2i3 it2i1 9 NULL 5 100.00 Using where; Using index +1 PRIMARY t1i index NULL _it1_idx # NULL 3 100.00 Using where; +2 SUBQUERY t2i index it2i1,it2i3 it2i1 # NULL 5 100.00 Using where; Warnings: -Note 1003 select `test`.`t1i`.`a1` AS `a1`,`test`.`t1i`.`a2` AS `a2` from `test`.`t1i` where <expr_cache><`test`.`t1i`.`a1`>(<in_optimizer>(`test`.`t1i`.`a1`,`test`.`t1i`.`a1` in ( <materialize> (select `test`.`t2i`.`b1` from `test`.`t2i` where (`test`.`t2i`.`b1` > '0') ), <primary_index_lookup>(`test`.`t1i`.`a1` in <temporary table> on distinct_key where ((`test`.`t1i`.`a1` = `materialized subselect`.`b1`)))))) +Note 1003 select `test`.`t1i`.`a1` AS `a1`,`test`.`t1i`.`a2` AS `a2` from `test`.`t1i` where <expr_cache><`test`.`t1i`.`a1`>(<in_optimizer>(`test`.`t1i`.`a1`,`test`.`t1i`.`a1` in ( <materialize> (select `test`.`t2i`.`b1` from `test`.`t2i` where (`test`.`t2i`.`b1` > '0') ), <primary_index_lookup>(`test`.`t1i`.`a1` in <temporary table> on distinct_key where ((`test`.`t1i`.`a1` = `<subquery2>`.`b1`)))))) select * from t1i where a1 in (select b1 from t2i where b1 > '0'); a1 a2 1 - 01 2 - 01 @@ -93,10 +96,10 @@ a1 a2 explain extended select * from t1i where a1 in (select b1 from t2i where b1 > '0' group by b1); id select_type table type possible_keys key key_len ref rows filtered Extra -1 PRIMARY t1i index NULL it1i3 18 NULL 3 100.00 Using where; Using index -2 SUBQUERY t2i range it2i1,it2i3 it2i1 9 NULL 3 100.00 Using where; Using index for group-by +1 PRIMARY t1i index NULL # 18 # 3 100.00 # +2 SUBQUERY t2i range it2i1,it2i3 # 9 # 3 100.00 # Warnings: -Note 1003 select `test`.`t1i`.`a1` AS `a1`,`test`.`t1i`.`a2` AS `a2` from `test`.`t1i` where <expr_cache><`test`.`t1i`.`a1`>(<in_optimizer>(`test`.`t1i`.`a1`,`test`.`t1i`.`a1` in ( <materialize> (select `test`.`t2i`.`b1` from `test`.`t2i` where (`test`.`t2i`.`b1` > '0') group by `test`.`t2i`.`b1` ), <primary_index_lookup>(`test`.`t1i`.`a1` in <temporary table> on distinct_key where ((`test`.`t1i`.`a1` = `materialized subselect`.`b1`)))))) +Note 1003 select `test`.`t1i`.`a1` AS `a1`,`test`.`t1i`.`a2` AS `a2` from `test`.`t1i` where <expr_cache><`test`.`t1i`.`a1`>(<in_optimizer>(`test`.`t1i`.`a1`,`test`.`t1i`.`a1` in ( <materialize> (select `test`.`t2i`.`b1` from `test`.`t2i` where (`test`.`t2i`.`b1` > '0') group by `test`.`t2i`.`b1` ), <primary_index_lookup>(`test`.`t1i`.`a1` in <temporary table> on distinct_key where ((`test`.`t1i`.`a1` = `<subquery2>`.`b1`)))))) select * from t1i where a1 in (select b1 from t2i where b1 > '0' group by b1); a1 a2 1 - 01 2 - 01 @@ -104,10 +107,10 @@ a1 a2 explain extended select * from t1i where (a1, a2) in (select b1, b2 from t2i where b1 > '0'); id select_type table type possible_keys key key_len ref rows filtered Extra -1 PRIMARY t1i index NULL it1i3 18 NULL 3 100.00 Using where; Using index -2 SUBQUERY t2i index it2i1,it2i3 it2i3 18 NULL 5 100.00 Using where; Using index +1 PRIMARY t1i index NULL _it1_idx # NULL 3 100.00 Using where; +2 SUBQUERY t2i index it2i1,it2i3 it2i3 # NULL 5 100.00 Using where; Warnings: -Note 1003 select `test`.`t1i`.`a1` AS `a1`,`test`.`t1i`.`a2` AS `a2` from `test`.`t1i` where <expr_cache><`test`.`t1i`.`a2`,`test`.`t1i`.`a1`>(<in_optimizer>((`test`.`t1i`.`a1`,`test`.`t1i`.`a2`),(`test`.`t1i`.`a1`,`test`.`t1i`.`a2`) in ( <materialize> (select `test`.`t2i`.`b1`,`test`.`t2i`.`b2` from `test`.`t2i` where (`test`.`t2i`.`b1` > '0') ), <primary_index_lookup>(`test`.`t1i`.`a1` in <temporary table> on distinct_key where ((`test`.`t1i`.`a1` = `materialized subselect`.`b1`) and (`test`.`t1i`.`a2` = `materialized subselect`.`b2`)))))) +Note 1003 select `test`.`t1i`.`a1` AS `a1`,`test`.`t1i`.`a2` AS `a2` from `test`.`t1i` where <expr_cache><`test`.`t1i`.`a2`,`test`.`t1i`.`a1`>(<in_optimizer>((`test`.`t1i`.`a1`,`test`.`t1i`.`a2`),(`test`.`t1i`.`a1`,`test`.`t1i`.`a2`) in ( <materialize> (select `test`.`t2i`.`b1`,`test`.`t2i`.`b2` from `test`.`t2i` where (`test`.`t2i`.`b1` > '0') ), <primary_index_lookup>(`test`.`t1i`.`a1` in <temporary table> on distinct_key where ((`test`.`t1i`.`a1` = `<subquery2>`.`b1`) and (`test`.`t1i`.`a2` = `<subquery2>`.`b2`)))))) select * from t1i where (a1, a2) in (select b1, b2 from t2i where b1 > '0'); a1 a2 1 - 01 2 - 01 @@ -115,10 +118,10 @@ a1 a2 explain extended select * from t1i where (a1, a2) in (select b1, b2 from t2i where b1 > '0' group by b1, b2); id select_type table type possible_keys key key_len ref rows filtered Extra -1 PRIMARY t1i index NULL it1i3 18 NULL 3 100.00 Using where; Using index -2 SUBQUERY t2i range it2i1,it2i3 it2i3 18 NULL 3 100.00 Using where; Using index for group-by +1 PRIMARY t1i index NULL # # # 3 100.00 # +2 SUBQUERY t2i range it2i1,it2i3 # # # 3 100.00 # Warnings: -Note 1003 select `test`.`t1i`.`a1` AS `a1`,`test`.`t1i`.`a2` AS `a2` from `test`.`t1i` where <expr_cache><`test`.`t1i`.`a2`,`test`.`t1i`.`a1`>(<in_optimizer>((`test`.`t1i`.`a1`,`test`.`t1i`.`a2`),(`test`.`t1i`.`a1`,`test`.`t1i`.`a2`) in ( <materialize> (select `test`.`t2i`.`b1`,`test`.`t2i`.`b2` from `test`.`t2i` where (`test`.`t2i`.`b1` > '0') group by `test`.`t2i`.`b1`,`test`.`t2i`.`b2` ), <primary_index_lookup>(`test`.`t1i`.`a1` in <temporary table> on distinct_key where ((`test`.`t1i`.`a1` = `materialized subselect`.`b1`) and (`test`.`t1i`.`a2` = `materialized subselect`.`b2`)))))) +Note 1003 select `test`.`t1i`.`a1` AS `a1`,`test`.`t1i`.`a2` AS `a2` from `test`.`t1i` where <expr_cache><`test`.`t1i`.`a2`,`test`.`t1i`.`a1`>(<in_optimizer>((`test`.`t1i`.`a1`,`test`.`t1i`.`a2`),(`test`.`t1i`.`a1`,`test`.`t1i`.`a2`) in ( <materialize> (select `test`.`t2i`.`b1`,`test`.`t2i`.`b2` from `test`.`t2i` where (`test`.`t2i`.`b1` > '0') group by `test`.`t2i`.`b1`,`test`.`t2i`.`b2` ), <primary_index_lookup>(`test`.`t1i`.`a1` in <temporary table> on distinct_key where ((`test`.`t1i`.`a1` = `<subquery2>`.`b1`) and (`test`.`t1i`.`a2` = `<subquery2>`.`b2`)))))) select * from t1i where (a1, a2) in (select b1, b2 from t2i where b1 > '0' group by b1, b2); a1 a2 1 - 01 2 - 01 @@ -126,10 +129,10 @@ a1 a2 explain extended select * from t1i where (a1, a2) in (select b1, min(b2) from t2i where b1 > '0' group by b1); id select_type table type possible_keys key key_len ref rows filtered Extra -1 PRIMARY t1i index NULL it1i3 18 NULL 3 100.00 Using where; Using index -2 SUBQUERY t2i range it2i1,it2i3 it2i3 18 NULL 3 100.00 Using where; Using index for group-by +1 PRIMARY t1i index NULL # # # 3 100.00 # +2 SUBQUERY t2i range it2i1,it2i3 # # # 3 100.00 # Warnings: -Note 1003 select `test`.`t1i`.`a1` AS `a1`,`test`.`t1i`.`a2` AS `a2` from `test`.`t1i` where <expr_cache><`test`.`t1i`.`a2`,`test`.`t1i`.`a1`>(<in_optimizer>((`test`.`t1i`.`a1`,`test`.`t1i`.`a2`),(`test`.`t1i`.`a1`,`test`.`t1i`.`a2`) in ( <materialize> (select `test`.`t2i`.`b1`,min(`test`.`t2i`.`b2`) from `test`.`t2i` where (`test`.`t2i`.`b1` > '0') group by `test`.`t2i`.`b1` ), <primary_index_lookup>(`test`.`t1i`.`a1` in <temporary table> on distinct_key where ((`test`.`t1i`.`a1` = `materialized subselect`.`b1`) and (`test`.`t1i`.`a2` = `materialized subselect`.`min(b2)`)))))) +Note 1003 select `test`.`t1i`.`a1` AS `a1`,`test`.`t1i`.`a2` AS `a2` from `test`.`t1i` where <expr_cache><`test`.`t1i`.`a2`,`test`.`t1i`.`a1`>(<in_optimizer>((`test`.`t1i`.`a1`,`test`.`t1i`.`a2`),(`test`.`t1i`.`a1`,`test`.`t1i`.`a2`) in ( <materialize> (select `test`.`t2i`.`b1`,min(`test`.`t2i`.`b2`) from `test`.`t2i` where (`test`.`t2i`.`b1` > '0') group by `test`.`t2i`.`b1` ), <primary_index_lookup>(`test`.`t1i`.`a1` in <temporary table> on distinct_key where ((`test`.`t1i`.`a1` = `<subquery2>`.`b1`) and (`test`.`t1i`.`a2` = `<subquery2>`.`min(b2)`)))))) select * from t1i where (a1, a2) in (select b1, min(b2) from t2i where b1 > '0' group by b1); a1 a2 1 - 01 2 - 01 @@ -140,7 +143,7 @@ id select_type table type possible_keys key key_len ref rows filtered Extra 1 PRIMARY t1 ALL NULL NULL NULL NULL 3 100.00 Using where 2 SUBQUERY t2i range NULL it2i3 9 NULL 3 100.00 Using index for group-by Warnings: -Note 1003 select `test`.`t1`.`a1` AS `a1`,`test`.`t1`.`a2` AS `a2` from `test`.`t1` where <expr_cache><`test`.`t1`.`a2`,`test`.`t1`.`a1`>(<in_optimizer>((`test`.`t1`.`a1`,`test`.`t1`.`a2`),(`test`.`t1`.`a1`,`test`.`t1`.`a2`) in ( <materialize> (select `test`.`t2i`.`b1`,max(`test`.`t2i`.`b2`) from `test`.`t2i` group by `test`.`t2i`.`b1` ), <primary_index_lookup>(`test`.`t1`.`a1` in <temporary table> on distinct_key where ((`test`.`t1`.`a1` = `materialized subselect`.`b1`) and (`test`.`t1`.`a2` = `materialized subselect`.`max(b2)`)))))) +Note 1003 select `test`.`t1`.`a1` AS `a1`,`test`.`t1`.`a2` AS `a2` from `test`.`t1` where <expr_cache><`test`.`t1`.`a2`,`test`.`t1`.`a1`>(<in_optimizer>((`test`.`t1`.`a1`,`test`.`t1`.`a2`),(`test`.`t1`.`a1`,`test`.`t1`.`a2`) in ( <materialize> (select `test`.`t2i`.`b1`,max(`test`.`t2i`.`b2`) from `test`.`t2i` group by `test`.`t2i`.`b1` ), <primary_index_lookup>(`test`.`t1`.`a1` in <temporary table> on distinct_key where ((`test`.`t1`.`a1` = `<subquery2>`.`b1`) and (`test`.`t1`.`a2` = `<subquery2>`.`max(b2)`)))))) select * from t1 where (a1, a2) in (select b1, max(b2) from t2i group by b1); a1 a2 1 - 01 2 - 01 @@ -169,47 +172,48 @@ id select_type table type possible_keys key key_len ref rows filtered Extra 1 PRIMARY t1 ALL NULL NULL NULL NULL 3 100.00 Using where 2 SUBQUERY t2i range it2i1,it2i3 it2i3 18 NULL 3 100.00 Using where; Using index for group-by Warnings: -Note 1003 select `test`.`t1`.`a1` AS `a1`,`test`.`t1`.`a2` AS `a2` from `test`.`t1` where <expr_cache><`test`.`t1`.`a2`,`test`.`t1`.`a1`>(<in_optimizer>((`test`.`t1`.`a1`,`test`.`t1`.`a2`),(`test`.`t1`.`a1`,`test`.`t1`.`a2`) in ( <materialize> (select `test`.`t2i`.`b1`,min(`test`.`t2i`.`b2`) from `test`.`t2i` where (`test`.`t2i`.`b1` > '0') group by `test`.`t2i`.`b1` ), <primary_index_lookup>(`test`.`t1`.`a1` in <temporary table> on distinct_key where ((`test`.`t1`.`a1` = `materialized subselect`.`b1`) and (`test`.`t1`.`a2` = `materialized subselect`.`min(b2)`)))))) +Note 1003 select `test`.`t1`.`a1` AS `a1`,`test`.`t1`.`a2` AS `a2` from `test`.`t1` where <expr_cache><`test`.`t1`.`a2`,`test`.`t1`.`a1`>(<in_optimizer>((`test`.`t1`.`a1`,`test`.`t1`.`a2`),(`test`.`t1`.`a1`,`test`.`t1`.`a2`) in ( <materialize> (select `test`.`t2i`.`b1`,min(`test`.`t2i`.`b2`) from `test`.`t2i` where (`test`.`t2i`.`b1` > '0') group by `test`.`t2i`.`b1` ), <primary_index_lookup>(`test`.`t1`.`a1` in <temporary table> on distinct_key where ((`test`.`t1`.`a1` = `<subquery2>`.`b1`) and (`test`.`t1`.`a2` = `<subquery2>`.`min(b2)`)))))) select * from t1 where (a1, a2) in (select b1, min(b2) from t2i where b1 > '0' group by b1); a1 a2 1 - 01 2 - 01 1 - 02 2 - 02 select * from t1 where (a1, a2) in (select b1, min(b2) from t2i limit 1,1); ERROR 42000: This version of MySQL doesn't yet support 'LIMIT & IN/ALL/ANY/SOME subquery' -set @@optimizer_switch='materialization=on,in_to_exists=off,semijoin=on'; +set @save_optimizer_switch=@@optimizer_switch; +set @@optimizer_switch='default,semijoin=off'; prepare st1 from "select * from t1 where (a1, a2) in (select b1, min(b2) from t2 where b1 > '0' group by b1)"; -set @@optimizer_switch='materialization=off,in_to_exists=on,semijoin=on'; +set @@optimizer_switch='default,materialization=off'; execute st1; a1 a2 1 - 01 2 - 01 1 - 02 2 - 02 -set @@optimizer_switch='materialization=on,in_to_exists=off,semijoin=on'; +set @@optimizer_switch='default,semijoin=off'; execute st1; a1 a2 1 - 01 2 - 01 1 - 02 2 - 02 -set @@optimizer_switch='materialization=off,in_to_exists=on,semijoin=on'; +set @@optimizer_switch='default,materialization=off'; prepare st1 from "select * from t1 where (a1, a2) in (select b1, min(b2) from t2 where b1 > '0' group by b1)"; -set @@optimizer_switch='materialization=on,in_to_exists=off,semijoin=off'; +set @@optimizer_switch='default,semijoin=off'; execute st1; a1 a2 1 - 01 2 - 01 1 - 02 2 - 02 -set @@optimizer_switch='materialization=off,in_to_exists=on,semijoin=on'; +set @@optimizer_switch='default,materialization=off'; execute st1; a1 a2 1 - 01 2 - 01 1 - 02 2 - 02 -set @@optimizer_switch='materialization=on,in_to_exists=off,semijoin=off'; +set @@optimizer_switch=@save_optimizer_switch; explain extended select * from t1 where (a1, a2) in (select b1, b2 from t2 order by b1, b2); id select_type table type possible_keys key key_len ref rows filtered Extra 1 PRIMARY t1 ALL NULL NULL NULL NULL 3 100.00 Using where 2 SUBQUERY t2 ALL NULL NULL NULL NULL 5 100.00 Warnings: -Note 1003 select `test`.`t1`.`a1` AS `a1`,`test`.`t1`.`a2` AS `a2` from `test`.`t1` where <expr_cache><`test`.`t1`.`a2`,`test`.`t1`.`a1`>(<in_optimizer>((`test`.`t1`.`a1`,`test`.`t1`.`a2`),(`test`.`t1`.`a1`,`test`.`t1`.`a2`) in ( <materialize> (select `test`.`t2`.`b1`,`test`.`t2`.`b2` from `test`.`t2` order by `test`.`t2`.`b1`,`test`.`t2`.`b2` ), <primary_index_lookup>(`test`.`t1`.`a1` in <temporary table> on distinct_key where ((`test`.`t1`.`a1` = `materialized subselect`.`b1`) and (`test`.`t1`.`a2` = `materialized subselect`.`b2`)))))) +Note 1003 select `test`.`t1`.`a1` AS `a1`,`test`.`t1`.`a2` AS `a2` from `test`.`t1` where <expr_cache><`test`.`t1`.`a2`,`test`.`t1`.`a1`>(<in_optimizer>((`test`.`t1`.`a1`,`test`.`t1`.`a2`),(`test`.`t1`.`a1`,`test`.`t1`.`a2`) in ( <materialize> (select `test`.`t2`.`b1`,`test`.`t2`.`b2` from `test`.`t2` order by `test`.`t2`.`b1`,`test`.`t2`.`b2` ), <primary_index_lookup>(`test`.`t1`.`a1` in <temporary table> on distinct_key where ((`test`.`t1`.`a1` = `<subquery2>`.`b1`) and (`test`.`t1`.`a2` = `<subquery2>`.`b2`)))))) select * from t1 where (a1, a2) in (select b1, b2 from t2 order by b1, b2); a1 a2 1 - 01 2 - 01 @@ -220,7 +224,7 @@ id select_type table type possible_keys key key_len ref rows filtered Extra 1 PRIMARY t1i index NULL it1i3 18 NULL 3 100.00 Using where; Using index 2 SUBQUERY t2i index NULL it2i3 18 NULL 5 100.00 Using index Warnings: -Note 1003 select `test`.`t1i`.`a1` AS `a1`,`test`.`t1i`.`a2` AS `a2` from `test`.`t1i` where <expr_cache><`test`.`t1i`.`a2`,`test`.`t1i`.`a1`>(<in_optimizer>((`test`.`t1i`.`a1`,`test`.`t1i`.`a2`),(`test`.`t1i`.`a1`,`test`.`t1i`.`a2`) in ( <materialize> (select `test`.`t2i`.`b1`,`test`.`t2i`.`b2` from `test`.`t2i` order by `test`.`t2i`.`b1`,`test`.`t2i`.`b2` ), <primary_index_lookup>(`test`.`t1i`.`a1` in <temporary table> on distinct_key where ((`test`.`t1i`.`a1` = `materialized subselect`.`b1`) and (`test`.`t1i`.`a2` = `materialized subselect`.`b2`)))))) +Note 1003 select `test`.`t1i`.`a1` AS `a1`,`test`.`t1i`.`a2` AS `a2` from `test`.`t1i` where <expr_cache><`test`.`t1i`.`a2`,`test`.`t1i`.`a1`>(<in_optimizer>((`test`.`t1i`.`a1`,`test`.`t1i`.`a2`),(`test`.`t1i`.`a1`,`test`.`t1i`.`a2`) in ( <materialize> (select `test`.`t2i`.`b1`,`test`.`t2i`.`b2` from `test`.`t2i` order by `test`.`t2i`.`b1`,`test`.`t2i`.`b2` ), <primary_index_lookup>(`test`.`t1i`.`a1` in <temporary table> on distinct_key where ((`test`.`t1i`.`a1` = `<subquery2>`.`b1`) and (`test`.`t1i`.`a2` = `<subquery2>`.`b2`)))))) select * from t1i where (a1, a2) in (select b1, b2 from t2i order by b1, b2); a1 a2 1 - 01 2 - 01 @@ -275,7 +279,7 @@ id select_type table type possible_keys key key_len ref rows filtered Extra 4 SUBQUERY t2i index it2i2 it2i3 18 NULL 5 100.00 Using where; Using index 2 SUBQUERY t2 ALL NULL NULL NULL NULL 5 100.00 Using where Warnings: -Note 1003 select `test`.`t1`.`a1` AS `a1`,`test`.`t1`.`a2` AS `a2` from `test`.`t1` where (<expr_cache><`test`.`t1`.`a2`,`test`.`t1`.`a1`>(<in_optimizer>((`test`.`t1`.`a1`,`test`.`t1`.`a2`),(`test`.`t1`.`a1`,`test`.`t1`.`a2`) in ( <materialize> (select `test`.`t2`.`b1`,`test`.`t2`.`b2` from `test`.`t2` where (`test`.`t2`.`b1` > '0') ), <primary_index_lookup>(`test`.`t1`.`a1` in <temporary table> on distinct_key where ((`test`.`t1`.`a1` = `materialized subselect`.`b1`) and (`test`.`t1`.`a2` = `materialized subselect`.`b2`)))))) and <expr_cache><`test`.`t1`.`a2`,`test`.`t1`.`a1`>(<in_optimizer>((`test`.`t1`.`a1`,`test`.`t1`.`a2`),(`test`.`t1`.`a1`,`test`.`t1`.`a2`) in ( <materialize> (select `test`.`t3`.`c1`,`test`.`t3`.`c2` from `test`.`t3` where <expr_cache><`test`.`t3`.`c2`,`test`.`t3`.`c1`>(<in_optimizer>((`test`.`t3`.`c1`,`test`.`t3`.`c2`),(`test`.`t3`.`c1`,`test`.`t3`.`c2`) in ( <materialize> (select `test`.`t2i`.`b1`,`test`.`t2i`.`b2` from `test`.`t2i` where (`test`.`t2i`.`b2` > '0') ), <primary_index_lookup>(`test`.`t3`.`c1` in <temporary table> on distinct_key where ((`test`.`t3`.`c1` = `materialized subselect`.`b1`) and (`test`.`t3`.`c2` = `materialized subselect`.`b2`)))))) ), <primary_index_lookup>(`test`.`t1`.`a1` in <temporary table> on distinct_key where ((`test`.`t1`.`a1` = `materialized subselect`.`c1`) and (`test`.`t1`.`a2` = `materialized subselect`.`c2`))))))) +Note 1003 select `test`.`t1`.`a1` AS `a1`,`test`.`t1`.`a2` AS `a2` from `test`.`t1` where (<expr_cache><`test`.`t1`.`a2`,`test`.`t1`.`a1`>(<in_optimizer>((`test`.`t1`.`a1`,`test`.`t1`.`a2`),(`test`.`t1`.`a1`,`test`.`t1`.`a2`) in ( <materialize> (select `test`.`t2`.`b1`,`test`.`t2`.`b2` from `test`.`t2` where (`test`.`t2`.`b1` > '0') ), <primary_index_lookup>(`test`.`t1`.`a1` in <temporary table> on distinct_key where ((`test`.`t1`.`a1` = `<subquery2>`.`b1`) and (`test`.`t1`.`a2` = `<subquery2>`.`b2`)))))) and <expr_cache><`test`.`t1`.`a2`,`test`.`t1`.`a1`>(<in_optimizer>((`test`.`t1`.`a1`,`test`.`t1`.`a2`),(`test`.`t1`.`a1`,`test`.`t1`.`a2`) in ( <materialize> (select `test`.`t3`.`c1`,`test`.`t3`.`c2` from `test`.`t3` where <expr_cache><`test`.`t3`.`c2`,`test`.`t3`.`c1`>(<in_optimizer>((`test`.`t3`.`c1`,`test`.`t3`.`c2`),(`test`.`t3`.`c1`,`test`.`t3`.`c2`) in ( <materialize> (select `test`.`t2i`.`b1`,`test`.`t2i`.`b2` from `test`.`t2i` where (`test`.`t2i`.`b2` > '0') ), <primary_index_lookup>(`test`.`t3`.`c1` in <temporary table> on distinct_key where ((`test`.`t3`.`c1` = `<subquery4>`.`b1`) and (`test`.`t3`.`c2` = `<subquery4>`.`b2`)))))) ), <primary_index_lookup>(`test`.`t1`.`a1` in <temporary table> on distinct_key where ((`test`.`t1`.`a1` = `<subquery3>`.`c1`) and (`test`.`t1`.`a2` = `<subquery3>`.`c2`))))))) select * from t1 where (a1, a2) in (select b1, b2 from t2 where b1 > '0') and (a1, a2) in (select c1, c2 from t3 @@ -289,12 +293,12 @@ where (a1, a2) in (select b1, b2 from t2i where b1 > '0') and (a1, a2) in (select c1, c2 from t3i where (c1, c2) in (select b1, b2 from t2i where b2 > '0')); id select_type table type possible_keys key key_len ref rows filtered Extra -1 PRIMARY t1i index NULL it1i3 18 NULL 3 100.00 Using where; Using index -3 SUBQUERY t3i index NULL it3i3 18 NULL 4 100.00 Using where; Using index -4 SUBQUERY t2i index it2i2 it2i3 18 NULL 5 100.00 Using where; Using index -2 SUBQUERY t2i index it2i1,it2i3 it2i3 18 NULL 5 100.00 Using where; Using index +1 PRIMARY t1i index NULL # # # 3 100.00 # +3 SUBQUERY t3i index NULL # # # 4 100.00 # +4 SUBQUERY t2i index it2i2 # # # 5 100.00 # +2 SUBQUERY t2i index it2i1,it2i3 # # # 5 100.00 # Warnings: -Note 1003 select `test`.`t1i`.`a1` AS `a1`,`test`.`t1i`.`a2` AS `a2` from `test`.`t1i` where (<expr_cache><`test`.`t1i`.`a2`,`test`.`t1i`.`a1`>(<in_optimizer>((`test`.`t1i`.`a1`,`test`.`t1i`.`a2`),(`test`.`t1i`.`a1`,`test`.`t1i`.`a2`) in ( <materialize> (select `test`.`t2i`.`b1`,`test`.`t2i`.`b2` from `test`.`t2i` where (`test`.`t2i`.`b1` > '0') ), <primary_index_lookup>(`test`.`t1i`.`a1` in <temporary table> on distinct_key where ((`test`.`t1i`.`a1` = `materialized subselect`.`b1`) and (`test`.`t1i`.`a2` = `materialized subselect`.`b2`)))))) and <expr_cache><`test`.`t1i`.`a2`,`test`.`t1i`.`a1`>(<in_optimizer>((`test`.`t1i`.`a1`,`test`.`t1i`.`a2`),(`test`.`t1i`.`a1`,`test`.`t1i`.`a2`) in ( <materialize> (select `test`.`t3i`.`c1`,`test`.`t3i`.`c2` from `test`.`t3i` where <expr_cache><`test`.`t3i`.`c2`,`test`.`t3i`.`c1`>(<in_optimizer>((`test`.`t3i`.`c1`,`test`.`t3i`.`c2`),(`test`.`t3i`.`c1`,`test`.`t3i`.`c2`) in ( <materialize> (select `test`.`t2i`.`b1`,`test`.`t2i`.`b2` from `test`.`t2i` where (`test`.`t2i`.`b2` > '0') ), <primary_index_lookup>(`test`.`t3i`.`c1` in <temporary table> on distinct_key where ((`test`.`t3i`.`c1` = `materialized subselect`.`b1`) and (`test`.`t3i`.`c2` = `materialized subselect`.`b2`)))))) ), <primary_index_lookup>(`test`.`t1i`.`a1` in <temporary table> on distinct_key where ((`test`.`t1i`.`a1` = `materialized subselect`.`c1`) and (`test`.`t1i`.`a2` = `materialized subselect`.`c2`))))))) +Note 1003 select `test`.`t1i`.`a1` AS `a1`,`test`.`t1i`.`a2` AS `a2` from `test`.`t1i` where (<expr_cache><`test`.`t1i`.`a2`,`test`.`t1i`.`a1`>(<in_optimizer>((`test`.`t1i`.`a1`,`test`.`t1i`.`a2`),(`test`.`t1i`.`a1`,`test`.`t1i`.`a2`) in ( <materialize> (select `test`.`t2i`.`b1`,`test`.`t2i`.`b2` from `test`.`t2i` where (`test`.`t2i`.`b1` > '0') ), <primary_index_lookup>(`test`.`t1i`.`a1` in <temporary table> on distinct_key where ((`test`.`t1i`.`a1` = `<subquery2>`.`b1`) and (`test`.`t1i`.`a2` = `<subquery2>`.`b2`)))))) and <expr_cache><`test`.`t1i`.`a2`,`test`.`t1i`.`a1`>(<in_optimizer>((`test`.`t1i`.`a1`,`test`.`t1i`.`a2`),(`test`.`t1i`.`a1`,`test`.`t1i`.`a2`) in ( <materialize> (select `test`.`t3i`.`c1`,`test`.`t3i`.`c2` from `test`.`t3i` where <expr_cache><`test`.`t3i`.`c2`,`test`.`t3i`.`c1`>(<in_optimizer>((`test`.`t3i`.`c1`,`test`.`t3i`.`c2`),(`test`.`t3i`.`c1`,`test`.`t3i`.`c2`) in ( <materialize> (select `test`.`t2i`.`b1`,`test`.`t2i`.`b2` from `test`.`t2i` where (`test`.`t2i`.`b2` > '0') ), <primary_index_lookup>(`test`.`t3i`.`c1` in <temporary table> on distinct_key where ((`test`.`t3i`.`c1` = `<subquery4>`.`b1`) and (`test`.`t3i`.`c2` = `<subquery4>`.`b2`)))))) ), <primary_index_lookup>(`test`.`t1i`.`a1` in <temporary table> on distinct_key where ((`test`.`t1i`.`a1` = `<subquery3>`.`c1`) and (`test`.`t1i`.`a2` = `<subquery3>`.`c2`))))))) select * from t1i where (a1, a2) in (select b1, b2 from t2i where b1 > '0') and (a1, a2) in (select c1, c2 from t3i @@ -317,7 +321,7 @@ id select_type table type possible_keys key key_len ref rows filtered Extra 4 SUBQUERY t3 ALL NULL NULL NULL NULL 4 100.00 Using where 3 SUBQUERY t3 ALL NULL NULL NULL NULL 4 100.00 Using where Warnings: -Note 1003 select `test`.`t1`.`a1` AS `a1`,`test`.`t1`.`a2` AS `a2` from `test`.`t1` where (<expr_cache><`test`.`t1`.`a2`,`test`.`t1`.`a1`>(<in_optimizer>((`test`.`t1`.`a1`,`test`.`t1`.`a2`),(`test`.`t1`.`a1`,`test`.`t1`.`a2`) in ( <materialize> (select `test`.`t2`.`b1`,`test`.`t2`.`b2` from `test`.`t2` where (<expr_cache><`test`.`t2`.`b2`>(<in_optimizer>(`test`.`t2`.`b2`,`test`.`t2`.`b2` in ( <materialize> (select `test`.`t3`.`c2` from `test`.`t3` where (`test`.`t3`.`c2` like '%02') ), <primary_index_lookup>(`test`.`t2`.`b2` in <temporary table> on distinct_key where ((`test`.`t2`.`b2` = `materialized subselect`.`c2`)))))) or <expr_cache><`test`.`t2`.`b2`>(<in_optimizer>(`test`.`t2`.`b2`,`test`.`t2`.`b2` in ( <materialize> (select `test`.`t3`.`c2` from `test`.`t3` where (`test`.`t3`.`c2` like '%03') ), <primary_index_lookup>(`test`.`t2`.`b2` in <temporary table> on distinct_key where ((`test`.`t2`.`b2` = `materialized subselect`.`c2`))))))) ), <primary_index_lookup>(`test`.`t1`.`a1` in <temporary table> on distinct_key where ((`test`.`t1`.`a1` = `materialized subselect`.`b1`) and (`test`.`t1`.`a2` = `materialized subselect`.`b2`)))))) and <expr_cache><`test`.`t1`.`a2`,`test`.`t1`.`a1`>(<in_optimizer>((`test`.`t1`.`a1`,`test`.`t1`.`a2`),(`test`.`t1`.`a1`,`test`.`t1`.`a2`) in ( <materialize> (select `test`.`t3`.`c1`,`test`.`t3`.`c2` from `test`.`t3` where <expr_cache><`test`.`t3`.`c2`,`test`.`t3`.`c1`>(<in_optimizer>((`test`.`t3`.`c1`,`test`.`t3`.`c2`),(`test`.`t3`.`c1`,`test`.`t3`.`c2`) in ( <materialize> (select `test`.`t2i`.`b1`,`test`.`t2i`.`b2` from `test`.`t2i` where (`test`.`t2i`.`b2` > '0') ), <primary_index_lookup>(`test`.`t3`.`c1` in <temporary table> on distinct_key where ((`test`.`t3`.`c1` = `materialized subselect`.`b1`) and (`test`.`t3`.`c2` = `materialized subselect`.`b2`)))))) ), <primary_index_lookup>(`test`.`t1`.`a1` in <temporary table> on distinct_key where ((`test`.`t1`.`a1` = `materialized subselect`.`c1`) and (`test`.`t1`.`a2` = `materialized subselect`.`c2`))))))) +Note 1003 select `test`.`t1`.`a1` AS `a1`,`test`.`t1`.`a2` AS `a2` from `test`.`t1` where (<expr_cache><`test`.`t1`.`a2`,`test`.`t1`.`a1`>(<in_optimizer>((`test`.`t1`.`a1`,`test`.`t1`.`a2`),(`test`.`t1`.`a1`,`test`.`t1`.`a2`) in ( <materialize> (select `test`.`t2`.`b1`,`test`.`t2`.`b2` from `test`.`t2` where (<expr_cache><`test`.`t2`.`b2`>(<in_optimizer>(`test`.`t2`.`b2`,`test`.`t2`.`b2` in ( <materialize> (select `test`.`t3`.`c2` from `test`.`t3` where (`test`.`t3`.`c2` like '%02') ), <primary_index_lookup>(`test`.`t2`.`b2` in <temporary table> on distinct_key where ((`test`.`t2`.`b2` = `<subquery3>`.`c2`)))))) or <expr_cache><`test`.`t2`.`b2`>(<in_optimizer>(`test`.`t2`.`b2`,`test`.`t2`.`b2` in ( <materialize> (select `test`.`t3`.`c2` from `test`.`t3` where (`test`.`t3`.`c2` like '%03') ), <primary_index_lookup>(`test`.`t2`.`b2` in <temporary table> on distinct_key where ((`test`.`t2`.`b2` = `<subquery4>`.`c2`))))))) ), <primary_index_lookup>(`test`.`t1`.`a1` in <temporary table> on distinct_key where ((`test`.`t1`.`a1` = `<subquery2>`.`b1`) and (`test`.`t1`.`a2` = `<subquery2>`.`b2`)))))) and <expr_cache><`test`.`t1`.`a2`,`test`.`t1`.`a1`>(<in_optimizer>((`test`.`t1`.`a1`,`test`.`t1`.`a2`),(`test`.`t1`.`a1`,`test`.`t1`.`a2`) in ( <materialize> (select `test`.`t3`.`c1`,`test`.`t3`.`c2` from `test`.`t3` where <expr_cache><`test`.`t3`.`c2`,`test`.`t3`.`c1`>(<in_optimizer>((`test`.`t3`.`c1`,`test`.`t3`.`c2`),(`test`.`t3`.`c1`,`test`.`t3`.`c2`) in ( <materialize> (select `test`.`t2i`.`b1`,`test`.`t2i`.`b2` from `test`.`t2i` where (`test`.`t2i`.`b2` > '0') ), <primary_index_lookup>(`test`.`t3`.`c1` in <temporary table> on distinct_key where ((`test`.`t3`.`c1` = `<subquery6>`.`b1`) and (`test`.`t3`.`c2` = `<subquery6>`.`b2`)))))) ), <primary_index_lookup>(`test`.`t1`.`a1` in <temporary table> on distinct_key where ((`test`.`t1`.`a1` = `<subquery5>`.`c1`) and (`test`.`t1`.`a2` = `<subquery5>`.`c2`))))))) select * from t1 where (a1, a2) in (select b1, b2 from t2 where b2 in (select c2 from t3 where c2 LIKE '%02') or @@ -342,7 +346,7 @@ id select_type table type possible_keys key key_len ref rows filtered Extra 3 DEPENDENT SUBQUERY t3a ALL NULL NULL NULL NULL 4 100.00 Using where Warnings: Note 1276 Field or reference 'test.t1.a1' of SELECT #3 was resolved in SELECT #1 -Note 1003 select `test`.`t1`.`a1` AS `a1`,`test`.`t1`.`a2` AS `a2` from `test`.`t1` where (<expr_cache><`test`.`t1`.`a2`,`test`.`t1`.`a1`,`test`.`t1`.`a1`>(<in_optimizer>((`test`.`t1`.`a1`,`test`.`t1`.`a2`),<exists>(select `test`.`t2`.`b1`,`test`.`t2`.`b2` from `test`.`t2` where ((<expr_cache><`test`.`t2`.`b2`,`test`.`t1`.`a1`>(<in_optimizer>(`test`.`t2`.`b2`,<exists>(select `test`.`t3a`.`c2` from `test`.`t3` `t3a` where ((`test`.`t3a`.`c1` = `test`.`t1`.`a1`) and (<cache>(`test`.`t2`.`b2`) = `test`.`t3a`.`c2`))))) or <expr_cache><`test`.`t2`.`b2`>(<in_optimizer>(`test`.`t2`.`b2`,`test`.`t2`.`b2` in ( <materialize> (select `test`.`t3b`.`c2` from `test`.`t3` `t3b` where (`test`.`t3b`.`c2` like '%03') ), <primary_index_lookup>(`test`.`t2`.`b2` in <temporary table> on distinct_key where ((`test`.`t2`.`b2` = `materialized subselect`.`c2`))))))) and (<cache>(`test`.`t1`.`a1`) = `test`.`t2`.`b1`) and (<cache>(`test`.`t1`.`a2`) = `test`.`t2`.`b2`))))) and <expr_cache><`test`.`t1`.`a2`,`test`.`t1`.`a1`>(<in_optimizer>((`test`.`t1`.`a1`,`test`.`t1`.`a2`),(`test`.`t1`.`a1`,`test`.`t1`.`a2`) in ( <materialize> (select `test`.`t3c`.`c1`,`test`.`t3c`.`c2` from `test`.`t3` `t3c` where <expr_cache><`test`.`t3c`.`c2`,`test`.`t3c`.`c1`>(<in_optimizer>((`test`.`t3c`.`c1`,`test`.`t3c`.`c2`),(`test`.`t3c`.`c1`,`test`.`t3c`.`c2`) in ( <materialize> (select `test`.`t2i`.`b1`,`test`.`t2i`.`b2` from `test`.`t2i` where (`test`.`t2i`.`b2` > '0') ), <primary_index_lookup>(`test`.`t3c`.`c1` in <temporary table> on distinct_key where ((`test`.`t3c`.`c1` = `materialized subselect`.`b1`) and (`test`.`t3c`.`c2` = `materialized subselect`.`b2`)))))) ), <primary_index_lookup>(`test`.`t1`.`a1` in <temporary table> on distinct_key where ((`test`.`t1`.`a1` = `materialized subselect`.`c1`) and (`test`.`t1`.`a2` = `materialized subselect`.`c2`))))))) +Note 1003 select `test`.`t1`.`a1` AS `a1`,`test`.`t1`.`a2` AS `a2` from `test`.`t1` where (<expr_cache><`test`.`t1`.`a2`,`test`.`t1`.`a1`,`test`.`t1`.`a1`>(<in_optimizer>((`test`.`t1`.`a1`,`test`.`t1`.`a2`),<exists>(select `test`.`t2`.`b1`,`test`.`t2`.`b2` from `test`.`t2` where ((<expr_cache><`test`.`t2`.`b2`,`test`.`t1`.`a1`>(<in_optimizer>(`test`.`t2`.`b2`,<exists>(select `test`.`t3a`.`c2` from `test`.`t3` `t3a` where ((`test`.`t3a`.`c1` = `test`.`t1`.`a1`) and (<cache>(`test`.`t2`.`b2`) = `test`.`t3a`.`c2`))))) or <expr_cache><`test`.`t2`.`b2`>(<in_optimizer>(`test`.`t2`.`b2`,`test`.`t2`.`b2` in ( <materialize> (select `test`.`t3b`.`c2` from `test`.`t3` `t3b` where (`test`.`t3b`.`c2` like '%03') ), <primary_index_lookup>(`test`.`t2`.`b2` in <temporary table> on distinct_key where ((`test`.`t2`.`b2` = `<subquery4>`.`c2`))))))) and (<cache>(`test`.`t1`.`a1`) = `test`.`t2`.`b1`) and (<cache>(`test`.`t1`.`a2`) = `test`.`t2`.`b2`))))) and <expr_cache><`test`.`t1`.`a2`,`test`.`t1`.`a1`>(<in_optimizer>((`test`.`t1`.`a1`,`test`.`t1`.`a2`),(`test`.`t1`.`a1`,`test`.`t1`.`a2`) in ( <materialize> (select `test`.`t3c`.`c1`,`test`.`t3c`.`c2` from `test`.`t3` `t3c` where <expr_cache><`test`.`t3c`.`c2`,`test`.`t3c`.`c1`>(<in_optimizer>((`test`.`t3c`.`c1`,`test`.`t3c`.`c2`),(`test`.`t3c`.`c1`,`test`.`t3c`.`c2`) in ( <materialize> (select `test`.`t2i`.`b1`,`test`.`t2i`.`b2` from `test`.`t2i` where (`test`.`t2i`.`b2` > '0') ), <primary_index_lookup>(`test`.`t3c`.`c1` in <temporary table> on distinct_key where ((`test`.`t3c`.`c1` = `<subquery6>`.`b1`) and (`test`.`t3c`.`c2` = `<subquery6>`.`b2`)))))) ), <primary_index_lookup>(`test`.`t1`.`a1` in <temporary table> on distinct_key where ((`test`.`t1`.`a1` = `<subquery5>`.`c1`) and (`test`.`t1`.`a2` = `<subquery5>`.`c2`))))))) select * from t1 where (a1, a2) in (select b1, b2 from t2 where b2 in (select c2 from t3 t3a where c1 = a1) or @@ -366,19 +370,19 @@ where (a1, a2) in (select b1, b2 from t2i where b1 > '0') and (a1, a2) in (select c1, c2 from t3i where (c1, c2) in (select b1, b2 from t2i where b2 > '0'))); id select_type table type possible_keys key key_len ref rows filtered Extra -1 PRIMARY t1 ALL NULL NULL NULL NULL 3 100.00 Using where -5 SUBQUERY t3 ALL NULL NULL NULL NULL 4 100.00 Using where -6 SUBQUERY t2i index it2i2 it2i3 18 NULL 5 100.00 Using where; Using index -2 SUBQUERY t2 ALL NULL NULL NULL NULL 5 100.00 Using where; Using temporary -4 SUBQUERY t3 ALL NULL NULL NULL NULL 4 100.00 Using where -3 SUBQUERY t3 ALL NULL NULL NULL NULL 4 100.00 Using where -7 UNION t1i index NULL it1i3 18 NULL 3 100.00 Using where; Using index -9 SUBQUERY t3i index NULL it3i3 18 NULL 4 100.00 Using where; Using index -10 SUBQUERY t2i index it2i2 it2i3 18 NULL 5 100.00 Using where; Using index -8 SUBQUERY t2i index it2i1,it2i3 it2i3 18 NULL 5 100.00 Using where; Using index -NULL UNION RESULT <union1,7> ALL NULL NULL NULL NULL NULL NULL +1 PRIMARY t1 ALL NULL # # # 3 100.00 # +5 SUBQUERY t3 ALL NULL # # # 4 100.00 # +6 SUBQUERY t2i index it2i2 # # # 5 100.00 # +2 SUBQUERY t2 ALL NULL # # # 5 100.00 # +4 SUBQUERY t3 ALL NULL # # # 4 100.00 # +3 SUBQUERY t3 ALL NULL # # # 4 100.00 # +7 UNION t1i index NULL # # # 3 100.00 # +9 SUBQUERY t3i index NULL # # # 4 100.00 # +10 SUBQUERY t2i index it2i2 # # # 5 100.00 # +8 SUBQUERY t2i index it2i1,it2i3 # # # 5 100.00 # +NULL UNION RESULT <union1,7> ALL NULL # # # NULL NULL # Warnings: -Note 1003 (select `test`.`t1`.`a1` AS `a1`,`test`.`t1`.`a2` AS `a2` from `test`.`t1` where (<expr_cache><`test`.`t1`.`a2`,`test`.`t1`.`a1`>(<in_optimizer>((`test`.`t1`.`a1`,`test`.`t1`.`a2`),(`test`.`t1`.`a1`,`test`.`t1`.`a2`) in ( <materialize> (select `test`.`t2`.`b1`,`test`.`t2`.`b2` from `test`.`t2` where (<expr_cache><`test`.`t2`.`b2`>(<in_optimizer>(`test`.`t2`.`b2`,`test`.`t2`.`b2` in ( <materialize> (select `test`.`t3`.`c2` from `test`.`t3` where (`test`.`t3`.`c2` like '%02') ), <primary_index_lookup>(`test`.`t2`.`b2` in <temporary table> on distinct_key where ((`test`.`t2`.`b2` = `materialized subselect`.`c2`)))))) or <expr_cache><`test`.`t2`.`b2`>(<in_optimizer>(`test`.`t2`.`b2`,`test`.`t2`.`b2` in ( <materialize> (select `test`.`t3`.`c2` from `test`.`t3` where (`test`.`t3`.`c2` like '%03') ), <primary_index_lookup>(`test`.`t2`.`b2` in <temporary table> on distinct_key where ((`test`.`t2`.`b2` = `materialized subselect`.`c2`))))))) group by `test`.`t2`.`b1`,`test`.`t2`.`b2` ), <primary_index_lookup>(`test`.`t1`.`a1` in <temporary table> on distinct_key where ((`test`.`t1`.`a1` = `materialized subselect`.`b1`) and (`test`.`t1`.`a2` = `materialized subselect`.`b2`)))))) and <expr_cache><`test`.`t1`.`a2`,`test`.`t1`.`a1`>(<in_optimizer>((`test`.`t1`.`a1`,`test`.`t1`.`a2`),(`test`.`t1`.`a1`,`test`.`t1`.`a2`) in ( <materialize> (select `test`.`t3`.`c1`,`test`.`t3`.`c2` from `test`.`t3` where <expr_cache><`test`.`t3`.`c2`,`test`.`t3`.`c1`>(<in_optimizer>((`test`.`t3`.`c1`,`test`.`t3`.`c2`),(`test`.`t3`.`c1`,`test`.`t3`.`c2`) in ( <materialize> (select `test`.`t2i`.`b1`,`test`.`t2i`.`b2` from `test`.`t2i` where (`test`.`t2i`.`b2` > '0') ), <primary_index_lookup>(`test`.`t3`.`c1` in <temporary table> on distinct_key where ((`test`.`t3`.`c1` = `materialized subselect`.`b1`) and (`test`.`t3`.`c2` = `materialized subselect`.`b2`)))))) ), <primary_index_lookup>(`test`.`t1`.`a1` in <temporary table> on distinct_key where ((`test`.`t1`.`a1` = `materialized subselect`.`c1`) and (`test`.`t1`.`a2` = `materialized subselect`.`c2`)))))))) union (select `test`.`t1i`.`a1` AS `a1`,`test`.`t1i`.`a2` AS `a2` from `test`.`t1i` where (<expr_cache><`test`.`t1i`.`a2`,`test`.`t1i`.`a1`>(<in_optimizer>((`test`.`t1i`.`a1`,`test`.`t1i`.`a2`),(`test`.`t1i`.`a1`,`test`.`t1i`.`a2`) in ( <materialize> (select `test`.`t2i`.`b1`,`test`.`t2i`.`b2` from `test`.`t2i` where (`test`.`t2i`.`b1` > '0') ), <primary_index_lookup>(`test`.`t1i`.`a1` in <temporary table> on distinct_key where ((`test`.`t1i`.`a1` = `materialized subselect`.`b1`) and (`test`.`t1i`.`a2` = `materialized subselect`.`b2`)))))) and <expr_cache><`test`.`t1i`.`a2`,`test`.`t1i`.`a1`>(<in_optimizer>((`test`.`t1i`.`a1`,`test`.`t1i`.`a2`),(`test`.`t1i`.`a1`,`test`.`t1i`.`a2`) in ( <materialize> (select `test`.`t3i`.`c1`,`test`.`t3i`.`c2` from `test`.`t3i` where <expr_cache><`test`.`t3i`.`c2`,`test`.`t3i`.`c1`>(<in_optimizer>((`test`.`t3i`.`c1`,`test`.`t3i`.`c2`),(`test`.`t3i`.`c1`,`test`.`t3i`.`c2`) in ( <materialize> (select `test`.`t2i`.`b1`,`test`.`t2i`.`b2` from `test`.`t2i` where (`test`.`t2i`.`b2` > '0') ), <primary_index_lookup>(`test`.`t3i`.`c1` in <temporary table> on distinct_key where ((`test`.`t3i`.`c1` = `materialized subselect`.`b1`) and (`test`.`t3i`.`c2` = `materialized subselect`.`b2`)))))) ), <primary_index_lookup>(`test`.`t1i`.`a1` in <temporary table> on distinct_key where ((`test`.`t1i`.`a1` = `materialized subselect`.`c1`) and (`test`.`t1i`.`a2` = `materialized subselect`.`c2`)))))))) +Note 1003 (select `test`.`t1`.`a1` AS `a1`,`test`.`t1`.`a2` AS `a2` from `test`.`t1` where (<expr_cache><`test`.`t1`.`a2`,`test`.`t1`.`a1`>(<in_optimizer>((`test`.`t1`.`a1`,`test`.`t1`.`a2`),(`test`.`t1`.`a1`,`test`.`t1`.`a2`) in ( <materialize> (select `test`.`t2`.`b1`,`test`.`t2`.`b2` from `test`.`t2` where (<expr_cache><`test`.`t2`.`b2`>(<in_optimizer>(`test`.`t2`.`b2`,`test`.`t2`.`b2` in ( <materialize> (select `test`.`t3`.`c2` from `test`.`t3` where (`test`.`t3`.`c2` like '%02') ), <primary_index_lookup>(`test`.`t2`.`b2` in <temporary table> on distinct_key where ((`test`.`t2`.`b2` = `<subquery3>`.`c2`)))))) or <expr_cache><`test`.`t2`.`b2`>(<in_optimizer>(`test`.`t2`.`b2`,`test`.`t2`.`b2` in ( <materialize> (select `test`.`t3`.`c2` from `test`.`t3` where (`test`.`t3`.`c2` like '%03') ), <primary_index_lookup>(`test`.`t2`.`b2` in <temporary table> on distinct_key where ((`test`.`t2`.`b2` = `<subquery4>`.`c2`))))))) group by `test`.`t2`.`b1`,`test`.`t2`.`b2` ), <primary_index_lookup>(`test`.`t1`.`a1` in <temporary table> on distinct_key where ((`test`.`t1`.`a1` = `<subquery2>`.`b1`) and (`test`.`t1`.`a2` = `<subquery2>`.`b2`)))))) and <expr_cache><`test`.`t1`.`a2`,`test`.`t1`.`a1`>(<in_optimizer>((`test`.`t1`.`a1`,`test`.`t1`.`a2`),(`test`.`t1`.`a1`,`test`.`t1`.`a2`) in ( <materialize> (select `test`.`t3`.`c1`,`test`.`t3`.`c2` from `test`.`t3` where <expr_cache><`test`.`t3`.`c2`,`test`.`t3`.`c1`>(<in_optimizer>((`test`.`t3`.`c1`,`test`.`t3`.`c2`),(`test`.`t3`.`c1`,`test`.`t3`.`c2`) in ( <materialize> (select `test`.`t2i`.`b1`,`test`.`t2i`.`b2` from `test`.`t2i` where (`test`.`t2i`.`b2` > '0') ), <primary_index_lookup>(`test`.`t3`.`c1` in <temporary table> on distinct_key where ((`test`.`t3`.`c1` = `<subquery6>`.`b1`) and (`test`.`t3`.`c2` = `<subquery6>`.`b2`)))))) ), <primary_index_lookup>(`test`.`t1`.`a1` in <temporary table> on distinct_key where ((`test`.`t1`.`a1` = `<subquery5>`.`c1`) and (`test`.`t1`.`a2` = `<subquery5>`.`c2`)))))))) union (select `test`.`t1i`.`a1` AS `a1`,`test`.`t1i`.`a2` AS `a2` from `test`.`t1i` where (<expr_cache><`test`.`t1i`.`a2`,`test`.`t1i`.`a1`>(<in_optimizer>((`test`.`t1i`.`a1`,`test`.`t1i`.`a2`),(`test`.`t1i`.`a1`,`test`.`t1i`.`a2`) in ( <materialize> (select `test`.`t2i`.`b1`,`test`.`t2i`.`b2` from `test`.`t2i` where (`test`.`t2i`.`b1` > '0') ), <primary_index_lookup>(`test`.`t1i`.`a1` in <temporary table> on distinct_key where ((`test`.`t1i`.`a1` = `<subquery8>`.`b1`) and (`test`.`t1i`.`a2` = `<subquery8>`.`b2`)))))) and <expr_cache><`test`.`t1i`.`a2`,`test`.`t1i`.`a1`>(<in_optimizer>((`test`.`t1i`.`a1`,`test`.`t1i`.`a2`),(`test`.`t1i`.`a1`,`test`.`t1i`.`a2`) in ( <materialize> (select `test`.`t3i`.`c1`,`test`.`t3i`.`c2` from `test`.`t3i` where <expr_cache><`test`.`t3i`.`c2`,`test`.`t3i`.`c1`>(<in_optimizer>((`test`.`t3i`.`c1`,`test`.`t3i`.`c2`),(`test`.`t3i`.`c1`,`test`.`t3i`.`c2`) in ( <materialize> (select `test`.`t2i`.`b1`,`test`.`t2i`.`b2` from `test`.`t2i` where (`test`.`t2i`.`b2` > '0') ), <primary_index_lookup>(`test`.`t3i`.`c1` in <temporary table> on distinct_key where ((`test`.`t3i`.`c1` = `<subquery10>`.`b1`) and (`test`.`t3i`.`c2` = `<subquery10>`.`b2`)))))) ), <primary_index_lookup>(`test`.`t1i`.`a1` in <temporary table> on distinct_key where ((`test`.`t1i`.`a1` = `<subquery9>`.`c1`) and (`test`.`t1i`.`a2` = `<subquery9>`.`c2`)))))))) (select * from t1 where (a1, a2) in (select b1, b2 from t2 where b2 in (select c2 from t3 where c2 LIKE '%02') or @@ -407,7 +411,7 @@ id select_type table type possible_keys key key_len ref rows filtered Extra 3 DEPENDENT UNION t2 ALL NULL NULL NULL NULL 5 100.00 Using where NULL UNION RESULT <union2,3> ALL NULL NULL NULL NULL NULL NULL Warnings: -Note 1003 select `test`.`t1`.`a1` AS `a1`,`test`.`t1`.`a2` AS `a2` from `test`.`t1` where (<expr_cache><`test`.`t1`.`a2`,`test`.`t1`.`a1`>(<in_optimizer>((`test`.`t1`.`a1`,`test`.`t1`.`a2`),<exists>(select `test`.`t1`.`a1`,`test`.`t1`.`a2` from `test`.`t1` where ((`test`.`t1`.`a1` > '0') and (<cache>(`test`.`t1`.`a1`) = `test`.`t1`.`a1`) and (<cache>(`test`.`t1`.`a2`) = `test`.`t1`.`a2`)) union select `test`.`t2`.`b1`,`test`.`t2`.`b2` from `test`.`t2` where ((`test`.`t2`.`b1` < '9') and (<cache>(`test`.`t1`.`a1`) = `test`.`t2`.`b1`) and (<cache>(`test`.`t1`.`a2`) = `test`.`t2`.`b2`))))) and <expr_cache><`test`.`t1`.`a2`,`test`.`t1`.`a1`>(<in_optimizer>((`test`.`t1`.`a1`,`test`.`t1`.`a2`),(`test`.`t1`.`a1`,`test`.`t1`.`a2`) in ( <materialize> (select `test`.`t3`.`c1`,`test`.`t3`.`c2` from `test`.`t3` where <expr_cache><`test`.`t3`.`c2`,`test`.`t3`.`c1`>(<in_optimizer>((`test`.`t3`.`c1`,`test`.`t3`.`c2`),(`test`.`t3`.`c1`,`test`.`t3`.`c2`) in ( <materialize> (select `test`.`t2i`.`b1`,`test`.`t2i`.`b2` from `test`.`t2i` where (`test`.`t2i`.`b2` > '0') ), <primary_index_lookup>(`test`.`t3`.`c1` in <temporary table> on distinct_key where ((`test`.`t3`.`c1` = `materialized subselect`.`b1`) and (`test`.`t3`.`c2` = `materialized subselect`.`b2`)))))) ), <primary_index_lookup>(`test`.`t1`.`a1` in <temporary table> on distinct_key where ((`test`.`t1`.`a1` = `materialized subselect`.`c1`) and (`test`.`t1`.`a2` = `materialized subselect`.`c2`))))))) +Note 1003 select `test`.`t1`.`a1` AS `a1`,`test`.`t1`.`a2` AS `a2` from `test`.`t1` where (<expr_cache><`test`.`t1`.`a2`,`test`.`t1`.`a1`>(<in_optimizer>((`test`.`t1`.`a1`,`test`.`t1`.`a2`),<exists>(select `test`.`t1`.`a1`,`test`.`t1`.`a2` from `test`.`t1` where ((`test`.`t1`.`a1` > '0') and (<cache>(`test`.`t1`.`a1`) = `test`.`t1`.`a1`) and (<cache>(`test`.`t1`.`a2`) = `test`.`t1`.`a2`)) union select `test`.`t2`.`b1`,`test`.`t2`.`b2` from `test`.`t2` where ((`test`.`t2`.`b1` < '9') and (<cache>(`test`.`t1`.`a1`) = `test`.`t2`.`b1`) and (<cache>(`test`.`t1`.`a2`) = `test`.`t2`.`b2`))))) and <expr_cache><`test`.`t1`.`a2`,`test`.`t1`.`a1`>(<in_optimizer>((`test`.`t1`.`a1`,`test`.`t1`.`a2`),(`test`.`t1`.`a1`,`test`.`t1`.`a2`) in ( <materialize> (select `test`.`t3`.`c1`,`test`.`t3`.`c2` from `test`.`t3` where <expr_cache><`test`.`t3`.`c2`,`test`.`t3`.`c1`>(<in_optimizer>((`test`.`t3`.`c1`,`test`.`t3`.`c2`),(`test`.`t3`.`c1`,`test`.`t3`.`c2`) in ( <materialize> (select `test`.`t2i`.`b1`,`test`.`t2i`.`b2` from `test`.`t2i` where (`test`.`t2i`.`b2` > '0') ), <primary_index_lookup>(`test`.`t3`.`c1` in <temporary table> on distinct_key where ((`test`.`t3`.`c1` = `<subquery5>`.`b1`) and (`test`.`t3`.`c2` = `<subquery5>`.`b2`)))))) ), <primary_index_lookup>(`test`.`t1`.`a1` in <temporary table> on distinct_key where ((`test`.`t1`.`a1` = `<subquery4>`.`c1`) and (`test`.`t1`.`a2` = `<subquery4>`.`c2`))))))) select * from t1 where (a1, a2) in (select * from t1 where a1 > '0' UNION select * from t2 where b1 < '9') and (a1, a2) in (select c1, c2 from t3 @@ -430,7 +434,7 @@ id select_type table type possible_keys key key_len ref rows filtered Extra 3 DEPENDENT UNION t2 ALL NULL NULL NULL NULL 5 100.00 Using where NULL UNION RESULT <union2,3> ALL NULL NULL NULL NULL NULL NULL Warnings: -Note 1003 select `test`.`t1`.`a1` AS `a1`,`test`.`t1`.`a2` AS `a2`,`test`.`t3`.`c1` AS `c1`,`test`.`t3`.`c2` AS `c2` from `test`.`t1` join `test`.`t3` where ((`test`.`t3`.`c1` = `test`.`t1`.`a1`) and <expr_cache><`test`.`t1`.`a2`,`test`.`t1`.`a1`>(<in_optimizer>((`test`.`t1`.`a1`,`test`.`t1`.`a2`),<exists>(select `test`.`t1`.`a1`,`test`.`t1`.`a2` from `test`.`t1` where ((`test`.`t1`.`a1` > '0') and (<cache>(`test`.`t1`.`a1`) = `test`.`t1`.`a1`) and (<cache>(`test`.`t1`.`a2`) = `test`.`t1`.`a2`)) union select `test`.`t2`.`b1`,`test`.`t2`.`b2` from `test`.`t2` where ((`test`.`t2`.`b1` < '9') and (<cache>(`test`.`t1`.`a1`) = `test`.`t2`.`b1`) and (<cache>(`test`.`t1`.`a2`) = `test`.`t2`.`b2`))))) and <expr_cache><`test`.`t3`.`c2`,`test`.`t3`.`c1`>(<in_optimizer>((`test`.`t3`.`c1`,`test`.`t3`.`c2`),(`test`.`t3`.`c1`,`test`.`t3`.`c2`) in ( <materialize> (select `test`.`t3`.`c1`,`test`.`t3`.`c2` from `test`.`t3` where <expr_cache><`test`.`t3`.`c2`,`test`.`t3`.`c1`>(<in_optimizer>((`test`.`t3`.`c1`,`test`.`t3`.`c2`),(`test`.`t3`.`c1`,`test`.`t3`.`c2`) in ( <materialize> (select `test`.`t2i`.`b1`,`test`.`t2i`.`b2` from `test`.`t2i` where (`test`.`t2i`.`b2` > '0') ), <primary_index_lookup>(`test`.`t3`.`c1` in <temporary table> on distinct_key where ((`test`.`t3`.`c1` = `materialized subselect`.`b1`) and (`test`.`t3`.`c2` = `materialized subselect`.`b2`)))))) ), <primary_index_lookup>(`test`.`t3`.`c1` in <temporary table> on distinct_key where ((`test`.`t3`.`c1` = `materialized subselect`.`c1`) and (`test`.`t3`.`c2` = `materialized subselect`.`c2`))))))) +Note 1003 select `test`.`t1`.`a1` AS `a1`,`test`.`t1`.`a2` AS `a2`,`test`.`t3`.`c1` AS `c1`,`test`.`t3`.`c2` AS `c2` from `test`.`t1` join `test`.`t3` where ((`test`.`t3`.`c1` = `test`.`t1`.`a1`) and <expr_cache><`test`.`t1`.`a2`,`test`.`t1`.`a1`>(<in_optimizer>((`test`.`t1`.`a1`,`test`.`t1`.`a2`),<exists>(select `test`.`t1`.`a1`,`test`.`t1`.`a2` from `test`.`t1` where ((`test`.`t1`.`a1` > '0') and (<cache>(`test`.`t1`.`a1`) = `test`.`t1`.`a1`) and (<cache>(`test`.`t1`.`a2`) = `test`.`t1`.`a2`)) union select `test`.`t2`.`b1`,`test`.`t2`.`b2` from `test`.`t2` where ((`test`.`t2`.`b1` < '9') and (<cache>(`test`.`t1`.`a1`) = `test`.`t2`.`b1`) and (<cache>(`test`.`t1`.`a2`) = `test`.`t2`.`b2`))))) and <expr_cache><`test`.`t3`.`c2`,`test`.`t3`.`c1`>(<in_optimizer>((`test`.`t3`.`c1`,`test`.`t3`.`c2`),(`test`.`t3`.`c1`,`test`.`t3`.`c2`) in ( <materialize> (select `test`.`t3`.`c1`,`test`.`t3`.`c2` from `test`.`t3` where <expr_cache><`test`.`t3`.`c2`,`test`.`t3`.`c1`>(<in_optimizer>((`test`.`t3`.`c1`,`test`.`t3`.`c2`),(`test`.`t3`.`c1`,`test`.`t3`.`c2`) in ( <materialize> (select `test`.`t2i`.`b1`,`test`.`t2i`.`b2` from `test`.`t2i` where (`test`.`t2i`.`b2` > '0') ), <primary_index_lookup>(`test`.`t3`.`c1` in <temporary table> on distinct_key where ((`test`.`t3`.`c1` = `<subquery5>`.`b1`) and (`test`.`t3`.`c2` = `<subquery5>`.`b2`)))))) ), <primary_index_lookup>(`test`.`t3`.`c1` in <temporary table> on distinct_key where ((`test`.`t3`.`c1` = `<subquery4>`.`c1`) and (`test`.`t3`.`c2` = `<subquery4>`.`c2`))))))) select * from t1, t3 where (a1, a2) in (select * from t1 where a1 > '0' UNION select * from t2 where b1 < '9') and (c1, c2) in (select c1, c2 from t3 @@ -476,7 +480,7 @@ id select_type table type possible_keys key key_len ref rows filtered Extra Warnings: Note 1276 Field or reference 'test.t1.a1' of SELECT #3 was resolved in SELECT #1 Note 1276 Field or reference 'test.t1.a2' of SELECT #6 was resolved in SELECT #1 -Note 1003 select `test`.`t1`.`a1` AS `a1`,`test`.`t1`.`a2` AS `a2` from `test`.`t1` where (<expr_cache><`test`.`t1`.`a2`,`test`.`t1`.`a1`,`test`.`t1`.`a1`>(<in_optimizer>((`test`.`t1`.`a1`,`test`.`t1`.`a2`),<exists>(select `test`.`t2`.`b1`,`test`.`t2`.`b2` from `test`.`t2` where ((<expr_cache><`test`.`t2`.`b2`,`test`.`t1`.`a1`>(<in_optimizer>(`test`.`t2`.`b2`,<exists>(select `test`.`t3a`.`c2` from `test`.`t3` `t3a` where ((`test`.`t3a`.`c1` = `test`.`t1`.`a1`) and (<cache>(`test`.`t2`.`b2`) = `test`.`t3a`.`c2`))))) or <expr_cache><`test`.`t2`.`b2`>(<in_optimizer>(`test`.`t2`.`b2`,`test`.`t2`.`b2` in ( <materialize> (select `test`.`t3b`.`c2` from `test`.`t3` `t3b` where (`test`.`t3b`.`c2` like '%03') ), <primary_index_lookup>(`test`.`t2`.`b2` in <temporary table> on distinct_key where ((`test`.`t2`.`b2` = `materialized subselect`.`c2`))))))) and (<cache>(`test`.`t1`.`a1`) = `test`.`t2`.`b1`) and (<cache>(`test`.`t1`.`a2`) = `test`.`t2`.`b2`))))) and <expr_cache><`test`.`t1`.`a2`,`test`.`t1`.`a1`,`test`.`t1`.`a2`>(<in_optimizer>((`test`.`t1`.`a1`,`test`.`t1`.`a2`),<exists>(select `test`.`t3c`.`c1`,`test`.`t3c`.`c2` from `test`.`t3` `t3c` where (<expr_cache><`test`.`t3c`.`c2`,`test`.`t3c`.`c1`,`test`.`t1`.`a2`>(<in_optimizer>((`test`.`t3c`.`c1`,`test`.`t3c`.`c2`),<exists>(<index_lookup>(<cache>(`test`.`t3c`.`c1`) in t2i on it2i3 where (((`test`.`t2i`.`b2` > '0') or (`test`.`t2i`.`b2` = `test`.`t1`.`a2`)) and (<cache>(`test`.`t3c`.`c1`) = `test`.`t2i`.`b1`) and (<cache>(`test`.`t3c`.`c2`) = `test`.`t2i`.`b2`)))))) and (<cache>(`test`.`t1`.`a1`) = `test`.`t3c`.`c1`) and (<cache>(`test`.`t1`.`a2`) = `test`.`t3c`.`c2`)))))) +Note 1003 select `test`.`t1`.`a1` AS `a1`,`test`.`t1`.`a2` AS `a2` from `test`.`t1` where (<expr_cache><`test`.`t1`.`a2`,`test`.`t1`.`a1`,`test`.`t1`.`a1`>(<in_optimizer>((`test`.`t1`.`a1`,`test`.`t1`.`a2`),<exists>(select `test`.`t2`.`b1`,`test`.`t2`.`b2` from `test`.`t2` where ((<expr_cache><`test`.`t2`.`b2`,`test`.`t1`.`a1`>(<in_optimizer>(`test`.`t2`.`b2`,<exists>(select `test`.`t3a`.`c2` from `test`.`t3` `t3a` where ((`test`.`t3a`.`c1` = `test`.`t1`.`a1`) and (<cache>(`test`.`t2`.`b2`) = `test`.`t3a`.`c2`))))) or <expr_cache><`test`.`t2`.`b2`>(<in_optimizer>(`test`.`t2`.`b2`,`test`.`t2`.`b2` in ( <materialize> (select `test`.`t3b`.`c2` from `test`.`t3` `t3b` where (`test`.`t3b`.`c2` like '%03') ), <primary_index_lookup>(`test`.`t2`.`b2` in <temporary table> on distinct_key where ((`test`.`t2`.`b2` = `<subquery4>`.`c2`))))))) and (<cache>(`test`.`t1`.`a1`) = `test`.`t2`.`b1`) and (<cache>(`test`.`t1`.`a2`) = `test`.`t2`.`b2`))))) and <expr_cache><`test`.`t1`.`a2`,`test`.`t1`.`a1`,`test`.`t1`.`a2`>(<in_optimizer>((`test`.`t1`.`a1`,`test`.`t1`.`a2`),<exists>(select `test`.`t3c`.`c1`,`test`.`t3c`.`c2` from `test`.`t3` `t3c` where (<expr_cache><`test`.`t3c`.`c2`,`test`.`t3c`.`c1`,`test`.`t1`.`a2`>(<in_optimizer>((`test`.`t3c`.`c1`,`test`.`t3c`.`c2`),<exists>(<index_lookup>(<cache>(`test`.`t3c`.`c1`) in t2i on it2i3 where (((`test`.`t2i`.`b2` > '0') or (`test`.`t2i`.`b2` = `test`.`t1`.`a2`)) and (<cache>(`test`.`t3c`.`c1`) = `test`.`t2i`.`b1`) and (<cache>(`test`.`t3c`.`c2`) = `test`.`t2i`.`b2`)))))) and (<cache>(`test`.`t1`.`a1`) = `test`.`t3c`.`c1`) and (<cache>(`test`.`t1`.`a2`) = `test`.`t3c`.`c2`)))))) explain extended select * from t1 where (a1, a2) in (select '1 - 01', '2 - 01'); id select_type table type possible_keys key key_len ref rows filtered Extra @@ -549,7 +553,6 @@ a1 a2 Test that BLOBs are not materialized (except when arguments of some functions). */ # force materialization to be always considered -set @@optimizer_switch='materialization=on,in_to_exists=off,semijoin=off'; set @prefix_len = 6; set @blob_len = 16; set @suffix_len = @blob_len - @prefix_len; @@ -611,7 +614,7 @@ id select_type table type possible_keys key key_len ref rows filtered Extra 1 PRIMARY t1_16 ALL NULL NULL NULL NULL 3 100.00 Using where 2 SUBQUERY t2_16 ALL NULL NULL NULL NULL 3 100.00 Using where Warnings: -Note 1003 select left(`test`.`t1_16`.`a1`,7) AS `left(a1,7)`,left(`test`.`t1_16`.`a2`,7) AS `left(a2,7)` from `test`.`t1_16` where <expr_cache><`test`.`t1_16`.`a1`>(<in_optimizer>(`test`.`t1_16`.`a1`,`test`.`t1_16`.`a1` in ( <materialize> (select substr(`test`.`t2_16`.`b1`,1,16) from `test`.`t2_16` where (`test`.`t2_16`.`b1` > '0') ), <primary_index_lookup>(`test`.`t1_16`.`a1` in <temporary table> on distinct_key where ((`test`.`t1_16`.`a1` = `materialized subselect`.`substring(b1,1,16)`)))))) +Note 1003 select left(`test`.`t1_16`.`a1`,7) AS `left(a1,7)`,left(`test`.`t1_16`.`a2`,7) AS `left(a2,7)` from `test`.`t1_16` where <expr_cache><`test`.`t1_16`.`a1`>(<in_optimizer>(`test`.`t1_16`.`a1`,`test`.`t1_16`.`a1` in ( <materialize> (select substr(`test`.`t2_16`.`b1`,1,16) from `test`.`t2_16` where (`test`.`t2_16`.`b1` > '0') ), <primary_index_lookup>(`test`.`t1_16`.`a1` in <temporary table> on distinct_key where ((`test`.`t1_16`.`a1` = `<subquery2>`.`substring(b1,1,16)`)))))) select left(a1,7), left(a2,7) from t1_16 where a1 in (select substring(b1,1,16) from t2_16 where b1 > '0'); @@ -640,7 +643,7 @@ id select_type table type possible_keys key key_len ref rows filtered Extra 1 PRIMARY t1_16 ALL NULL NULL NULL NULL 3 100.00 Using where 2 SUBQUERY t2_16 ALL NULL NULL NULL NULL 3 100.00 Using filesort Warnings: -Note 1003 select left(`test`.`t1_16`.`a1`,7) AS `left(a1,7)`,left(`test`.`t1_16`.`a2`,7) AS `left(a2,7)` from `test`.`t1_16` where <expr_cache><`test`.`t1_16`.`a1`>(<in_optimizer>(`test`.`t1_16`.`a1`,`test`.`t1_16`.`a1` in ( <materialize> (select group_concat(`test`.`t2_16`.`b1` separator ',') from `test`.`t2_16` group by `test`.`t2_16`.`b2` ), <primary_index_lookup>(`test`.`t1_16`.`a1` in <temporary table> on distinct_key where ((`test`.`t1_16`.`a1` = `materialized subselect`.`group_concat(b1)`)))))) +Note 1003 select left(`test`.`t1_16`.`a1`,7) AS `left(a1,7)`,left(`test`.`t1_16`.`a2`,7) AS `left(a2,7)` from `test`.`t1_16` where <expr_cache><`test`.`t1_16`.`a1`>(<in_optimizer>(`test`.`t1_16`.`a1`,`test`.`t1_16`.`a1` in ( <materialize> (select group_concat(`test`.`t2_16`.`b1` separator ',') from `test`.`t2_16` group by `test`.`t2_16`.`b2` ), <primary_index_lookup>(`test`.`t1_16`.`a1` in <temporary table> on distinct_key where ((`test`.`t1_16`.`a1` = `<subquery2>`.`group_concat(b1)`)))))) select left(a1,7), left(a2,7) from t1_16 where a1 in (select group_concat(b1) from t2_16 group by b2); @@ -662,7 +665,7 @@ id select_type table type possible_keys key key_len ref rows filtered Extra 3 DEPENDENT SUBQUERY t2 ALL NULL NULL NULL NULL 5 100.00 Using where; Using join buffer (flat, BNL join) 4 SUBQUERY t3 ALL NULL NULL NULL NULL 4 100.00 Using where Warnings: -Note 1003 select `test`.`t1`.`a1` AS `a1`,`test`.`t1`.`a2` AS `a2` from `test`.`t1` where <expr_cache><concat(`test`.`t1`.`a1`,'x')>(<in_optimizer>(concat(`test`.`t1`.`a1`,'x'),<exists>(select left(`test`.`t1_16`.`a1`,8) from `test`.`t1_16` where (<expr_cache><`test`.`t1_16`.`a2`,`test`.`t1_16`.`a1`>(<in_optimizer>((`test`.`t1_16`.`a1`,`test`.`t1_16`.`a2`),<exists>(select `test`.`t2_16`.`b1`,`test`.`t2_16`.`b2` from `test`.`t2_16` join `test`.`t2` where ((`test`.`t2`.`b2` = substr(`test`.`t2_16`.`b2`,1,6)) and <expr_cache><`test`.`t2`.`b1`>(<in_optimizer>(`test`.`t2`.`b1`,`test`.`t2`.`b1` in ( <materialize> (select `test`.`t3`.`c1` from `test`.`t3` where (`test`.`t3`.`c2` > '0') ), <primary_index_lookup>(`test`.`t2`.`b1` in <temporary table> on distinct_key where ((`test`.`t2`.`b1` = `materialized subselect`.`c1`)))))) and (<cache>(`test`.`t1_16`.`a1`) = `test`.`t2_16`.`b1`) and (<cache>(`test`.`t1_16`.`a2`) = `test`.`t2_16`.`b2`))))) and (<cache>(concat(`test`.`t1`.`a1`,'x')) = left(`test`.`t1_16`.`a1`,8)))))) +Note 1003 select `test`.`t1`.`a1` AS `a1`,`test`.`t1`.`a2` AS `a2` from `test`.`t1` where <expr_cache><concat(`test`.`t1`.`a1`,'x')>(<in_optimizer>(concat(`test`.`t1`.`a1`,'x'),<exists>(select left(`test`.`t1_16`.`a1`,8) from `test`.`t1_16` where (<expr_cache><`test`.`t1_16`.`a2`,`test`.`t1_16`.`a1`>(<in_optimizer>((`test`.`t1_16`.`a1`,`test`.`t1_16`.`a2`),<exists>(select `test`.`t2_16`.`b1`,`test`.`t2_16`.`b2` from `test`.`t2_16` join `test`.`t2` where ((`test`.`t2`.`b2` = substr(`test`.`t2_16`.`b2`,1,6)) and <expr_cache><`test`.`t2`.`b1`>(<in_optimizer>(`test`.`t2`.`b1`,`test`.`t2`.`b1` in ( <materialize> (select `test`.`t3`.`c1` from `test`.`t3` where (`test`.`t3`.`c2` > '0') ), <primary_index_lookup>(`test`.`t2`.`b1` in <temporary table> on distinct_key where ((`test`.`t2`.`b1` = `<subquery4>`.`c1`)))))) and (<cache>(`test`.`t1_16`.`a1`) = `test`.`t2_16`.`b1`) and (<cache>(`test`.`t1_16`.`a2`) = `test`.`t2_16`.`b2`))))) and (<cache>(concat(`test`.`t1`.`a1`,'x')) = left(`test`.`t1_16`.`a1`,8)))))) drop table t1_16, t2_16, t3_16; set @blob_len = 512; set @suffix_len = @blob_len - @prefix_len; @@ -724,7 +727,7 @@ id select_type table type possible_keys key key_len ref rows filtered Extra 1 PRIMARY t1_512 ALL NULL NULL NULL NULL 3 100.00 Using where 2 SUBQUERY t2_512 ALL NULL NULL NULL NULL 3 100.00 Using where Warnings: -Note 1003 select left(`test`.`t1_512`.`a1`,7) AS `left(a1,7)`,left(`test`.`t1_512`.`a2`,7) AS `left(a2,7)` from `test`.`t1_512` where <expr_cache><`test`.`t1_512`.`a1`>(<in_optimizer>(`test`.`t1_512`.`a1`,`test`.`t1_512`.`a1` in ( <materialize> (select substr(`test`.`t2_512`.`b1`,1,512) from `test`.`t2_512` where (`test`.`t2_512`.`b1` > '0') ), <primary_index_lookup>(`test`.`t1_512`.`a1` in <temporary table> on distinct_key where ((`test`.`t1_512`.`a1` = `materialized subselect`.`substring(b1,1,512)`)))))) +Note 1003 select left(`test`.`t1_512`.`a1`,7) AS `left(a1,7)`,left(`test`.`t1_512`.`a2`,7) AS `left(a2,7)` from `test`.`t1_512` where <expr_cache><`test`.`t1_512`.`a1`>(<in_optimizer>(`test`.`t1_512`.`a1`,`test`.`t1_512`.`a1` in ( <materialize> (select substr(`test`.`t2_512`.`b1`,1,512) from `test`.`t2_512` where (`test`.`t2_512`.`b1` > '0') ), <primary_index_lookup>(`test`.`t1_512`.`a1` in <temporary table> on distinct_key where ((`test`.`t1_512`.`a1` = `<subquery2>`.`substring(b1,1,512)`)))))) select left(a1,7), left(a2,7) from t1_512 where a1 in (select substring(b1,1,512) from t2_512 where b1 > '0'); @@ -738,7 +741,7 @@ id select_type table type possible_keys key key_len ref rows filtered Extra 1 PRIMARY t1_512 ALL NULL NULL NULL NULL 3 100.00 Using where 2 SUBQUERY t2_512 ALL NULL NULL NULL NULL 3 100.00 Using filesort Warnings: -Note 1003 select left(`test`.`t1_512`.`a1`,7) AS `left(a1,7)`,left(`test`.`t1_512`.`a2`,7) AS `left(a2,7)` from `test`.`t1_512` where <expr_cache><`test`.`t1_512`.`a1`>(<in_optimizer>(`test`.`t1_512`.`a1`,`test`.`t1_512`.`a1` in ( <materialize> (select group_concat(`test`.`t2_512`.`b1` separator ',') from `test`.`t2_512` group by `test`.`t2_512`.`b2` ), <primary_index_lookup>(`test`.`t1_512`.`a1` in <temporary table> on distinct_key where ((`test`.`t1_512`.`a1` = `materialized subselect`.`group_concat(b1)`)))))) +Note 1003 select left(`test`.`t1_512`.`a1`,7) AS `left(a1,7)`,left(`test`.`t1_512`.`a2`,7) AS `left(a2,7)` from `test`.`t1_512` where <expr_cache><`test`.`t1_512`.`a1`>(<in_optimizer>(`test`.`t1_512`.`a1`,`test`.`t1_512`.`a1` in ( <materialize> (select group_concat(`test`.`t2_512`.`b1` separator ',') from `test`.`t2_512` group by `test`.`t2_512`.`b2` ), <primary_index_lookup>(`test`.`t1_512`.`a1` in <temporary table> on distinct_key where ((`test`.`t1_512`.`a1` = `<subquery2>`.`group_concat(b1)`)))))) select left(a1,7), left(a2,7) from t1_512 where a1 in (select group_concat(b1) from t2_512 group by b2); @@ -751,7 +754,7 @@ id select_type table type possible_keys key key_len ref rows filtered Extra 1 PRIMARY t1_512 ALL NULL NULL NULL NULL 3 100.00 Using where 2 SUBQUERY t2_512 ALL NULL NULL NULL NULL 3 100.00 Using filesort Warnings: -Note 1003 select left(`test`.`t1_512`.`a1`,7) AS `left(a1,7)`,left(`test`.`t1_512`.`a2`,7) AS `left(a2,7)` from `test`.`t1_512` where <expr_cache><`test`.`t1_512`.`a1`>(<in_optimizer>(`test`.`t1_512`.`a1`,`test`.`t1_512`.`a1` in ( <materialize> (select group_concat(`test`.`t2_512`.`b1` separator ',') from `test`.`t2_512` group by `test`.`t2_512`.`b2` ), <primary_index_lookup>(`test`.`t1_512`.`a1` in <temporary table> on distinct_key where ((`test`.`t1_512`.`a1` = `materialized subselect`.`group_concat(b1)`)))))) +Note 1003 select left(`test`.`t1_512`.`a1`,7) AS `left(a1,7)`,left(`test`.`t1_512`.`a2`,7) AS `left(a2,7)` from `test`.`t1_512` where <expr_cache><`test`.`t1_512`.`a1`>(<in_optimizer>(`test`.`t1_512`.`a1`,`test`.`t1_512`.`a1` in ( <materialize> (select group_concat(`test`.`t2_512`.`b1` separator ',') from `test`.`t2_512` group by `test`.`t2_512`.`b2` ), <primary_index_lookup>(`test`.`t1_512`.`a1` in <temporary table> on distinct_key where ((`test`.`t1_512`.`a1` = `<subquery2>`.`group_concat(b1)`)))))) select left(a1,7), left(a2,7) from t1_512 where a1 in (select group_concat(b1) from t2_512 group by b2); @@ -831,7 +834,7 @@ id select_type table type possible_keys key key_len ref rows filtered Extra 1 PRIMARY t1_1024 ALL NULL NULL NULL NULL 3 100.00 Using where 2 SUBQUERY t2_1024 ALL NULL NULL NULL NULL 3 100.00 Using filesort Warnings: -Note 1003 select left(`test`.`t1_1024`.`a1`,7) AS `left(a1,7)`,left(`test`.`t1_1024`.`a2`,7) AS `left(a2,7)` from `test`.`t1_1024` where <expr_cache><`test`.`t1_1024`.`a1`>(<in_optimizer>(`test`.`t1_1024`.`a1`,`test`.`t1_1024`.`a1` in ( <materialize> (select group_concat(`test`.`t2_1024`.`b1` separator ',') from `test`.`t2_1024` group by `test`.`t2_1024`.`b2` ), <primary_index_lookup>(`test`.`t1_1024`.`a1` in <temporary table> on distinct_key where ((`test`.`t1_1024`.`a1` = `materialized subselect`.`group_concat(b1)`)))))) +Note 1003 select left(`test`.`t1_1024`.`a1`,7) AS `left(a1,7)`,left(`test`.`t1_1024`.`a2`,7) AS `left(a2,7)` from `test`.`t1_1024` where <expr_cache><`test`.`t1_1024`.`a1`>(<in_optimizer>(`test`.`t1_1024`.`a1`,`test`.`t1_1024`.`a1` in ( <materialize> (select group_concat(`test`.`t2_1024`.`b1` separator ',') from `test`.`t2_1024` group by `test`.`t2_1024`.`b2` ), <primary_index_lookup>(`test`.`t1_1024`.`a1` in <temporary table> on distinct_key where ((`test`.`t1_1024`.`a1` = `<subquery2>`.`group_concat(b1)`)))))) select left(a1,7), left(a2,7) from t1_1024 where a1 in (select group_concat(b1) from t2_1024 group by b2); @@ -844,7 +847,7 @@ id select_type table type possible_keys key key_len ref rows filtered Extra 1 PRIMARY t1_1024 ALL NULL NULL NULL NULL 3 100.00 Using where 2 SUBQUERY t2_1024 ALL NULL NULL NULL NULL 3 100.00 Using filesort Warnings: -Note 1003 select left(`test`.`t1_1024`.`a1`,7) AS `left(a1,7)`,left(`test`.`t1_1024`.`a2`,7) AS `left(a2,7)` from `test`.`t1_1024` where <expr_cache><`test`.`t1_1024`.`a1`>(<in_optimizer>(`test`.`t1_1024`.`a1`,`test`.`t1_1024`.`a1` in ( <materialize> (select group_concat(`test`.`t2_1024`.`b1` separator ',') from `test`.`t2_1024` group by `test`.`t2_1024`.`b2` ), <primary_index_lookup>(`test`.`t1_1024`.`a1` in <temporary table> on distinct_key where ((`test`.`t1_1024`.`a1` = `materialized subselect`.`group_concat(b1)`)))))) +Note 1003 select left(`test`.`t1_1024`.`a1`,7) AS `left(a1,7)`,left(`test`.`t1_1024`.`a2`,7) AS `left(a2,7)` from `test`.`t1_1024` where <expr_cache><`test`.`t1_1024`.`a1`>(<in_optimizer>(`test`.`t1_1024`.`a1`,`test`.`t1_1024`.`a1` in ( <materialize> (select group_concat(`test`.`t2_1024`.`b1` separator ',') from `test`.`t2_1024` group by `test`.`t2_1024`.`b2` ), <primary_index_lookup>(`test`.`t1_1024`.`a1` in <temporary table> on distinct_key where ((`test`.`t1_1024`.`a1` = `<subquery2>`.`group_concat(b1)`)))))) select left(a1,7), left(a2,7) from t1_1024 where a1 in (select group_concat(b1) from t2_1024 group by b2); @@ -924,7 +927,7 @@ id select_type table type possible_keys key key_len ref rows filtered Extra 1 PRIMARY t1_1025 ALL NULL NULL NULL NULL 3 100.00 Using where 2 SUBQUERY t2_1025 ALL NULL NULL NULL NULL 3 100.00 Using filesort Warnings: -Note 1003 select left(`test`.`t1_1025`.`a1`,7) AS `left(a1,7)`,left(`test`.`t1_1025`.`a2`,7) AS `left(a2,7)` from `test`.`t1_1025` where <expr_cache><`test`.`t1_1025`.`a1`>(<in_optimizer>(`test`.`t1_1025`.`a1`,`test`.`t1_1025`.`a1` in ( <materialize> (select group_concat(`test`.`t2_1025`.`b1` separator ',') from `test`.`t2_1025` group by `test`.`t2_1025`.`b2` ), <primary_index_lookup>(`test`.`t1_1025`.`a1` in <temporary table> on distinct_key where ((`test`.`t1_1025`.`a1` = `materialized subselect`.`group_concat(b1)`)))))) +Note 1003 select left(`test`.`t1_1025`.`a1`,7) AS `left(a1,7)`,left(`test`.`t1_1025`.`a2`,7) AS `left(a2,7)` from `test`.`t1_1025` where <expr_cache><`test`.`t1_1025`.`a1`>(<in_optimizer>(`test`.`t1_1025`.`a1`,`test`.`t1_1025`.`a1` in ( <materialize> (select group_concat(`test`.`t2_1025`.`b1` separator ',') from `test`.`t2_1025` group by `test`.`t2_1025`.`b2` ), <primary_index_lookup>(`test`.`t1_1025`.`a1` in <temporary table> on distinct_key where ((`test`.`t1_1025`.`a1` = `<subquery2>`.`group_concat(b1)`)))))) select left(a1,7), left(a2,7) from t1_1025 where a1 in (select group_concat(b1) from t2_1025 group by b2); @@ -937,7 +940,7 @@ id select_type table type possible_keys key key_len ref rows filtered Extra 1 PRIMARY t1_1025 ALL NULL NULL NULL NULL 3 100.00 Using where 2 SUBQUERY t2_1025 ALL NULL NULL NULL NULL 3 100.00 Using filesort Warnings: -Note 1003 select left(`test`.`t1_1025`.`a1`,7) AS `left(a1,7)`,left(`test`.`t1_1025`.`a2`,7) AS `left(a2,7)` from `test`.`t1_1025` where <expr_cache><`test`.`t1_1025`.`a1`>(<in_optimizer>(`test`.`t1_1025`.`a1`,`test`.`t1_1025`.`a1` in ( <materialize> (select group_concat(`test`.`t2_1025`.`b1` separator ',') from `test`.`t2_1025` group by `test`.`t2_1025`.`b2` ), <primary_index_lookup>(`test`.`t1_1025`.`a1` in <temporary table> on distinct_key where ((`test`.`t1_1025`.`a1` = `materialized subselect`.`group_concat(b1)`)))))) +Note 1003 select left(`test`.`t1_1025`.`a1`,7) AS `left(a1,7)`,left(`test`.`t1_1025`.`a2`,7) AS `left(a2,7)` from `test`.`t1_1025` where <expr_cache><`test`.`t1_1025`.`a1`>(<in_optimizer>(`test`.`t1_1025`.`a1`,`test`.`t1_1025`.`a1` in ( <materialize> (select group_concat(`test`.`t2_1025`.`b1` separator ',') from `test`.`t2_1025` group by `test`.`t2_1025`.`b2` ), <primary_index_lookup>(`test`.`t1_1025`.`a1` in <temporary table> on distinct_key where ((`test`.`t1_1025`.`a1` = `<subquery2>`.`group_concat(b1)`)))))) select left(a1,7), left(a2,7) from t1_1025 where a1 in (select group_concat(b1) from t2_1025 group by b2); @@ -951,7 +954,6 @@ insert into t1bit values (b'010', b'110'); insert into t2bit values (b'001', b'101'); insert into t2bit values (b'010', b'110'); insert into t2bit values (b'110', b'111'); -set @@optimizer_switch='materialization=on,in_to_exists=off,semijoin=off'; explain extended select bin(a1), bin(a2) from t1bit where (a1, a2) in (select b1, b2 from t2bit); @@ -959,7 +961,7 @@ id select_type table type possible_keys key key_len ref rows filtered Extra 1 PRIMARY t1bit ALL NULL NULL NULL NULL 3 100.00 Using where 2 SUBQUERY t2bit ALL NULL NULL NULL NULL 3 100.00 Warnings: -Note 1003 select conv(`test`.`t1bit`.`a1`,10,2) AS `bin(a1)`,conv(`test`.`t1bit`.`a2`,10,2) AS `bin(a2)` from `test`.`t1bit` where <expr_cache><`test`.`t1bit`.`a2`,`test`.`t1bit`.`a1`>(<in_optimizer>((`test`.`t1bit`.`a1`,`test`.`t1bit`.`a2`),(`test`.`t1bit`.`a1`,`test`.`t1bit`.`a2`) in ( <materialize> (select `test`.`t2bit`.`b1`,`test`.`t2bit`.`b2` from `test`.`t2bit` ), <primary_index_lookup>(`test`.`t1bit`.`a1` in <temporary table> on distinct_key where ((`test`.`t1bit`.`a1` = `materialized subselect`.`b1`) and (`test`.`t1bit`.`a2` = `materialized subselect`.`b2`)))))) +Note 1003 select conv(`test`.`t1bit`.`a1`,10,2) AS `bin(a1)`,conv(`test`.`t1bit`.`a2`,10,2) AS `bin(a2)` from `test`.`t1bit` where <expr_cache><`test`.`t1bit`.`a2`,`test`.`t1bit`.`a1`>(<in_optimizer>((`test`.`t1bit`.`a1`,`test`.`t1bit`.`a2`),(`test`.`t1bit`.`a1`,`test`.`t1bit`.`a2`) in ( <materialize> (select `test`.`t2bit`.`b1`,`test`.`t2bit`.`b2` from `test`.`t2bit` ), <primary_index_lookup>(`test`.`t1bit`.`a1` in <temporary table> on distinct_key where ((`test`.`t1bit`.`a1` = `<subquery2>`.`b1`) and (`test`.`t1bit`.`a2` = `<subquery2>`.`b2`)))))) select bin(a1), bin(a2) from t1bit where (a1, a2) in (select b1, b2 from t2bit); @@ -994,7 +996,7 @@ drop table t1, t2, t3, t1i, t2i, t3i, columns; /****************************************************************************** * Test the cache of the left operand of IN. ******************************************************************************/ -set @@optimizer_switch='materialization=on,in_to_exists=off,semijoin=off'; +# Test that default values of Cached_item are not used for comparison create table t1 (s1 int); create table t2 (s2 int); insert into t1 values (5),(1),(0); @@ -1030,7 +1032,7 @@ id select_type table type possible_keys key key_len ref rows filtered Extra 1 PRIMARY t1 ALL NULL NULL NULL NULL 7 100.00 Using where 2 SUBQUERY t2 ALL NULL NULL NULL NULL 6 100.00 Using where Warnings: -Note 1003 select `test`.`t1`.`a` AS `a` from `test`.`t1` where <expr_cache><`test`.`t1`.`a`>(<in_optimizer>(`test`.`t1`.`a`,`test`.`t1`.`a` in ( <materialize> (select `test`.`t2`.`c` from `test`.`t2` where (`test`.`t2`.`d` >= 20) ), <primary_index_lookup>(`test`.`t1`.`a` in <temporary table> on distinct_key where ((`test`.`t1`.`a` = `materialized subselect`.`c`)))))) +Note 1003 select `test`.`t1`.`a` AS `a` from `test`.`t1` where <expr_cache><`test`.`t1`.`a`>(<in_optimizer>(`test`.`t1`.`a`,`test`.`t1`.`a` in ( <materialize> (select `test`.`t2`.`c` from `test`.`t2` where (`test`.`t2`.`d` >= 20) ), <primary_index_lookup>(`test`.`t1`.`a` in <temporary table> on distinct_key where ((`test`.`t1`.`a` = `<subquery2>`.`c`)))))) select a from t1 where a in (select c from t2 where d >= 20); a 2 @@ -1044,7 +1046,7 @@ id select_type table type possible_keys key key_len ref rows filtered Extra 1 PRIMARY t1 index NULL it1a 4 NULL 7 100.00 Using where; Using index 2 SUBQUERY t2 ALL NULL NULL NULL NULL 6 100.00 Using where Warnings: -Note 1003 select `test`.`t1`.`a` AS `a` from `test`.`t1` where <expr_cache><`test`.`t1`.`a`>(<in_optimizer>(`test`.`t1`.`a`,`test`.`t1`.`a` in ( <materialize> (select `test`.`t2`.`c` from `test`.`t2` where (`test`.`t2`.`d` >= 20) ), <primary_index_lookup>(`test`.`t1`.`a` in <temporary table> on distinct_key where ((`test`.`t1`.`a` = `materialized subselect`.`c`)))))) +Note 1003 select `test`.`t1`.`a` AS `a` from `test`.`t1` where <expr_cache><`test`.`t1`.`a`>(<in_optimizer>(`test`.`t1`.`a`,`test`.`t1`.`a` in ( <materialize> (select `test`.`t2`.`c` from `test`.`t2` where (`test`.`t2`.`d` >= 20) ), <primary_index_lookup>(`test`.`t1`.`a` in <temporary table> on distinct_key where ((`test`.`t1`.`a` = `<subquery2>`.`c`)))))) select a from t1 where a in (select c from t2 where d >= 20); a 2 @@ -1058,7 +1060,7 @@ id select_type table type possible_keys key key_len ref rows filtered Extra 1 PRIMARY t1 index NULL it1a 4 NULL 7 100.00 Using where; Using index 2 SUBQUERY t2 ALL NULL NULL NULL NULL 7 100.00 Using where Warnings: -Note 1003 select `test`.`t1`.`a` AS `a` from `test`.`t1` where <expr_cache><`test`.`t1`.`a`>(<in_optimizer>(`test`.`t1`.`a`,`test`.`t1`.`a` in ( <materialize> (select `test`.`t2`.`c` from `test`.`t2` where (`test`.`t2`.`d` >= 20) ), <primary_index_lookup>(`test`.`t1`.`a` in <temporary table> on distinct_key where ((`test`.`t1`.`a` = `materialized subselect`.`c`)))))) +Note 1003 select `test`.`t1`.`a` AS `a` from `test`.`t1` where <expr_cache><`test`.`t1`.`a`>(<in_optimizer>(`test`.`t1`.`a`,`test`.`t1`.`a` in ( <materialize> (select `test`.`t2`.`c` from `test`.`t2` where (`test`.`t2`.`d` >= 20) ), <primary_index_lookup>(`test`.`t1`.`a` in <temporary table> on distinct_key where ((`test`.`t1`.`a` = `<subquery2>`.`c`)))))) select a from t1 where a in (select c from t2 where d >= 20); a 2 @@ -1071,7 +1073,7 @@ id select_type table type possible_keys key key_len ref rows filtered Extra 1 PRIMARY t1 index NULL it1a 4 NULL 7 100.00 Using index 2 SUBQUERY t2 ALL NULL NULL NULL NULL 7 100.00 Using where Warnings: -Note 1003 select `test`.`t1`.`a` AS `a` from `test`.`t1` group by `test`.`t1`.`a` having <expr_cache><`test`.`t1`.`a`>(<in_optimizer>(`test`.`t1`.`a`,`test`.`t1`.`a` in ( <materialize> (select `test`.`t2`.`c` from `test`.`t2` where (`test`.`t2`.`d` >= 20) ), <primary_index_lookup>(`test`.`t1`.`a` in <temporary table> on distinct_key where ((`test`.`t1`.`a` = `materialized subselect`.`c`)))))) +Note 1003 select `test`.`t1`.`a` AS `a` from `test`.`t1` group by `test`.`t1`.`a` having <expr_cache><`test`.`t1`.`a`>(<in_optimizer>(`test`.`t1`.`a`,`test`.`t1`.`a` in ( <materialize> (select `test`.`t2`.`c` from `test`.`t2` where (`test`.`t2`.`d` >= 20) ), <primary_index_lookup>(`test`.`t1`.`a` in <temporary table> on distinct_key where ((`test`.`t1`.`a` = `<subquery2>`.`c`)))))) select a from t1 group by a having a in (select c from t2 where d >= 20); a 2 @@ -1083,7 +1085,7 @@ id select_type table type possible_keys key key_len ref rows filtered Extra 1 PRIMARY t1 index NULL it1a 4 NULL 7 100.00 Using index 2 SUBQUERY t2 ALL NULL NULL NULL NULL 7 100.00 Using where Warnings: -Note 1003 select `test`.`t1`.`a` AS `a` from `test`.`t1` group by `test`.`t1`.`a` having <expr_cache><`test`.`t1`.`a`>(<in_optimizer>(`test`.`t1`.`a`,`test`.`t1`.`a` in ( <materialize> (select `test`.`t2`.`c` from `test`.`t2` where (`test`.`t2`.`d` >= 20) ), <primary_index_lookup>(`test`.`t1`.`a` in <temporary table> on distinct_key where ((`test`.`t1`.`a` = `materialized subselect`.`c`)))))) +Note 1003 select `test`.`t1`.`a` AS `a` from `test`.`t1` group by `test`.`t1`.`a` having <expr_cache><`test`.`t1`.`a`>(<in_optimizer>(`test`.`t1`.`a`,`test`.`t1`.`a` in ( <materialize> (select `test`.`t2`.`c` from `test`.`t2` where (`test`.`t2`.`d` >= 20) ), <primary_index_lookup>(`test`.`t1`.`a` in <temporary table> on distinct_key where ((`test`.`t1`.`a` = `<subquery2>`.`c`)))))) select a from t1 group by a having a in (select c from t2 where d >= 20); a 2 @@ -1136,47 +1138,47 @@ drop table t2; create table t1 (a1 int key); create table t2 (b1 int); insert into t1 values (5); -Only the last query returns correct result. Filed as BUG#40037. -set @@optimizer_switch='materialization=on,in_to_exists=off,semijoin=off'; explain select min(a1) from t1 where 7 in (select b1 from t2 group by b1); id select_type table type possible_keys key key_len ref rows Extra 1 PRIMARY NULL NULL NULL NULL NULL NULL NULL Select tables optimized away 2 SUBQUERY t2 system NULL NULL NULL NULL 0 const row not found select min(a1) from t1 where 7 in (select b1 from t2 group by b1); min(a1) -set @@optimizer_switch='materialization=off,in_to_exists=on,semijoin=off'; +set @save_optimizer_switch=@@optimizer_switch; +set @@optimizer_switch='default,materialization=off'; explain select min(a1) from t1 where 7 in (select b1 from t2 group by b1); id select_type table type possible_keys key key_len ref rows Extra 1 PRIMARY NULL NULL NULL NULL NULL NULL NULL Select tables optimized away 2 DEPENDENT SUBQUERY t2 system NULL NULL NULL NULL 0 const row not found select min(a1) from t1 where 7 in (select b1 from t2 group by b1); min(a1) -set @@optimizer_switch='materialization=on,in_to_exists=off,semijoin=off'; -explain select min(a1) from t1 where 7 in (select b1 from t2); -id select_type table type possible_keys key key_len ref rows Extra -1 PRIMARY NULL NULL NULL NULL NULL NULL NULL Select tables optimized away -2 SUBQUERY t2 system NULL NULL NULL NULL 0 const row not found -select min(a1) from t1 where 7 in (select b1 from t2); -min(a1) -set @@optimizer_switch='materialization=off,in_to_exists=on,semijoin=off'; +set @@optimizer_switch='default,semijoin=off'; explain select min(a1) from t1 where 7 in (select b1 from t2); id select_type table type possible_keys key key_len ref rows Extra 1 PRIMARY NULL NULL NULL NULL NULL NULL NULL Select tables optimized away 2 DEPENDENT SUBQUERY NULL NULL NULL NULL NULL NULL NULL Impossible WHERE noticed after reading const tables select min(a1) from t1 where 7 in (select b1 from t2); min(a1) -set @@optimizer_switch='materialization=off,in_to_exists=off,semijoin=on'; +set @@optimizer_switch='default,materialization=off'; +# with MariaDB and MWL#90, this particular case is solved: explain select min(a1) from t1 where 7 in (select b1 from t2); id select_type table type possible_keys key key_len ref rows Extra 1 PRIMARY NULL NULL NULL NULL NULL NULL NULL Impossible WHERE noticed after reading const tables select min(a1) from t1 where 7 in (select b1 from t2); min(a1) NULL +# but when we go around MWL#90 code, the problem still shows up: +explain select min(a1) from t1 where 7 in (select b1 from t2) or 2> 4; +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY NULL NULL NULL NULL NULL NULL NULL Select tables optimized away +2 DEPENDENT SUBQUERY NULL NULL NULL NULL NULL NULL NULL Impossible WHERE noticed after reading const tables +select min(a1) from t1 where 7 in (select b1 from t2) or 2> 4; +min(a1) +set @@optimizer_switch= @save_optimizer_switch; drop table t1,t2; create table t1 (a char(2), b varchar(10)); insert into t1 values ('a', 'aaa'); insert into t1 values ('aa', 'aaaa'); -set @@optimizer_switch='materialization=on,in_to_exists=off,semijoin=off'; explain select a,b from t1 where b in (select a from t1); id select_type table type possible_keys key key_len ref rows Extra 1 PRIMARY t1 ALL NULL NULL NULL NULL 2 Using where @@ -1196,15 +1198,17 @@ INSERT INTO t1 (f1, f2) VALUES (10, 1.668); CREATE TABLE t2 LIKE t1; INSERT INTO t2 VALUES (1, 1.789); INSERT INTO t2 VALUES (13, 1.454); -SET @@optimizer_switch='materialization=on,in_to_exists=off,semijoin=on'; +set @save_optimizer_switch=@@optimizer_switch; +SET @@optimizer_switch='default,semijoin=on,materialization=on'; EXPLAIN SELECT COUNT(*) FROM t1 WHERE (f1,f2) IN (SELECT f1,f2 FROM t2); id select_type table type possible_keys key key_len ref rows Extra -1 PRIMARY subselect2 ALL unique_key NULL NULL NULL 2 +1 PRIMARY <subquery2> ALL distinct_key NULL NULL NULL 2 1 PRIMARY t1 ALL NULL NULL NULL NULL 3 Using where; Using join buffer (flat, BNL join) 2 SUBQUERY t2 ALL NULL NULL NULL NULL 2 SELECT COUNT(*) FROM t1 WHERE (f1,f2) IN (SELECT f1,f2 FROM t2); COUNT(*) 2 +set @@optimizer_switch= @save_optimizer_switch; DROP TABLE t1, t2; CREATE TABLE t1 ( pk int, @@ -1217,11 +1221,12 @@ PRIMARY KEY (pk) INSERT INTO t1 VALUES (1,'o','ffff','ffff','ffoo'),(2,'f','ffff','ffff','ffff'); CREATE TABLE t2 LIKE t1; INSERT INTO t2 VALUES (1,'i','iiii','iiii','iiii'),(2,'f','ffff','ffff','ffff'); -SET @@optimizer_switch='materialization=on,in_to_exists=off,semijoin=on'; +set @save_optimizer_switch=@@optimizer_switch; +SET @@optimizer_switch='default,semijoin=on,materialization=on'; EXPLAIN SELECT pk FROM t1 WHERE (a) IN (SELECT a FROM t2 WHERE pk > 0); id select_type table type possible_keys key key_len ref rows Extra 1 PRIMARY t1 ALL NULL NULL NULL NULL 2 -1 PRIMARY subselect2 eq_ref unique_key unique_key 5 func 1 +1 PRIMARY <subquery2> eq_ref distinct_key distinct_key 5 func 1 2 SUBQUERY t2 range PRIMARY PRIMARY 4 NULL 2 Using index condition; Rowid-ordered scan SELECT pk FROM t1 WHERE (a) IN (SELECT a FROM t2 WHERE pk > 0); pk @@ -1230,6 +1235,7 @@ SELECT pk FROM t1 WHERE (b,c,d) IN (SELECT b,c,d FROM t2 WHERE pk > 0); pk 2 DROP TABLE t1, t2; +set optimizer_switch=@save_optimizer_switch; # # BUG#50019: Wrong result for IN-subquery with materialization # @@ -1281,6 +1287,66 @@ a a in (select a from t1) 1 0 2 0 drop table t0, t1; +set optimizer_switch='firstmatch=on'; +# +# MWL#90, review feedback: check what happens when the subquery +# looks like candidate for MWL#90 checking at the first glance +# but then subselect_hash_sj_engine::init_permanent() discovers +# that it's not possible to perform duplicate removal for the +# selected datatypes, and so materialization isn't applicable after +# all. +# +set @blob_len = 1024; +set @suffix_len = @blob_len - @prefix_len; +create table t1_1024 (a1 blob(1024), a2 blob(1024)); +create table t2_1024 (b1 blob(1024), b2 blob(1024)); +insert into t1_1024 values +(concat('1 - 00', repeat('x', @suffix_len)), concat('2 - 00', repeat('x', @suffix_len))); +insert into t1_1024 values +(concat('1 - 01', repeat('x', @suffix_len)), concat('2 - 01', repeat('x', @suffix_len))); +insert into t1_1024 values +(concat('1 - 02', repeat('x', @suffix_len)), concat('2 - 02', repeat('x', @suffix_len))); +insert into t2_1024 values +(concat('1 - 01', repeat('x', @suffix_len)), concat('2 - 01', repeat('x', @suffix_len))); +insert into t2_1024 values +(concat('1 - 02', repeat('x', @suffix_len)), concat('2 - 02', repeat('x', @suffix_len))); +insert into t2_1024 values +(concat('1 - 03', repeat('x', @suffix_len)), concat('2 - 03', repeat('x', @suffix_len))); +explain select left(a1,7), left(a2,7) from t1_1024 where (a1,3) in (select substring(b1,1,1024), count(*) from t2_1024 where b1 > '0'); +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY t1_1024 ALL NULL NULL NULL NULL 3 Using where +2 DEPENDENT SUBQUERY t2_1024 ALL NULL NULL NULL NULL 3 Using where +select left(a1,7), left(a2,7) from t1_1024 where (a1,3) in (select substring(b1,1,1024), count(*) from t2_1024 where b1 > '0'); +left(a1,7) left(a2,7) +1 - 01x 2 - 01x +drop table t1_1024, t2_1024; +set @@optimizer_switch='materialization=on,in_to_exists=off,semijoin=off'; +create table t0 (a int); +insert into t0 values (0),(1),(2); +create table t1 (a int); +insert into t1 values (0),(1),(2); +explain select a, a in (select a from t1) from t0; +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY t0 ALL NULL NULL NULL NULL 3 +2 SUBQUERY t1 ALL NULL NULL NULL NULL 3 +select a, a in (select a from t1) from t0; +a a in (select a from t1) +0 1 +1 1 +2 1 +prepare s from 'select a, a in (select a from t1) from t0'; +execute s; +a a in (select a from t1) +0 1 +1 1 +2 1 +update t1 set a=123; +execute s; +a a in (select a from t1) +0 0 +1 0 +2 0 +drop table t0, t1; # # LPBUG#609121: RQG: wrong result on aggregate + NOT IN + HAVING and # partial_match_table_scan=on @@ -1371,7 +1437,7 @@ SET @@optimizer_switch='default,semijoin=on,materialization=on'; EXPLAIN SELECT pk FROM t1 WHERE (a) IN (SELECT a FROM t2 WHERE pk > 0); id select_type table type possible_keys key key_len ref rows Extra 1 PRIMARY t1 ALL NULL NULL NULL NULL 2 -1 PRIMARY subselect2 eq_ref unique_key unique_key 5 func 1 +1 PRIMARY <subquery2> eq_ref distinct_key distinct_key 5 func 1 2 SUBQUERY t2 range PRIMARY PRIMARY 4 NULL 2 Using index condition; Rowid-ordered scan SELECT pk FROM t1 WHERE (a) IN (SELECT a FROM t2 WHERE pk > 0); pk @@ -1381,6 +1447,29 @@ pk 2 DROP TABLE t1, t2; # +# BUG#724228: Wrong result with materialization=on and three aggregates in maria-5.3-mwl90 +# +CREATE TABLE t1 ( f2 int(11)) ; +INSERT IGNORE INTO t1 VALUES ('7'),('9'),('7'),('4'),('2'),('6'),('8'),('5'),('6'),('188'),('2'),('1'),('1'),('0'),('9'),('4'); +CREATE TABLE t2 ( f1 int(11), f2 int(11)) ENGINE=MyISAM; +INSERT IGNORE INTO t2 VALUES ('1','1'); +CREATE TABLE t3 ( f1 int(11), f2 int(11), f3 int(11), PRIMARY KEY (f1)) ; +INSERT IGNORE INTO t3 VALUES ('16','6','1'),('18','3','4'),('19',NULL,'9'),('20','0','6'),('41','2','0'),('42','2','5'),('43','9','6'),('44','7','4'),('45','1','4'),('46','222','238'),('47','3','6'),('48','6','6'),('49',NULL,'1'),('50','5','1'); +SET @_save_join_cache_level = @@join_cache_level; +SET @_save_optimizer_switch = @@optimizer_switch; +SET join_cache_level = 1; +SET optimizer_switch='materialization=on'; +SELECT f1 FROM t3 +WHERE +f1 NOT IN (SELECT MAX(f2) FROM t1) AND +f3 IN (SELECT MIN(f1) FROM t2) AND +f1 IN (SELECT COUNT(f2) FROM t1); +f1 +16 +SET @@join_cache_level = @_save_join_cache_level; +SET @@optimizer_switch = @_save_optimizer_switch; +drop table t1, t2, t3; +# # LPBUG#719198 Ordered_key::cmp_key_with_search_key(rownum_t): Assertion `!compare_pred[i]->null_value' # failed with subquery on both sides of NOT IN and materialization # diff --git a/mysql-test/r/subselect_no_mat.result b/mysql-test/r/subselect_no_mat.result index bab40911084..7acaad7e659 100644 --- a/mysql-test/r/subselect_no_mat.result +++ b/mysql-test/r/subselect_no_mat.result @@ -1368,7 +1368,7 @@ explain extended select * from t2 where t2.a in (select t1.a from t1,t3 where t1 id select_type table type possible_keys key key_len ref rows filtered Extra 1 PRIMARY t2 index a a 5 NULL 4 100.00 Using where; Using index 1 PRIMARY t3 index a a 5 NULL 3 100.00 Using where; Using index -1 PRIMARY t1 ref a a 10 test.t2.a,test.t3.a 116 100.61 Using index; FirstMatch(t2) +1 PRIMARY t1 ref a a 10 test.t2.a,test.t3.a 116 100.00 Using index; FirstMatch(t2) Warnings: Note 1003 select `test`.`t2`.`a` AS `a` from `test`.`t2` semi join (`test`.`t1` join `test`.`t3`) where ((`test`.`t1`.`a` = `test`.`t2`.`a`) and (`test`.`t1`.`b` = `test`.`t3`.`a`)) insert into t1 values (3,31); diff --git a/mysql-test/r/subselect_sj.result b/mysql-test/r/subselect_sj.result index 3d8a1f1d741..2df5089b064 100644 --- a/mysql-test/r/subselect_sj.result +++ b/mysql-test/r/subselect_sj.result @@ -961,7 +961,7 @@ FROM t1 WHERE `varchar_nokey` < 'n' XOR `pk` ) ; id select_type table type possible_keys key key_len ref rows filtered Extra 1 PRIMARY t2 ALL NULL NULL NULL NULL 18 100.00 -1 PRIMARY t1 ref varchar_key varchar_key 3 test.t2.varchar_nokey 2 105.00 Using where; FirstMatch(t2) +1 PRIMARY t1 ref varchar_key varchar_key 3 test.t2.varchar_nokey 2 100.00 Using where; FirstMatch(t2) Warnings: Note 1003 select `test`.`t2`.`varchar_nokey` AS `varchar_nokey` from `test`.`t2` semi join (`test`.`t1`) where ((`test`.`t1`.`varchar_key` = `test`.`t2`.`varchar_nokey`) and (`test`.`t1`.`varchar_nokey` = `test`.`t2`.`varchar_nokey`) and ((`test`.`t2`.`varchar_nokey` < 'n') xor `test`.`t1`.`pk`)) SELECT varchar_nokey diff --git a/mysql-test/r/subselect_sj2.result b/mysql-test/r/subselect_sj2.result index 99c8b4bb01a..e4bf6488d5e 100644 --- a/mysql-test/r/subselect_sj2.result +++ b/mysql-test/r/subselect_sj2.result @@ -354,6 +354,10 @@ Canada China Czech Republic drop table t1, t2; +drop procedure if exists p1; +drop procedure if exists p2; +drop procedure if exists p3; +drop procedure if exists p4; CREATE TABLE t1(a INT); CREATE TABLE t2(c INT); CREATE PROCEDURE p1(v1 int) diff --git a/mysql-test/r/subselect_sj2_jcl6.result b/mysql-test/r/subselect_sj2_jcl6.result index cbf3fce7a0a..d3101e1dc80 100644 --- a/mysql-test/r/subselect_sj2_jcl6.result +++ b/mysql-test/r/subselect_sj2_jcl6.result @@ -361,6 +361,10 @@ Canada China Czech Republic drop table t1, t2; +drop procedure if exists p1; +drop procedure if exists p2; +drop procedure if exists p3; +drop procedure if exists p4; CREATE TABLE t1(a INT); CREATE TABLE t2(c INT); CREATE PROCEDURE p1(v1 int) diff --git a/mysql-test/r/subselect_sj2_mat.result b/mysql-test/r/subselect_sj2_mat.result index b4e5dfa962a..884e186743b 100644 --- a/mysql-test/r/subselect_sj2_mat.result +++ b/mysql-test/r/subselect_sj2_mat.result @@ -33,7 +33,7 @@ a b 9 5 explain select * from t2 where b in (select a from t1); id select_type table type possible_keys key key_len ref rows Extra -1 PRIMARY subselect2 ALL unique_key NULL NULL NULL 3 +1 PRIMARY <subquery2> ALL distinct_key NULL NULL NULL 3 1 PRIMARY t2 ref b b 5 test.t1.a 2 2 SUBQUERY t1 ALL NULL NULL NULL NULL 3 Using where select * from t2 where b in (select a from t1); @@ -53,7 +53,7 @@ insert into t3 select a,a, a,a,a from t0; explain select * from t3 where b in (select a from t1); id select_type table type possible_keys key key_len ref rows Extra 1 PRIMARY t3 ALL b NULL NULL NULL 10 -1 PRIMARY subselect2 eq_ref unique_key unique_key 5 func 1 +1 PRIMARY <subquery2> eq_ref distinct_key distinct_key 5 func 1 2 SUBQUERY t1 ALL NULL NULL NULL NULL 3 select * from t3 where b in (select a from t1); a b pk1 pk2 pk3 @@ -76,7 +76,7 @@ A.a + 10*B.a, A.a + 10*B.a, A.a + 10*B.a, A.a + 10*B.a from t0 A, t0 B where B.a <5; explain select * from t3 where b in (select a from t0); id select_type table type possible_keys key key_len ref rows Extra -1 PRIMARY subselect2 ALL unique_key NULL NULL NULL 10 +1 PRIMARY <subquery2> ALL distinct_key NULL NULL NULL 10 1 PRIMARY t3 ref b b 5 test.t0.a 1 2 SUBQUERY t0 ALL NULL NULL NULL NULL 10 Using where set @save_ecp= @@engine_condition_pushdown; @@ -103,7 +103,7 @@ set max_heap_table_size= @save_max_heap_table_size; explain select * from t1 where a in (select b from t2); id select_type table type possible_keys key key_len ref rows Extra 1 PRIMARY t1 ALL NULL NULL NULL NULL 3 -1 PRIMARY subselect2 eq_ref unique_key unique_key 5 func 1 +1 PRIMARY <subquery2> eq_ref distinct_key distinct_key 5 func 1 2 SUBQUERY t2 index b b 5 NULL 10 Using index select * from t1; a b @@ -131,7 +131,7 @@ explain select a, mid(filler1, 1,10), length(filler1)=length(filler2) as Z from t1 ot where a in (select a from t2 it); id select_type table type possible_keys key key_len ref rows Extra -1 PRIMARY subselect2 ALL unique_key NULL NULL NULL 22 +1 PRIMARY <subquery2> ALL distinct_key NULL NULL NULL 22 1 PRIMARY ot ALL NULL NULL NULL NULL 32 Using where; Using join buffer (flat, BNL join) 2 SUBQUERY it ALL NULL NULL NULL NULL 22 select @@ -165,7 +165,7 @@ a, mid(filler1, 1,10), length(filler1)=length(filler2) from t2 ot where a in (select a from t1 it); id select_type table type possible_keys key key_len ref rows Extra 1 PRIMARY ot ALL NULL NULL NULL NULL 22 -1 PRIMARY subselect2 eq_ref unique_key unique_key 5 func 1 +1 PRIMARY <subquery2> eq_ref distinct_key distinct_key 5 func 1 2 SUBQUERY it ALL NULL NULL NULL NULL 32 select a, mid(filler1, 1,10), length(filler1)=length(filler2) @@ -199,7 +199,7 @@ explain select a, mid(filler1, 1,10), length(filler1)=length(filler2) as Z from t1 ot where a in (select a from t2 it); id select_type table type possible_keys key key_len ref rows Extra -1 PRIMARY subselect2 ALL unique_key NULL NULL NULL 22 +1 PRIMARY <subquery2> ALL distinct_key NULL NULL NULL 22 1 PRIMARY ot ALL NULL NULL NULL NULL 52 Using where; Using join buffer (flat, BNL join) 2 SUBQUERY it ALL NULL NULL NULL NULL 22 select @@ -233,7 +233,7 @@ a, mid(filler1, 1,10), length(filler1)=length(filler2) from t2 ot where a in (select a from t1 it); id select_type table type possible_keys key key_len ref rows Extra 1 PRIMARY ot ALL NULL NULL NULL NULL 22 -1 PRIMARY subselect2 eq_ref unique_key unique_key 5 func 1 +1 PRIMARY <subquery2> eq_ref distinct_key distinct_key 5 func 1 2 SUBQUERY it ALL NULL NULL NULL NULL 52 select a, mid(filler1, 1,10), length(filler1)=length(filler2) @@ -353,7 +353,7 @@ WHERE t1.Code IN ( SELECT t2.CountryCode FROM t2 WHERE Population > 5000000); id select_type table type possible_keys key key_len ref rows Extra 1 PRIMARY t1 ALL PRIMARY NULL NULL NULL 31 -1 PRIMARY subselect2 eq_ref unique_key unique_key 3 func 1 +1 PRIMARY <subquery2> eq_ref distinct_key distinct_key 3 func 1 2 SUBQUERY t2 ALL CountryCode NULL NULL NULL 545 Using where SELECT Name FROM t1 WHERE t1.Code IN ( @@ -364,6 +364,10 @@ Canada China Czech Republic drop table t1, t2; +drop procedure if exists p1; +drop procedure if exists p2; +drop procedure if exists p3; +drop procedure if exists p4; CREATE TABLE t1(a INT); CREATE TABLE t2(c INT); CREATE PROCEDURE p1(v1 int) @@ -697,7 +701,7 @@ The following must use loose index scan over t3, key a: explain select count(a) from t2 where a in ( SELECT a FROM t3); id select_type table type possible_keys key key_len ref rows Extra 1 PRIMARY t2 index a a 5 NULL 1000 Using index -1 PRIMARY subselect2 eq_ref unique_key unique_key 5 func 1 +1 PRIMARY <subquery2> eq_ref distinct_key distinct_key 5 func 1 2 SUBQUERY t3 index a a 5 NULL 30000 Using index select count(a) from t2 where a in ( SELECT a FROM t3); count(a) diff --git a/mysql-test/r/subselect_sj_jcl6.result b/mysql-test/r/subselect_sj_jcl6.result index c804cfb8892..22335c25655 100644 --- a/mysql-test/r/subselect_sj_jcl6.result +++ b/mysql-test/r/subselect_sj_jcl6.result @@ -969,7 +969,7 @@ FROM t1 WHERE `varchar_nokey` < 'n' XOR `pk` ) ; id select_type table type possible_keys key key_len ref rows filtered Extra 1 PRIMARY t2 ALL NULL NULL NULL NULL 18 100.00 -1 PRIMARY t1 ref varchar_key varchar_key 3 test.t2.varchar_nokey 2 105.00 Using where; FirstMatch(t2); Using join buffer (flat, BKA join); Key-ordered Rowid-ordered scan +1 PRIMARY t1 ref varchar_key varchar_key 3 test.t2.varchar_nokey 2 100.00 Using where; FirstMatch(t2); Using join buffer (flat, BKA join); Key-ordered Rowid-ordered scan Warnings: Note 1003 select `test`.`t2`.`varchar_nokey` AS `varchar_nokey` from `test`.`t2` semi join (`test`.`t1`) where ((`test`.`t1`.`varchar_key` = `test`.`t2`.`varchar_nokey`) and (`test`.`t1`.`varchar_nokey` = `test`.`t2`.`varchar_nokey`) and ((`test`.`t2`.`varchar_nokey` < 'n') xor `test`.`t1`.`pk`)) SELECT varchar_nokey diff --git a/mysql-test/r/subselect_sj_mat.result b/mysql-test/r/subselect_sj_mat.result new file mode 100644 index 00000000000..f1e26561f4c --- /dev/null +++ b/mysql-test/r/subselect_sj_mat.result @@ -0,0 +1,1360 @@ +drop table if exists t1, t2, t3, t1i, t2i, t3i; +drop table if exists columns; +drop table if exists t1_16, t2_16, t3_16; +drop view if exists v1, v2, v1m, v2m; +create table t1 (a1 char(8), a2 char(8)); +create table t2 (b1 char(8), b2 char(8)); +create table t3 (c1 char(8), c2 char(8)); +insert into t1 values ('1 - 00', '2 - 00'); +insert into t1 values ('1 - 01', '2 - 01'); +insert into t1 values ('1 - 02', '2 - 02'); +insert into t2 values ('1 - 01', '2 - 01'); +insert into t2 values ('1 - 01', '2 - 01'); +insert into t2 values ('1 - 02', '2 - 02'); +insert into t2 values ('1 - 02', '2 - 02'); +insert into t2 values ('1 - 03', '2 - 03'); +insert into t3 values ('1 - 01', '2 - 01'); +insert into t3 values ('1 - 02', '2 - 02'); +insert into t3 values ('1 - 03', '2 - 03'); +insert into t3 values ('1 - 04', '2 - 04'); +create table t1i (a1 char(8), a2 char(8)); +create table t2i (b1 char(8), b2 char(8)); +create table t3i (c1 char(8), c2 char(8)); +create index it1i1 on t1i (a1); +create index it1i2 on t1i (a2); +create index it1i3 on t1i (a1, a2); +create index it2i1 on t2i (b1); +create index it2i2 on t2i (b2); +create index it2i3 on t2i (b1, b2); +create index it3i1 on t3i (c1); +create index it3i2 on t3i (c2); +create index it3i3 on t3i (c1, c2); +insert into t1i select * from t1; +insert into t2i select * from t2; +insert into t3i select * from t3; +set @@optimizer_switch='materialization=on,in_to_exists=off,firstmatch=off'; +/****************************************************************************** +* Simple tests. +******************************************************************************/ +# non-indexed nullable fields +explain extended +select * from t1 where a1 in (select b1 from t2 where b1 > '0'); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t1 ALL NULL NULL NULL NULL 3 100.00 +1 PRIMARY <subquery2> eq_ref distinct_key distinct_key 9 func 1 100.00 +2 SUBQUERY t2 ALL NULL NULL NULL NULL 5 100.00 Using where +Warnings: +Note 1003 select `test`.`t1`.`a1` AS `a1`,`test`.`t1`.`a2` AS `a2` from `test`.`t1` semi join (`test`.`t2`) where ((`test`.`t2`.`b1` > '0')) +select * from t1 where a1 in (select b1 from t2 where b1 > '0'); +a1 a2 +1 - 01 2 - 01 +1 - 02 2 - 02 +explain extended +select * from t1 where a1 in (select b1 from t2 where b1 > '0' group by b1); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t1 ALL NULL NULL NULL NULL 3 100.00 Using where +1 PRIMARY <subquery2> eq_ref distinct_key distinct_key 9 test.t1.a1 1 100.00 +2 SUBQUERY t2 ALL NULL NULL NULL NULL 5 100.00 Using where; Using temporary +Warnings: +Note 1003 select `test`.`t1`.`a1` AS `a1`,`test`.`t1`.`a2` AS `a2` from <materialize> (select `test`.`t2`.`b1` from `test`.`t2` where (`test`.`t2`.`b1` > '0') group by `test`.`t2`.`b1`) join `test`.`t1` where (`<subquery2>`.`b1` = `test`.`t1`.`a1`) +select * from t1 where a1 in (select b1 from t2 where b1 > '0' group by b1); +a1 a2 +1 - 01 2 - 01 +1 - 02 2 - 02 +explain extended +select * from t1 where (a1, a2) in (select b1, b2 from t2 where b1 > '0' group by b1, b2); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t1 ALL NULL NULL NULL NULL 3 100.00 Using where +1 PRIMARY <subquery2> eq_ref distinct_key distinct_key 18 test.t1.a1,test.t1.a2 1 100.00 +2 SUBQUERY t2 ALL NULL NULL NULL NULL 5 100.00 Using where; Using temporary +Warnings: +Note 1003 select `test`.`t1`.`a1` AS `a1`,`test`.`t1`.`a2` AS `a2` from <materialize> (select `test`.`t2`.`b1`,`test`.`t2`.`b2` from `test`.`t2` where (`test`.`t2`.`b1` > '0') group by `test`.`t2`.`b1`,`test`.`t2`.`b2`) join `test`.`t1` where ((`<subquery2>`.`b2` = `test`.`t1`.`a2`) and (`<subquery2>`.`b1` = `test`.`t1`.`a1`)) +select * from t1 where (a1, a2) in (select b1, b2 from t2 where b1 > '0' group by b1, b2); +a1 a2 +1 - 01 2 - 01 +1 - 02 2 - 02 +explain extended +select * from t1 where (a1, a2) in (select b1, min(b2) from t2 where b1 > '0' group by b1); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t1 ALL NULL NULL NULL NULL 3 100.00 Using where +1 PRIMARY <subquery2> eq_ref distinct_key distinct_key 18 test.t1.a1,test.t1.a2 1 100.00 +2 SUBQUERY t2 ALL NULL NULL NULL NULL 5 100.00 Using where; Using temporary +Warnings: +Note 1003 select `test`.`t1`.`a1` AS `a1`,`test`.`t1`.`a2` AS `a2` from <materialize> (select `test`.`t2`.`b1`,min(`test`.`t2`.`b2`) from `test`.`t2` where (`test`.`t2`.`b1` > '0') group by `test`.`t2`.`b1`) join `test`.`t1` where ((`<subquery2>`.`min(b2)` = `test`.`t1`.`a2`) and (`<subquery2>`.`b1` = `test`.`t1`.`a1`)) +select * from t1 where (a1, a2) in (select b1, min(b2) from t2 where b1 > '0' group by b1); +a1 a2 +1 - 01 2 - 01 +1 - 02 2 - 02 +explain extended +select * from t1i where a1 in (select b1 from t2i where b1 > '0'); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t2i index it2i1,it2i3 it2i1 # NULL 5 40.00 Using where; Using index; LooseScan +1 PRIMARY t1i ref _it1_idx _it1_idx # _ref_ 1 100.00 +Warnings: +Note 1003 select `test`.`t1i`.`a1` AS `a1`,`test`.`t1i`.`a2` AS `a2` from `test`.`t1i` semi join (`test`.`t2i`) where ((`test`.`t1i`.`a1` = `test`.`t2i`.`b1`) and (`test`.`t2i`.`b1` > '0')) +select * from t1i where a1 in (select b1 from t2i where b1 > '0'); +a1 a2 +1 - 01 2 - 01 +1 - 02 2 - 02 +explain extended +select * from t1i where a1 in (select b1 from t2i where b1 > '0' group by b1); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY <subquery2> ALL distinct_key # NULL # 3 100.00 # +1 PRIMARY t1i ref it1i1,it1i3 # 9 # 1 100.00 # +2 SUBQUERY t2i range it2i1,it2i3 # 9 # 3 100.00 # +Warnings: +Note 1003 select `test`.`t1i`.`a1` AS `a1`,`test`.`t1i`.`a2` AS `a2` from <materialize> (select `test`.`t2i`.`b1` from `test`.`t2i` where (`test`.`t2i`.`b1` > '0') group by `test`.`t2i`.`b1`) join `test`.`t1i` where (`test`.`t1i`.`a1` = `<subquery2>`.`b1`) +select * from t1i where a1 in (select b1 from t2i where b1 > '0' group by b1); +a1 a2 +1 - 01 2 - 01 +1 - 02 2 - 02 +explain extended +select * from t1i where (a1, a2) in (select b1, b2 from t2i where b1 > '0'); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t2i index it2i1,it2i2,it2i3 it2i3 # NULL 5 40.00 Using where; Using index; LooseScan +1 PRIMARY t1i ref _it1_idx _it1_idx # _ref_ 1 100.00 +Warnings: +Note 1003 select `test`.`t1i`.`a1` AS `a1`,`test`.`t1i`.`a2` AS `a2` from `test`.`t1i` semi join (`test`.`t2i`) where ((`test`.`t1i`.`a2` = `test`.`t2i`.`b2`) and (`test`.`t1i`.`a1` = `test`.`t2i`.`b1`) and (`test`.`t2i`.`b1` > '0')) +select * from t1i where (a1, a2) in (select b1, b2 from t2i where b1 > '0'); +a1 a2 +1 - 01 2 - 01 +1 - 02 2 - 02 +explain extended +select * from t1i where (a1, a2) in (select b1, b2 from t2i where b1 > '0' group by b1, b2); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t1i index it1i1,it1i2,it1i3 # # # 3 100.00 # +1 PRIMARY <subquery2> eq_ref distinct_key # # # 1 100.00 # +2 SUBQUERY t2i range it2i1,it2i3 # # # 3 100.00 # +Warnings: +Note 1003 select `test`.`t1i`.`a1` AS `a1`,`test`.`t1i`.`a2` AS `a2` from <materialize> (select `test`.`t2i`.`b1`,`test`.`t2i`.`b2` from `test`.`t2i` where (`test`.`t2i`.`b1` > '0') group by `test`.`t2i`.`b1`,`test`.`t2i`.`b2`) join `test`.`t1i` where ((`<subquery2>`.`b2` = `test`.`t1i`.`a2`) and (`<subquery2>`.`b1` = `test`.`t1i`.`a1`)) +select * from t1i where (a1, a2) in (select b1, b2 from t2i where b1 > '0' group by b1, b2); +a1 a2 +1 - 01 2 - 01 +1 - 02 2 - 02 +explain extended +select * from t1i where (a1, a2) in (select b1, min(b2) from t2i where b1 > '0' group by b1); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t1i index it1i1,it1i2,it1i3 # # # 3 100.00 # +1 PRIMARY <subquery2> eq_ref distinct_key # # # 1 100.00 # +2 SUBQUERY t2i range it2i1,it2i3 # # # 3 100.00 # +Warnings: +Note 1003 select `test`.`t1i`.`a1` AS `a1`,`test`.`t1i`.`a2` AS `a2` from <materialize> (select `test`.`t2i`.`b1`,min(`test`.`t2i`.`b2`) from `test`.`t2i` where (`test`.`t2i`.`b1` > '0') group by `test`.`t2i`.`b1`) join `test`.`t1i` where ((`<subquery2>`.`min(b2)` = `test`.`t1i`.`a2`) and (`<subquery2>`.`b1` = `test`.`t1i`.`a1`)) +select * from t1i where (a1, a2) in (select b1, min(b2) from t2i where b1 > '0' group by b1); +a1 a2 +1 - 01 2 - 01 +1 - 02 2 - 02 +explain extended +select * from t1 where (a1, a2) in (select b1, max(b2) from t2i group by b1); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t1 ALL NULL NULL NULL NULL 3 100.00 Using where +1 PRIMARY <subquery2> eq_ref distinct_key distinct_key 18 test.t1.a1,test.t1.a2 1 100.00 +2 SUBQUERY t2i range NULL it2i3 9 NULL 3 100.00 Using index for group-by +Warnings: +Note 1003 select `test`.`t1`.`a1` AS `a1`,`test`.`t1`.`a2` AS `a2` from <materialize> (select `test`.`t2i`.`b1`,max(`test`.`t2i`.`b2`) from `test`.`t2i` group by `test`.`t2i`.`b1`) join `test`.`t1` where ((`<subquery2>`.`max(b2)` = `test`.`t1`.`a2`) and (`<subquery2>`.`b1` = `test`.`t1`.`a1`)) +select * from t1 where (a1, a2) in (select b1, max(b2) from t2i group by b1); +a1 a2 +1 - 01 2 - 01 +1 - 02 2 - 02 +prepare st1 from "explain select * from t1 where (a1, a2) in (select b1, max(b2) from t2i group by b1)"; +execute st1; +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY t1 ALL NULL NULL NULL NULL 3 Using where +1 PRIMARY <subquery2> eq_ref distinct_key distinct_key 18 test.t1.a1,test.t1.a2 1 +2 SUBQUERY t2i range NULL it2i3 9 NULL 3 Using index for group-by +execute st1; +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY t1 ALL NULL NULL NULL NULL 3 Using where +1 PRIMARY <subquery2> eq_ref distinct_key distinct_key 18 test.t1.a1,test.t1.a2 1 +2 SUBQUERY t2i range NULL it2i3 9 NULL 3 Using index for group-by +prepare st2 from "select * from t1 where (a1, a2) in (select b1, max(b2) from t2i group by b1)"; +execute st2; +a1 a2 +1 - 01 2 - 01 +1 - 02 2 - 02 +execute st2; +a1 a2 +1 - 01 2 - 01 +1 - 02 2 - 02 +explain extended +select * from t1 where (a1, a2) in (select b1, min(b2) from t2i where b1 > '0' group by b1); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t1 ALL NULL NULL NULL NULL 3 100.00 Using where +1 PRIMARY <subquery2> eq_ref distinct_key distinct_key 18 test.t1.a1,test.t1.a2 1 100.00 +2 SUBQUERY t2i range it2i1,it2i3 it2i3 18 NULL 3 100.00 Using where; Using index for group-by +Warnings: +Note 1003 select `test`.`t1`.`a1` AS `a1`,`test`.`t1`.`a2` AS `a2` from <materialize> (select `test`.`t2i`.`b1`,min(`test`.`t2i`.`b2`) from `test`.`t2i` where (`test`.`t2i`.`b1` > '0') group by `test`.`t2i`.`b1`) join `test`.`t1` where ((`<subquery2>`.`min(b2)` = `test`.`t1`.`a2`) and (`<subquery2>`.`b1` = `test`.`t1`.`a1`)) +select * from t1 where (a1, a2) in (select b1, min(b2) from t2i where b1 > '0' group by b1); +a1 a2 +1 - 01 2 - 01 +1 - 02 2 - 02 +select * from t1 where (a1, a2) in (select b1, min(b2) from t2i limit 1,1); +ERROR 42000: This version of MySQL doesn't yet support 'LIMIT & IN/ALL/ANY/SOME subquery' +set @save_optimizer_switch=@@optimizer_switch; +set @@optimizer_switch='default,semijoin=off'; +prepare st1 from +"select * from t1 where (a1, a2) in (select b1, min(b2) from t2 where b1 > '0' group by b1)"; +set @@optimizer_switch='default,materialization=off'; +execute st1; +a1 a2 +1 - 01 2 - 01 +1 - 02 2 - 02 +set @@optimizer_switch='default,semijoin=off'; +execute st1; +a1 a2 +1 - 01 2 - 01 +1 - 02 2 - 02 +set @@optimizer_switch='default,materialization=off'; +prepare st1 from +"select * from t1 where (a1, a2) in (select b1, min(b2) from t2 where b1 > '0' group by b1)"; +set @@optimizer_switch='default,semijoin=off'; +execute st1; +a1 a2 +1 - 01 2 - 01 +1 - 02 2 - 02 +set @@optimizer_switch='default,materialization=off'; +execute st1; +a1 a2 +1 - 01 2 - 01 +1 - 02 2 - 02 +set @@optimizer_switch=@save_optimizer_switch; +explain extended +select * from t1 where (a1, a2) in (select b1, b2 from t2 order by b1, b2); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t1 ALL NULL NULL NULL NULL 3 100.00 Using where +1 PRIMARY <subquery2> eq_ref distinct_key distinct_key 18 test.t1.a1,test.t1.a2 1 100.00 +2 SUBQUERY t2 ALL NULL NULL NULL NULL 5 100.00 +Warnings: +Note 1003 select `test`.`t1`.`a1` AS `a1`,`test`.`t1`.`a2` AS `a2` from <materialize> (select `test`.`t2`.`b1`,`test`.`t2`.`b2` from `test`.`t2` order by `test`.`t2`.`b1`,`test`.`t2`.`b2`) join `test`.`t1` where ((`<subquery2>`.`b2` = `test`.`t1`.`a2`) and (`<subquery2>`.`b1` = `test`.`t1`.`a1`)) +select * from t1 where (a1, a2) in (select b1, b2 from t2 order by b1, b2); +a1 a2 +1 - 01 2 - 01 +1 - 02 2 - 02 +explain extended +select * from t1i where (a1, a2) in (select b1, b2 from t2i order by b1, b2); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t1i index it1i1,it1i2,it1i3 it1i3 18 NULL 3 100.00 Using where; Using index +1 PRIMARY <subquery2> eq_ref distinct_key distinct_key 18 test.t1i.a1,test.t1i.a2 1 100.00 +2 SUBQUERY t2i index NULL it2i3 18 NULL 5 100.00 Using index +Warnings: +Note 1003 select `test`.`t1i`.`a1` AS `a1`,`test`.`t1i`.`a2` AS `a2` from <materialize> (select `test`.`t2i`.`b1`,`test`.`t2i`.`b2` from `test`.`t2i` order by `test`.`t2i`.`b1`,`test`.`t2i`.`b2`) join `test`.`t1i` where ((`<subquery2>`.`b2` = `test`.`t1i`.`a2`) and (`<subquery2>`.`b1` = `test`.`t1i`.`a1`)) +select * from t1i where (a1, a2) in (select b1, b2 from t2i order by b1, b2); +a1 a2 +1 - 01 2 - 01 +1 - 02 2 - 02 +/****************************************************************************** +* Views, UNIONs, several levels of nesting. +******************************************************************************/ +# materialize the result of subquery over temp-table view +create algorithm=merge view v1 as +select b1, c2 from t2, t3 where b2 > c2; +create algorithm=merge view v2 as +select b1, c2 from t2, t3 group by b2, c2; +Warnings: +Warning 1354 View merge algorithm can't be used here for now (assumed undefined algorithm) +create algorithm=temptable view v1m as +select b1, c2 from t2, t3 where b2 > c2; +create algorithm=temptable view v2m as +select b1, c2 from t2, t3 group by b2, c2; +select * from v1 where (c2, b1) in (select c2, b1 from v2 where b1 is not null); +b1 c2 +1 - 02 2 - 01 +1 - 02 2 - 01 +1 - 03 2 - 01 +1 - 03 2 - 02 +select * from v1 where (c2, b1) in (select distinct c2, b1 from v2 where b1 is not null); +b1 c2 +1 - 02 2 - 01 +1 - 02 2 - 01 +1 - 03 2 - 01 +1 - 03 2 - 02 +select * from v1m where (c2, b1) in (select c2, b1 from v2m where b1 is not null); +b1 c2 +1 - 02 2 - 01 +1 - 02 2 - 01 +1 - 03 2 - 01 +1 - 03 2 - 02 +select * from v1m where (c2, b1) in (select distinct c2, b1 from v2m where b1 is not null); +b1 c2 +1 - 02 2 - 01 +1 - 02 2 - 01 +1 - 03 2 - 01 +1 - 03 2 - 02 +drop view v1, v2, v1m, v2m; +explain extended +select * from t1 +where (a1, a2) in (select b1, b2 from t2 where b1 > '0') and +(a1, a2) in (select c1, c2 from t3 +where (c1, c2) in (select b1, b2 from t2i where b2 > '0')); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t1 ALL NULL NULL NULL NULL 3 100.00 +1 PRIMARY <subquery2> eq_ref distinct_key distinct_key 18 func,func 1 100.00 +1 PRIMARY <subquery3> eq_ref distinct_key distinct_key 18 func,func 1 100.00 +2 SUBQUERY t2 ALL NULL NULL NULL NULL 5 100.00 Using where +3 SUBQUERY t3 ALL NULL NULL NULL NULL 4 100.00 Using where +3 SUBQUERY t2i index it2i1,it2i2,it2i3 it2i3 18 NULL 5 80.00 Using where; Using index; Using join buffer (flat, BNL join) +Warnings: +Note 1003 select `test`.`t1`.`a1` AS `a1`,`test`.`t1`.`a2` AS `a2` from `test`.`t1` semi join (`test`.`t2`) semi join (`test`.`t2i` join `test`.`t3`) where ((`test`.`t2i`.`b2` = `test`.`t3`.`c2`) and (`test`.`t2i`.`b1` = `test`.`t3`.`c1`) and (`test`.`t2`.`b1` > '0') and (`test`.`t3`.`c2` > '0')) +select * from t1 +where (a1, a2) in (select b1, b2 from t2 where b1 > '0') and +(a1, a2) in (select c1, c2 from t3 +where (c1, c2) in (select b1, b2 from t2i where b2 > '0')); +a1 a2 +1 - 01 2 - 01 +1 - 02 2 - 02 +explain extended +select * from t1i +where (a1, a2) in (select b1, b2 from t2i where b1 > '0') and +(a1, a2) in (select c1, c2 from t3i +where (c1, c2) in (select b1, b2 from t2i where b2 > '0')); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t1i index it1i1,it1i2,it1i3 # # # 3 100.00 # +1 PRIMARY <subquery2> eq_ref distinct_key # # # 1 100.00 # +1 PRIMARY <subquery3> eq_ref distinct_key # # # 1 100.00 # +2 SUBQUERY t2i index it2i1,it2i2,it2i3 # # # 5 100.00 # +3 SUBQUERY t2i index it2i1,it2i2,it2i3 # # # 5 100.00 # +3 SUBQUERY t3i index it3i1,it3i2,it3i3 # # # 4 75.00 # +Warnings: +Note 1003 select `test`.`t1i`.`a1` AS `a1`,`test`.`t1i`.`a2` AS `a2` from `test`.`t1i` semi join (`test`.`t2i`) semi join (`test`.`t2i` join `test`.`t3i`) where ((`test`.`t3i`.`c2` = `test`.`t2i`.`b2`) and (`test`.`t3i`.`c1` = `test`.`t2i`.`b1`) and (`test`.`t2i`.`b1` > '0') and (`test`.`t2i`.`b2` > '0')) +select * from t1i +where (a1, a2) in (select b1, b2 from t2i where b1 > '0') and +(a1, a2) in (select c1, c2 from t3i +where (c1, c2) in (select b1, b2 from t2i where b2 > '0')); +a1 a2 +1 - 01 2 - 01 +1 - 02 2 - 02 +explain extended +select * from t1 +where (a1, a2) in (select b1, b2 from t2 +where b2 in (select c2 from t3 where c2 LIKE '%02') or +b2 in (select c2 from t3 where c2 LIKE '%03')) and +(a1, a2) in (select c1, c2 from t3 +where (c1, c2) in (select b1, b2 from t2i where b2 > '0')); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t1 ALL NULL NULL NULL NULL 3 100.00 +1 PRIMARY <subquery2> eq_ref distinct_key distinct_key 18 func,func 1 100.00 +1 PRIMARY <subquery5> eq_ref distinct_key distinct_key 18 func,func 1 100.00 +2 SUBQUERY t2 ALL NULL NULL NULL NULL 5 100.00 Using where +5 SUBQUERY t3 ALL NULL NULL NULL NULL 4 100.00 Using where +5 SUBQUERY t2i index it2i1,it2i2,it2i3 it2i3 18 NULL 5 80.00 Using where; Using index; Using join buffer (flat, BNL join) +4 SUBQUERY t3 ALL NULL NULL NULL NULL 4 100.00 Using where +3 SUBQUERY t3 ALL NULL NULL NULL NULL 4 100.00 Using where +Warnings: +Note 1003 select `test`.`t1`.`a1` AS `a1`,`test`.`t1`.`a2` AS `a2` from `test`.`t1` semi join (`test`.`t2`) semi join (`test`.`t2i` join `test`.`t3`) where ((`test`.`t2i`.`b2` = `test`.`t3`.`c2`) and (`test`.`t2i`.`b1` = `test`.`t3`.`c1`) and (<expr_cache><`test`.`t2`.`b2`>(<in_optimizer>(`test`.`t2`.`b2`,`test`.`t2`.`b2` in ( <materialize> (select `test`.`t3`.`c2` from `test`.`t3` where (`test`.`t3`.`c2` like '%02') ), <primary_index_lookup>(`test`.`t2`.`b2` in <temporary table> on distinct_key where ((`test`.`t2`.`b2` = `<subquery3>`.`c2`)))))) or <expr_cache><`test`.`t2`.`b2`>(<in_optimizer>(`test`.`t2`.`b2`,`test`.`t2`.`b2` in ( <materialize> (select `test`.`t3`.`c2` from `test`.`t3` where (`test`.`t3`.`c2` like '%03') ), <primary_index_lookup>(`test`.`t2`.`b2` in <temporary table> on distinct_key where ((`test`.`t2`.`b2` = `<subquery4>`.`c2`))))))) and (`test`.`t3`.`c2` > '0')) +select * from t1 +where (a1, a2) in (select b1, b2 from t2 +where b2 in (select c2 from t3 where c2 LIKE '%02') or +b2 in (select c2 from t3 where c2 LIKE '%03')) and +(a1, a2) in (select c1, c2 from t3 +where (c1, c2) in (select b1, b2 from t2i where b2 > '0')); +a1 a2 +1 - 02 2 - 02 +explain extended +select * from t1 +where (a1, a2) in (select b1, b2 from t2 +where b2 in (select c2 from t3 t3a where c1 = a1) or +b2 in (select c2 from t3 t3b where c2 LIKE '%03')) and +(a1, a2) in (select c1, c2 from t3 t3c +where (c1, c2) in (select b1, b2 from t2i where b2 > '0')); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t1 ALL NULL NULL NULL NULL 3 100.00 Start temporary +1 PRIMARY t2 ALL NULL NULL NULL NULL 5 100.00 Using where; End temporary; Using join buffer (flat, BNL join) +1 PRIMARY <subquery5> eq_ref distinct_key distinct_key 18 func,func 1 100.00 +5 SUBQUERY t3c ALL NULL NULL NULL NULL 4 100.00 Using where +5 SUBQUERY t2i index it2i1,it2i2,it2i3 it2i3 18 NULL 5 80.00 Using where; Using index; Using join buffer (flat, BNL join) +4 SUBQUERY t3b ALL NULL NULL NULL NULL 4 100.00 Using where +3 DEPENDENT SUBQUERY t3a ALL NULL NULL NULL NULL 4 100.00 Using where +Warnings: +Note 1276 Field or reference 'test.t1.a1' of SELECT #3 was resolved in SELECT #1 +Note 1003 select `test`.`t1`.`a1` AS `a1`,`test`.`t1`.`a2` AS `a2` from `test`.`t1` semi join (`test`.`t2`) semi join (`test`.`t2i` join `test`.`t3` `t3c`) where ((`test`.`t2`.`b2` = `test`.`t1`.`a2`) and (`test`.`t2i`.`b2` = `test`.`t3c`.`c2`) and (`test`.`t2`.`b1` = `test`.`t1`.`a1`) and (`test`.`t2i`.`b1` = `test`.`t3c`.`c1`) and (<expr_cache><`test`.`t2`.`b2`,`test`.`t1`.`a1`>(<in_optimizer>(`test`.`t2`.`b2`,<exists>(select `test`.`t3a`.`c2` from `test`.`t3` `t3a` where ((`test`.`t3a`.`c1` = `test`.`t1`.`a1`) and (<cache>(`test`.`t2`.`b2`) = `test`.`t3a`.`c2`))))) or <expr_cache><`test`.`t2`.`b2`>(<in_optimizer>(`test`.`t2`.`b2`,`test`.`t2`.`b2` in ( <materialize> (select `test`.`t3b`.`c2` from `test`.`t3` `t3b` where (`test`.`t3b`.`c2` like '%03') ), <primary_index_lookup>(`test`.`t2`.`b2` in <temporary table> on distinct_key where ((`test`.`t2`.`b2` = `<subquery4>`.`c2`))))))) and (`test`.`t3c`.`c2` > '0')) +select * from t1 +where (a1, a2) in (select b1, b2 from t2 +where b2 in (select c2 from t3 t3a where c1 = a1) or +b2 in (select c2 from t3 t3b where c2 LIKE '%03')) and +(a1, a2) in (select c1, c2 from t3 t3c +where (c1, c2) in (select b1, b2 from t2i where b2 > '0')); +a1 a2 +1 - 01 2 - 01 +1 - 02 2 - 02 +explain extended +(select * from t1 +where (a1, a2) in (select b1, b2 from t2 +where b2 in (select c2 from t3 where c2 LIKE '%02') or +b2 in (select c2 from t3 where c2 LIKE '%03') +group by b1, b2) and +(a1, a2) in (select c1, c2 from t3 +where (c1, c2) in (select b1, b2 from t2i where b2 > '0'))) +UNION +(select * from t1i +where (a1, a2) in (select b1, b2 from t2i where b1 > '0') and +(a1, a2) in (select c1, c2 from t3i +where (c1, c2) in (select b1, b2 from t2i where b2 > '0'))); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY <subquery2> ALL distinct_key # # # 5 0.00 # +1 PRIMARY t3 ALL NULL # # # 4 100.00 # +1 PRIMARY t1 ALL NULL # # # 3 100.00 # +1 PRIMARY t2i ref it2i1,it2i2,it2i3 # # # 2 100.00 # +2 SUBQUERY t2 ALL NULL # # # 5 100.00 # +4 SUBQUERY t3 ALL NULL # # # 4 100.00 # +3 SUBQUERY t3 ALL NULL # # # 4 100.00 # +7 UNION t1i index it1i1,it1i2,it1i3 # # # 3 100.00 # +7 UNION <subquery8> eq_ref distinct_key # # # 1 100.00 # +7 UNION <subquery9> eq_ref distinct_key # # # 1 100.00 # +8 SUBQUERY t2i index it2i1,it2i2,it2i3 # # # 5 100.00 # +9 SUBQUERY t2i index it2i1,it2i2,it2i3 # # # 5 100.00 # +9 SUBQUERY t3i index it3i1,it3i2,it3i3 # # # 4 75.00 # +NULL UNION RESULT <union1,7> ALL NULL # # # NULL NULL # +Warnings: +Note 1003 (select `test`.`t1`.`a1` AS `a1`,`test`.`t1`.`a2` AS `a2` from <materialize> (select `test`.`t2`.`b1`,`test`.`t2`.`b2` from `test`.`t2` where (<expr_cache><`test`.`t2`.`b2`>(<in_optimizer>(`test`.`t2`.`b2`,`test`.`t2`.`b2` in ( <materialize> (select `test`.`t3`.`c2` from `test`.`t3` where (`test`.`t3`.`c2` like '%02') ), <primary_index_lookup>(`test`.`t2`.`b2` in <temporary table> on distinct_key where ((`test`.`t2`.`b2` = `<subquery3>`.`c2`)))))) or <expr_cache><`test`.`t2`.`b2`>(<in_optimizer>(`test`.`t2`.`b2`,`test`.`t2`.`b2` in ( <materialize> (select `test`.`t3`.`c2` from `test`.`t3` where (`test`.`t3`.`c2` like '%03') ), <primary_index_lookup>(`test`.`t2`.`b2` in <temporary table> on distinct_key where ((`test`.`t2`.`b2` = `<subquery4>`.`c2`))))))) group by `test`.`t2`.`b1`,`test`.`t2`.`b2`) semi join (`test`.`t2i` join `test`.`t3`) join `test`.`t1` where ((`test`.`t3`.`c2` = `<subquery2>`.`b2`) and (`test`.`t1`.`a2` = `<subquery2>`.`b2`) and (`test`.`t2i`.`b2` = `<subquery2>`.`b2`) and (`test`.`t3`.`c1` = `<subquery2>`.`b1`) and (`test`.`t1`.`a1` = `<subquery2>`.`b1`) and (`test`.`t2i`.`b1` = `<subquery2>`.`b1`) and (`<subquery2>`.`b2` > '0'))) union (select `test`.`t1i`.`a1` AS `a1`,`test`.`t1i`.`a2` AS `a2` from `test`.`t1i` semi join (`test`.`t2i`) semi join (`test`.`t2i` join `test`.`t3i`) where ((`test`.`t3i`.`c2` = `test`.`t2i`.`b2`) and (`test`.`t3i`.`c1` = `test`.`t2i`.`b1`) and (`test`.`t2i`.`b1` > '0') and (`test`.`t2i`.`b2` > '0'))) +(select * from t1 +where (a1, a2) in (select b1, b2 from t2 +where b2 in (select c2 from t3 where c2 LIKE '%02') or +b2 in (select c2 from t3 where c2 LIKE '%03') +group by b1, b2) and +(a1, a2) in (select c1, c2 from t3 +where (c1, c2) in (select b1, b2 from t2i where b2 > '0'))) +UNION +(select * from t1i +where (a1, a2) in (select b1, b2 from t2i where b1 > '0') and +(a1, a2) in (select c1, c2 from t3i +where (c1, c2) in (select b1, b2 from t2i where b2 > '0'))); +a1 a2 +1 - 02 2 - 02 +1 - 01 2 - 01 +explain extended +select * from t1 +where (a1, a2) in (select * from t1 where a1 > '0' UNION select * from t2 where b1 < '9') and +(a1, a2) in (select c1, c2 from t3 +where (c1, c2) in (select b1, b2 from t2i where b2 > '0')); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t1 ALL NULL NULL NULL NULL 3 100.00 Using where +1 PRIMARY <subquery4> eq_ref distinct_key distinct_key 18 func,func 1 100.00 +4 SUBQUERY t3 ALL NULL NULL NULL NULL 4 100.00 Using where +4 SUBQUERY t2i index it2i1,it2i2,it2i3 it2i3 18 NULL 5 80.00 Using where; Using index; Using join buffer (flat, BNL join) +2 DEPENDENT SUBQUERY t1 ALL NULL NULL NULL NULL 3 100.00 Using where +3 DEPENDENT UNION t2 ALL NULL NULL NULL NULL 5 100.00 Using where +NULL UNION RESULT <union2,3> ALL NULL NULL NULL NULL NULL NULL +Warnings: +Note 1003 select `test`.`t1`.`a1` AS `a1`,`test`.`t1`.`a2` AS `a2` from `test`.`t1` semi join (`test`.`t2i` join `test`.`t3`) where ((`test`.`t2i`.`b2` = `test`.`t3`.`c2`) and (`test`.`t2i`.`b1` = `test`.`t3`.`c1`) and <expr_cache><`test`.`t1`.`a2`,`test`.`t1`.`a1`>(<in_optimizer>((`test`.`t1`.`a1`,`test`.`t1`.`a2`),<exists>(select `test`.`t1`.`a1`,`test`.`t1`.`a2` from `test`.`t1` where ((`test`.`t1`.`a1` > '0') and (<cache>(`test`.`t1`.`a1`) = `test`.`t1`.`a1`) and (<cache>(`test`.`t1`.`a2`) = `test`.`t1`.`a2`)) union select `test`.`t2`.`b1`,`test`.`t2`.`b2` from `test`.`t2` where ((`test`.`t2`.`b1` < '9') and (<cache>(`test`.`t1`.`a1`) = `test`.`t2`.`b1`) and (<cache>(`test`.`t1`.`a2`) = `test`.`t2`.`b2`))))) and (`test`.`t3`.`c2` > '0')) +select * from t1 +where (a1, a2) in (select * from t1 where a1 > '0' UNION select * from t2 where b1 < '9') and +(a1, a2) in (select c1, c2 from t3 +where (c1, c2) in (select b1, b2 from t2i where b2 > '0')); +a1 a2 +1 - 01 2 - 01 +1 - 02 2 - 02 +explain extended +select * from t1, t3 +where (a1, a2) in (select * from t1 where a1 > '0' UNION select * from t2 where b1 < '9') and +(c1, c2) in (select c1, c2 from t3 +where (c1, c2) in (select b1, b2 from t2i where b2 > '0')) and +a1 = c1; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t1 ALL NULL NULL NULL NULL 3 100.00 Using where +1 PRIMARY t3 ALL NULL NULL NULL NULL 4 100.00 Using where; Using join buffer (flat, BNL join) +1 PRIMARY <subquery4> eq_ref distinct_key distinct_key 18 func,func 1 100.00 +4 SUBQUERY t3 ALL NULL NULL NULL NULL 4 100.00 Using where +4 SUBQUERY t2i index it2i1,it2i2,it2i3 it2i3 18 NULL 5 80.00 Using where; Using index; Using join buffer (flat, BNL join) +2 DEPENDENT SUBQUERY t1 ALL NULL NULL NULL NULL 3 100.00 Using where +3 DEPENDENT UNION t2 ALL NULL NULL NULL NULL 5 100.00 Using where +NULL UNION RESULT <union2,3> ALL NULL NULL NULL NULL NULL NULL +Warnings: +Note 1003 select `test`.`t1`.`a1` AS `a1`,`test`.`t1`.`a2` AS `a2`,`test`.`t3`.`c1` AS `c1`,`test`.`t3`.`c2` AS `c2` from `test`.`t1` semi join (`test`.`t2i` join `test`.`t3`) join `test`.`t3` where ((`test`.`t2i`.`b2` = `test`.`t3`.`c2`) and (`test`.`t3`.`c1` = `test`.`t1`.`a1`) and (`test`.`t2i`.`b1` = `test`.`t3`.`c1`) and <expr_cache><`test`.`t1`.`a2`,`test`.`t1`.`a1`>(<in_optimizer>((`test`.`t1`.`a1`,`test`.`t1`.`a2`),<exists>(select `test`.`t1`.`a1`,`test`.`t1`.`a2` from `test`.`t1` where ((`test`.`t1`.`a1` > '0') and (<cache>(`test`.`t1`.`a1`) = `test`.`t1`.`a1`) and (<cache>(`test`.`t1`.`a2`) = `test`.`t1`.`a2`)) union select `test`.`t2`.`b1`,`test`.`t2`.`b2` from `test`.`t2` where ((`test`.`t2`.`b1` < '9') and (<cache>(`test`.`t1`.`a1`) = `test`.`t2`.`b1`) and (<cache>(`test`.`t1`.`a2`) = `test`.`t2`.`b2`))))) and (`test`.`t3`.`c2` > '0')) +select * from t1, t3 +where (a1, a2) in (select * from t1 where a1 > '0' UNION select * from t2 where b1 < '9') and +(c1, c2) in (select c1, c2 from t3 +where (c1, c2) in (select b1, b2 from t2i where b2 > '0')) and +a1 = c1; +a1 a2 c1 c2 +1 - 01 2 - 01 1 - 01 2 - 01 +1 - 02 2 - 02 1 - 02 2 - 02 +/****************************************************************************** +* Negative tests, where materialization should not be applied. +******************************************************************************/ +# UNION in a subquery +explain extended +select * from t3 +where c1 in (select a1 from t1 where a1 > '0' UNION select b1 from t2 where b1 < '9'); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t3 ALL NULL NULL NULL NULL 4 100.00 Using where +2 DEPENDENT SUBQUERY t1 ALL NULL NULL NULL NULL 3 100.00 Using where +3 DEPENDENT UNION t2 ALL NULL NULL NULL NULL 5 100.00 Using where +NULL UNION RESULT <union2,3> ALL NULL NULL NULL NULL NULL NULL +Warnings: +Note 1003 select `test`.`t3`.`c1` AS `c1`,`test`.`t3`.`c2` AS `c2` from `test`.`t3` where <expr_cache><`test`.`t3`.`c1`>(<in_optimizer>(`test`.`t3`.`c1`,<exists>(select `test`.`t1`.`a1` from `test`.`t1` where ((`test`.`t1`.`a1` > '0') and (<cache>(`test`.`t3`.`c1`) = `test`.`t1`.`a1`)) union select `test`.`t2`.`b1` from `test`.`t2` where ((`test`.`t2`.`b1` < '9') and (<cache>(`test`.`t3`.`c1`) = `test`.`t2`.`b1`))))) +select * from t3 +where c1 in (select a1 from t1 where a1 > '0' UNION select b1 from t2 where b1 < '9'); +c1 c2 +1 - 01 2 - 01 +1 - 02 2 - 02 +1 - 03 2 - 03 +explain extended +select * from t1 +where (a1, a2) in (select b1, b2 from t2 +where b2 in (select c2 from t3 t3a where c1 = a1) or +b2 in (select c2 from t3 t3b where c2 LIKE '%03')) and +(a1, a2) in (select c1, c2 from t3 t3c +where (c1, c2) in (select b1, b2 from t2i where b2 > '0' or b2 = a2)); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t1 ALL NULL NULL NULL NULL 3 100.00 Using where; Start temporary +1 PRIMARY t2i ref it2i1,it2i2,it2i3 it2i3 18 test.t1.a1,test.t1.a2 2 100.00 Using index +1 PRIMARY t2 ALL NULL NULL NULL NULL 5 100.00 Using where; Using join buffer (flat, BNL join) +1 PRIMARY t3c ALL NULL NULL NULL NULL 4 100.00 Using where; End temporary; Using join buffer (flat, BNL join) +4 SUBQUERY t3b ALL NULL NULL NULL NULL 4 100.00 Using where +3 DEPENDENT SUBQUERY t3a ALL NULL NULL NULL NULL 4 100.00 Using where +Warnings: +Note 1276 Field or reference 'test.t1.a1' of SELECT #3 was resolved in SELECT #1 +Note 1276 Field or reference 'test.t1.a2' of SELECT #6 was resolved in SELECT #1 +Note 1003 select `test`.`t1`.`a1` AS `a1`,`test`.`t1`.`a2` AS `a2` from `test`.`t1` semi join (`test`.`t2`) semi join (`test`.`t2i` join `test`.`t3` `t3c`) where ((`test`.`t2i`.`b2` = `test`.`t1`.`a2`) and (`test`.`t2`.`b2` = `test`.`t1`.`a2`) and (`test`.`t3c`.`c2` = `test`.`t1`.`a2`) and (`test`.`t2i`.`b1` = `test`.`t1`.`a1`) and (`test`.`t2`.`b1` = `test`.`t1`.`a1`) and (`test`.`t3c`.`c1` = `test`.`t1`.`a1`) and (<expr_cache><`test`.`t2`.`b2`,`test`.`t1`.`a1`>(<in_optimizer>(`test`.`t2`.`b2`,<exists>(select `test`.`t3a`.`c2` from `test`.`t3` `t3a` where ((`test`.`t3a`.`c1` = `test`.`t1`.`a1`) and (<cache>(`test`.`t2`.`b2`) = `test`.`t3a`.`c2`))))) or <expr_cache><`test`.`t2`.`b2`>(<in_optimizer>(`test`.`t2`.`b2`,`test`.`t2`.`b2` in ( <materialize> (select `test`.`t3b`.`c2` from `test`.`t3` `t3b` where (`test`.`t3b`.`c2` like '%03') ), <primary_index_lookup>(`test`.`t2`.`b2` in <temporary table> on distinct_key where ((`test`.`t2`.`b2` = `<subquery4>`.`c2`)))))))) +explain extended +select * from t1 where (a1, a2) in (select '1 - 01', '2 - 01'); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t1 ALL NULL NULL NULL NULL 3 100.00 Using where +2 DEPENDENT SUBQUERY NULL NULL NULL NULL NULL NULL NULL NULL No tables used +Warnings: +Note 1003 select `test`.`t1`.`a1` AS `a1`,`test`.`t1`.`a2` AS `a2` from `test`.`t1` where <expr_cache><`test`.`t1`.`a2`,`test`.`t1`.`a1`>(<in_optimizer>((`test`.`t1`.`a1`,`test`.`t1`.`a2`),<exists>(select '1 - 01','2 - 01' having (((<cache>(`test`.`t1`.`a1`) = '1 - 01') or isnull('1 - 01')) and ((<cache>(`test`.`t1`.`a2`) = '2 - 01') or isnull('2 - 01')) and <is_not_null_test>('1 - 01') and <is_not_null_test>('2 - 01'))))) +select * from t1 where (a1, a2) in (select '1 - 01', '2 - 01'); +a1 a2 +1 - 01 2 - 01 +explain extended +select * from t1 where (a1, a2) in (select '1 - 01', '2 - 01' from dual); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t1 ALL NULL NULL NULL NULL 3 100.00 Using where +2 DEPENDENT SUBQUERY NULL NULL NULL NULL NULL NULL NULL NULL No tables used +Warnings: +Note 1003 select `test`.`t1`.`a1` AS `a1`,`test`.`t1`.`a2` AS `a2` from `test`.`t1` where <expr_cache><`test`.`t1`.`a2`,`test`.`t1`.`a1`>(<in_optimizer>((`test`.`t1`.`a1`,`test`.`t1`.`a2`),<exists>(select '1 - 01','2 - 01' having (((<cache>(`test`.`t1`.`a1`) = '1 - 01') or isnull('1 - 01')) and ((<cache>(`test`.`t1`.`a2`) = '2 - 01') or isnull('2 - 01')) and <is_not_null_test>('1 - 01') and <is_not_null_test>('2 - 01'))))) +select * from t1 where (a1, a2) in (select '1 - 01', '2 - 01' from dual); +a1 a2 +1 - 01 2 - 01 +/****************************************************************************** +* Subqueries in other uncovered clauses. +******************************************************************************/ +/* SELECT clause */ +select ((a1,a2) IN (select * from t2 where b2 > '0')) IS NULL from t1; +((a1,a2) IN (select * from t2 where b2 > '0')) IS NULL +0 +0 +0 +/* GROUP BY clause */ +create table columns (col int key); +insert into columns values (1), (2); +explain extended +select * from t1 group by (select col from columns limit 1); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t1 ALL NULL NULL NULL NULL 3 100.00 +2 SUBQUERY columns index NULL PRIMARY 4 NULL 2 100.00 Using index +Warnings: +Note 1003 select `test`.`t1`.`a1` AS `a1`,`test`.`t1`.`a2` AS `a2` from `test`.`t1` group by (select `test`.`columns`.`col` from `test`.`columns` limit 1) +select * from t1 group by (select col from columns limit 1); +a1 a2 +1 - 00 2 - 00 +explain extended +select * from t1 group by (a1 in (select col from columns)); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t1 ALL NULL NULL NULL NULL 3 100.00 Using temporary; Using filesort +2 DEPENDENT SUBQUERY columns unique_subquery PRIMARY PRIMARY 4 func 1 100.00 Using index; Using where; Full scan on NULL key +Warnings: +Note 1003 select `test`.`t1`.`a1` AS `a1`,`test`.`t1`.`a2` AS `a2` from `test`.`t1` group by <expr_cache><`test`.`t1`.`a1`>(<in_optimizer>(`test`.`t1`.`a1`,<exists>(<primary_index_lookup>(<cache>(`test`.`t1`.`a1`) in columns on PRIMARY where trigcond((<cache>(`test`.`t1`.`a1`) = `test`.`columns`.`col`)))))) +select * from t1 group by (a1 in (select col from columns)); +a1 a2 +1 - 00 2 - 00 +/* ORDER BY clause */ +explain extended +select * from t1 order by (select col from columns limit 1); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t1 ALL NULL NULL NULL NULL 3 100.00 +2 SUBQUERY columns index NULL PRIMARY 4 NULL 2 100.00 Using index +Warnings: +Note 1003 select `test`.`t1`.`a1` AS `a1`,`test`.`t1`.`a2` AS `a2` from `test`.`t1` order by (select `test`.`columns`.`col` from `test`.`columns` limit 1) +select * from t1 order by (select col from columns limit 1); +a1 a2 +1 - 00 2 - 00 +1 - 01 2 - 01 +1 - 02 2 - 02 +/****************************************************************************** +* Column types/sizes that affect materialization. +******************************************************************************/ +/* +Test that BLOBs are not materialized (except when arguments of some functions). +*/ +# force materialization to be always considered +set @prefix_len = 6; +set @blob_len = 16; +set @suffix_len = @blob_len - @prefix_len; +create table t1_16 (a1 blob(16), a2 blob(16)); +create table t2_16 (b1 blob(16), b2 blob(16)); +create table t3_16 (c1 blob(16), c2 blob(16)); +insert into t1_16 values +(concat('1 - 00', repeat('x', @suffix_len)), concat('2 - 00', repeat('x', @suffix_len))); +insert into t1_16 values +(concat('1 - 01', repeat('x', @suffix_len)), concat('2 - 01', repeat('x', @suffix_len))); +insert into t1_16 values +(concat('1 - 02', repeat('x', @suffix_len)), concat('2 - 02', repeat('x', @suffix_len))); +insert into t2_16 values +(concat('1 - 01', repeat('x', @suffix_len)), concat('2 - 01', repeat('x', @suffix_len))); +insert into t2_16 values +(concat('1 - 02', repeat('x', @suffix_len)), concat('2 - 02', repeat('x', @suffix_len))); +insert into t2_16 values +(concat('1 - 03', repeat('x', @suffix_len)), concat('2 - 03', repeat('x', @suffix_len))); +insert into t3_16 values +(concat('1 - 01', repeat('x', @suffix_len)), concat('2 - 01', repeat('x', @suffix_len))); +insert into t3_16 values +(concat('1 - 02', repeat('x', @suffix_len)), concat('2 - 02', repeat('x', @suffix_len))); +insert into t3_16 values +(concat('1 - 03', repeat('x', @suffix_len)), concat('2 - 03', repeat('x', @suffix_len))); +insert into t3_16 values +(concat('1 - 04', repeat('x', @suffix_len)), concat('2 - 04', repeat('x', @suffix_len))); +explain extended select left(a1,7), left(a2,7) +from t1_16 +where a1 in (select b1 from t2_16 where b1 > '0'); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t1_16 ALL NULL NULL NULL NULL 3 100.00 Using where; Start temporary +1 PRIMARY t2_16 ALL NULL NULL NULL NULL 3 100.00 Using where; End temporary; Using join buffer (flat, BNL join) +Warnings: +Note 1003 select left(`test`.`t1_16`.`a1`,7) AS `left(a1,7)`,left(`test`.`t1_16`.`a2`,7) AS `left(a2,7)` from `test`.`t1_16` semi join (`test`.`t2_16`) where ((`test`.`t2_16`.`b1` = `test`.`t1_16`.`a1`) and (`test`.`t1_16`.`a1` > '0')) +select left(a1,7), left(a2,7) +from t1_16 +where a1 in (select b1 from t2_16 where b1 > '0'); +left(a1,7) left(a2,7) +1 - 01x 2 - 01x +1 - 02x 2 - 02x +explain extended select left(a1,7), left(a2,7) +from t1_16 +where (a1,a2) in (select b1, b2 from t2_16 where b1 > '0'); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t1_16 ALL NULL NULL NULL NULL 3 100.00 Using where; Start temporary +1 PRIMARY t2_16 ALL NULL NULL NULL NULL 3 100.00 Using where; End temporary; Using join buffer (flat, BNL join) +Warnings: +Note 1003 select left(`test`.`t1_16`.`a1`,7) AS `left(a1,7)`,left(`test`.`t1_16`.`a2`,7) AS `left(a2,7)` from `test`.`t1_16` semi join (`test`.`t2_16`) where ((`test`.`t2_16`.`b2` = `test`.`t1_16`.`a2`) and (`test`.`t2_16`.`b1` = `test`.`t1_16`.`a1`) and (`test`.`t1_16`.`a1` > '0')) +select left(a1,7), left(a2,7) +from t1_16 +where (a1,a2) in (select b1, b2 from t2_16 where b1 > '0'); +left(a1,7) left(a2,7) +1 - 01x 2 - 01x +1 - 02x 2 - 02x +explain extended select left(a1,7), left(a2,7) +from t1_16 +where a1 in (select substring(b1,1,16) from t2_16 where b1 > '0'); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t1_16 ALL NULL NULL NULL NULL 3 100.00 +1 PRIMARY <subquery2> eq_ref distinct_key distinct_key 20 func 1 100.00 Using where +2 SUBQUERY t2_16 ALL NULL NULL NULL NULL 3 100.00 Using where +Warnings: +Note 1003 select left(`test`.`t1_16`.`a1`,7) AS `left(a1,7)`,left(`test`.`t1_16`.`a2`,7) AS `left(a2,7)` from `test`.`t1_16` semi join (`test`.`t2_16`) where ((`test`.`t2_16`.`b1` > '0') and (`test`.`t1_16`.`a1` = substr(`test`.`t2_16`.`b1`,1,16))) +select left(a1,7), left(a2,7) +from t1_16 +where a1 in (select substring(b1,1,16) from t2_16 where b1 > '0'); +left(a1,7) left(a2,7) +1 - 01x 2 - 01x +1 - 02x 2 - 02x +explain extended select left(a1,7), left(a2,7) +from t1_16 +where a1 in (select group_concat(b1) from t2_16 group by b2); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t1_16 ALL NULL NULL NULL NULL 3 100.00 Using where +2 DEPENDENT SUBQUERY t2_16 ALL NULL NULL NULL NULL 3 100.00 Using filesort +Warnings: +Note 1003 select left(`test`.`t1_16`.`a1`,7) AS `left(a1,7)`,left(`test`.`t1_16`.`a2`,7) AS `left(a2,7)` from `test`.`t1_16` where <expr_cache><`test`.`t1_16`.`a1`>(<in_optimizer>(`test`.`t1_16`.`a1`,<exists>(select group_concat(`test`.`t2_16`.`b1` separator ',') from `test`.`t2_16` group by `test`.`t2_16`.`b2` having (<cache>(`test`.`t1_16`.`a1`) = <ref_null_helper>(group_concat(`test`.`t2_16`.`b1` separator ',')))))) +select left(a1,7), left(a2,7) +from t1_16 +where a1 in (select group_concat(b1) from t2_16 group by b2); +left(a1,7) left(a2,7) +1 - 01x 2 - 01x +1 - 02x 2 - 02x +set @@group_concat_max_len = 256; +explain extended select left(a1,7), left(a2,7) +from t1_16 +where a1 in (select group_concat(b1) from t2_16 group by b2); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t1_16 ALL NULL NULL NULL NULL 3 100.00 Using where +1 PRIMARY <subquery2> eq_ref distinct_key distinct_key 261 test.t1_16.a1 1 100.00 Using where +2 SUBQUERY t2_16 ALL NULL NULL NULL NULL 3 100.00 Using filesort +Warnings: +Note 1003 select left(`test`.`t1_16`.`a1`,7) AS `left(a1,7)`,left(`test`.`t1_16`.`a2`,7) AS `left(a2,7)` from <materialize> (select group_concat(`test`.`t2_16`.`b1` separator ',') from `test`.`t2_16` group by `test`.`t2_16`.`b2`) join `test`.`t1_16` where (`test`.`t1_16`.`a1` = `<subquery2>`.`group_concat(b1)`) +select left(a1,7), left(a2,7) +from t1_16 +where a1 in (select group_concat(b1) from t2_16 group by b2); +left(a1,7) left(a2,7) +1 - 01x 2 - 01x +1 - 02x 2 - 02x +explain extended +select * from t1 +where concat(a1,'x') IN +(select left(a1,8) from t1_16 +where (a1, a2) IN +(select t2_16.b1, t2_16.b2 from t2_16, t2 +where t2.b2 = substring(t2_16.b2,1,6) and +t2.b1 IN (select c1 from t3 where c2 > '0'))); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t1 ALL NULL NULL NULL NULL 3 100.00 Start temporary +1 PRIMARY t1_16 ALL NULL NULL NULL NULL 3 100.00 Using where; Using join buffer (flat, BNL join) +1 PRIMARY t2_16 ALL NULL NULL NULL NULL 3 100.00 Using where; Using join buffer (flat, BNL join) +1 PRIMARY t3 ALL NULL NULL NULL NULL 4 100.00 Using where; Using join buffer (flat, BNL join) +1 PRIMARY t2 ALL NULL NULL NULL NULL 5 100.00 Using where; End temporary; Using join buffer (flat, BNL join) +Warnings: +Note 1003 select `test`.`t1`.`a1` AS `a1`,`test`.`t1`.`a2` AS `a2` from `test`.`t1` semi join (`test`.`t3` join `test`.`t2_16` join `test`.`t2` join `test`.`t1_16`) where ((`test`.`t2_16`.`b2` = `test`.`t1_16`.`a2`) and (`test`.`t2_16`.`b1` = `test`.`t1_16`.`a1`) and (`test`.`t2`.`b1` = `test`.`t3`.`c1`) and (`test`.`t2`.`b2` = substr(`test`.`t1_16`.`a2`,1,6)) and (`test`.`t3`.`c2` > '0') and (concat(`test`.`t1`.`a1`,'x') = left(`test`.`t1_16`.`a1`,8))) +drop table t1_16, t2_16, t3_16; +set @blob_len = 512; +set @suffix_len = @blob_len - @prefix_len; +create table t1_512 (a1 blob(512), a2 blob(512)); +create table t2_512 (b1 blob(512), b2 blob(512)); +create table t3_512 (c1 blob(512), c2 blob(512)); +insert into t1_512 values +(concat('1 - 00', repeat('x', @suffix_len)), concat('2 - 00', repeat('x', @suffix_len))); +insert into t1_512 values +(concat('1 - 01', repeat('x', @suffix_len)), concat('2 - 01', repeat('x', @suffix_len))); +insert into t1_512 values +(concat('1 - 02', repeat('x', @suffix_len)), concat('2 - 02', repeat('x', @suffix_len))); +insert into t2_512 values +(concat('1 - 01', repeat('x', @suffix_len)), concat('2 - 01', repeat('x', @suffix_len))); +insert into t2_512 values +(concat('1 - 02', repeat('x', @suffix_len)), concat('2 - 02', repeat('x', @suffix_len))); +insert into t2_512 values +(concat('1 - 03', repeat('x', @suffix_len)), concat('2 - 03', repeat('x', @suffix_len))); +insert into t3_512 values +(concat('1 - 01', repeat('x', @suffix_len)), concat('2 - 01', repeat('x', @suffix_len))); +insert into t3_512 values +(concat('1 - 02', repeat('x', @suffix_len)), concat('2 - 02', repeat('x', @suffix_len))); +insert into t3_512 values +(concat('1 - 03', repeat('x', @suffix_len)), concat('2 - 03', repeat('x', @suffix_len))); +insert into t3_512 values +(concat('1 - 04', repeat('x', @suffix_len)), concat('2 - 04', repeat('x', @suffix_len))); +explain extended select left(a1,7), left(a2,7) +from t1_512 +where a1 in (select b1 from t2_512 where b1 > '0'); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t1_512 ALL NULL NULL NULL NULL 3 100.00 Using where; Start temporary +1 PRIMARY t2_512 ALL NULL NULL NULL NULL 3 100.00 Using where; End temporary; Using join buffer (flat, BNL join) +Warnings: +Note 1003 select left(`test`.`t1_512`.`a1`,7) AS `left(a1,7)`,left(`test`.`t1_512`.`a2`,7) AS `left(a2,7)` from `test`.`t1_512` semi join (`test`.`t2_512`) where ((`test`.`t2_512`.`b1` = `test`.`t1_512`.`a1`) and (`test`.`t1_512`.`a1` > '0')) +select left(a1,7), left(a2,7) +from t1_512 +where a1 in (select b1 from t2_512 where b1 > '0'); +left(a1,7) left(a2,7) +1 - 01x 2 - 01x +1 - 02x 2 - 02x +explain extended select left(a1,7), left(a2,7) +from t1_512 +where (a1,a2) in (select b1, b2 from t2_512 where b1 > '0'); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t1_512 ALL NULL NULL NULL NULL 3 100.00 Using where; Start temporary +1 PRIMARY t2_512 ALL NULL NULL NULL NULL 3 100.00 Using where; End temporary; Using join buffer (flat, BNL join) +Warnings: +Note 1003 select left(`test`.`t1_512`.`a1`,7) AS `left(a1,7)`,left(`test`.`t1_512`.`a2`,7) AS `left(a2,7)` from `test`.`t1_512` semi join (`test`.`t2_512`) where ((`test`.`t2_512`.`b2` = `test`.`t1_512`.`a2`) and (`test`.`t2_512`.`b1` = `test`.`t1_512`.`a1`) and (`test`.`t1_512`.`a1` > '0')) +select left(a1,7), left(a2,7) +from t1_512 +where (a1,a2) in (select b1, b2 from t2_512 where b1 > '0'); +left(a1,7) left(a2,7) +1 - 01x 2 - 01x +1 - 02x 2 - 02x +explain extended select left(a1,7), left(a2,7) +from t1_512 +where a1 in (select substring(b1,1,512) from t2_512 where b1 > '0'); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t1_512 ALL NULL NULL NULL NULL 3 100.00 +1 PRIMARY <subquery2> eq_ref distinct_key distinct_key 517 func 1 100.00 Using where +2 SUBQUERY t2_512 ALL NULL NULL NULL NULL 3 100.00 Using where +Warnings: +Note 1003 select left(`test`.`t1_512`.`a1`,7) AS `left(a1,7)`,left(`test`.`t1_512`.`a2`,7) AS `left(a2,7)` from `test`.`t1_512` semi join (`test`.`t2_512`) where ((`test`.`t2_512`.`b1` > '0') and (`test`.`t1_512`.`a1` = substr(`test`.`t2_512`.`b1`,1,512))) +select left(a1,7), left(a2,7) +from t1_512 +where a1 in (select substring(b1,1,512) from t2_512 where b1 > '0'); +left(a1,7) left(a2,7) +1 - 01x 2 - 01x +1 - 02x 2 - 02x +explain extended select left(a1,7), left(a2,7) +from t1_512 +where a1 in (select group_concat(b1) from t2_512 group by b2); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t1_512 ALL NULL NULL NULL NULL 3 100.00 Using where +1 PRIMARY <subquery2> eq_ref distinct_key distinct_key 261 test.t1_512.a1 1 100.00 Using where +2 SUBQUERY t2_512 ALL NULL NULL NULL NULL 3 100.00 Using filesort +Warnings: +Note 1003 select left(`test`.`t1_512`.`a1`,7) AS `left(a1,7)`,left(`test`.`t1_512`.`a2`,7) AS `left(a2,7)` from <materialize> (select group_concat(`test`.`t2_512`.`b1` separator ',') from `test`.`t2_512` group by `test`.`t2_512`.`b2`) join `test`.`t1_512` where (`test`.`t1_512`.`a1` = `<subquery2>`.`group_concat(b1)`) +select left(a1,7), left(a2,7) +from t1_512 +where a1 in (select group_concat(b1) from t2_512 group by b2); +left(a1,7) left(a2,7) +set @@group_concat_max_len = 256; +explain extended select left(a1,7), left(a2,7) +from t1_512 +where a1 in (select group_concat(b1) from t2_512 group by b2); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t1_512 ALL NULL NULL NULL NULL 3 100.00 Using where +1 PRIMARY <subquery2> eq_ref distinct_key distinct_key 261 test.t1_512.a1 1 100.00 Using where +2 SUBQUERY t2_512 ALL NULL NULL NULL NULL 3 100.00 Using filesort +Warnings: +Note 1003 select left(`test`.`t1_512`.`a1`,7) AS `left(a1,7)`,left(`test`.`t1_512`.`a2`,7) AS `left(a2,7)` from <materialize> (select group_concat(`test`.`t2_512`.`b1` separator ',') from `test`.`t2_512` group by `test`.`t2_512`.`b2`) join `test`.`t1_512` where (`test`.`t1_512`.`a1` = `<subquery2>`.`group_concat(b1)`) +select left(a1,7), left(a2,7) +from t1_512 +where a1 in (select group_concat(b1) from t2_512 group by b2); +left(a1,7) left(a2,7) +drop table t1_512, t2_512, t3_512; +set @blob_len = 1024; +set @suffix_len = @blob_len - @prefix_len; +create table t1_1024 (a1 blob(1024), a2 blob(1024)); +create table t2_1024 (b1 blob(1024), b2 blob(1024)); +create table t3_1024 (c1 blob(1024), c2 blob(1024)); +insert into t1_1024 values +(concat('1 - 00', repeat('x', @suffix_len)), concat('2 - 00', repeat('x', @suffix_len))); +insert into t1_1024 values +(concat('1 - 01', repeat('x', @suffix_len)), concat('2 - 01', repeat('x', @suffix_len))); +insert into t1_1024 values +(concat('1 - 02', repeat('x', @suffix_len)), concat('2 - 02', repeat('x', @suffix_len))); +insert into t2_1024 values +(concat('1 - 01', repeat('x', @suffix_len)), concat('2 - 01', repeat('x', @suffix_len))); +insert into t2_1024 values +(concat('1 - 02', repeat('x', @suffix_len)), concat('2 - 02', repeat('x', @suffix_len))); +insert into t2_1024 values +(concat('1 - 03', repeat('x', @suffix_len)), concat('2 - 03', repeat('x', @suffix_len))); +insert into t3_1024 values +(concat('1 - 01', repeat('x', @suffix_len)), concat('2 - 01', repeat('x', @suffix_len))); +insert into t3_1024 values +(concat('1 - 02', repeat('x', @suffix_len)), concat('2 - 02', repeat('x', @suffix_len))); +insert into t3_1024 values +(concat('1 - 03', repeat('x', @suffix_len)), concat('2 - 03', repeat('x', @suffix_len))); +insert into t3_1024 values +(concat('1 - 04', repeat('x', @suffix_len)), concat('2 - 04', repeat('x', @suffix_len))); +explain extended select left(a1,7), left(a2,7) +from t1_1024 +where a1 in (select b1 from t2_1024 where b1 > '0'); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t1_1024 ALL NULL NULL NULL NULL 3 100.00 Using where; Start temporary +1 PRIMARY t2_1024 ALL NULL NULL NULL NULL 3 100.00 Using where; End temporary; Using join buffer (flat, BNL join) +Warnings: +Note 1003 select left(`test`.`t1_1024`.`a1`,7) AS `left(a1,7)`,left(`test`.`t1_1024`.`a2`,7) AS `left(a2,7)` from `test`.`t1_1024` semi join (`test`.`t2_1024`) where ((`test`.`t2_1024`.`b1` = `test`.`t1_1024`.`a1`) and (`test`.`t1_1024`.`a1` > '0')) +select left(a1,7), left(a2,7) +from t1_1024 +where a1 in (select b1 from t2_1024 where b1 > '0'); +left(a1,7) left(a2,7) +1 - 01x 2 - 01x +1 - 02x 2 - 02x +explain extended select left(a1,7), left(a2,7) +from t1_1024 +where (a1,a2) in (select b1, b2 from t2_1024 where b1 > '0'); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t1_1024 ALL NULL NULL NULL NULL 3 100.00 Using where; Start temporary +1 PRIMARY t2_1024 ALL NULL NULL NULL NULL 3 100.00 Using where; End temporary; Using join buffer (flat, BNL join) +Warnings: +Note 1003 select left(`test`.`t1_1024`.`a1`,7) AS `left(a1,7)`,left(`test`.`t1_1024`.`a2`,7) AS `left(a2,7)` from `test`.`t1_1024` semi join (`test`.`t2_1024`) where ((`test`.`t2_1024`.`b2` = `test`.`t1_1024`.`a2`) and (`test`.`t2_1024`.`b1` = `test`.`t1_1024`.`a1`) and (`test`.`t1_1024`.`a1` > '0')) +select left(a1,7), left(a2,7) +from t1_1024 +where (a1,a2) in (select b1, b2 from t2_1024 where b1 > '0'); +left(a1,7) left(a2,7) +1 - 01x 2 - 01x +1 - 02x 2 - 02x +explain extended select left(a1,7), left(a2,7) +from t1_1024 +where a1 in (select substring(b1,1,1024) from t2_1024 where b1 > '0'); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t1_1024 ALL NULL NULL NULL NULL 3 100.00 +1 PRIMARY <subquery2> eq_ref NULL distinct_key 15 func,func 1 100.00 Using where +2 SUBQUERY t2_1024 ALL NULL NULL NULL NULL 3 100.00 Using where +Warnings: +Note 1003 select left(`test`.`t1_1024`.`a1`,7) AS `left(a1,7)`,left(`test`.`t1_1024`.`a2`,7) AS `left(a2,7)` from `test`.`t1_1024` semi join (`test`.`t2_1024`) where ((`test`.`t2_1024`.`b1` > '0') and (`test`.`t1_1024`.`a1` = substr(`test`.`t2_1024`.`b1`,1,1024))) +select left(a1,7), left(a2,7) +from t1_1024 +where a1 in (select substring(b1,1,1024) from t2_1024 where b1 > '0'); +left(a1,7) left(a2,7) +explain extended select left(a1,7), left(a2,7) +from t1_1024 +where a1 in (select group_concat(b1) from t2_1024 group by b2); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t1_1024 ALL NULL NULL NULL NULL 3 100.00 Using where +1 PRIMARY <subquery2> eq_ref distinct_key distinct_key 261 test.t1_1024.a1 1 100.00 Using where +2 SUBQUERY t2_1024 ALL NULL NULL NULL NULL 3 100.00 Using filesort +Warnings: +Note 1003 select left(`test`.`t1_1024`.`a1`,7) AS `left(a1,7)`,left(`test`.`t1_1024`.`a2`,7) AS `left(a2,7)` from <materialize> (select group_concat(`test`.`t2_1024`.`b1` separator ',') from `test`.`t2_1024` group by `test`.`t2_1024`.`b2`) join `test`.`t1_1024` where (`test`.`t1_1024`.`a1` = `<subquery2>`.`group_concat(b1)`) +select left(a1,7), left(a2,7) +from t1_1024 +where a1 in (select group_concat(b1) from t2_1024 group by b2); +left(a1,7) left(a2,7) +set @@group_concat_max_len = 256; +explain extended select left(a1,7), left(a2,7) +from t1_1024 +where a1 in (select group_concat(b1) from t2_1024 group by b2); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t1_1024 ALL NULL NULL NULL NULL 3 100.00 Using where +1 PRIMARY <subquery2> eq_ref distinct_key distinct_key 261 test.t1_1024.a1 1 100.00 Using where +2 SUBQUERY t2_1024 ALL NULL NULL NULL NULL 3 100.00 Using filesort +Warnings: +Note 1003 select left(`test`.`t1_1024`.`a1`,7) AS `left(a1,7)`,left(`test`.`t1_1024`.`a2`,7) AS `left(a2,7)` from <materialize> (select group_concat(`test`.`t2_1024`.`b1` separator ',') from `test`.`t2_1024` group by `test`.`t2_1024`.`b2`) join `test`.`t1_1024` where (`test`.`t1_1024`.`a1` = `<subquery2>`.`group_concat(b1)`) +select left(a1,7), left(a2,7) +from t1_1024 +where a1 in (select group_concat(b1) from t2_1024 group by b2); +left(a1,7) left(a2,7) +drop table t1_1024, t2_1024, t3_1024; +set @blob_len = 1025; +set @suffix_len = @blob_len - @prefix_len; +create table t1_1025 (a1 blob(1025), a2 blob(1025)); +create table t2_1025 (b1 blob(1025), b2 blob(1025)); +create table t3_1025 (c1 blob(1025), c2 blob(1025)); +insert into t1_1025 values +(concat('1 - 00', repeat('x', @suffix_len)), concat('2 - 00', repeat('x', @suffix_len))); +insert into t1_1025 values +(concat('1 - 01', repeat('x', @suffix_len)), concat('2 - 01', repeat('x', @suffix_len))); +insert into t1_1025 values +(concat('1 - 02', repeat('x', @suffix_len)), concat('2 - 02', repeat('x', @suffix_len))); +insert into t2_1025 values +(concat('1 - 01', repeat('x', @suffix_len)), concat('2 - 01', repeat('x', @suffix_len))); +insert into t2_1025 values +(concat('1 - 02', repeat('x', @suffix_len)), concat('2 - 02', repeat('x', @suffix_len))); +insert into t2_1025 values +(concat('1 - 03', repeat('x', @suffix_len)), concat('2 - 03', repeat('x', @suffix_len))); +insert into t3_1025 values +(concat('1 - 01', repeat('x', @suffix_len)), concat('2 - 01', repeat('x', @suffix_len))); +insert into t3_1025 values +(concat('1 - 02', repeat('x', @suffix_len)), concat('2 - 02', repeat('x', @suffix_len))); +insert into t3_1025 values +(concat('1 - 03', repeat('x', @suffix_len)), concat('2 - 03', repeat('x', @suffix_len))); +insert into t3_1025 values +(concat('1 - 04', repeat('x', @suffix_len)), concat('2 - 04', repeat('x', @suffix_len))); +explain extended select left(a1,7), left(a2,7) +from t1_1025 +where a1 in (select b1 from t2_1025 where b1 > '0'); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t1_1025 ALL NULL NULL NULL NULL 3 100.00 Using where; Start temporary +1 PRIMARY t2_1025 ALL NULL NULL NULL NULL 3 100.00 Using where; End temporary; Using join buffer (flat, BNL join) +Warnings: +Note 1003 select left(`test`.`t1_1025`.`a1`,7) AS `left(a1,7)`,left(`test`.`t1_1025`.`a2`,7) AS `left(a2,7)` from `test`.`t1_1025` semi join (`test`.`t2_1025`) where ((`test`.`t2_1025`.`b1` = `test`.`t1_1025`.`a1`) and (`test`.`t1_1025`.`a1` > '0')) +select left(a1,7), left(a2,7) +from t1_1025 +where a1 in (select b1 from t2_1025 where b1 > '0'); +left(a1,7) left(a2,7) +1 - 01x 2 - 01x +1 - 02x 2 - 02x +explain extended select left(a1,7), left(a2,7) +from t1_1025 +where (a1,a2) in (select b1, b2 from t2_1025 where b1 > '0'); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t1_1025 ALL NULL NULL NULL NULL 3 100.00 Using where; Start temporary +1 PRIMARY t2_1025 ALL NULL NULL NULL NULL 3 100.00 Using where; End temporary; Using join buffer (flat, BNL join) +Warnings: +Note 1003 select left(`test`.`t1_1025`.`a1`,7) AS `left(a1,7)`,left(`test`.`t1_1025`.`a2`,7) AS `left(a2,7)` from `test`.`t1_1025` semi join (`test`.`t2_1025`) where ((`test`.`t2_1025`.`b2` = `test`.`t1_1025`.`a2`) and (`test`.`t2_1025`.`b1` = `test`.`t1_1025`.`a1`) and (`test`.`t1_1025`.`a1` > '0')) +select left(a1,7), left(a2,7) +from t1_1025 +where (a1,a2) in (select b1, b2 from t2_1025 where b1 > '0'); +left(a1,7) left(a2,7) +1 - 01x 2 - 01x +1 - 02x 2 - 02x +explain extended select left(a1,7), left(a2,7) +from t1_1025 +where a1 in (select substring(b1,1,1025) from t2_1025 where b1 > '0'); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t1_1025 ALL NULL NULL NULL NULL 3 100.00 +1 PRIMARY <subquery2> eq_ref NULL distinct_key 15 func,func 1 100.00 Using where +2 SUBQUERY t2_1025 ALL NULL NULL NULL NULL 3 100.00 Using where +Warnings: +Note 1003 select left(`test`.`t1_1025`.`a1`,7) AS `left(a1,7)`,left(`test`.`t1_1025`.`a2`,7) AS `left(a2,7)` from `test`.`t1_1025` semi join (`test`.`t2_1025`) where ((`test`.`t2_1025`.`b1` > '0') and (`test`.`t1_1025`.`a1` = substr(`test`.`t2_1025`.`b1`,1,1025))) +select left(a1,7), left(a2,7) +from t1_1025 +where a1 in (select substring(b1,1,1025) from t2_1025 where b1 > '0'); +left(a1,7) left(a2,7) +explain extended select left(a1,7), left(a2,7) +from t1_1025 +where a1 in (select group_concat(b1) from t2_1025 group by b2); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t1_1025 ALL NULL NULL NULL NULL 3 100.00 Using where +1 PRIMARY <subquery2> eq_ref distinct_key distinct_key 261 test.t1_1025.a1 1 100.00 Using where +2 SUBQUERY t2_1025 ALL NULL NULL NULL NULL 3 100.00 Using filesort +Warnings: +Note 1003 select left(`test`.`t1_1025`.`a1`,7) AS `left(a1,7)`,left(`test`.`t1_1025`.`a2`,7) AS `left(a2,7)` from <materialize> (select group_concat(`test`.`t2_1025`.`b1` separator ',') from `test`.`t2_1025` group by `test`.`t2_1025`.`b2`) join `test`.`t1_1025` where (`test`.`t1_1025`.`a1` = `<subquery2>`.`group_concat(b1)`) +select left(a1,7), left(a2,7) +from t1_1025 +where a1 in (select group_concat(b1) from t2_1025 group by b2); +left(a1,7) left(a2,7) +set @@group_concat_max_len = 256; +explain extended select left(a1,7), left(a2,7) +from t1_1025 +where a1 in (select group_concat(b1) from t2_1025 group by b2); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t1_1025 ALL NULL NULL NULL NULL 3 100.00 Using where +1 PRIMARY <subquery2> eq_ref distinct_key distinct_key 261 test.t1_1025.a1 1 100.00 Using where +2 SUBQUERY t2_1025 ALL NULL NULL NULL NULL 3 100.00 Using filesort +Warnings: +Note 1003 select left(`test`.`t1_1025`.`a1`,7) AS `left(a1,7)`,left(`test`.`t1_1025`.`a2`,7) AS `left(a2,7)` from <materialize> (select group_concat(`test`.`t2_1025`.`b1` separator ',') from `test`.`t2_1025` group by `test`.`t2_1025`.`b2`) join `test`.`t1_1025` where (`test`.`t1_1025`.`a1` = `<subquery2>`.`group_concat(b1)`) +select left(a1,7), left(a2,7) +from t1_1025 +where a1 in (select group_concat(b1) from t2_1025 group by b2); +left(a1,7) left(a2,7) +drop table t1_1025, t2_1025, t3_1025; +create table t1bit (a1 bit(3), a2 bit(3)); +create table t2bit (b1 bit(3), b2 bit(3)); +insert into t1bit values (b'000', b'100'); +insert into t1bit values (b'001', b'101'); +insert into t1bit values (b'010', b'110'); +insert into t2bit values (b'001', b'101'); +insert into t2bit values (b'010', b'110'); +insert into t2bit values (b'110', b'111'); +explain extended select bin(a1), bin(a2) +from t1bit +where (a1, a2) in (select b1, b2 from t2bit); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t1bit ALL NULL NULL NULL NULL 3 100.00 +1 PRIMARY <subquery2> eq_ref distinct_key distinct_key 10 func,func 1 100.00 +2 SUBQUERY t2bit ALL NULL NULL NULL NULL 3 100.00 +Warnings: +Note 1003 select conv(`test`.`t1bit`.`a1`,10,2) AS `bin(a1)`,conv(`test`.`t1bit`.`a2`,10,2) AS `bin(a2)` from `test`.`t1bit` semi join (`test`.`t2bit`) where 1 +select bin(a1), bin(a2) +from t1bit +where (a1, a2) in (select b1, b2 from t2bit); +bin(a1) bin(a2) +1 101 +10 110 +drop table t1bit, t2bit; +create table t1bb (a1 bit(3), a2 blob(3)); +create table t2bb (b1 bit(3), b2 blob(3)); +insert into t1bb values (b'000', '100'); +insert into t1bb values (b'001', '101'); +insert into t1bb values (b'010', '110'); +insert into t2bb values (b'001', '101'); +insert into t2bb values (b'010', '110'); +insert into t2bb values (b'110', '111'); +explain extended select bin(a1), a2 +from t1bb +where (a1, a2) in (select b1, b2 from t2bb); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t1bb ALL NULL NULL NULL NULL 3 100.00 Start temporary +1 PRIMARY t2bb ALL NULL NULL NULL NULL 3 100.00 Using where; End temporary; Using join buffer (flat, BNL join) +Warnings: +Note 1003 select conv(`test`.`t1bb`.`a1`,10,2) AS `bin(a1)`,`test`.`t1bb`.`a2` AS `a2` from `test`.`t1bb` semi join (`test`.`t2bb`) where ((`test`.`t2bb`.`b2` = `test`.`t1bb`.`a2`) and (`test`.`t2bb`.`b1` = `test`.`t1bb`.`a1`)) +select bin(a1), a2 +from t1bb +where (a1, a2) in (select b1, b2 from t2bb); +bin(a1) a2 +1 101 +10 110 +drop table t1bb, t2bb; +drop table t1, t2, t3, t1i, t2i, t3i, columns; +/****************************************************************************** +* Test the cache of the left operand of IN. +******************************************************************************/ +# Test that default values of Cached_item are not used for comparison +create table t1 (s1 int); +create table t2 (s2 int); +insert into t1 values (5),(1),(0); +insert into t2 values (0), (1); +select s2 from t2 where s2 in (select s1 from t1); +s2 +0 +1 +drop table t1, t2; +create table t1 (a int not null, b int not null); +create table t2 (c int not null, d int not null); +create table t3 (e int not null); +insert into t1 values (1,10); +insert into t1 values (1,20); +insert into t1 values (2,10); +insert into t1 values (2,20); +insert into t1 values (2,30); +insert into t1 values (3,20); +insert into t1 values (4,40); +insert into t2 values (2,10); +insert into t2 values (2,20); +insert into t2 values (2,40); +insert into t2 values (3,20); +insert into t2 values (4,10); +insert into t2 values (5,10); +insert into t3 values (10); +insert into t3 values (10); +insert into t3 values (20); +insert into t3 values (30); +explain extended +select a from t1 where a in (select c from t2 where d >= 20); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY <subquery2> ALL distinct_key NULL NULL NULL 6 100.00 +1 PRIMARY t1 ALL NULL NULL NULL NULL 7 100.00 Using where; Using join buffer (flat, BNL join) +2 SUBQUERY t2 ALL NULL NULL NULL NULL 6 100.00 Using where +Warnings: +Note 1003 select `test`.`t1`.`a` AS `a` from `test`.`t1` semi join (`test`.`t2`) where ((`test`.`t1`.`a` = `test`.`t2`.`c`) and (`test`.`t2`.`d` >= 20)) +select a from t1 where a in (select c from t2 where d >= 20); +a +2 +2 +2 +3 +create index it1a on t1(a); +explain extended +select a from t1 where a in (select c from t2 where d >= 20); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY <subquery2> ALL distinct_key NULL NULL NULL 6 100.00 +1 PRIMARY t1 ref it1a it1a 4 test.t2.c 2 100.00 Using index +2 SUBQUERY t2 ALL NULL NULL NULL NULL 6 100.00 Using where +Warnings: +Note 1003 select `test`.`t1`.`a` AS `a` from `test`.`t1` semi join (`test`.`t2`) where ((`test`.`t1`.`a` = `test`.`t2`.`c`) and (`test`.`t2`.`d` >= 20)) +select a from t1 where a in (select c from t2 where d >= 20); +a +2 +2 +2 +3 +insert into t2 values (1,10); +explain extended +select a from t1 where a in (select c from t2 where d >= 20); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t1 index it1a it1a 4 NULL 7 100.00 Using index +1 PRIMARY <subquery2> eq_ref distinct_key distinct_key 4 func 1 100.00 +2 SUBQUERY t2 ALL NULL NULL NULL NULL 7 100.00 Using where +Warnings: +Note 1003 select `test`.`t1`.`a` AS `a` from `test`.`t1` semi join (`test`.`t2`) where ((`test`.`t2`.`d` >= 20)) +select a from t1 where a in (select c from t2 where d >= 20); +a +2 +2 +2 +3 +explain extended +select a from t1 group by a having a in (select c from t2 where d >= 20); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t1 index NULL it1a 4 NULL 7 100.00 Using index +2 SUBQUERY t2 ALL NULL NULL NULL NULL 7 100.00 Using where +Warnings: +Note 1003 select `test`.`t1`.`a` AS `a` from `test`.`t1` group by `test`.`t1`.`a` having <expr_cache><`test`.`t1`.`a`>(<in_optimizer>(`test`.`t1`.`a`,`test`.`t1`.`a` in ( <materialize> (select `test`.`t2`.`c` from `test`.`t2` where (`test`.`t2`.`d` >= 20) ), <primary_index_lookup>(`test`.`t1`.`a` in <temporary table> on distinct_key where ((`test`.`t1`.`a` = `<subquery2>`.`c`)))))) +select a from t1 group by a having a in (select c from t2 where d >= 20); +a +2 +3 +create index iab on t1(a, b); +explain extended +select a from t1 group by a having a in (select c from t2 where d >= 20); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t1 index NULL it1a 4 NULL 7 100.00 Using index +2 SUBQUERY t2 ALL NULL NULL NULL NULL 7 100.00 Using where +Warnings: +Note 1003 select `test`.`t1`.`a` AS `a` from `test`.`t1` group by `test`.`t1`.`a` having <expr_cache><`test`.`t1`.`a`>(<in_optimizer>(`test`.`t1`.`a`,`test`.`t1`.`a` in ( <materialize> (select `test`.`t2`.`c` from `test`.`t2` where (`test`.`t2`.`d` >= 20) ), <primary_index_lookup>(`test`.`t1`.`a` in <temporary table> on distinct_key where ((`test`.`t1`.`a` = `<subquery2>`.`c`)))))) +select a from t1 group by a having a in (select c from t2 where d >= 20); +a +2 +3 +explain extended +select a from t1 group by a +having a in (select c from t2 where d >= some(select e from t3 where max(b)=e)); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t1 index NULL iab 8 NULL 7 100.00 Using index +2 DEPENDENT SUBQUERY t2 ALL NULL NULL NULL NULL 7 100.00 Using where +3 DEPENDENT SUBQUERY t3 ALL NULL NULL NULL NULL 4 100.00 Using where +Warnings: +Note 1276 Field or reference 'test.t1.b' of SELECT #3 was resolved in SELECT #1 +Note 1003 select `test`.`t1`.`a` AS `a` from `test`.`t1` group by `test`.`t1`.`a` having <expr_cache><`test`.`t1`.`a`,max(`test`.`t1`.`b`)>(<in_optimizer>(`test`.`t1`.`a`,<exists>(select `test`.`t2`.`c` from `test`.`t2` where (<nop>(<expr_cache><`test`.`t2`.`d`,max(`test`.`t1`.`b`)>(<in_optimizer>(`test`.`t2`.`d`,<exists>(select `test`.`t3`.`e` from `test`.`t3` where (max(`test`.`t1`.`b`) = `test`.`t3`.`e`) having (<cache>(`test`.`t2`.`d`) >= <ref_null_helper>(`test`.`t3`.`e`)))))) and (<cache>(`test`.`t1`.`a`) = `test`.`t2`.`c`))))) +select a from t1 group by a +having a in (select c from t2 where d >= some(select e from t3 where max(b)=e)); +a +2 +3 +explain extended +select a from t1 +where a in (select c from t2 where d >= some(select e from t3 where b=e)); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t2 ALL NULL NULL NULL NULL 7 100.00 Start temporary +1 PRIMARY t1 ref it1a,iab iab 4 test.t2.c 1 100.00 Using where; Using index; End temporary +3 DEPENDENT SUBQUERY t3 ALL NULL NULL NULL NULL 4 100.00 Using where +Warnings: +Note 1276 Field or reference 'test.t1.b' of SELECT #3 was resolved in SELECT #1 +Note 1003 select `test`.`t1`.`a` AS `a` from `test`.`t1` semi join (`test`.`t2`) where ((`test`.`t1`.`a` = `test`.`t2`.`c`) and <nop>(<expr_cache><`test`.`t2`.`d`,`test`.`t1`.`b`>(<in_optimizer>(`test`.`t2`.`d`,<exists>(select `test`.`t3`.`e` from `test`.`t3` where ((`test`.`t1`.`b` = `test`.`t3`.`e`) and (<cache>(`test`.`t2`.`d`) >= `test`.`t3`.`e`))))))) +select a from t1 +where a in (select c from t2 where d >= some(select e from t3 where b=e)); +a +2 +2 +2 +3 +1 +drop table t1, t2, t3; +create table t2 (a int, b int, key(a), key(b)); +insert into t2 values (3,3),(3,3),(3,3); +select 1 from t2 where +t2.a > 1 +or +t2.a = 3 and not t2.a not in (select t2.b from t2); +1 +1 +1 +1 +drop table t2; +create table t1 (a1 int key); +create table t2 (b1 int); +insert into t1 values (5); +explain select min(a1) from t1 where 7 in (select b1 from t2 group by b1); +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY t1 system NULL NULL NULL NULL 1 +1 PRIMARY <subquery2> const distinct_key distinct_key 5 const 1 +2 SUBQUERY NULL NULL NULL NULL NULL NULL NULL no matching row in const table +select min(a1) from t1 where 7 in (select b1 from t2 group by b1); +min(a1) +NULL +set @save_optimizer_switch=@@optimizer_switch; +set @@optimizer_switch='default,materialization=off'; +explain select min(a1) from t1 where 7 in (select b1 from t2 group by b1); +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY NULL NULL NULL NULL NULL NULL NULL Select tables optimized away +2 DEPENDENT SUBQUERY t2 system NULL NULL NULL NULL 0 const row not found +select min(a1) from t1 where 7 in (select b1 from t2 group by b1); +min(a1) +set @@optimizer_switch='default,semijoin=off'; +explain select min(a1) from t1 where 7 in (select b1 from t2); +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY NULL NULL NULL NULL NULL NULL NULL Select tables optimized away +2 DEPENDENT SUBQUERY NULL NULL NULL NULL NULL NULL NULL Impossible WHERE noticed after reading const tables +select min(a1) from t1 where 7 in (select b1 from t2); +min(a1) +set @@optimizer_switch='default,materialization=off'; +# with MariaDB and MWL#90, this particular case is solved: +explain select min(a1) from t1 where 7 in (select b1 from t2); +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY NULL NULL NULL NULL NULL NULL NULL Impossible WHERE noticed after reading const tables +select min(a1) from t1 where 7 in (select b1 from t2); +min(a1) +NULL +# but when we go around MWL#90 code, the problem still shows up: +explain select min(a1) from t1 where 7 in (select b1 from t2) or 2> 4; +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY NULL NULL NULL NULL NULL NULL NULL Select tables optimized away +2 DEPENDENT SUBQUERY NULL NULL NULL NULL NULL NULL NULL Impossible WHERE noticed after reading const tables +select min(a1) from t1 where 7 in (select b1 from t2) or 2> 4; +min(a1) +set @@optimizer_switch= @save_optimizer_switch; +drop table t1,t2; +create table t1 (a char(2), b varchar(10)); +insert into t1 values ('a', 'aaa'); +insert into t1 values ('aa', 'aaaa'); +explain select a,b from t1 where b in (select a from t1); +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY t1 ALL NULL NULL NULL NULL 2 +1 PRIMARY <subquery2> eq_ref distinct_key distinct_key 3 func 1 Using where +2 SUBQUERY t1 ALL NULL NULL NULL NULL 2 +select a,b from t1 where b in (select a from t1); +a b +prepare st1 from "select a,b from t1 where b in (select a from t1)"; +execute st1; +a b +execute st1; +a b +drop table t1; +CREATE TABLE t1 (f1 INT, f2 DECIMAL(5,3)) ENGINE=MyISAM; +INSERT INTO t1 (f1, f2) VALUES (1, 1.789); +INSERT INTO t1 (f1, f2) VALUES (13, 1.454); +INSERT INTO t1 (f1, f2) VALUES (10, 1.668); +CREATE TABLE t2 LIKE t1; +INSERT INTO t2 VALUES (1, 1.789); +INSERT INTO t2 VALUES (13, 1.454); +set @save_optimizer_switch=@@optimizer_switch; +SET @@optimizer_switch='default,semijoin=on,materialization=on'; +EXPLAIN SELECT COUNT(*) FROM t1 WHERE (f1,f2) IN (SELECT f1,f2 FROM t2); +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY <subquery2> ALL distinct_key NULL NULL NULL 2 +1 PRIMARY t1 ALL NULL NULL NULL NULL 3 Using where; Using join buffer (flat, BNL join) +2 SUBQUERY t2 ALL NULL NULL NULL NULL 2 +SELECT COUNT(*) FROM t1 WHERE (f1,f2) IN (SELECT f1,f2 FROM t2); +COUNT(*) +2 +set @@optimizer_switch= @save_optimizer_switch; +DROP TABLE t1, t2; +CREATE TABLE t1 ( +pk int, +a varchar(1), +b varchar(4), +c varchar(4), +d varchar(4), +PRIMARY KEY (pk) +); +INSERT INTO t1 VALUES (1,'o','ffff','ffff','ffoo'),(2,'f','ffff','ffff','ffff'); +CREATE TABLE t2 LIKE t1; +INSERT INTO t2 VALUES (1,'i','iiii','iiii','iiii'),(2,'f','ffff','ffff','ffff'); +set @save_optimizer_switch=@@optimizer_switch; +SET @@optimizer_switch='default,semijoin=on,materialization=on'; +EXPLAIN SELECT pk FROM t1 WHERE (a) IN (SELECT a FROM t2 WHERE pk > 0); +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY t1 ALL NULL NULL NULL NULL 2 +1 PRIMARY <subquery2> eq_ref distinct_key distinct_key 5 func 1 +2 SUBQUERY t2 range PRIMARY PRIMARY 4 NULL 2 Using index condition; Rowid-ordered scan +SELECT pk FROM t1 WHERE (a) IN (SELECT a FROM t2 WHERE pk > 0); +pk +2 +SELECT pk FROM t1 WHERE (b,c,d) IN (SELECT b,c,d FROM t2 WHERE pk > 0); +pk +2 +DROP TABLE t1, t2; +set optimizer_switch=@save_optimizer_switch; +# +# BUG#50019: Wrong result for IN-subquery with materialization +# +create table t1(i int); +insert into t1 values (1), (2), (3), (4), (5), (6), (7), (8), (9), (10); +create table t2(i int); +insert into t2 values (1), (2), (3), (4), (5), (6), (7), (8), (9), (10); +create table t3(i int); +insert into t3 values (1), (2), (3), (4), (5), (6), (7), (8), (9), (10); +select * from t1 where t1.i in (select t2.i from t2 join t3 where t2.i + t3.i = 5); +i +1 +2 +3 +4 +set @save_optimizer_switch=@@optimizer_switch; +set session optimizer_switch='materialization=off,in_to_exists=on'; +select * from t1 where t1.i in (select t2.i from t2 join t3 where t2.i + t3.i = 5); +i +4 +3 +2 +1 +set session optimizer_switch=@save_optimizer_switch; +drop table t1, t2, t3; +create table t0 (a int); +insert into t0 values (0),(1),(2); +create table t1 (a int); +insert into t1 values (0),(1),(2); +explain select a, a in (select a from t1) from t0; +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY t0 ALL NULL NULL NULL NULL 3 +2 SUBQUERY t1 ALL NULL NULL NULL NULL 3 +select a, a in (select a from t1) from t0; +a a in (select a from t1) +0 1 +1 1 +2 1 +prepare s from 'select a, a in (select a from t1) from t0'; +execute s; +a a in (select a from t1) +0 1 +1 1 +2 1 +update t1 set a=123; +execute s; +a a in (select a from t1) +0 0 +1 0 +2 0 +drop table t0, t1; +set optimizer_switch='firstmatch=on'; +# +# MWL#90, review feedback: check what happens when the subquery +# looks like candidate for MWL#90 checking at the first glance +# but then subselect_hash_sj_engine::init_permanent() discovers +# that it's not possible to perform duplicate removal for the +# selected datatypes, and so materialization isn't applicable after +# all. +# +set @blob_len = 1024; +set @suffix_len = @blob_len - @prefix_len; +create table t1_1024 (a1 blob(1024), a2 blob(1024)); +create table t2_1024 (b1 blob(1024), b2 blob(1024)); +insert into t1_1024 values +(concat('1 - 00', repeat('x', @suffix_len)), concat('2 - 00', repeat('x', @suffix_len))); +insert into t1_1024 values +(concat('1 - 01', repeat('x', @suffix_len)), concat('2 - 01', repeat('x', @suffix_len))); +insert into t1_1024 values +(concat('1 - 02', repeat('x', @suffix_len)), concat('2 - 02', repeat('x', @suffix_len))); +insert into t2_1024 values +(concat('1 - 01', repeat('x', @suffix_len)), concat('2 - 01', repeat('x', @suffix_len))); +insert into t2_1024 values +(concat('1 - 02', repeat('x', @suffix_len)), concat('2 - 02', repeat('x', @suffix_len))); +insert into t2_1024 values +(concat('1 - 03', repeat('x', @suffix_len)), concat('2 - 03', repeat('x', @suffix_len))); +explain select left(a1,7), left(a2,7) from t1_1024 where (a1,3) in (select substring(b1,1,1024), count(*) from t2_1024 where b1 > '0'); +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY t1_1024 ALL NULL NULL NULL NULL 3 Using where +2 DEPENDENT SUBQUERY t2_1024 ALL NULL NULL NULL NULL 3 Using where +select left(a1,7), left(a2,7) from t1_1024 where (a1,3) in (select substring(b1,1,1024), count(*) from t2_1024 where b1 > '0'); +left(a1,7) left(a2,7) +1 - 01x 2 - 01x +drop table t1_1024, t2_1024; diff --git a/mysql-test/r/subselect_sj_nonmerged.result b/mysql-test/r/subselect_sj_nonmerged.result new file mode 100644 index 00000000000..9919ebfd939 --- /dev/null +++ b/mysql-test/r/subselect_sj_nonmerged.result @@ -0,0 +1,103 @@ +drop table if exists t0, t1, t2, t3, t4; +set @save_optimizer_switch=@@optimizer_switch; +set optimizer_switch='materialization=on'; +create table t0 (a int); +insert into t0 values (0),(1),(2),(3),(4),(5),(6),(7),(8),(9); +create table t1 as select * from t0; +# The following should use full scan on <subquery2> and it must scan 1 row: +explain select * from t0 where a in (select max(a) from t1); +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY <subquery2> ALL distinct_key NULL NULL NULL 1 +1 PRIMARY t0 ALL NULL NULL NULL NULL 10 Using where; Using join buffer (flat, BNL join) +2 SUBQUERY t1 ALL NULL NULL NULL NULL 10 +select * from t0 where a in (select max(a) from t1); +a +9 +insert into t1 values (11); +select * from t0 where a in (select max(a) from t1); +a +delete from t1 where a=11; +insert into t0 values (NULL); +select * from t0 where a in (select max(a) from t1); +a +9 +delete from t0 where a is NULL; +delete from t1; +select * from t0 where a in (select max(a) from t1); +a +insert into t0 values (NULL); +select * from t0 where a in (select max(a) from t1); +a +delete from t0 where a is NULL; +drop table t1; +create table t1 (a int, b int); +insert into t1 select a,a from t0; +create table t2 as select * from t1 where a<5; +create table t3 as select (A.a + 10*B.a) as a from t0 A, t0 B; +alter table t3 add primary key(a); +# The following should have do a full scan on <subquery2> and scan 5 rows +# (despite that subquery's join output estimate is 50 rows) +explain select * from t3 where a in (select max(t2.a) from t1, t2 group by t2.b); +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY <subquery2> ALL distinct_key NULL NULL NULL 5 Using where +1 PRIMARY t3 eq_ref PRIMARY PRIMARY 8 <subquery2>.max(t2.a) 1 Using where; Using index +2 SUBQUERY t2 ALL NULL NULL NULL NULL 5 Using temporary +2 SUBQUERY t1 ALL NULL NULL NULL NULL 10 Using join buffer (flat, BNL join) +# Compare to this which really will have 50 record combinations: +explain select * from t3 where a in (select max(t2.a) from t1, t2 group by t2.b, t1.b); +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY <subquery2> ALL distinct_key NULL NULL NULL 50 Using where +1 PRIMARY t3 eq_ref PRIMARY PRIMARY 8 <subquery2>.max(t2.a) 1 Using where; Using index +2 SUBQUERY t2 ALL NULL NULL NULL NULL 5 Using temporary +2 SUBQUERY t1 ALL NULL NULL NULL NULL 10 Using join buffer (flat, BNL join) +# Outer joins also work: +explain select * from t3 +where a in (select max(t2.a) from t1 left join t2 on t1.a=t2.a group by t2.b, t1.b); +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY <subquery2> ALL distinct_key NULL NULL NULL 50 Using where +1 PRIMARY t3 eq_ref PRIMARY PRIMARY 8 <subquery2>.max(t2.a) 1 Using where; Using index +2 SUBQUERY t1 ALL NULL NULL NULL NULL 10 Using temporary +2 SUBQUERY t2 ALL NULL NULL NULL NULL 5 Using where +create table t4 (a int, b int, filler char(20), unique key(a,b)); +insert into t4 select A.a + 10*B.a, A.a + 10*B.a, 'filler' from t0 A, t0 B; +explain select * from t0, t4 where +t4.b=t0.a and t4.a in (select max(t2.a) from t1, t2 group by t2.b); +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY t0 ALL NULL NULL NULL NULL 10 +1 PRIMARY t4 ALL a NULL NULL NULL 100 Using where; Using join buffer (flat, BNL join) +1 PRIMARY <subquery2> eq_ref distinct_key distinct_key 5 test.t4.a 1 +2 SUBQUERY t2 ALL NULL NULL NULL NULL 5 Using temporary +2 SUBQUERY t1 ALL NULL NULL NULL NULL 10 Using join buffer (flat, BNL join) +insert into t4 select 100 + (B.a *100 + A.a), 100 + (B.a*100 + A.a), 'filler' from t4 A, t0 B; +explain select * from t4 where +t4.a in (select max(t2.a) from t1, t2 group by t2.b) and +t4.b in (select max(t2.a) from t1, t2 group by t2.b); +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY <subquery2> ALL distinct_key NULL NULL NULL 5 Using where +1 PRIMARY <subquery3> ALL distinct_key NULL NULL NULL 5 Using where; Using join buffer (flat, BNL join) +1 PRIMARY t4 ref a a 10 <subquery2>.max(t2.a),<subquery3>.max(t2.a) 12 +3 SUBQUERY t2 ALL NULL NULL NULL NULL 5 Using temporary +3 SUBQUERY t1 ALL NULL NULL NULL NULL 10 Using join buffer (flat, BNL join) +2 SUBQUERY t2 ALL NULL NULL NULL NULL 5 Using temporary +2 SUBQUERY t1 ALL NULL NULL NULL NULL 10 Using join buffer (flat, BNL join) +drop table t1,t2,t3,t4; +drop table t0; +# +# BUG#780359: Crash with get_fanout_with_deps in maria-5.3-mwl90 +# +CREATE TABLE t1 (f1 int); +INSERT INTO t1 VALUES (2),(2); +CREATE TABLE t2 (f3 int); +INSERT INTO t2 VALUES (2),(2); +SELECT * +FROM t1 +WHERE ( f1 ) IN ( +SELECT t2.f3 +FROM t2 +WHERE t2.f3 = 97 +AND t2.f3 = 50 +GROUP BY 1 +); +f1 +DROP TABLE t1, t2; +set optimizer_switch=@save_optimizer_switch; diff --git a/mysql-test/suite/vcol/inc/vcol_view.inc b/mysql-test/suite/vcol/inc/vcol_view.inc index 2bf413e2471..64149a7bb31 100644 --- a/mysql-test/suite/vcol/inc/vcol_view.inc +++ b/mysql-test/suite/vcol/inc/vcol_view.inc @@ -69,10 +69,14 @@ create table t1 (a int not null, insert into t1 (a) values (1), (2), (3), (4); create view v1 as select b+1 from t1 order by 1 desc limit 2; select * from v1; +--echo MariaDB-5.3: the following EXPLAIN produces incorrect #rows for table t1. +--echo MariaDB-5.3: this is expected to go away when FROM subquery optimizations are pushed explain select * from v1; drop view v1; create view v1 as select c+1 from t1 order by 1 desc limit 2; select * from v1; +--echo MariaDB-5.3: the following EXPLAIN produces incorrect #rows for table t1. +--echo MariaDB-5.3: this is expected to go away when FROM subquery optimizations are pushed explain select * from v1; drop view v1; drop table t1; diff --git a/mysql-test/suite/vcol/r/vcol_view_innodb.result b/mysql-test/suite/vcol/r/vcol_view_innodb.result index ae834722606..6db6f7414b1 100644 --- a/mysql-test/suite/vcol/r/vcol_view_innodb.result +++ b/mysql-test/suite/vcol/r/vcol_view_innodb.result @@ -103,6 +103,8 @@ select * from v1; b+1 0 -1 +MariaDB-5.3: the following EXPLAIN produces incorrect #rows for table t1. +MariaDB-5.3: this is expected to go away when FROM subquery optimizations are pushed explain select * from v1; id select_type table type possible_keys key key_len ref rows Extra 1 PRIMARY <derived2> ALL NULL NULL NULL NULL 2 @@ -113,6 +115,8 @@ select * from v1; c+1 0 -1 +MariaDB-5.3: the following EXPLAIN produces incorrect #rows for table t1. +MariaDB-5.3: this is expected to go away when FROM subquery optimizations are pushed explain select * from v1; id select_type table type possible_keys key key_len ref rows Extra 1 PRIMARY <derived2> ALL NULL NULL NULL NULL 2 diff --git a/mysql-test/suite/vcol/r/vcol_view_myisam.result b/mysql-test/suite/vcol/r/vcol_view_myisam.result index bd5999792ff..dedb62a00fb 100644 --- a/mysql-test/suite/vcol/r/vcol_view_myisam.result +++ b/mysql-test/suite/vcol/r/vcol_view_myisam.result @@ -103,6 +103,8 @@ select * from v1; b+1 0 -1 +MariaDB-5.3: the following EXPLAIN produces incorrect #rows for table t1. +MariaDB-5.3: this is expected to go away when FROM subquery optimizations are pushed explain select * from v1; id select_type table type possible_keys key key_len ref rows Extra 1 PRIMARY <derived2> ALL NULL NULL NULL NULL 2 @@ -113,6 +115,8 @@ select * from v1; c+1 0 -1 +MariaDB-5.3: the following EXPLAIN produces incorrect #rows for table t1. +MariaDB-5.3: this is expected to go away when FROM subquery optimizations are pushed explain select * from v1; id select_type table type possible_keys key key_len ref rows Extra 1 PRIMARY <derived2> ALL NULL NULL NULL NULL 2 diff --git a/mysql-test/t/subselect4.test b/mysql-test/t/subselect4.test index 768f37c1b6c..89a1f75a9dc 100644 --- a/mysql-test/t/subselect4.test +++ b/mysql-test/t/subselect4.test @@ -1,5 +1,8 @@ # General purpose bug fix tests go here : subselect.test too large +--disable_warnings +drop table if exists t1,t2,t3,t4,t5,t6; +--enable_warnings --echo # --echo # Bug #46791: Assertion failed:(table->key_read==0),function unknown diff --git a/mysql-test/t/subselect_mat.test b/mysql-test/t/subselect_mat.test index 90ece548b58..4a300561d58 100644 --- a/mysql-test/t/subselect_mat.test +++ b/mysql-test/t/subselect_mat.test @@ -3,913 +3,12 @@ # (WL#1110: Subquery optimization: materialization) # ---disable_warnings -drop table if exists t1, t2, t3, t1i, t2i, t3i; -drop view if exists v1, v2, v1m, v2m; ---enable_warnings - -create table t1 (a1 char(8), a2 char(8)); -create table t2 (b1 char(8), b2 char(8)); -create table t3 (c1 char(8), c2 char(8)); - -insert into t1 values ('1 - 00', '2 - 00'); -insert into t1 values ('1 - 01', '2 - 01'); -insert into t1 values ('1 - 02', '2 - 02'); - -insert into t2 values ('1 - 01', '2 - 01'); -insert into t2 values ('1 - 01', '2 - 01'); -insert into t2 values ('1 - 02', '2 - 02'); -insert into t2 values ('1 - 02', '2 - 02'); -insert into t2 values ('1 - 03', '2 - 03'); - -insert into t3 values ('1 - 01', '2 - 01'); -insert into t3 values ('1 - 02', '2 - 02'); -insert into t3 values ('1 - 03', '2 - 03'); -insert into t3 values ('1 - 04', '2 - 04'); - -# Indexed columns -create table t1i (a1 char(8), a2 char(8)); -create table t2i (b1 char(8), b2 char(8)); -create table t3i (c1 char(8), c2 char(8)); -create index it1i1 on t1i (a1); -create index it1i2 on t1i (a2); -create index it1i3 on t1i (a1, a2); - -create index it2i1 on t2i (b1); -create index it2i2 on t2i (b2); -create index it2i3 on t2i (b1, b2); - -create index it3i1 on t3i (c1); -create index it3i2 on t3i (c2); -create index it3i3 on t3i (c1, c2); - -insert into t1i select * from t1; -insert into t2i select * from t2; -insert into t3i select * from t3; # force the use of materialization set @@optimizer_switch='materialization=on,in_to_exists=off,semijoin=off'; -/****************************************************************************** -* Simple tests. -******************************************************************************/ -# non-indexed nullable fields -explain extended -select * from t1 where a1 in (select b1 from t2 where b1 > '0'); -select * from t1 where a1 in (select b1 from t2 where b1 > '0'); - -explain extended -select * from t1 where a1 in (select b1 from t2 where b1 > '0' group by b1); -select * from t1 where a1 in (select b1 from t2 where b1 > '0' group by b1); - -explain extended -select * from t1 where (a1, a2) in (select b1, b2 from t2 where b1 > '0' group by b1, b2); -select * from t1 where (a1, a2) in (select b1, b2 from t2 where b1 > '0' group by b1, b2); - -explain extended -select * from t1 where (a1, a2) in (select b1, min(b2) from t2 where b1 > '0' group by b1); -select * from t1 where (a1, a2) in (select b1, min(b2) from t2 where b1 > '0' group by b1); - -# indexed columns -explain extended -select * from t1i where a1 in (select b1 from t2i where b1 > '0'); -select * from t1i where a1 in (select b1 from t2i where b1 > '0'); - -explain extended -select * from t1i where a1 in (select b1 from t2i where b1 > '0' group by b1); -select * from t1i where a1 in (select b1 from t2i where b1 > '0' group by b1); - -explain extended -select * from t1i where (a1, a2) in (select b1, b2 from t2i where b1 > '0'); -select * from t1i where (a1, a2) in (select b1, b2 from t2i where b1 > '0'); - -explain extended -select * from t1i where (a1, a2) in (select b1, b2 from t2i where b1 > '0' group by b1, b2); -select * from t1i where (a1, a2) in (select b1, b2 from t2i where b1 > '0' group by b1, b2); - -explain extended -select * from t1i where (a1, a2) in (select b1, min(b2) from t2i where b1 > '0' group by b1); -select * from t1i where (a1, a2) in (select b1, min(b2) from t2i where b1 > '0' group by b1); - -# BUG#31639: Wrong plan for uncorrelated subquery when loose scan is applicable. -explain extended -select * from t1 where (a1, a2) in (select b1, max(b2) from t2i group by b1); -select * from t1 where (a1, a2) in (select b1, max(b2) from t2i group by b1); - -prepare st1 from "explain select * from t1 where (a1, a2) in (select b1, max(b2) from t2i group by b1)"; -execute st1; -execute st1; -prepare st2 from "select * from t1 where (a1, a2) in (select b1, max(b2) from t2i group by b1)"; -execute st2; -execute st2; - -explain extended -select * from t1 where (a1, a2) in (select b1, min(b2) from t2i where b1 > '0' group by b1); -select * from t1 where (a1, a2) in (select b1, min(b2) from t2i where b1 > '0' group by b1); --- error 1235 -select * from t1 where (a1, a2) in (select b1, min(b2) from t2i limit 1,1); - -# test re-optimization/re-execution with different execution methods -# prepare once, exec with different modes -set @@optimizer_switch='materialization=on,in_to_exists=off,semijoin=on'; -prepare st1 from -"select * from t1 where (a1, a2) in (select b1, min(b2) from t2 where b1 > '0' group by b1)"; -set @@optimizer_switch='materialization=off,in_to_exists=on,semijoin=on'; -execute st1; -set @@optimizer_switch='materialization=on,in_to_exists=off,semijoin=on'; -execute st1; - -set @@optimizer_switch='materialization=off,in_to_exists=on,semijoin=on'; -prepare st1 from -"select * from t1 where (a1, a2) in (select b1, min(b2) from t2 where b1 > '0' group by b1)"; -set @@optimizer_switch='materialization=on,in_to_exists=off,semijoin=off'; -execute st1; -set @@optimizer_switch='materialization=off,in_to_exists=on,semijoin=on'; -execute st1; -set @@optimizer_switch='materialization=on,in_to_exists=off,semijoin=off'; - -# materialize the result of ORDER BY -# non-indexed fields -explain extended -select * from t1 where (a1, a2) in (select b1, b2 from t2 order by b1, b2); -select * from t1 where (a1, a2) in (select b1, b2 from t2 order by b1, b2); -# indexed fields -explain extended -select * from t1i where (a1, a2) in (select b1, b2 from t2i order by b1, b2); -select * from t1i where (a1, a2) in (select b1, b2 from t2i order by b1, b2); - -/****************************************************************************** -* Views, UNIONs, several levels of nesting. -******************************************************************************/ -# materialize the result of subquery over temp-table view - -create algorithm=merge view v1 as -select b1, c2 from t2, t3 where b2 > c2; - -create algorithm=merge view v2 as -select b1, c2 from t2, t3 group by b2, c2; - -create algorithm=temptable view v1m as -select b1, c2 from t2, t3 where b2 > c2; - -create algorithm=temptable view v2m as -select b1, c2 from t2, t3 group by b2, c2; - -select * from v1 where (c2, b1) in (select c2, b1 from v2 where b1 is not null); -select * from v1 where (c2, b1) in (select distinct c2, b1 from v2 where b1 is not null); - -select * from v1m where (c2, b1) in (select c2, b1 from v2m where b1 is not null); -select * from v1m where (c2, b1) in (select distinct c2, b1 from v2m where b1 is not null); - -drop view v1, v2, v1m, v2m; - -# nested subqueries, views -explain extended -select * from t1 -where (a1, a2) in (select b1, b2 from t2 where b1 > '0') and - (a1, a2) in (select c1, c2 from t3 - where (c1, c2) in (select b1, b2 from t2i where b2 > '0')); -select * from t1 -where (a1, a2) in (select b1, b2 from t2 where b1 > '0') and - (a1, a2) in (select c1, c2 from t3 - where (c1, c2) in (select b1, b2 from t2i where b2 > '0')); - -explain extended -select * from t1i -where (a1, a2) in (select b1, b2 from t2i where b1 > '0') and - (a1, a2) in (select c1, c2 from t3i - where (c1, c2) in (select b1, b2 from t2i where b2 > '0')); -select * from t1i -where (a1, a2) in (select b1, b2 from t2i where b1 > '0') and - (a1, a2) in (select c1, c2 from t3i - where (c1, c2) in (select b1, b2 from t2i where b2 > '0')); - -explain extended -select * from t1 -where (a1, a2) in (select b1, b2 from t2 - where b2 in (select c2 from t3 where c2 LIKE '%02') or - b2 in (select c2 from t3 where c2 LIKE '%03')) and - (a1, a2) in (select c1, c2 from t3 - where (c1, c2) in (select b1, b2 from t2i where b2 > '0')); -select * from t1 -where (a1, a2) in (select b1, b2 from t2 - where b2 in (select c2 from t3 where c2 LIKE '%02') or - b2 in (select c2 from t3 where c2 LIKE '%03')) and - (a1, a2) in (select c1, c2 from t3 - where (c1, c2) in (select b1, b2 from t2i where b2 > '0')); - -# as above with correlated innermost subquery -explain extended -select * from t1 -where (a1, a2) in (select b1, b2 from t2 - where b2 in (select c2 from t3 t3a where c1 = a1) or - b2 in (select c2 from t3 t3b where c2 LIKE '%03')) and - (a1, a2) in (select c1, c2 from t3 t3c - where (c1, c2) in (select b1, b2 from t2i where b2 > '0')); -select * from t1 -where (a1, a2) in (select b1, b2 from t2 - where b2 in (select c2 from t3 t3a where c1 = a1) or - b2 in (select c2 from t3 t3b where c2 LIKE '%03')) and - (a1, a2) in (select c1, c2 from t3 t3c - where (c1, c2) in (select b1, b2 from t2i where b2 > '0')); - - -# multiple levels of nesting subqueries, unions -explain extended -(select * from t1 -where (a1, a2) in (select b1, b2 from t2 - where b2 in (select c2 from t3 where c2 LIKE '%02') or - b2 in (select c2 from t3 where c2 LIKE '%03') - group by b1, b2) and - (a1, a2) in (select c1, c2 from t3 - where (c1, c2) in (select b1, b2 from t2i where b2 > '0'))) -UNION -(select * from t1i -where (a1, a2) in (select b1, b2 from t2i where b1 > '0') and - (a1, a2) in (select c1, c2 from t3i - where (c1, c2) in (select b1, b2 from t2i where b2 > '0'))); - -(select * from t1 -where (a1, a2) in (select b1, b2 from t2 - where b2 in (select c2 from t3 where c2 LIKE '%02') or - b2 in (select c2 from t3 where c2 LIKE '%03') - group by b1, b2) and - (a1, a2) in (select c1, c2 from t3 - where (c1, c2) in (select b1, b2 from t2i where b2 > '0'))) -UNION -(select * from t1i -where (a1, a2) in (select b1, b2 from t2i where b1 > '0') and - (a1, a2) in (select c1, c2 from t3i - where (c1, c2) in (select b1, b2 from t2i where b2 > '0'))); - - -# UNION of subqueries as a subquery (thus it is not computed via materialization) -explain extended -select * from t1 -where (a1, a2) in (select * from t1 where a1 > '0' UNION select * from t2 where b1 < '9') and - (a1, a2) in (select c1, c2 from t3 - where (c1, c2) in (select b1, b2 from t2i where b2 > '0')); -select * from t1 -where (a1, a2) in (select * from t1 where a1 > '0' UNION select * from t2 where b1 < '9') and - (a1, a2) in (select c1, c2 from t3 - where (c1, c2) in (select b1, b2 from t2i where b2 > '0')); -# as above, with a join conditon between the outer references -explain extended -select * from t1, t3 -where (a1, a2) in (select * from t1 where a1 > '0' UNION select * from t2 where b1 < '9') and - (c1, c2) in (select c1, c2 from t3 - where (c1, c2) in (select b1, b2 from t2i where b2 > '0')) and - a1 = c1; -select * from t1, t3 -where (a1, a2) in (select * from t1 where a1 > '0' UNION select * from t2 where b1 < '9') and - (c1, c2) in (select c1, c2 from t3 - where (c1, c2) in (select b1, b2 from t2i where b2 > '0')) and - a1 = c1; - - -/****************************************************************************** -* Negative tests, where materialization should not be applied. -******************************************************************************/ -# UNION in a subquery -explain extended -select * from t3 -where c1 in (select a1 from t1 where a1 > '0' UNION select b1 from t2 where b1 < '9'); -select * from t3 -where c1 in (select a1 from t1 where a1 > '0' UNION select b1 from t2 where b1 < '9'); - -# correlation -explain extended -select * from t1 -where (a1, a2) in (select b1, b2 from t2 - where b2 in (select c2 from t3 t3a where c1 = a1) or - b2 in (select c2 from t3 t3b where c2 LIKE '%03')) and - (a1, a2) in (select c1, c2 from t3 t3c - where (c1, c2) in (select b1, b2 from t2i where b2 > '0' or b2 = a2)); - -# subquery has no tables -explain extended -select * from t1 where (a1, a2) in (select '1 - 01', '2 - 01'); -select * from t1 where (a1, a2) in (select '1 - 01', '2 - 01'); -explain extended -select * from t1 where (a1, a2) in (select '1 - 01', '2 - 01' from dual); -select * from t1 where (a1, a2) in (select '1 - 01', '2 - 01' from dual); - - -/****************************************************************************** -* Subqueries in other uncovered clauses. -******************************************************************************/ - -/* SELECT clause */ -select ((a1,a2) IN (select * from t2 where b2 > '0')) IS NULL from t1; - -/* GROUP BY clause */ -create table columns (col int key); -insert into columns values (1), (2); - -explain extended -select * from t1 group by (select col from columns limit 1); -select * from t1 group by (select col from columns limit 1); - -explain extended -select * from t1 group by (a1 in (select col from columns)); -select * from t1 group by (a1 in (select col from columns)); - -/* ORDER BY clause */ -explain extended -select * from t1 order by (select col from columns limit 1); -select * from t1 order by (select col from columns limit 1); - -/****************************************************************************** -* Column types/sizes that affect materialization. -******************************************************************************/ - -/* - Test that BLOBs are not materialized (except when arguments of some functions). -*/ -# force materialization to be always considered +--source t/subselect_sj_mat.test set @@optimizer_switch='materialization=on,in_to_exists=off,semijoin=off'; -set @prefix_len = 6; - -# BLOB == 16 (small blobs that could be stored in HEAP tables) -set @blob_len = 16; -set @suffix_len = @blob_len - @prefix_len; - -create table t1_16 (a1 blob(16), a2 blob(16)); -create table t2_16 (b1 blob(16), b2 blob(16)); -create table t3_16 (c1 blob(16), c2 blob(16)); - -insert into t1_16 values - (concat('1 - 00', repeat('x', @suffix_len)), concat('2 - 00', repeat('x', @suffix_len))); -insert into t1_16 values - (concat('1 - 01', repeat('x', @suffix_len)), concat('2 - 01', repeat('x', @suffix_len))); -insert into t1_16 values - (concat('1 - 02', repeat('x', @suffix_len)), concat('2 - 02', repeat('x', @suffix_len))); - -insert into t2_16 values - (concat('1 - 01', repeat('x', @suffix_len)), concat('2 - 01', repeat('x', @suffix_len))); -insert into t2_16 values - (concat('1 - 02', repeat('x', @suffix_len)), concat('2 - 02', repeat('x', @suffix_len))); -insert into t2_16 values - (concat('1 - 03', repeat('x', @suffix_len)), concat('2 - 03', repeat('x', @suffix_len))); - -insert into t3_16 values - (concat('1 - 01', repeat('x', @suffix_len)), concat('2 - 01', repeat('x', @suffix_len))); -insert into t3_16 values - (concat('1 - 02', repeat('x', @suffix_len)), concat('2 - 02', repeat('x', @suffix_len))); -insert into t3_16 values - (concat('1 - 03', repeat('x', @suffix_len)), concat('2 - 03', repeat('x', @suffix_len))); -insert into t3_16 values - (concat('1 - 04', repeat('x', @suffix_len)), concat('2 - 04', repeat('x', @suffix_len))); - -# single value transformer -explain extended select left(a1,7), left(a2,7) -from t1_16 -where a1 in (select b1 from t2_16 where b1 > '0'); - -select left(a1,7), left(a2,7) -from t1_16 -where a1 in (select b1 from t2_16 where b1 > '0'); - -# row value transformer -explain extended select left(a1,7), left(a2,7) -from t1_16 -where (a1,a2) in (select b1, b2 from t2_16 where b1 > '0'); - -select left(a1,7), left(a2,7) -from t1_16 -where (a1,a2) in (select b1, b2 from t2_16 where b1 > '0'); - -# string function with a blob argument, the return type may be != blob -explain extended select left(a1,7), left(a2,7) -from t1_16 -where a1 in (select substring(b1,1,16) from t2_16 where b1 > '0'); - -select left(a1,7), left(a2,7) -from t1_16 -where a1 in (select substring(b1,1,16) from t2_16 where b1 > '0'); - -# group_concat with a blob argument - depends on -# the variable group_concat_max_len, and -# convert_blob_length == max_len*collation->mbmaxlen > CONVERT_IF_BIGGER_TO_BLOB -explain extended select left(a1,7), left(a2,7) -from t1_16 -where a1 in (select group_concat(b1) from t2_16 group by b2); - -select left(a1,7), left(a2,7) -from t1_16 -where a1 in (select group_concat(b1) from t2_16 group by b2); - -set @@group_concat_max_len = 256; # anything < (CONVERT_IF_BIGGER_TO_BLOB = 512) - -explain extended select left(a1,7), left(a2,7) -from t1_16 -where a1 in (select group_concat(b1) from t2_16 group by b2); - -select left(a1,7), left(a2,7) -from t1_16 -where a1 in (select group_concat(b1) from t2_16 group by b2); - -# BLOB column at the second (intermediate) level of nesting -explain extended -select * from t1 -where concat(a1,'x') IN - (select left(a1,8) from t1_16 - where (a1, a2) IN - (select t2_16.b1, t2_16.b2 from t2_16, t2 - where t2.b2 = substring(t2_16.b2,1,6) and - t2.b1 IN (select c1 from t3 where c2 > '0'))); - - -drop table t1_16, t2_16, t3_16; - - -# BLOB == 512 (CONVERT_IF_BIGGER_TO_BLOB == 512) -set @blob_len = 512; -set @suffix_len = @blob_len - @prefix_len; - -create table t1_512 (a1 blob(512), a2 blob(512)); -create table t2_512 (b1 blob(512), b2 blob(512)); -create table t3_512 (c1 blob(512), c2 blob(512)); - -insert into t1_512 values - (concat('1 - 00', repeat('x', @suffix_len)), concat('2 - 00', repeat('x', @suffix_len))); -insert into t1_512 values - (concat('1 - 01', repeat('x', @suffix_len)), concat('2 - 01', repeat('x', @suffix_len))); -insert into t1_512 values - (concat('1 - 02', repeat('x', @suffix_len)), concat('2 - 02', repeat('x', @suffix_len))); - -insert into t2_512 values - (concat('1 - 01', repeat('x', @suffix_len)), concat('2 - 01', repeat('x', @suffix_len))); -insert into t2_512 values - (concat('1 - 02', repeat('x', @suffix_len)), concat('2 - 02', repeat('x', @suffix_len))); -insert into t2_512 values - (concat('1 - 03', repeat('x', @suffix_len)), concat('2 - 03', repeat('x', @suffix_len))); - -insert into t3_512 values - (concat('1 - 01', repeat('x', @suffix_len)), concat('2 - 01', repeat('x', @suffix_len))); -insert into t3_512 values - (concat('1 - 02', repeat('x', @suffix_len)), concat('2 - 02', repeat('x', @suffix_len))); -insert into t3_512 values - (concat('1 - 03', repeat('x', @suffix_len)), concat('2 - 03', repeat('x', @suffix_len))); -insert into t3_512 values - (concat('1 - 04', repeat('x', @suffix_len)), concat('2 - 04', repeat('x', @suffix_len))); - -# single value transformer -explain extended select left(a1,7), left(a2,7) -from t1_512 -where a1 in (select b1 from t2_512 where b1 > '0'); - -select left(a1,7), left(a2,7) -from t1_512 -where a1 in (select b1 from t2_512 where b1 > '0'); - -# row value transformer -explain extended select left(a1,7), left(a2,7) -from t1_512 -where (a1,a2) in (select b1, b2 from t2_512 where b1 > '0'); - -select left(a1,7), left(a2,7) -from t1_512 -where (a1,a2) in (select b1, b2 from t2_512 where b1 > '0'); - -# string function with a blob argument, the return type may be != blob -explain extended select left(a1,7), left(a2,7) -from t1_512 -where a1 in (select substring(b1,1,512) from t2_512 where b1 > '0'); - -select left(a1,7), left(a2,7) -from t1_512 -where a1 in (select substring(b1,1,512) from t2_512 where b1 > '0'); - -# group_concat with a blob argument - depends on -# the variable group_concat_max_len, and -# convert_blob_length == max_len*collation->mbmaxlen > CONVERT_IF_BIGGER_TO_BLOB -explain extended select left(a1,7), left(a2,7) -from t1_512 -where a1 in (select group_concat(b1) from t2_512 group by b2); - -select left(a1,7), left(a2,7) -from t1_512 -where a1 in (select group_concat(b1) from t2_512 group by b2); - -set @@group_concat_max_len = 256; # anything < (CONVERT_IF_BIGGER_TO_BLOB = 512) - -explain extended select left(a1,7), left(a2,7) -from t1_512 -where a1 in (select group_concat(b1) from t2_512 group by b2); - -select left(a1,7), left(a2,7) -from t1_512 -where a1 in (select group_concat(b1) from t2_512 group by b2); - -drop table t1_512, t2_512, t3_512; - - -# BLOB == 1024 (group_concat_max_len == 1024) -set @blob_len = 1024; -set @suffix_len = @blob_len - @prefix_len; - -create table t1_1024 (a1 blob(1024), a2 blob(1024)); -create table t2_1024 (b1 blob(1024), b2 blob(1024)); -create table t3_1024 (c1 blob(1024), c2 blob(1024)); - -insert into t1_1024 values - (concat('1 - 00', repeat('x', @suffix_len)), concat('2 - 00', repeat('x', @suffix_len))); -insert into t1_1024 values - (concat('1 - 01', repeat('x', @suffix_len)), concat('2 - 01', repeat('x', @suffix_len))); -insert into t1_1024 values - (concat('1 - 02', repeat('x', @suffix_len)), concat('2 - 02', repeat('x', @suffix_len))); - -insert into t2_1024 values - (concat('1 - 01', repeat('x', @suffix_len)), concat('2 - 01', repeat('x', @suffix_len))); -insert into t2_1024 values - (concat('1 - 02', repeat('x', @suffix_len)), concat('2 - 02', repeat('x', @suffix_len))); -insert into t2_1024 values - (concat('1 - 03', repeat('x', @suffix_len)), concat('2 - 03', repeat('x', @suffix_len))); - -insert into t3_1024 values - (concat('1 - 01', repeat('x', @suffix_len)), concat('2 - 01', repeat('x', @suffix_len))); -insert into t3_1024 values - (concat('1 - 02', repeat('x', @suffix_len)), concat('2 - 02', repeat('x', @suffix_len))); -insert into t3_1024 values - (concat('1 - 03', repeat('x', @suffix_len)), concat('2 - 03', repeat('x', @suffix_len))); -insert into t3_1024 values - (concat('1 - 04', repeat('x', @suffix_len)), concat('2 - 04', repeat('x', @suffix_len))); - -# single value transformer -explain extended select left(a1,7), left(a2,7) -from t1_1024 -where a1 in (select b1 from t2_1024 where b1 > '0'); - -select left(a1,7), left(a2,7) -from t1_1024 -where a1 in (select b1 from t2_1024 where b1 > '0'); - -# row value transformer -explain extended select left(a1,7), left(a2,7) -from t1_1024 -where (a1,a2) in (select b1, b2 from t2_1024 where b1 > '0'); - -select left(a1,7), left(a2,7) -from t1_1024 -where (a1,a2) in (select b1, b2 from t2_1024 where b1 > '0'); - -# string function with a blob argument, the return type may be != blob -explain extended select left(a1,7), left(a2,7) -from t1_1024 -where a1 in (select substring(b1,1,1024) from t2_1024 where b1 > '0'); - -select left(a1,7), left(a2,7) -from t1_1024 -where a1 in (select substring(b1,1,1024) from t2_1024 where b1 > '0'); - -# group_concat with a blob argument - depends on -# the variable group_concat_max_len, and -# convert_blob_length == max_len*collation->mbmaxlen > CONVERT_IF_BIGGER_TO_BLOB -explain extended select left(a1,7), left(a2,7) -from t1_1024 -where a1 in (select group_concat(b1) from t2_1024 group by b2); - -select left(a1,7), left(a2,7) -from t1_1024 -where a1 in (select group_concat(b1) from t2_1024 group by b2); - -set @@group_concat_max_len = 256; # anything < (CONVERT_IF_BIGGER_TO_BLOB = 1024) - -explain extended select left(a1,7), left(a2,7) -from t1_1024 -where a1 in (select group_concat(b1) from t2_1024 group by b2); - -select left(a1,7), left(a2,7) -from t1_1024 -where a1 in (select group_concat(b1) from t2_1024 group by b2); - -drop table t1_1024, t2_1024, t3_1024; - - -# BLOB == 1025 -set @blob_len = 1025; -set @suffix_len = @blob_len - @prefix_len; - -create table t1_1025 (a1 blob(1025), a2 blob(1025)); -create table t2_1025 (b1 blob(1025), b2 blob(1025)); -create table t3_1025 (c1 blob(1025), c2 blob(1025)); - -insert into t1_1025 values - (concat('1 - 00', repeat('x', @suffix_len)), concat('2 - 00', repeat('x', @suffix_len))); -insert into t1_1025 values - (concat('1 - 01', repeat('x', @suffix_len)), concat('2 - 01', repeat('x', @suffix_len))); -insert into t1_1025 values - (concat('1 - 02', repeat('x', @suffix_len)), concat('2 - 02', repeat('x', @suffix_len))); - -insert into t2_1025 values - (concat('1 - 01', repeat('x', @suffix_len)), concat('2 - 01', repeat('x', @suffix_len))); -insert into t2_1025 values - (concat('1 - 02', repeat('x', @suffix_len)), concat('2 - 02', repeat('x', @suffix_len))); -insert into t2_1025 values - (concat('1 - 03', repeat('x', @suffix_len)), concat('2 - 03', repeat('x', @suffix_len))); - -insert into t3_1025 values - (concat('1 - 01', repeat('x', @suffix_len)), concat('2 - 01', repeat('x', @suffix_len))); -insert into t3_1025 values - (concat('1 - 02', repeat('x', @suffix_len)), concat('2 - 02', repeat('x', @suffix_len))); -insert into t3_1025 values - (concat('1 - 03', repeat('x', @suffix_len)), concat('2 - 03', repeat('x', @suffix_len))); -insert into t3_1025 values - (concat('1 - 04', repeat('x', @suffix_len)), concat('2 - 04', repeat('x', @suffix_len))); - -# single value transformer -explain extended select left(a1,7), left(a2,7) -from t1_1025 -where a1 in (select b1 from t2_1025 where b1 > '0'); - -select left(a1,7), left(a2,7) -from t1_1025 -where a1 in (select b1 from t2_1025 where b1 > '0'); - -# row value transformer -explain extended select left(a1,7), left(a2,7) -from t1_1025 -where (a1,a2) in (select b1, b2 from t2_1025 where b1 > '0'); - -select left(a1,7), left(a2,7) -from t1_1025 -where (a1,a2) in (select b1, b2 from t2_1025 where b1 > '0'); - -# string function with a blob argument, the return type may be != blob -explain extended select left(a1,7), left(a2,7) -from t1_1025 -where a1 in (select substring(b1,1,1025) from t2_1025 where b1 > '0'); - -select left(a1,7), left(a2,7) -from t1_1025 -where a1 in (select substring(b1,1,1025) from t2_1025 where b1 > '0'); - -# group_concat with a blob argument - depends on -# the variable group_concat_max_len, and -# convert_blob_length == max_len*collation->mbmaxlen > CONVERT_IF_BIGGER_TO_BLOB -explain extended select left(a1,7), left(a2,7) -from t1_1025 -where a1 in (select group_concat(b1) from t2_1025 group by b2); - -select left(a1,7), left(a2,7) -from t1_1025 -where a1 in (select group_concat(b1) from t2_1025 group by b2); - -set @@group_concat_max_len = 256; # anything < (CONVERT_IF_BIGGER_TO_BLOB = 1025) - -explain extended select left(a1,7), left(a2,7) -from t1_1025 -where a1 in (select group_concat(b1) from t2_1025 group by b2); - -select left(a1,7), left(a2,7) -from t1_1025 -where a1 in (select group_concat(b1) from t2_1025 group by b2); - -drop table t1_1025, t2_1025, t3_1025; - -# test for BIT fields -create table t1bit (a1 bit(3), a2 bit(3)); -create table t2bit (b1 bit(3), b2 bit(3)); - -insert into t1bit values (b'000', b'100'); -insert into t1bit values (b'001', b'101'); -insert into t1bit values (b'010', b'110'); - -insert into t2bit values (b'001', b'101'); -insert into t2bit values (b'010', b'110'); -insert into t2bit values (b'110', b'111'); - -set @@optimizer_switch='materialization=on,in_to_exists=off,semijoin=off'; - -explain extended select bin(a1), bin(a2) -from t1bit -where (a1, a2) in (select b1, b2 from t2bit); - -select bin(a1), bin(a2) -from t1bit -where (a1, a2) in (select b1, b2 from t2bit); - -drop table t1bit, t2bit; - -# test mixture of BIT and BLOB -create table t1bb (a1 bit(3), a2 blob(3)); -create table t2bb (b1 bit(3), b2 blob(3)); - -insert into t1bb values (b'000', '100'); -insert into t1bb values (b'001', '101'); -insert into t1bb values (b'010', '110'); - -insert into t2bb values (b'001', '101'); -insert into t2bb values (b'010', '110'); -insert into t2bb values (b'110', '111'); - -explain extended select bin(a1), a2 -from t1bb -where (a1, a2) in (select b1, b2 from t2bb); - -select bin(a1), a2 -from t1bb -where (a1, a2) in (select b1, b2 from t2bb); - -drop table t1bb, t2bb; -drop table t1, t2, t3, t1i, t2i, t3i, columns; - -/****************************************************************************** -* Test the cache of the left operand of IN. -******************************************************************************/ -set @@optimizer_switch='materialization=on,in_to_exists=off,semijoin=off'; - -# Test that default values of Cached_item are not used for comparison -create table t1 (s1 int); -create table t2 (s2 int); -insert into t1 values (5),(1),(0); -insert into t2 values (0), (1); -select s2 from t2 where s2 in (select s1 from t1); -drop table t1, t2; - -create table t1 (a int not null, b int not null); -create table t2 (c int not null, d int not null); -create table t3 (e int not null); - -# the first outer row has no matching inner row -insert into t1 values (1,10); -insert into t1 values (1,20); -insert into t1 values (2,10); -insert into t1 values (2,20); -insert into t1 values (2,30); -insert into t1 values (3,20); -insert into t1 values (4,40); - -insert into t2 values (2,10); -insert into t2 values (2,20); -insert into t2 values (2,40); -insert into t2 values (3,20); -insert into t2 values (4,10); -insert into t2 values (5,10); - -insert into t3 values (10); -insert into t3 values (10); -insert into t3 values (20); -insert into t3 values (30); - -explain extended -select a from t1 where a in (select c from t2 where d >= 20); -select a from t1 where a in (select c from t2 where d >= 20); - -create index it1a on t1(a); - -explain extended -select a from t1 where a in (select c from t2 where d >= 20); -select a from t1 where a in (select c from t2 where d >= 20); - -# the first outer row has a matching inner row -insert into t2 values (1,10); - -explain extended -select a from t1 where a in (select c from t2 where d >= 20); -select a from t1 where a in (select c from t2 where d >= 20); - -# cacheing for IN predicates inside a having clause - here the cached -# items are changed to point to temporary tables. -explain extended -select a from t1 group by a having a in (select c from t2 where d >= 20); -select a from t1 group by a having a in (select c from t2 where d >= 20); - -# create an index that can be used for the outer query GROUP BY -create index iab on t1(a, b); -explain extended -select a from t1 group by a having a in (select c from t2 where d >= 20); -select a from t1 group by a having a in (select c from t2 where d >= 20); - -explain extended -select a from t1 group by a -having a in (select c from t2 where d >= some(select e from t3 where max(b)=e)); -select a from t1 group by a -having a in (select c from t2 where d >= some(select e from t3 where max(b)=e)); -explain extended -select a from t1 -where a in (select c from t2 where d >= some(select e from t3 where b=e)); -select a from t1 -where a in (select c from t2 where d >= some(select e from t3 where b=e)); - -drop table t1, t2, t3; - -# -# BUG#36133 "Assertion `exec_method != MATERIALIZATION || (exec_method == MATERIALIZATION &&" -# -create table t2 (a int, b int, key(a), key(b)); -insert into t2 values (3,3),(3,3),(3,3); -select 1 from t2 where - t2.a > 1 - or - t2.a = 3 and not t2.a not in (select t2.b from t2); -drop table t2; - -# -# BUG#37896 Assertion on entry of Item_in_subselect::exec on subquery with AND NOT -# -create table t1 (a1 int key); -create table t2 (b1 int); -insert into t1 values (5); --- echo Only the last query returns correct result. Filed as BUG#40037. -# Query with group by, executed via materialization -set @@optimizer_switch='materialization=on,in_to_exists=off,semijoin=off'; -explain select min(a1) from t1 where 7 in (select b1 from t2 group by b1); -select min(a1) from t1 where 7 in (select b1 from t2 group by b1); -# Query with group by, executed via IN=>EXISTS -set @@optimizer_switch='materialization=off,in_to_exists=on,semijoin=off'; -explain select min(a1) from t1 where 7 in (select b1 from t2 group by b1); -select min(a1) from t1 where 7 in (select b1 from t2 group by b1); - -# Executed with materialization -set @@optimizer_switch='materialization=on,in_to_exists=off,semijoin=off'; -explain select min(a1) from t1 where 7 in (select b1 from t2); -select min(a1) from t1 where 7 in (select b1 from t2); -# Executed via IN=>EXISTS -set @@optimizer_switch='materialization=off,in_to_exists=on,semijoin=off'; -explain select min(a1) from t1 where 7 in (select b1 from t2); -select min(a1) from t1 where 7 in (select b1 from t2); -# Executed with semi-join. Notice, this time we get a different result (NULL). -# This is the only correct result of all five queries. This difference is -# filed as BUG#40037. -set @@optimizer_switch='materialization=off,in_to_exists=off,semijoin=on'; -explain select min(a1) from t1 where 7 in (select b1 from t2); -select min(a1) from t1 where 7 in (select b1 from t2); -drop table t1,t2; - -# -# BUG#36752 "subquery materialization produces wrong results when comparing different types" -# -create table t1 (a char(2), b varchar(10)); -insert into t1 values ('a', 'aaa'); -insert into t1 values ('aa', 'aaaa'); - -set @@optimizer_switch='materialization=on,in_to_exists=off,semijoin=off'; -explain select a,b from t1 where b in (select a from t1); -select a,b from t1 where b in (select a from t1); -prepare st1 from "select a,b from t1 where b in (select a from t1)"; -execute st1; -execute st1; -drop table t1; - -# -# Bug #44303 Assertion failures in Field_new_decimal::store_decimal -# when executing materialized InsideOut semijoin -# -CREATE TABLE t1 (f1 INT, f2 DECIMAL(5,3)) ENGINE=MyISAM; -INSERT INTO t1 (f1, f2) VALUES (1, 1.789); -INSERT INTO t1 (f1, f2) VALUES (13, 1.454); -INSERT INTO t1 (f1, f2) VALUES (10, 1.668); - -CREATE TABLE t2 LIKE t1; -INSERT INTO t2 VALUES (1, 1.789); -INSERT INTO t2 VALUES (13, 1.454); - -SET @@optimizer_switch='materialization=on,in_to_exists=off,semijoin=on'; -EXPLAIN SELECT COUNT(*) FROM t1 WHERE (f1,f2) IN (SELECT f1,f2 FROM t2); -SELECT COUNT(*) FROM t1 WHERE (f1,f2) IN (SELECT f1,f2 FROM t2); - -DROP TABLE t1, t2; - -# -# BUG#46548 IN-subqueries return 0 rows with materialization=on -# -CREATE TABLE t1 ( - pk int, - a varchar(1), - b varchar(4), - c varchar(4), - d varchar(4), - PRIMARY KEY (pk) -); -INSERT INTO t1 VALUES (1,'o','ffff','ffff','ffoo'),(2,'f','ffff','ffff','ffff'); - -CREATE TABLE t2 LIKE t1; -INSERT INTO t2 VALUES (1,'i','iiii','iiii','iiii'),(2,'f','ffff','ffff','ffff'); - -SET @@optimizer_switch='materialization=on,in_to_exists=off,semijoin=on'; -EXPLAIN SELECT pk FROM t1 WHERE (a) IN (SELECT a FROM t2 WHERE pk > 0); -SELECT pk FROM t1 WHERE (a) IN (SELECT a FROM t2 WHERE pk > 0); -SELECT pk FROM t1 WHERE (b,c,d) IN (SELECT b,c,d FROM t2 WHERE pk > 0); -DROP TABLE t1, t2; - ---echo # ---echo # BUG#50019: Wrong result for IN-subquery with materialization ---echo # -create table t1(i int); -insert into t1 values (1), (2), (3), (4), (5), (6), (7), (8), (9), (10); -create table t2(i int); -insert into t2 values (1), (2), (3), (4), (5), (6), (7), (8), (9), (10); -create table t3(i int); -insert into t3 values (1), (2), (3), (4), (5), (6), (7), (8), (9), (10); -select * from t1 where t1.i in (select t2.i from t2 join t3 where t2.i + t3.i = 5); -set @save_optimizer_switch=@@optimizer_switch; -set session optimizer_switch='materialization=off,in_to_exists=on'; -select * from t1 where t1.i in (select t2.i from t2 join t3 where t2.i + t3.i = 5); -set session optimizer_switch=@save_optimizer_switch; -drop table t1, t2, t3; - # # Test that the contents of the temp table of a materialized subquery is # cleaned up between PS re-executions. @@ -1016,6 +115,36 @@ SELECT pk FROM t1 WHERE (a) IN (SELECT a FROM t2 WHERE pk > 0); SELECT pk FROM t1 WHERE (b,c,d) IN (SELECT b,c,d FROM t2 WHERE pk > 0); DROP TABLE t1, t2; + +-- echo # +-- echo # BUG#724228: Wrong result with materialization=on and three aggregates in maria-5.3-mwl90 +-- echo # +CREATE TABLE t1 ( f2 int(11)) ; +INSERT IGNORE INTO t1 VALUES ('7'),('9'),('7'),('4'),('2'),('6'),('8'),('5'),('6'),('188'),('2'),('1'),('1'),('0'),('9'),('4'); + +CREATE TABLE t2 ( f1 int(11), f2 int(11)) ENGINE=MyISAM; +INSERT IGNORE INTO t2 VALUES ('1','1'); + +CREATE TABLE t3 ( f1 int(11), f2 int(11), f3 int(11), PRIMARY KEY (f1)) ; +INSERT IGNORE INTO t3 VALUES ('16','6','1'),('18','3','4'),('19',NULL,'9'),('20','0','6'),('41','2','0'),('42','2','5'),('43','9','6'),('44','7','4'),('45','1','4'),('46','222','238'),('47','3','6'),('48','6','6'),('49',NULL,'1'),('50','5','1'); + +SET @_save_join_cache_level = @@join_cache_level; +SET @_save_optimizer_switch = @@optimizer_switch; + +SET join_cache_level = 1; +SET optimizer_switch='materialization=on'; + +SELECT f1 FROM t3 +WHERE + f1 NOT IN (SELECT MAX(f2) FROM t1) AND + f3 IN (SELECT MIN(f1) FROM t2) AND + f1 IN (SELECT COUNT(f2) FROM t1); + +SET @@join_cache_level = @_save_join_cache_level; +SET @@optimizer_switch = @_save_optimizer_switch; + +drop table t1, t2, t3; + --echo # --echo # LPBUG#719198 Ordered_key::cmp_key_with_search_key(rownum_t): Assertion `!compare_pred[i]->null_value' --echo # failed with subquery on both sides of NOT IN and materialization diff --git a/mysql-test/t/subselect_sj2.test b/mysql-test/t/subselect_sj2.test index 9e71d91b47b..274e7b42c6f 100644 --- a/mysql-test/t/subselect_sj2.test +++ b/mysql-test/t/subselect_sj2.test @@ -494,6 +494,12 @@ drop table t1, t2; # Bug#33062: subquery in stored routine cause crash # +--disable_warnings +drop procedure if exists p1; +drop procedure if exists p2; +drop procedure if exists p3; +drop procedure if exists p4; +--enable_warnings CREATE TABLE t1(a INT); CREATE TABLE t2(c INT); diff --git a/mysql-test/t/subselect_sj_mat.test b/mysql-test/t/subselect_sj_mat.test new file mode 100644 index 00000000000..728945be880 --- /dev/null +++ b/mysql-test/t/subselect_sj_mat.test @@ -0,0 +1,974 @@ +# +# Hash semi-join regression tests +# (WL#1110: Subquery optimization: materialization) +# + +--disable_warnings +drop table if exists t1, t2, t3, t1i, t2i, t3i; +drop table if exists columns; +drop table if exists t1_16, t2_16, t3_16; +drop view if exists v1, v2, v1m, v2m; +--enable_warnings + +create table t1 (a1 char(8), a2 char(8)); +create table t2 (b1 char(8), b2 char(8)); +create table t3 (c1 char(8), c2 char(8)); + +insert into t1 values ('1 - 00', '2 - 00'); +insert into t1 values ('1 - 01', '2 - 01'); +insert into t1 values ('1 - 02', '2 - 02'); + +insert into t2 values ('1 - 01', '2 - 01'); +insert into t2 values ('1 - 01', '2 - 01'); +insert into t2 values ('1 - 02', '2 - 02'); +insert into t2 values ('1 - 02', '2 - 02'); +insert into t2 values ('1 - 03', '2 - 03'); + +insert into t3 values ('1 - 01', '2 - 01'); +insert into t3 values ('1 - 02', '2 - 02'); +insert into t3 values ('1 - 03', '2 - 03'); +insert into t3 values ('1 - 04', '2 - 04'); + +# Indexed columns +create table t1i (a1 char(8), a2 char(8)); +create table t2i (b1 char(8), b2 char(8)); +create table t3i (c1 char(8), c2 char(8)); +create index it1i1 on t1i (a1); +create index it1i2 on t1i (a2); +create index it1i3 on t1i (a1, a2); + +create index it2i1 on t2i (b1); +create index it2i2 on t2i (b2); +create index it2i3 on t2i (b1, b2); + +create index it3i1 on t3i (c1); +create index it3i2 on t3i (c2); +create index it3i3 on t3i (c1, c2); + +insert into t1i select * from t1; +insert into t2i select * from t2; +insert into t3i select * from t3; + +# force the use of materialization +set @@optimizer_switch='materialization=on,in_to_exists=off,firstmatch=off'; + +/****************************************************************************** +* Simple tests. +******************************************************************************/ +# non-indexed nullable fields +explain extended +select * from t1 where a1 in (select b1 from t2 where b1 > '0'); +select * from t1 where a1 in (select b1 from t2 where b1 > '0'); + +explain extended +select * from t1 where a1 in (select b1 from t2 where b1 > '0' group by b1); +select * from t1 where a1 in (select b1 from t2 where b1 > '0' group by b1); + +explain extended +select * from t1 where (a1, a2) in (select b1, b2 from t2 where b1 > '0' group by b1, b2); +select * from t1 where (a1, a2) in (select b1, b2 from t2 where b1 > '0' group by b1, b2); + +explain extended +select * from t1 where (a1, a2) in (select b1, min(b2) from t2 where b1 > '0' group by b1); +select * from t1 where (a1, a2) in (select b1, min(b2) from t2 where b1 > '0' group by b1); + +# indexed columns +--replace_column 7 # +--replace_regex /it1.*/_it1_idx/ /test.t2i.*/_ref_/ /Using index$// /Using where$// +explain extended +select * from t1i where a1 in (select b1 from t2i where b1 > '0'); +select * from t1i where a1 in (select b1 from t2i where b1 > '0'); + +--replace_column 6 # 8 # 11 # +explain extended +select * from t1i where a1 in (select b1 from t2i where b1 > '0' group by b1); +select * from t1i where a1 in (select b1 from t2i where b1 > '0' group by b1); + +--replace_column 7 # +--replace_regex /it1.*/_it1_idx/ /test.t2i.*/_ref_/ /Using index$// /Using where$// +explain extended +select * from t1i where (a1, a2) in (select b1, b2 from t2i where b1 > '0'); +select * from t1i where (a1, a2) in (select b1, b2 from t2i where b1 > '0'); + +--replace_column 6 # 7 # 8 # 11 # +explain extended +select * from t1i where (a1, a2) in (select b1, b2 from t2i where b1 > '0' group by b1, b2); +select * from t1i where (a1, a2) in (select b1, b2 from t2i where b1 > '0' group by b1, b2); + +--replace_column 6 # 7 # 8 # 11 # +explain extended +select * from t1i where (a1, a2) in (select b1, min(b2) from t2i where b1 > '0' group by b1); +select * from t1i where (a1, a2) in (select b1, min(b2) from t2i where b1 > '0' group by b1); + +# BUG#31639: Wrong plan for uncorrelated subquery when loose scan is applicable. +explain extended +select * from t1 where (a1, a2) in (select b1, max(b2) from t2i group by b1); +select * from t1 where (a1, a2) in (select b1, max(b2) from t2i group by b1); + +prepare st1 from "explain select * from t1 where (a1, a2) in (select b1, max(b2) from t2i group by b1)"; +execute st1; +execute st1; +prepare st2 from "select * from t1 where (a1, a2) in (select b1, max(b2) from t2i group by b1)"; +execute st2; +execute st2; + +explain extended +select * from t1 where (a1, a2) in (select b1, min(b2) from t2i where b1 > '0' group by b1); +select * from t1 where (a1, a2) in (select b1, min(b2) from t2i where b1 > '0' group by b1); +-- error 1235 +select * from t1 where (a1, a2) in (select b1, min(b2) from t2i limit 1,1); + +# test re-optimization/re-execution with different execution methods +# prepare once, exec with different modes +set @save_optimizer_switch=@@optimizer_switch; +set @@optimizer_switch='default,semijoin=off'; +prepare st1 from +"select * from t1 where (a1, a2) in (select b1, min(b2) from t2 where b1 > '0' group by b1)"; +set @@optimizer_switch='default,materialization=off'; +execute st1; +set @@optimizer_switch='default,semijoin=off'; +execute st1; + +set @@optimizer_switch='default,materialization=off'; +prepare st1 from +"select * from t1 where (a1, a2) in (select b1, min(b2) from t2 where b1 > '0' group by b1)"; +set @@optimizer_switch='default,semijoin=off'; +execute st1; +set @@optimizer_switch='default,materialization=off'; +execute st1; +set @@optimizer_switch=@save_optimizer_switch; + +# materialize the result of ORDER BY +# non-indexed fields +explain extended +select * from t1 where (a1, a2) in (select b1, b2 from t2 order by b1, b2); +select * from t1 where (a1, a2) in (select b1, b2 from t2 order by b1, b2); +# indexed fields +explain extended +select * from t1i where (a1, a2) in (select b1, b2 from t2i order by b1, b2); +select * from t1i where (a1, a2) in (select b1, b2 from t2i order by b1, b2); + +/****************************************************************************** +* Views, UNIONs, several levels of nesting. +******************************************************************************/ +# materialize the result of subquery over temp-table view + +create algorithm=merge view v1 as +select b1, c2 from t2, t3 where b2 > c2; + +create algorithm=merge view v2 as +select b1, c2 from t2, t3 group by b2, c2; + +create algorithm=temptable view v1m as +select b1, c2 from t2, t3 where b2 > c2; + +create algorithm=temptable view v2m as +select b1, c2 from t2, t3 group by b2, c2; + +select * from v1 where (c2, b1) in (select c2, b1 from v2 where b1 is not null); +select * from v1 where (c2, b1) in (select distinct c2, b1 from v2 where b1 is not null); + +select * from v1m where (c2, b1) in (select c2, b1 from v2m where b1 is not null); +select * from v1m where (c2, b1) in (select distinct c2, b1 from v2m where b1 is not null); + +drop view v1, v2, v1m, v2m; + +# nested subqueries, views +explain extended +select * from t1 +where (a1, a2) in (select b1, b2 from t2 where b1 > '0') and + (a1, a2) in (select c1, c2 from t3 + where (c1, c2) in (select b1, b2 from t2i where b2 > '0')); +select * from t1 +where (a1, a2) in (select b1, b2 from t2 where b1 > '0') and + (a1, a2) in (select c1, c2 from t3 + where (c1, c2) in (select b1, b2 from t2i where b2 > '0')); + +--replace_column 6 # 7 # 8 # 11 # +explain extended +select * from t1i +where (a1, a2) in (select b1, b2 from t2i where b1 > '0') and + (a1, a2) in (select c1, c2 from t3i + where (c1, c2) in (select b1, b2 from t2i where b2 > '0')); +select * from t1i +where (a1, a2) in (select b1, b2 from t2i where b1 > '0') and + (a1, a2) in (select c1, c2 from t3i + where (c1, c2) in (select b1, b2 from t2i where b2 > '0')); + +explain extended +select * from t1 +where (a1, a2) in (select b1, b2 from t2 + where b2 in (select c2 from t3 where c2 LIKE '%02') or + b2 in (select c2 from t3 where c2 LIKE '%03')) and + (a1, a2) in (select c1, c2 from t3 + where (c1, c2) in (select b1, b2 from t2i where b2 > '0')); +select * from t1 +where (a1, a2) in (select b1, b2 from t2 + where b2 in (select c2 from t3 where c2 LIKE '%02') or + b2 in (select c2 from t3 where c2 LIKE '%03')) and + (a1, a2) in (select c1, c2 from t3 + where (c1, c2) in (select b1, b2 from t2i where b2 > '0')); + +# as above with correlated innermost subquery +explain extended +select * from t1 +where (a1, a2) in (select b1, b2 from t2 + where b2 in (select c2 from t3 t3a where c1 = a1) or + b2 in (select c2 from t3 t3b where c2 LIKE '%03')) and + (a1, a2) in (select c1, c2 from t3 t3c + where (c1, c2) in (select b1, b2 from t2i where b2 > '0')); +select * from t1 +where (a1, a2) in (select b1, b2 from t2 + where b2 in (select c2 from t3 t3a where c1 = a1) or + b2 in (select c2 from t3 t3b where c2 LIKE '%03')) and + (a1, a2) in (select c1, c2 from t3 t3c + where (c1, c2) in (select b1, b2 from t2i where b2 > '0')); + + +# multiple levels of nesting subqueries, unions +--replace_column 6 # 7 # 8 # 11 # +explain extended +(select * from t1 +where (a1, a2) in (select b1, b2 from t2 + where b2 in (select c2 from t3 where c2 LIKE '%02') or + b2 in (select c2 from t3 where c2 LIKE '%03') + group by b1, b2) and + (a1, a2) in (select c1, c2 from t3 + where (c1, c2) in (select b1, b2 from t2i where b2 > '0'))) +UNION +(select * from t1i +where (a1, a2) in (select b1, b2 from t2i where b1 > '0') and + (a1, a2) in (select c1, c2 from t3i + where (c1, c2) in (select b1, b2 from t2i where b2 > '0'))); + +(select * from t1 +where (a1, a2) in (select b1, b2 from t2 + where b2 in (select c2 from t3 where c2 LIKE '%02') or + b2 in (select c2 from t3 where c2 LIKE '%03') + group by b1, b2) and + (a1, a2) in (select c1, c2 from t3 + where (c1, c2) in (select b1, b2 from t2i where b2 > '0'))) +UNION +(select * from t1i +where (a1, a2) in (select b1, b2 from t2i where b1 > '0') and + (a1, a2) in (select c1, c2 from t3i + where (c1, c2) in (select b1, b2 from t2i where b2 > '0'))); + + +# UNION of subqueries as a subquery (thus it is not computed via materialization) +explain extended +select * from t1 +where (a1, a2) in (select * from t1 where a1 > '0' UNION select * from t2 where b1 < '9') and + (a1, a2) in (select c1, c2 from t3 + where (c1, c2) in (select b1, b2 from t2i where b2 > '0')); +select * from t1 +where (a1, a2) in (select * from t1 where a1 > '0' UNION select * from t2 where b1 < '9') and + (a1, a2) in (select c1, c2 from t3 + where (c1, c2) in (select b1, b2 from t2i where b2 > '0')); +# as above, with a join conditon between the outer references +explain extended +select * from t1, t3 +where (a1, a2) in (select * from t1 where a1 > '0' UNION select * from t2 where b1 < '9') and + (c1, c2) in (select c1, c2 from t3 + where (c1, c2) in (select b1, b2 from t2i where b2 > '0')) and + a1 = c1; +select * from t1, t3 +where (a1, a2) in (select * from t1 where a1 > '0' UNION select * from t2 where b1 < '9') and + (c1, c2) in (select c1, c2 from t3 + where (c1, c2) in (select b1, b2 from t2i where b2 > '0')) and + a1 = c1; + + +/****************************************************************************** +* Negative tests, where materialization should not be applied. +******************************************************************************/ +# UNION in a subquery +explain extended +select * from t3 +where c1 in (select a1 from t1 where a1 > '0' UNION select b1 from t2 where b1 < '9'); +select * from t3 +where c1 in (select a1 from t1 where a1 > '0' UNION select b1 from t2 where b1 < '9'); + +# correlation +explain extended +select * from t1 +where (a1, a2) in (select b1, b2 from t2 + where b2 in (select c2 from t3 t3a where c1 = a1) or + b2 in (select c2 from t3 t3b where c2 LIKE '%03')) and + (a1, a2) in (select c1, c2 from t3 t3c + where (c1, c2) in (select b1, b2 from t2i where b2 > '0' or b2 = a2)); + +# subquery has no tables +explain extended +select * from t1 where (a1, a2) in (select '1 - 01', '2 - 01'); +select * from t1 where (a1, a2) in (select '1 - 01', '2 - 01'); +explain extended +select * from t1 where (a1, a2) in (select '1 - 01', '2 - 01' from dual); +select * from t1 where (a1, a2) in (select '1 - 01', '2 - 01' from dual); + + +/****************************************************************************** +* Subqueries in other uncovered clauses. +******************************************************************************/ + +/* SELECT clause */ +select ((a1,a2) IN (select * from t2 where b2 > '0')) IS NULL from t1; + +/* GROUP BY clause */ +create table columns (col int key); +insert into columns values (1), (2); + +explain extended +select * from t1 group by (select col from columns limit 1); +select * from t1 group by (select col from columns limit 1); + +explain extended +select * from t1 group by (a1 in (select col from columns)); +select * from t1 group by (a1 in (select col from columns)); + +/* ORDER BY clause */ +explain extended +select * from t1 order by (select col from columns limit 1); +select * from t1 order by (select col from columns limit 1); + +/****************************************************************************** +* Column types/sizes that affect materialization. +******************************************************************************/ + +/* + Test that BLOBs are not materialized (except when arguments of some functions). +*/ +# force materialization to be always considered +set @prefix_len = 6; + +# BLOB == 16 (small blobs that could be stored in HEAP tables) +set @blob_len = 16; +set @suffix_len = @blob_len - @prefix_len; + +create table t1_16 (a1 blob(16), a2 blob(16)); +create table t2_16 (b1 blob(16), b2 blob(16)); +create table t3_16 (c1 blob(16), c2 blob(16)); + +insert into t1_16 values + (concat('1 - 00', repeat('x', @suffix_len)), concat('2 - 00', repeat('x', @suffix_len))); +insert into t1_16 values + (concat('1 - 01', repeat('x', @suffix_len)), concat('2 - 01', repeat('x', @suffix_len))); +insert into t1_16 values + (concat('1 - 02', repeat('x', @suffix_len)), concat('2 - 02', repeat('x', @suffix_len))); + +insert into t2_16 values + (concat('1 - 01', repeat('x', @suffix_len)), concat('2 - 01', repeat('x', @suffix_len))); +insert into t2_16 values + (concat('1 - 02', repeat('x', @suffix_len)), concat('2 - 02', repeat('x', @suffix_len))); +insert into t2_16 values + (concat('1 - 03', repeat('x', @suffix_len)), concat('2 - 03', repeat('x', @suffix_len))); + +insert into t3_16 values + (concat('1 - 01', repeat('x', @suffix_len)), concat('2 - 01', repeat('x', @suffix_len))); +insert into t3_16 values + (concat('1 - 02', repeat('x', @suffix_len)), concat('2 - 02', repeat('x', @suffix_len))); +insert into t3_16 values + (concat('1 - 03', repeat('x', @suffix_len)), concat('2 - 03', repeat('x', @suffix_len))); +insert into t3_16 values + (concat('1 - 04', repeat('x', @suffix_len)), concat('2 - 04', repeat('x', @suffix_len))); + +# single value transformer +explain extended select left(a1,7), left(a2,7) +from t1_16 +where a1 in (select b1 from t2_16 where b1 > '0'); + +select left(a1,7), left(a2,7) +from t1_16 +where a1 in (select b1 from t2_16 where b1 > '0'); + +# row value transformer +explain extended select left(a1,7), left(a2,7) +from t1_16 +where (a1,a2) in (select b1, b2 from t2_16 where b1 > '0'); + +select left(a1,7), left(a2,7) +from t1_16 +where (a1,a2) in (select b1, b2 from t2_16 where b1 > '0'); + +# string function with a blob argument, the return type may be != blob +explain extended select left(a1,7), left(a2,7) +from t1_16 +where a1 in (select substring(b1,1,16) from t2_16 where b1 > '0'); + +select left(a1,7), left(a2,7) +from t1_16 +where a1 in (select substring(b1,1,16) from t2_16 where b1 > '0'); + +# group_concat with a blob argument - depends on +# the variable group_concat_max_len, and +# convert_blob_length == max_len*collation->mbmaxlen > CONVERT_IF_BIGGER_TO_BLOB +explain extended select left(a1,7), left(a2,7) +from t1_16 +where a1 in (select group_concat(b1) from t2_16 group by b2); + +select left(a1,7), left(a2,7) +from t1_16 +where a1 in (select group_concat(b1) from t2_16 group by b2); + +set @@group_concat_max_len = 256; # anything < (CONVERT_IF_BIGGER_TO_BLOB = 512) + +explain extended select left(a1,7), left(a2,7) +from t1_16 +where a1 in (select group_concat(b1) from t2_16 group by b2); + +select left(a1,7), left(a2,7) +from t1_16 +where a1 in (select group_concat(b1) from t2_16 group by b2); + +# BLOB column at the second (intermediate) level of nesting +explain extended +select * from t1 +where concat(a1,'x') IN + (select left(a1,8) from t1_16 + where (a1, a2) IN + (select t2_16.b1, t2_16.b2 from t2_16, t2 + where t2.b2 = substring(t2_16.b2,1,6) and + t2.b1 IN (select c1 from t3 where c2 > '0'))); + + +drop table t1_16, t2_16, t3_16; + + +# BLOB == 512 (CONVERT_IF_BIGGER_TO_BLOB == 512) +set @blob_len = 512; +set @suffix_len = @blob_len - @prefix_len; + +create table t1_512 (a1 blob(512), a2 blob(512)); +create table t2_512 (b1 blob(512), b2 blob(512)); +create table t3_512 (c1 blob(512), c2 blob(512)); + +insert into t1_512 values + (concat('1 - 00', repeat('x', @suffix_len)), concat('2 - 00', repeat('x', @suffix_len))); +insert into t1_512 values + (concat('1 - 01', repeat('x', @suffix_len)), concat('2 - 01', repeat('x', @suffix_len))); +insert into t1_512 values + (concat('1 - 02', repeat('x', @suffix_len)), concat('2 - 02', repeat('x', @suffix_len))); + +insert into t2_512 values + (concat('1 - 01', repeat('x', @suffix_len)), concat('2 - 01', repeat('x', @suffix_len))); +insert into t2_512 values + (concat('1 - 02', repeat('x', @suffix_len)), concat('2 - 02', repeat('x', @suffix_len))); +insert into t2_512 values + (concat('1 - 03', repeat('x', @suffix_len)), concat('2 - 03', repeat('x', @suffix_len))); + +insert into t3_512 values + (concat('1 - 01', repeat('x', @suffix_len)), concat('2 - 01', repeat('x', @suffix_len))); +insert into t3_512 values + (concat('1 - 02', repeat('x', @suffix_len)), concat('2 - 02', repeat('x', @suffix_len))); +insert into t3_512 values + (concat('1 - 03', repeat('x', @suffix_len)), concat('2 - 03', repeat('x', @suffix_len))); +insert into t3_512 values + (concat('1 - 04', repeat('x', @suffix_len)), concat('2 - 04', repeat('x', @suffix_len))); + +# single value transformer +explain extended select left(a1,7), left(a2,7) +from t1_512 +where a1 in (select b1 from t2_512 where b1 > '0'); + +select left(a1,7), left(a2,7) +from t1_512 +where a1 in (select b1 from t2_512 where b1 > '0'); + +# row value transformer +explain extended select left(a1,7), left(a2,7) +from t1_512 +where (a1,a2) in (select b1, b2 from t2_512 where b1 > '0'); + +select left(a1,7), left(a2,7) +from t1_512 +where (a1,a2) in (select b1, b2 from t2_512 where b1 > '0'); + +# string function with a blob argument, the return type may be != blob +explain extended select left(a1,7), left(a2,7) +from t1_512 +where a1 in (select substring(b1,1,512) from t2_512 where b1 > '0'); + +select left(a1,7), left(a2,7) +from t1_512 +where a1 in (select substring(b1,1,512) from t2_512 where b1 > '0'); + +# group_concat with a blob argument - depends on +# the variable group_concat_max_len, and +# convert_blob_length == max_len*collation->mbmaxlen > CONVERT_IF_BIGGER_TO_BLOB +explain extended select left(a1,7), left(a2,7) +from t1_512 +where a1 in (select group_concat(b1) from t2_512 group by b2); + +select left(a1,7), left(a2,7) +from t1_512 +where a1 in (select group_concat(b1) from t2_512 group by b2); + +set @@group_concat_max_len = 256; # anything < (CONVERT_IF_BIGGER_TO_BLOB = 512) + +explain extended select left(a1,7), left(a2,7) +from t1_512 +where a1 in (select group_concat(b1) from t2_512 group by b2); + +select left(a1,7), left(a2,7) +from t1_512 +where a1 in (select group_concat(b1) from t2_512 group by b2); + +drop table t1_512, t2_512, t3_512; + + +# BLOB == 1024 (group_concat_max_len == 1024) +set @blob_len = 1024; +set @suffix_len = @blob_len - @prefix_len; + +create table t1_1024 (a1 blob(1024), a2 blob(1024)); +create table t2_1024 (b1 blob(1024), b2 blob(1024)); +create table t3_1024 (c1 blob(1024), c2 blob(1024)); + +insert into t1_1024 values + (concat('1 - 00', repeat('x', @suffix_len)), concat('2 - 00', repeat('x', @suffix_len))); +insert into t1_1024 values + (concat('1 - 01', repeat('x', @suffix_len)), concat('2 - 01', repeat('x', @suffix_len))); +insert into t1_1024 values + (concat('1 - 02', repeat('x', @suffix_len)), concat('2 - 02', repeat('x', @suffix_len))); + +insert into t2_1024 values + (concat('1 - 01', repeat('x', @suffix_len)), concat('2 - 01', repeat('x', @suffix_len))); +insert into t2_1024 values + (concat('1 - 02', repeat('x', @suffix_len)), concat('2 - 02', repeat('x', @suffix_len))); +insert into t2_1024 values + (concat('1 - 03', repeat('x', @suffix_len)), concat('2 - 03', repeat('x', @suffix_len))); + +insert into t3_1024 values + (concat('1 - 01', repeat('x', @suffix_len)), concat('2 - 01', repeat('x', @suffix_len))); +insert into t3_1024 values + (concat('1 - 02', repeat('x', @suffix_len)), concat('2 - 02', repeat('x', @suffix_len))); +insert into t3_1024 values + (concat('1 - 03', repeat('x', @suffix_len)), concat('2 - 03', repeat('x', @suffix_len))); +insert into t3_1024 values + (concat('1 - 04', repeat('x', @suffix_len)), concat('2 - 04', repeat('x', @suffix_len))); + +# single value transformer +explain extended select left(a1,7), left(a2,7) +from t1_1024 +where a1 in (select b1 from t2_1024 where b1 > '0'); + +select left(a1,7), left(a2,7) +from t1_1024 +where a1 in (select b1 from t2_1024 where b1 > '0'); + +# row value transformer +explain extended select left(a1,7), left(a2,7) +from t1_1024 +where (a1,a2) in (select b1, b2 from t2_1024 where b1 > '0'); + +select left(a1,7), left(a2,7) +from t1_1024 +where (a1,a2) in (select b1, b2 from t2_1024 where b1 > '0'); + +# string function with a blob argument, the return type may be != blob +explain extended select left(a1,7), left(a2,7) +from t1_1024 +where a1 in (select substring(b1,1,1024) from t2_1024 where b1 > '0'); + +select left(a1,7), left(a2,7) +from t1_1024 +where a1 in (select substring(b1,1,1024) from t2_1024 where b1 > '0'); + +# group_concat with a blob argument - depends on +# the variable group_concat_max_len, and +# convert_blob_length == max_len*collation->mbmaxlen > CONVERT_IF_BIGGER_TO_BLOB +explain extended select left(a1,7), left(a2,7) +from t1_1024 +where a1 in (select group_concat(b1) from t2_1024 group by b2); + +select left(a1,7), left(a2,7) +from t1_1024 +where a1 in (select group_concat(b1) from t2_1024 group by b2); + +set @@group_concat_max_len = 256; # anything < (CONVERT_IF_BIGGER_TO_BLOB = 1024) + +explain extended select left(a1,7), left(a2,7) +from t1_1024 +where a1 in (select group_concat(b1) from t2_1024 group by b2); + +select left(a1,7), left(a2,7) +from t1_1024 +where a1 in (select group_concat(b1) from t2_1024 group by b2); + +drop table t1_1024, t2_1024, t3_1024; + + +# BLOB == 1025 +set @blob_len = 1025; +set @suffix_len = @blob_len - @prefix_len; + +create table t1_1025 (a1 blob(1025), a2 blob(1025)); +create table t2_1025 (b1 blob(1025), b2 blob(1025)); +create table t3_1025 (c1 blob(1025), c2 blob(1025)); + +insert into t1_1025 values + (concat('1 - 00', repeat('x', @suffix_len)), concat('2 - 00', repeat('x', @suffix_len))); +insert into t1_1025 values + (concat('1 - 01', repeat('x', @suffix_len)), concat('2 - 01', repeat('x', @suffix_len))); +insert into t1_1025 values + (concat('1 - 02', repeat('x', @suffix_len)), concat('2 - 02', repeat('x', @suffix_len))); + +insert into t2_1025 values + (concat('1 - 01', repeat('x', @suffix_len)), concat('2 - 01', repeat('x', @suffix_len))); +insert into t2_1025 values + (concat('1 - 02', repeat('x', @suffix_len)), concat('2 - 02', repeat('x', @suffix_len))); +insert into t2_1025 values + (concat('1 - 03', repeat('x', @suffix_len)), concat('2 - 03', repeat('x', @suffix_len))); + +insert into t3_1025 values + (concat('1 - 01', repeat('x', @suffix_len)), concat('2 - 01', repeat('x', @suffix_len))); +insert into t3_1025 values + (concat('1 - 02', repeat('x', @suffix_len)), concat('2 - 02', repeat('x', @suffix_len))); +insert into t3_1025 values + (concat('1 - 03', repeat('x', @suffix_len)), concat('2 - 03', repeat('x', @suffix_len))); +insert into t3_1025 values + (concat('1 - 04', repeat('x', @suffix_len)), concat('2 - 04', repeat('x', @suffix_len))); + +# single value transformer +explain extended select left(a1,7), left(a2,7) +from t1_1025 +where a1 in (select b1 from t2_1025 where b1 > '0'); + +select left(a1,7), left(a2,7) +from t1_1025 +where a1 in (select b1 from t2_1025 where b1 > '0'); + +# row value transformer +explain extended select left(a1,7), left(a2,7) +from t1_1025 +where (a1,a2) in (select b1, b2 from t2_1025 where b1 > '0'); + +select left(a1,7), left(a2,7) +from t1_1025 +where (a1,a2) in (select b1, b2 from t2_1025 where b1 > '0'); + +# string function with a blob argument, the return type may be != blob +explain extended select left(a1,7), left(a2,7) +from t1_1025 +where a1 in (select substring(b1,1,1025) from t2_1025 where b1 > '0'); + +select left(a1,7), left(a2,7) +from t1_1025 +where a1 in (select substring(b1,1,1025) from t2_1025 where b1 > '0'); + +# group_concat with a blob argument - depends on +# the variable group_concat_max_len, and +# convert_blob_length == max_len*collation->mbmaxlen > CONVERT_IF_BIGGER_TO_BLOB +explain extended select left(a1,7), left(a2,7) +from t1_1025 +where a1 in (select group_concat(b1) from t2_1025 group by b2); + +select left(a1,7), left(a2,7) +from t1_1025 +where a1 in (select group_concat(b1) from t2_1025 group by b2); + +set @@group_concat_max_len = 256; # anything < (CONVERT_IF_BIGGER_TO_BLOB = 1025) + +explain extended select left(a1,7), left(a2,7) +from t1_1025 +where a1 in (select group_concat(b1) from t2_1025 group by b2); + +select left(a1,7), left(a2,7) +from t1_1025 +where a1 in (select group_concat(b1) from t2_1025 group by b2); + +drop table t1_1025, t2_1025, t3_1025; + +# test for BIT fields +create table t1bit (a1 bit(3), a2 bit(3)); +create table t2bit (b1 bit(3), b2 bit(3)); + +insert into t1bit values (b'000', b'100'); +insert into t1bit values (b'001', b'101'); +insert into t1bit values (b'010', b'110'); + +insert into t2bit values (b'001', b'101'); +insert into t2bit values (b'010', b'110'); +insert into t2bit values (b'110', b'111'); + +explain extended select bin(a1), bin(a2) +from t1bit +where (a1, a2) in (select b1, b2 from t2bit); + +select bin(a1), bin(a2) +from t1bit +where (a1, a2) in (select b1, b2 from t2bit); + +drop table t1bit, t2bit; + +# test mixture of BIT and BLOB +create table t1bb (a1 bit(3), a2 blob(3)); +create table t2bb (b1 bit(3), b2 blob(3)); + +insert into t1bb values (b'000', '100'); +insert into t1bb values (b'001', '101'); +insert into t1bb values (b'010', '110'); + +insert into t2bb values (b'001', '101'); +insert into t2bb values (b'010', '110'); +insert into t2bb values (b'110', '111'); + +explain extended select bin(a1), a2 +from t1bb +where (a1, a2) in (select b1, b2 from t2bb); + +select bin(a1), a2 +from t1bb +where (a1, a2) in (select b1, b2 from t2bb); + +drop table t1bb, t2bb; +drop table t1, t2, t3, t1i, t2i, t3i, columns; + +/****************************************************************************** +* Test the cache of the left operand of IN. +******************************************************************************/ + +# Test that default values of Cached_item are not used for comparison +create table t1 (s1 int); +create table t2 (s2 int); +insert into t1 values (5),(1),(0); +insert into t2 values (0), (1); +select s2 from t2 where s2 in (select s1 from t1); +drop table t1, t2; + +create table t1 (a int not null, b int not null); +create table t2 (c int not null, d int not null); +create table t3 (e int not null); + +# the first outer row has no matching inner row +insert into t1 values (1,10); +insert into t1 values (1,20); +insert into t1 values (2,10); +insert into t1 values (2,20); +insert into t1 values (2,30); +insert into t1 values (3,20); +insert into t1 values (4,40); + +insert into t2 values (2,10); +insert into t2 values (2,20); +insert into t2 values (2,40); +insert into t2 values (3,20); +insert into t2 values (4,10); +insert into t2 values (5,10); + +insert into t3 values (10); +insert into t3 values (10); +insert into t3 values (20); +insert into t3 values (30); + +explain extended +select a from t1 where a in (select c from t2 where d >= 20); +select a from t1 where a in (select c from t2 where d >= 20); + +create index it1a on t1(a); + +explain extended +select a from t1 where a in (select c from t2 where d >= 20); +select a from t1 where a in (select c from t2 where d >= 20); + +# the first outer row has a matching inner row +insert into t2 values (1,10); + +explain extended +select a from t1 where a in (select c from t2 where d >= 20); +select a from t1 where a in (select c from t2 where d >= 20); + +# cacheing for IN predicates inside a having clause - here the cached +# items are changed to point to temporary tables. +explain extended +select a from t1 group by a having a in (select c from t2 where d >= 20); +select a from t1 group by a having a in (select c from t2 where d >= 20); + +# create an index that can be used for the outer query GROUP BY +create index iab on t1(a, b); +explain extended +select a from t1 group by a having a in (select c from t2 where d >= 20); +select a from t1 group by a having a in (select c from t2 where d >= 20); + +explain extended +select a from t1 group by a +having a in (select c from t2 where d >= some(select e from t3 where max(b)=e)); +select a from t1 group by a +having a in (select c from t2 where d >= some(select e from t3 where max(b)=e)); +explain extended +select a from t1 +where a in (select c from t2 where d >= some(select e from t3 where b=e)); +select a from t1 +where a in (select c from t2 where d >= some(select e from t3 where b=e)); + +drop table t1, t2, t3; + +# +# BUG#36133 "Assertion `exec_method != MATERIALIZATION || (exec_method == MATERIALIZATION &&" +# +create table t2 (a int, b int, key(a), key(b)); +insert into t2 values (3,3),(3,3),(3,3); +select 1 from t2 where + t2.a > 1 + or + t2.a = 3 and not t2.a not in (select t2.b from t2); +drop table t2; + +# +# BUG#37896 Assertion on entry of Item_in_subselect::exec on subquery with AND NOT +# +create table t1 (a1 int key); +create table t2 (b1 int); +insert into t1 values (5); + +# Query with group by, executed via materialization +explain select min(a1) from t1 where 7 in (select b1 from t2 group by b1); +select min(a1) from t1 where 7 in (select b1 from t2 group by b1); +# Query with group by, executed via IN=>EXISTS +set @save_optimizer_switch=@@optimizer_switch; +set @@optimizer_switch='default,materialization=off'; +explain select min(a1) from t1 where 7 in (select b1 from t2 group by b1); +select min(a1) from t1 where 7 in (select b1 from t2 group by b1); + +# Executed with materialization +set @@optimizer_switch='default,semijoin=off'; +explain select min(a1) from t1 where 7 in (select b1 from t2); +select min(a1) from t1 where 7 in (select b1 from t2); +# Executed with semi-join. Notice, this time we get a different result (NULL). +# This is the only correct result of all four queries. This difference is +# filed as BUG#40037. +set @@optimizer_switch='default,materialization=off'; +-- echo # with MariaDB and MWL#90, this particular case is solved: +explain select min(a1) from t1 where 7 in (select b1 from t2); +select min(a1) from t1 where 7 in (select b1 from t2); +-- echo # but when we go around MWL#90 code, the problem still shows up: +explain select min(a1) from t1 where 7 in (select b1 from t2) or 2> 4; +select min(a1) from t1 where 7 in (select b1 from t2) or 2> 4; +set @@optimizer_switch= @save_optimizer_switch; +drop table t1,t2; + +# +# BUG#36752 "subquery materialization produces wrong results when comparing different types" +# +create table t1 (a char(2), b varchar(10)); +insert into t1 values ('a', 'aaa'); +insert into t1 values ('aa', 'aaaa'); + +explain select a,b from t1 where b in (select a from t1); +select a,b from t1 where b in (select a from t1); +prepare st1 from "select a,b from t1 where b in (select a from t1)"; +execute st1; +execute st1; +drop table t1; + +# +# Bug #44303 Assertion failures in Field_new_decimal::store_decimal +# when executing materialized InsideOut semijoin +# +CREATE TABLE t1 (f1 INT, f2 DECIMAL(5,3)) ENGINE=MyISAM; +INSERT INTO t1 (f1, f2) VALUES (1, 1.789); +INSERT INTO t1 (f1, f2) VALUES (13, 1.454); +INSERT INTO t1 (f1, f2) VALUES (10, 1.668); + +CREATE TABLE t2 LIKE t1; +INSERT INTO t2 VALUES (1, 1.789); +INSERT INTO t2 VALUES (13, 1.454); + +set @save_optimizer_switch=@@optimizer_switch; +SET @@optimizer_switch='default,semijoin=on,materialization=on'; +EXPLAIN SELECT COUNT(*) FROM t1 WHERE (f1,f2) IN (SELECT f1,f2 FROM t2); +SELECT COUNT(*) FROM t1 WHERE (f1,f2) IN (SELECT f1,f2 FROM t2); +set @@optimizer_switch= @save_optimizer_switch; + +DROP TABLE t1, t2; + +# +# BUG#46548 IN-subqueries return 0 rows with materialization=on +# +CREATE TABLE t1 ( + pk int, + a varchar(1), + b varchar(4), + c varchar(4), + d varchar(4), + PRIMARY KEY (pk) +); +INSERT INTO t1 VALUES (1,'o','ffff','ffff','ffoo'),(2,'f','ffff','ffff','ffff'); + +CREATE TABLE t2 LIKE t1; +INSERT INTO t2 VALUES (1,'i','iiii','iiii','iiii'),(2,'f','ffff','ffff','ffff'); + +set @save_optimizer_switch=@@optimizer_switch; +SET @@optimizer_switch='default,semijoin=on,materialization=on'; +EXPLAIN SELECT pk FROM t1 WHERE (a) IN (SELECT a FROM t2 WHERE pk > 0); +SELECT pk FROM t1 WHERE (a) IN (SELECT a FROM t2 WHERE pk > 0); +SELECT pk FROM t1 WHERE (b,c,d) IN (SELECT b,c,d FROM t2 WHERE pk > 0); +DROP TABLE t1, t2; +set optimizer_switch=@save_optimizer_switch; + +--echo # +--echo # BUG#50019: Wrong result for IN-subquery with materialization +--echo # +create table t1(i int); +insert into t1 values (1), (2), (3), (4), (5), (6), (7), (8), (9), (10); +create table t2(i int); +insert into t2 values (1), (2), (3), (4), (5), (6), (7), (8), (9), (10); +create table t3(i int); +insert into t3 values (1), (2), (3), (4), (5), (6), (7), (8), (9), (10); +select * from t1 where t1.i in (select t2.i from t2 join t3 where t2.i + t3.i = 5); +set @save_optimizer_switch=@@optimizer_switch; +set session optimizer_switch='materialization=off,in_to_exists=on'; +select * from t1 where t1.i in (select t2.i from t2 join t3 where t2.i + t3.i = 5); +set session optimizer_switch=@save_optimizer_switch; +drop table t1, t2, t3; + +# +# Test that the contents of the temp table of a materialized subquery is +# cleaned up between PS re-executions. +# + +create table t0 (a int); +insert into t0 values (0),(1),(2); +create table t1 (a int); +insert into t1 values (0),(1),(2); +explain select a, a in (select a from t1) from t0; +select a, a in (select a from t1) from t0; +prepare s from 'select a, a in (select a from t1) from t0'; +execute s; +update t1 set a=123; +execute s; +drop table t0, t1; +set optimizer_switch='firstmatch=on'; + +--echo # +--echo # MWL#90, review feedback: check what happens when the subquery +--echo # looks like candidate for MWL#90 checking at the first glance +--echo # but then subselect_hash_sj_engine::init_permanent() discovers +--echo # that it's not possible to perform duplicate removal for the +--echo # selected datatypes, and so materialization isn't applicable after +--echo # all. +--echo # +set @blob_len = 1024; +set @suffix_len = @blob_len - @prefix_len; + +create table t1_1024 (a1 blob(1024), a2 blob(1024)); +create table t2_1024 (b1 blob(1024), b2 blob(1024)); + +insert into t1_1024 values + (concat('1 - 00', repeat('x', @suffix_len)), concat('2 - 00', repeat('x', @suffix_len))); +insert into t1_1024 values + (concat('1 - 01', repeat('x', @suffix_len)), concat('2 - 01', repeat('x', @suffix_len))); +insert into t1_1024 values + (concat('1 - 02', repeat('x', @suffix_len)), concat('2 - 02', repeat('x', @suffix_len))); + +insert into t2_1024 values + (concat('1 - 01', repeat('x', @suffix_len)), concat('2 - 01', repeat('x', @suffix_len))); +insert into t2_1024 values + (concat('1 - 02', repeat('x', @suffix_len)), concat('2 - 02', repeat('x', @suffix_len))); +insert into t2_1024 values + (concat('1 - 03', repeat('x', @suffix_len)), concat('2 - 03', repeat('x', @suffix_len))); + +explain select left(a1,7), left(a2,7) from t1_1024 where (a1,3) in (select substring(b1,1,1024), count(*) from t2_1024 where b1 > '0'); +select left(a1,7), left(a2,7) from t1_1024 where (a1,3) in (select substring(b1,1,1024), count(*) from t2_1024 where b1 > '0'); + +drop table t1_1024, t2_1024; diff --git a/mysql-test/t/subselect_sj_nonmerged.test b/mysql-test/t/subselect_sj_nonmerged.test new file mode 100644 index 00000000000..c73bd79777f --- /dev/null +++ b/mysql-test/t/subselect_sj_nonmerged.test @@ -0,0 +1,99 @@ +# +# Tests for non-merged semi-joins +# +--disable_warnings +drop table if exists t0, t1, t2, t3, t4; +--enable_warnings + +set @save_optimizer_switch=@@optimizer_switch; +set optimizer_switch='materialization=on'; + +create table t0 (a int); +insert into t0 values (0),(1),(2),(3),(4),(5),(6),(7),(8),(9); + + +# Check the case of subquery having agggregates but not having grouping + +create table t1 as select * from t0; +--echo # The following should use full scan on <subquery2> and it must scan 1 row: +explain select * from t0 where a in (select max(a) from t1); +select * from t0 where a in (select max(a) from t1); + +# Ok, now check the trivial match/no-match/NULL on the left/NULL on the right cases +insert into t1 values (11); +select * from t0 where a in (select max(a) from t1); +delete from t1 where a=11; + +insert into t0 values (NULL); +select * from t0 where a in (select max(a) from t1); +delete from t0 where a is NULL; + +delete from t1; +select * from t0 where a in (select max(a) from t1); + +insert into t0 values (NULL); +select * from t0 where a in (select max(a) from t1); +delete from t0 where a is NULL; + +drop table t1; + +# +# Try with join subqueries +# + +create table t1 (a int, b int); +insert into t1 select a,a from t0; # 10 rows +create table t2 as select * from t1 where a<5; # 5 rows +create table t3 as select (A.a + 10*B.a) as a from t0 A, t0 B; # 100 rows +alter table t3 add primary key(a); + +--echo # The following should have do a full scan on <subquery2> and scan 5 rows +--echo # (despite that subquery's join output estimate is 50 rows) +explain select * from t3 where a in (select max(t2.a) from t1, t2 group by t2.b); + +--echo # Compare to this which really will have 50 record combinations: +explain select * from t3 where a in (select max(t2.a) from t1, t2 group by t2.b, t1.b); + +--echo # Outer joins also work: +explain select * from t3 +where a in (select max(t2.a) from t1 left join t2 on t1.a=t2.a group by t2.b, t1.b); + +# +# Check if joins on the outer side also work +# +create table t4 (a int, b int, filler char(20), unique key(a,b)); +insert into t4 select A.a + 10*B.a, A.a + 10*B.a, 'filler' from t0 A, t0 B; # 100 rows +explain select * from t0, t4 where + t4.b=t0.a and t4.a in (select max(t2.a) from t1, t2 group by t2.b); + +insert into t4 select 100 + (B.a *100 + A.a), 100 + (B.a*100 + A.a), 'filler' from t4 A, t0 B; +explain select * from t4 where + t4.a in (select max(t2.a) from t1, t2 group by t2.b) and + t4.b in (select max(t2.a) from t1, t2 group by t2.b); + +drop table t1,t2,t3,t4; + +drop table t0; + +--echo # +--echo # BUG#780359: Crash with get_fanout_with_deps in maria-5.3-mwl90 +--echo # +CREATE TABLE t1 (f1 int); +INSERT INTO t1 VALUES (2),(2); + +CREATE TABLE t2 (f3 int); +INSERT INTO t2 VALUES (2),(2); + +SELECT * +FROM t1 +WHERE ( f1 ) IN ( + SELECT t2.f3 + FROM t2 + WHERE t2.f3 = 97 + AND t2.f3 = 50 + GROUP BY 1 +); + +DROP TABLE t1, t2; +set optimizer_switch=@save_optimizer_switch; + diff --git a/sql-bench/test-table-elimination.sh b/sql-bench/test-table-elimination.sh index 338a7ceb4b5..0dcfe975486 100755 --- a/sql-bench/test-table-elimination.sh +++ b/sql-bench/test-table-elimination.sh @@ -1,3 +1,4 @@ + #!@PERL@ # Test of table elimination feature diff --git a/sql/item.h b/sql/item.h index 5c10690faa5..572650dad3c 100644 --- a/sql/item.h +++ b/sql/item.h @@ -1249,7 +1249,8 @@ public: virtual Item_equal *find_item_equal(COND_EQUAL *cond_equal) { return NULL; } /** Set the join tab index to the minimal (left-most) JOIN_TAB to which this - Item is attached. + Item is attached. The number is an index is depth_first_tab() traversal + order. */ virtual void set_join_tab_idx(uint join_tab_idx_arg) { diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc index f631fc141de..bae8c4308e1 100644 --- a/sql/item_cmpfunc.cc +++ b/sql/item_cmpfunc.cc @@ -5813,28 +5813,12 @@ Item* Item_equal::get_first(Item *field_item) { /* It's a field from an materialized semi-join. We can substitute it only - for a field from the same semi-join. + for a field from the same semi-join. Find the first of such items. */ - JOIN_TAB *first= field_tab; - JOIN *join= field_tab->join; - int tab_idx= field_tab - field_tab->join->join_tab; - DBUG_ASSERT(join->join_tab[tab_idx].table->map & - emb_nest->sj_inner_tables); - - /* Find the first table of this semi-join nest */ - for (int i= tab_idx-1; i >= (int)join->const_tables; i--) - { - if (join->join_tab[i].table->map & emb_nest->sj_inner_tables) - first= join->join_tab + i; - else - // Found first tab that doesn't belong to current SJ. - break; - } - /* Find an item to substitute for. */ while ((item= it++)) { - if (it.get_curr_field()->table->reginfo.join_tab >= first) + if (it.get_curr_field()->table->pos_in_table_list->embedding == emb_nest) { /* If we found given field then return NULL to avoid unnecessary @@ -5846,33 +5830,28 @@ Item* Item_equal::get_first(Item *field_item) } else { -#if TO_BE_DELETED /* The field is not in SJ-Materialization nest. We must return the first - field that's not embedded in a SJ-Materialization nest. - Example: suppose we have a join order: + field in the join order. The field may be inside a semi-join nest, i.e + a join order may look like this: SJ-Mat(it1 it2) ot1 ot2 - and equality ot2.col = ot1.col = it2.col - If we're looking for best substitute for 'ot2.col', we should pick - ot1.col and not it2.col, because when we run a join between ot1 and ot2 - execution of SJ-Mat(...) has already finished and we can't rely on the - value of it*.*. - psergey-fix-fix: ^^ THAT IS INCORRECT ^^. Pick the first, whatever that - is. + where we're looking what to substitute ot2.col for. In this case we must + still return it1.col, here's a proof why: + + First let's note that either it1.col or it2.col participates in + subquery's IN-equality. It can't be otherwise, because materialization is + only applicable to uncorrelated subqueries, so the only way we could + infer "it1.col=ot1.col" is from the IN-equality. Ok, so IN-eqality has + it1.col or it2.col on its inner side. it1.col is first such item in the + join order, so it's not possible for SJ-Mat to be + SJ-Materialization-lookup, it is SJ-Materialization-Scan. The scan part + of this strategy will unpack value of it1.col=it2.col into it1.col + (that's the first equal item inside the subquery), and we'll be able to + get it from there. qed. */ - while ((item= it++)) - { - Item_field *fld_item= (Item_field *) (item->real_item()); - TABLE_LIST *emb_nest= fld_item->field->table->pos_in_table_list->embedding; - if (!emb_nest || !emb_nest->sj_mat_info || - !emb_nest->sj_mat_info->is_used) - { - return item; - } - } -#endif + return equal_items.head(); } // Shouldn't get here. diff --git a/sql/item_subselect.cc b/sql/item_subselect.cc index a25b7dcbec9..18c835dfcc2 100644 --- a/sql/item_subselect.cc +++ b/sql/item_subselect.cc @@ -31,13 +31,14 @@ #include "mysql_priv.h" #include "sql_select.h" +double get_post_group_estimate(JOIN* join, double join_op_rows); + Item_subselect::Item_subselect(): - Item_result_field(), value_assigned(0), own_engine(TRUE), - thd(0), substitution(0), - expr_cache(0), engine(0), old_engine(0), used_tables_cache(0), - have_to_be_excluded(0), const_item_cache(1), inside_first_fix_fields(0), - done_first_fix_fields(FALSE), forced_const(FALSE), eliminated(FALSE), + Item_result_field(), value_assigned(0), own_engine(0), thd(0), old_engine(0), + used_tables_cache(0), have_to_be_excluded(0), const_item_cache(1), + inside_first_fix_fields(0), done_first_fix_fields(FALSE), + substitution(0), expr_cache(0), engine(0), forced_const(FALSE), eliminated(FALSE), engine_changed(0), changed(0), is_correlated(FALSE) { DBUG_ENTER("Item_subselect::Item_subselect"); @@ -238,13 +239,13 @@ bool Item_subselect::fix_fields(THD *thd_param, Item **ref) if (!(*ref)->fixed) res= (*ref)->fix_fields(thd, ref); goto end; -//psergey-merge: done_first_fix_fields= FALSE; + } // Is it one field subselect? if (engine->cols() > max_columns) { my_error(ER_OPERAND_COLUMNS, MYF(0), 1); -//psergey-merge: done_first_fix_fields= FALSE; + goto end; } fix_length_and_dec(); @@ -262,6 +263,7 @@ bool Item_subselect::fix_fields(THD *thd_param, Item **ref) end: done_first_fix_fields= FALSE; + inside_first_fix_fields= FALSE; thd->where= save_where; return res; } @@ -556,6 +558,64 @@ bool Item_subselect::exec() } +/* +int Item_subselect::optimize(double *out_rows, double *cost) +{ + int res; + DBUG_ASSERT(0); + res= engine->optimize(out_rows, cost); + return res; +} +*/ + + +int Item_in_subselect::optimize(double *out_rows, double *cost) +{ + int res; + DBUG_ENTER("Item_in_subselect::optimize"); + SELECT_LEX *save_select= thd->lex->current_select; + JOIN *join= unit->first_select()->join; + + thd->lex->current_select= join->select_lex; + if ((res= join->optimize())) + DBUG_RETURN(res); + + /* Calculate #rows and cost of join execution */ + join->get_partial_cost_and_fanout(join->table_count - join->const_tables, + table_map(-1), + cost, out_rows); + + /* + Adjust join output cardinality. There can be these cases: + - Have no GROUP BY and no aggregate funcs: we won't get into this + function because such join will be processed as a merged semi-join + (TODO: does it really mean we don't need to handle such cases here at + all? put ASSERT) + - Have no GROUP BY but have aggregate funcs: output is 1 record. + - Have GROUP BY and have (or not) aggregate funcs: need to adjust output + cardinality. + */ + thd->lex->current_select= save_select; + if (!join->group_list && !join->group_optimized_away && + join->tmp_table_param.sum_func_count) + { + DBUG_PRINT("info",("Materialized join will have only 1 row (it has " + "aggregates but no GROUP BY")); + *out_rows= 1; + } + + /* Now with grouping */ + if (join->group_list) + { + DBUG_PRINT("info",("Materialized join has grouping, trying to estimate it")); + double output_rows= get_post_group_estimate(join, *out_rows); + DBUG_PRINT("info",("Got value of %g", output_rows)); + *out_rows= output_rows; + } + + DBUG_RETURN(res); + +} /** Check if an expression cache is needed for this subquery @@ -865,9 +925,6 @@ Item_singlerow_subselect::select_transformer(JOIN *join) void Item_singlerow_subselect::store(uint i, Item *item) { row[i]->store(item); - //psergey-merge: can do without that: row[i]->cache_value(); - //psergey-backport-timours: ^ really, without that ^ - //psergey-try-merge-again: row[i]->cache_value(); } @@ -1089,7 +1146,9 @@ bool Item_in_subselect::test_limit(st_select_lex_unit *unit_arg) Item_in_subselect::Item_in_subselect(Item * left_exp, st_select_lex *select_lex): Item_exists_subselect(), left_expr_cache(0), first_execution(TRUE), - optimizer(0), pushed_cond_guards(NULL), in_strategy(0), upper_item(0) + optimizer(0), pushed_cond_guards(NULL), in_strategy(0), + is_jtbm_merged(FALSE), is_flattenable_semijoin(FALSE), + upper_item(0) { DBUG_ENTER("Item_in_subselect::Item_in_subselect"); left_expr= left_exp; @@ -2415,7 +2474,8 @@ bool Item_in_subselect::setup_mat_engine() if (!(mat_engine= new subselect_hash_sj_engine(thd, this, select_engine))) DBUG_RETURN(TRUE); - if (mat_engine->init(&select_engine->join->fields_list)) + if (mat_engine->init(&select_engine->join->fields_list, + engine->get_identifier())) DBUG_RETURN(TRUE); engine= mat_engine; @@ -2444,7 +2504,7 @@ bool Item_in_subselect::init_left_expr_cache() An IN predicate might be evaluated in a query for which all tables have been optimzied away. */ - if (!outer_join || !outer_join->tables || !outer_join->tables_list) + if (!outer_join || !outer_join->table_count || !outer_join->tables_list) return TRUE; if (!(left_expr_cache= new List<Cached_item>)) @@ -2829,9 +2889,9 @@ int subselect_single_select_engine::exec() pushed down into the subquery. Those optimizations are ref[_or_null] acceses. Change them to be full table scans. */ - for (uint i=join->const_tables ; i < join->tables ; i++) + for (JOIN_TAB *tab= first_linear_tab(join, WITHOUT_CONST_TABLES); tab; + tab= next_linear_tab(join, tab, WITH_BUSH_ROOTS)) { - JOIN_TAB *tab=join->join_tab+i; if (tab && tab->keyuse) { for (uint i= 0; i < tab->ref.key_parts; i++) @@ -3943,6 +4003,8 @@ bitmap_init_memroot(MY_BITMAP *map, uint n_bits, MEM_ROOT *mem_root) reexecution. @param tmp_columns the items that produce the data for the temp table + @param subquery_id subquery's identifier (to make "<subquery%d>" name for + EXPLAIN) @details - Create a temporary table to store the result of the IN subquery. The @@ -3958,7 +4020,7 @@ bitmap_init_memroot(MY_BITMAP *map, uint n_bits, MEM_ROOT *mem_root) @retval FALSE otherwise */ -bool subselect_hash_sj_engine::init(List<Item> *tmp_columns) +bool subselect_hash_sj_engine::init(List<Item> *tmp_columns, uint subquery_id) { select_union *result_sink; /* Options to create_tmp_table. */ @@ -3996,10 +4058,18 @@ bool subselect_hash_sj_engine::init(List<Item> *tmp_columns) */ if (!(result_sink= new select_materialize_with_stats)) DBUG_RETURN(TRUE); + + char buf[32]; + uint len= my_snprintf(buf, sizeof(buf), "<subquery%d>", subquery_id); + char *name; + if (!(name= (char*)thd->alloc(len + 1))) + DBUG_RETURN(TRUE); + memcpy(name, buf, len+1); + result_sink->get_tmp_table_param()->materialized_subquery= true; if (result_sink->create_result_table(thd, tmp_columns, TRUE, tmp_create_options, - "materialized subselect", TRUE)) + name, TRUE)) DBUG_RETURN(TRUE); tmp_table= result_sink->table; @@ -4092,13 +4162,14 @@ bool subselect_hash_sj_engine::make_semi_join_conds() if (!(tmp_table_ref= (TABLE_LIST*) thd->alloc(sizeof(TABLE_LIST)))) DBUG_RETURN(TRUE); - tmp_table_ref->init_one_table("", "materialized subselect", TL_READ); + tmp_table_ref->init_one_table("", tmp_table->alias.c_ptr(), TL_READ); tmp_table_ref->table= tmp_table; context= new Name_resolution_context; context->init(); context->first_name_resolution_table= context->last_name_resolution_table= tmp_table_ref; + semi_join_conds_context= context; for (uint i= 0; i < item_in->left_expr->cols(); i++) { @@ -4227,6 +4298,223 @@ void subselect_hash_sj_engine::cleanup() } +/* + Get fanout produced by tables specified in the table_map +*/ + +double get_fanout_with_deps(JOIN *join, table_map tset) +{ + /* Handle the case of "Impossible WHERE" */ + if (join->table_count == 0) + return 0.0; + + /* First, recursively get all tables we depend on */ + table_map deps_to_check= tset; + table_map checked_deps= 0; + table_map further_deps; + do + { + further_deps= 0; + Table_map_iterator tm_it(deps_to_check); + int tableno; + while ((tableno = tm_it.next_bit()) != Table_map_iterator::BITMAP_END) + { + /* get tableno's dependency tables that are not in needed_set */ + further_deps |= join->map2table[tableno]->ref.depend_map & ~checked_deps; + } + + checked_deps |= deps_to_check; + deps_to_check= further_deps; + } while (further_deps != 0); + + + /* Now, walk the join order and calculate the fanout */ + double fanout= 1; + for (JOIN_TAB *tab= first_top_level_tab(join, WITHOUT_CONST_TABLES); tab; + tab= next_top_level_tab(join, tab)) + { + if ((tab->table->map & checked_deps) && !tab->emb_sj_nest && + tab->records_read != 0) + { + fanout *= rows2double(tab->records_read); + } + } + return fanout; +} + + +#if 0 +void check_out_index_stats(JOIN *join) +{ + ORDER *order; + uint n_order_items; + + /* + First, collect the keys that we can use in each table. + We can use a key if + - all tables refer to it. + */ + key_map key_start_use[MAX_TABLES]; + key_map key_infix_use[MAX_TABLES]; + table_map key_used=0; + table_map non_key_used= 0; + + bzero(&key_start_use, sizeof(key_start_use)); //psergey-todo: safe initialization! + bzero(&key_infix_use, sizeof(key_infix_use)); + + for (order= join->group_list; order; order= order->next) + { + Item *item= order->item[0]; + + if (item->real_type() == Item::FIELD_ITEM) + { + if (item->used_tables() & OUTER_REF_TABLE_BIT) + continue; /* outside references are like constants for us */ + + Field *field= ((Item_field*)item->real_item())->field; + uint table_no= field->table->tablenr; + if (!(non_key_used && table_map(1) << table_no) && + !field->part_of_key.is_clear_all()) + { + key_map infix_map= field->part_of_key; + infix_map.subtract(field->key_start); + key_start_use[table_no].merge(field->key_start); + key_infix_use[table_no].merge(infix_map); + key_used |= table_no; + } + continue; + } + /* + Note: the below will cause clauses like GROUP BY YEAR(date) not to be + handled. + */ + non_key_used |= item->used_tables(); + } + + Table_map_iterator tm_it(key_used & ~non_key_used); + int tableno; + while ((tableno = tm_it.next_bit()) != Table_map_iterator::BITMAP_END) + { + key_map::iterator key_it(key_start_use); + int keyno; + while ((keyno = tm_it.next_bit()) != key_map::iterator::BITMAP_END) + { + for (order= join->group_list; order; order= order->next) + { + Item *item= order->item[0]; + if (item->used_tables() & (table_map(1) << tableno)) + { + DBUG_ASSERT(item->real_type() == Item::FIELD_ITEM); + } + } + /* + if (continuation) + { + walk through list and find which key parts are occupied; + // note that the above can't be made any faster. + } + else + use rec_per_key[0]; + + find out the cardinality. + check if cardinality decreases if we use it; + */ + } + } +} +#endif + + +/* + Get an estimate of how many records will be produced after the GROUP BY + operation. + + @param join Join we're operating on + @param join_op_rows How many records will be produced by the join + operations (this is what join optimizer produces) + + @seealso + See also optimize_semijoin_nests(), grep for "Adjust output cardinality + estimates". Very similar code there that is not joined with this one + because we operate on different data structs and too much effort is + needed to abstract them out. + + @return + Number of records we expect to get after the GROUP BY operation +*/ + +double get_post_group_estimate(JOIN* join, double join_op_rows) +{ + table_map tables_in_group_list= table_map(0); + + /* Find out which tables are used in GROUP BY list */ + for (ORDER *order= join->group_list; order; order= order->next) + { + Item *item= order->item[0]; + if (item->used_tables() & RAND_TABLE_BIT) + { + /* Each join output record will be in its own group */ + return join_op_rows; + } + tables_in_group_list|= item->used_tables(); + } + tables_in_group_list &= ~PSEUDO_TABLE_BITS; + + /* + Use join fanouts to calculate the max. number of records in the group-list + */ + double fanout_rows[MAX_KEY]; + bzero(&fanout_rows, sizeof(fanout_rows)); + double out_rows; + + out_rows= get_fanout_with_deps(join, tables_in_group_list); + +#if 0 + /* The following will be needed when making use of index stats: */ + /* + Also generate max. number of records for each of the tables mentioned + in the group-list. We'll use that a baseline number that we'll try to + reduce by using + - #table-records + - index statistics. + */ + Table_map_iterator tm_it(tables_in_group_list); + int tableno; + while ((tableno = tm_it.next_bit()) != Table_map_iterator::BITMAP_END) + { + fanout_rows[tableno]= get_fanout_with_deps(join, table_map(1) << tableno); + } + + /* + Try to bring down estimates using index statistics. + */ + //check_out_index_stats(join); +#endif + + return out_rows; +} + + +/* + Optimize the underlying subselect's join + + @param out_rows OUT How many records we expect to get in the + materialized table + @param cost OUT Cost to materialize the subquery + + @return + 0 OK + 1 Fatal error +*/ +#if 0 +int subselect_hash_sj_engine::optimize(double *out_rows, double *cost) +{ + DBUG_ENTER("subselect_hash_sj_engine::optimize"); + DBUG_ASSERT(0); + DBUG_RETURN(0); +} +#endif + /** Execute a subquery IN predicate via materialization. diff --git a/sql/item_subselect.h b/sql/item_subselect.h index 1b1be973cf9..deae1cb6ca4 100644 --- a/sql/item_subselect.h +++ b/sql/item_subselect.h @@ -37,6 +37,22 @@ class Item_subselect :public Item_result_field protected: /* thread handler, will be assigned in fix_fields only */ THD *thd; + /* old engine if engine was changed */ + subselect_engine *old_engine; + /* cache of used external tables */ + table_map used_tables_cache; + /* allowed number of columns (1 for single value subqueries) */ + uint max_columns; + /* where subquery is placed */ + enum_parsing_place parsing_place; + /* work with 'substitution' */ + bool have_to_be_excluded; + /* cache of constant state */ + bool const_item_cache; + + bool inside_first_fix_fields; + bool done_first_fix_fields; +public: /* Used inside Item_subselect::fix_fields() according to this scenario: > Item_subselect::fix_fields @@ -47,31 +63,15 @@ protected: < child_join->prepare < engine->prepare *ref= substitution; + substitution= NULL; < Item_subselect::fix_fields */ Item *substitution; -public: /* unit of subquery */ st_select_lex_unit *unit; -protected: Item *expr_cache; /* engine that perform execution of subselect (single select or union) */ subselect_engine *engine; - /* old engine if engine was changed */ - subselect_engine *old_engine; - /* cache of used external tables */ - table_map used_tables_cache; - /* allowed number of columns (1 for single value subqueries) */ - uint max_columns; - /* where subquery is placed */ - enum_parsing_place parsing_place; - /* work with 'substitution' */ - bool have_to_be_excluded; - /* cache of constant state */ - bool const_item_cache; - - bool inside_first_fix_fields; - bool done_first_fix_fields; /* Set to TRUE if at optimization or execution time we determine that this item's value is a constant. We need this member because it is not possible @@ -79,7 +79,6 @@ protected: */ bool forced_const; -public: /* A reference from inside subquery predicate to somewhere outside of it */ class Ref_to_outside : public Sql_alloc { @@ -161,6 +160,7 @@ public: bool mark_as_dependent(THD *thd, st_select_lex *select, Item *item); void fix_after_pullout(st_select_lex *new_parent, Item **ref); void recalc_used_tables(st_select_lex *new_parent, bool after_pullout); + //virtual int optimize(double *out_rows, double *cost); virtual bool exec(); /* If subquery optimization or execution determines that the subquery has @@ -348,6 +348,8 @@ public: }; +TABLE_LIST * const NO_JOIN_NEST=(TABLE_LIST*)0x1; + /* Possible methods to execute an IN predicate. These are set by the optimizer based on user-set optimizer switches, semantic analysis and cost comparison. @@ -393,9 +395,11 @@ protected: all JOIN in UNION */ Item *expr; - Item_in_optimizer *optimizer; bool was_null; bool abort_on_null; +public: + Item_in_optimizer *optimizer; +protected: /* Used to trigger on/off conditions that were pushed down to subselect */ bool *pushed_cond_guards; Comp_creator *func; @@ -419,19 +423,13 @@ public: /* Used by subquery optimizations to keep track about in which clause this subquery predicate is located: - (TABLE_LIST*) 1 - the predicate is an AND-part of the WHERE + NO_JOIN_NEST - the predicate is an AND-part of the WHERE join nest pointer - the predicate is an AND-part of ON expression of a join nest NULL - for all other locations See also THD::emb_on_expr_nest. */ TABLE_LIST *emb_on_expr_nest; - /* - Location of the subquery predicate. It is either - - pointer to join nest if the subquery predicate is in the ON expression - - (TABLE_LIST*)1 if the predicate is in the WHERE. - */ - TABLE_LIST *expr_join_nest; /* Types of left_expr and subquery's select list allow to perform subquery materialization. Currently, we set this to FALSE when it as well could @@ -443,10 +441,31 @@ public: Same as above, but they also allow to scan the materialized table. */ bool sjm_scan_allowed; + double jtbm_read_time; + double jtbm_record_count; /* A bitmap of possible execution strategies for an IN predicate. */ uchar in_strategy; + bool is_jtbm_merged; + + /* + TRUE<=>this is a flattenable semi-join, false overwise. + */ + bool is_flattenable_semijoin; + + /* + Used to determine how this subselect item is represented in the item tree, + in case there is a need to locate it there and replace with something else. + Two options are possible: + 1. This item is there 'as-is'. + 1. This item is wrapped within Item_in_optimizer. + */ + Item *original_item() + { + return is_flattenable_semijoin ? (Item*)this : (Item*)optimizer; + } + bool *get_cond_guard(int i) { return pushed_cond_guards ? pushed_cond_guards + i : NULL; @@ -463,8 +482,9 @@ public: Item_in_subselect(Item * left_expr, st_select_lex *select_lex); Item_in_subselect() :Item_exists_subselect(), left_expr_cache(0), first_execution(TRUE), - optimizer(0), abort_on_null(0), + abort_on_null(0), optimizer(0), pushed_cond_guards(NULL), func(NULL), in_strategy(SUBS_NOT_TRANSFORMED), + is_jtbm_merged(FALSE), upper_item(0) {} void cleanup(); @@ -501,6 +521,7 @@ public: void set_first_execution() { if (first_execution) first_execution= FALSE; } bool expr_cache_is_needed(THD *thd); + int optimize(double *out_rows, double *cost); /* Return the identifier that we could use to identify the subquery for the user. @@ -572,6 +593,7 @@ public: THD * get_thd() { return thd; } virtual int prepare()= 0; virtual void fix_length_and_dec(Item_cache** row)= 0; + //virtual int optimize(double *out_rows, double *cost) { DBUG_ASSERT(0); return 0; } /* Execute the engine @@ -808,7 +830,7 @@ inline bool Item_subselect::is_uncacheable() const class subselect_hash_sj_engine : public subselect_engine { -protected: +public: /* The table into which the subquery is materialized. */ TABLE *tmp_table; /* TRUE if the subquery was materialized into a temp table. */ @@ -820,63 +842,35 @@ protected: of subselect_single_select_engine::[prepare | cols]. */ subselect_single_select_engine *materialize_engine; - /* The engine used to compute the IN predicate. */ - subselect_engine *lookup_engine; /* QEP to execute the subquery and materialize its result into a temporary table. Created during the first call to exec(). */ JOIN *materialize_join; - - /* Keyparts of the only non-NULL composite index in a rowid merge. */ - MY_BITMAP non_null_key_parts; - /* Keyparts of the single column indexes with NULL, one keypart per index. */ - MY_BITMAP partial_match_key_parts; - uint count_partial_match_columns; - uint count_null_only_columns; /* A conjunction of all the equality condtions between all pairs of expressions that are arguments of an IN predicate. We need these to post-filter some IN results because index lookups sometimes match values that are actually not equal to the search key in SQL terms. - */ + */ Item_cond_and *semi_join_conds; - /* Possible execution strategies that can be used to compute hash semi-join.*/ - enum exec_strategy { - UNDEFINED, - COMPLETE_MATCH, /* Use regular index lookups. */ - PARTIAL_MATCH, /* Use some partial matching strategy. */ - PARTIAL_MATCH_MERGE, /* Use partial matching through index merging. */ - PARTIAL_MATCH_SCAN, /* Use partial matching through table scan. */ - IMPOSSIBLE /* Subquery materialization is not applicable. */ - }; - /* The chosen execution strategy. Computed after materialization. */ - exec_strategy strategy; -protected: - exec_strategy get_strategy_using_schema(); - exec_strategy get_strategy_using_data(); - ulonglong rowid_merge_buff_size(bool has_non_null_key, - bool has_covering_null_row, - MY_BITMAP *partial_match_key_parts); - void choose_partial_match_strategy(bool has_non_null_key, - bool has_covering_null_row, - MY_BITMAP *partial_match_key_parts); - bool make_semi_join_conds(); - subselect_uniquesubquery_engine* make_unique_engine(); + Name_resolution_context *semi_join_conds_context; + -public: subselect_hash_sj_engine(THD *thd, Item_subselect *in_predicate, subselect_single_select_engine *old_engine) - :subselect_engine(thd, in_predicate, NULL), tmp_table(NULL), - is_materialized(FALSE), materialize_engine(old_engine), lookup_engine(NULL), - materialize_join(NULL), count_partial_match_columns(0), - count_null_only_columns(0), semi_join_conds(NULL), strategy(UNDEFINED) + : subselect_engine(thd, in_predicate, NULL), + tmp_table(NULL), is_materialized(FALSE), materialize_engine(old_engine), + materialize_join(NULL), semi_join_conds(NULL), lookup_engine(NULL), + count_partial_match_columns(0), count_null_only_columns(0), + strategy(UNDEFINED) {} ~subselect_hash_sj_engine(); - bool init(List<Item> *tmp_columns); + bool init(List<Item> *tmp_columns, uint subquery_id); void cleanup(); int prepare(); + //int optimize(double *out_rows, double *cost); int exec(); virtual void print(String *str, enum_query_type query_type); uint cols() @@ -898,6 +892,38 @@ public: select_result_interceptor *result, bool temp= FALSE); bool no_tables();//=>base class + +protected: + /* The engine used to compute the IN predicate. */ + subselect_engine *lookup_engine; + /* Keyparts of the only non-NULL composite index in a rowid merge. */ + MY_BITMAP non_null_key_parts; + /* Keyparts of the single column indexes with NULL, one keypart per index. */ + MY_BITMAP partial_match_key_parts; + uint count_partial_match_columns; + uint count_null_only_columns; + /* Possible execution strategies that can be used to compute hash semi-join.*/ + enum exec_strategy { + UNDEFINED, + COMPLETE_MATCH, /* Use regular index lookups. */ + PARTIAL_MATCH, /* Use some partial matching strategy. */ + PARTIAL_MATCH_MERGE, /* Use partial matching through index merging. */ + PARTIAL_MATCH_SCAN, /* Use partial matching through table scan. */ + IMPOSSIBLE /* Subquery materialization is not applicable. */ + }; + /* The chosen execution strategy. Computed after materialization. */ + exec_strategy strategy; + exec_strategy get_strategy_using_schema(); + exec_strategy get_strategy_using_data(); + ulonglong rowid_merge_buff_size(bool has_non_null_key, + bool has_covering_null_row, + MY_BITMAP *partial_match_key_parts); + void choose_partial_match_strategy(bool has_non_null_key, + bool has_covering_null_row, + MY_BITMAP *partial_match_key_parts); + bool make_semi_join_conds(); + subselect_uniquesubquery_engine* make_unique_engine(); + }; diff --git a/sql/opt_range.cc b/sql/opt_range.cc index fba2fb7d7c0..d94a25aeb66 100644 --- a/sql/opt_range.cc +++ b/sql/opt_range.cc @@ -341,8 +341,9 @@ public: SEL_ARG(Field *field, uint8 part, uchar *min_value, uchar *max_value, uint8 min_flag, uint8 max_flag, uint8 maybe_flag); SEL_ARG(enum Type type_arg) - :min_flag(0),elements(1),use_count(1),left(0),right(0),next_key_part(0), - color(BLACK), type(type_arg) + :min_flag(0), max_part_no(0) /* first key part means 1. 0 mean 'no parts'*/, + elements(1),use_count(1),left(0),right(0), + next_key_part(0), color(BLACK), type(type_arg) {} inline bool is_same(SEL_ARG *arg) { @@ -2926,7 +2927,8 @@ int SQL_SELECT::test_quick_select(THD *thd, key_map keys_to_use, quick=0; needed_reg.clear_all(); quick_keys.clear_all(); - if (keys_to_use.is_clear_all()) + DBUG_ASSERT(!head->is_filled_at_execution()); + if (keys_to_use.is_clear_all() || head->is_filled_at_execution()) DBUG_RETURN(0); records= head->file->stats.records; if (!records) @@ -4344,7 +4346,7 @@ double get_sweep_read_cost(const PARAM *param, ha_rows records) return 1; */ JOIN *join= param->thd->lex->select_lex.join; - if (!join || join->tables == 1) + if (!join || join->table_count == 1) { /* No join, assume reading is done in one 'sweep' */ result= busy_blocks*(DISK_SEEK_BASE_COST + @@ -11287,7 +11289,7 @@ get_best_group_min_max(PARAM *param, SEL_TREE *tree) /* Perform few 'cheap' tests whether this access method is applicable. */ if (!join) DBUG_RETURN(NULL); /* This is not a select statement. */ - if ((join->tables != 1) || /* The query must reference one table. */ + if ((join->table_count != 1) || /* The query must reference one table. */ ((!join->group_list) && /* Neither GROUP BY nor a DISTINCT query. */ (!join->select_distinct)) || (join->select_lex->olap == ROLLUP_TYPE)) /* Check (B3) for ROLLUP */ diff --git a/sql/opt_subselect.cc b/sql/opt_subselect.cc index 9aa41524858..3ab99d5c120 100644 --- a/sql/opt_subselect.cc +++ b/sql/opt_subselect.cc @@ -2,7 +2,7 @@ @file @brief - Subquery optimization code here. + Semi-join subquery optimizations code */ @@ -16,7 +16,162 @@ #include <my_bit.h> -// Our own: +/* + This file contains optimizations for semi-join subqueries. + + Contents + -------- + 1. What is a semi-join subquery + 2. General idea about semi-join execution + 2.1 Correlated vs uncorrelated semi-joins + 2.2 Mergeable vs non-mergeable semi-joins + 3. Code-level view of semi-join processing + 3.1 Conversion + 3.1.1 Merged semi-join TABLE_LIST object + 3.1.2 Non-merged semi-join data structure + 3.2 Semi-joins and query optimization + 3.2.1 Non-merged semi-joins and join optimization + 3.2.2 Merged semi-joins and join optimization + 3.3 Semi-joins and query execution + + 1. What is a semi-join subquery + ------------------------------- + We use this definition of semi-join: + + outer_tbl SEMI JOIN inner_tbl ON cond = {set of outer_tbl.row such that + exist inner_tbl.row, for which + cond(outer_tbl.row,inner_tbl.row) + is satisfied} + + That is, semi-join operation is similar to inner join operation, with + exception that we don't care how many matches a row from outer_tbl has in + inner_tbl. + + In SQL terms: a semi-join subquery is an IN subquery that is an AND-part of + the WHERE/ON clause. + + 2. General idea about semi-join execution + ----------------------------------------- + We can execute semi-join in a way similar to inner join, with exception that + we need to somehow ensure that we do not generate record combinations that + differ only in rows of inner tables. + There is a number of different ways to achieve this property, implemented by + a number of semi-join execution strategies. + Some strategies can handle any semi-joins, other can be applied only to + semi-joins that have certain properties that are described below: + + 2.1 Correlated vs uncorrelated semi-joins + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + Uncorrelated semi-joins are special in the respect that they allow to + - execute the subquery (possible as it's uncorrelated) + - somehow make sure that generated set does not have duplicates + - perform an inner join with outer tables. + + or, rephrasing in SQL form: + + SELECT ... FROM ot WHERE ot.col IN (SELECT it.col FROM it WHERE uncorr_cond) + -> + SELECT ... FROM ot JOIN (SELECT DISTINCT it.col FROM it WHERE uncorr_cond) + + 2.2 Mergeable vs non-mergeable semi-joins + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + Semi-join operation has some degree of commutability with inner join + operation: we can join subquery's tables with ouside table(s) and eliminate + duplicate record combination after that: + + ot1 JOIN ot2 SEMI_JOIN{it1,it2} (it1 JOIN it2) ON sjcond(ot2,it*) -> + | + +-------------------------------+ + v + ot1 SEMI_JOIN{it1,it2} (it1 JOIN it2 JOIN ot2) ON sjcond(ot2,it*) + + In order for this to work, subquery's top-level operation must be join, and + grouping or ordering with limit (grouping or ordering with limit are not + commutative with duplicate removal). In other words, the conversion is + possible when the subquery doesn't have GROUP BY clause, any aggregate + functions*, or ORDER BY ... LIMIT clause. + + Definitions: + - Subquery whose top-level operation is a join is called *mergeable semi-join* + - All other kinds of semi-join subqueries are considered non-mergeable. + + *- this requirement is actually too strong, but its exceptions are too + complicated to be considered here. + + 3. Code-level view of semi-join processing + ------------------------------------------ + + 3.1 Conversion and pre-optimization data structures + --------------------------------------------------- + * When doing JOIN::prepare for the subquery, we detect that it can be + converted into a semi-join and register it in parent_join->sj_subselects + + * At the start of parent_join->optimize(), the predicate is converted into + a semi-join node. A semi-join node is a TABLE_LIST object that is linked + somewhere in parent_join->join_list (either it is just present there, or + it is a descendant of some of its members). + + There are two kinds of semi-joins: + - Merged semi-joins + - Non-merged semi-joins + + 3.1.1 Merged semi-join TABLE_LIST object + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + Merged semi-join object is a TABLE_LIST that contains a sub-join of + subquery tables and the semi-join ON expression (in this respect it is + very similar to nested outer join representation) + Merged semi-join represents this SQL: + + ... SEMI JOIN (inner_tbl1 JOIN ... JOIN inner_tbl_n) ON sj_on_expr + + Semi-join objects of this kind have TABLE_LIST::sj_subq_pred set. + + 3.1.2 Non-merged semi-join data structure + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + Non-merged semi-join object is a leaf TABLE_LIST object that has a subquery + that produces rows. It is similar to a base table and represents this SQL: + + ... SEMI_JOIN (SELECT non_mergeable_select) ON sj_on_expr + + Subquery items that were converted into semi-joins are removed from the WHERE + clause. (They do remain in PS-saved WHERE clause, and they replace themselves + with Item_int(1) on subsequent re-executions). + + 3.2 Semi-joins and join optimization + ------------------------------------ + + 3.2.1 Non-merged semi-joins and join optimization + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + For join optimization purposes, non-merged semi-join nests are similar to + base tables - they've got one JOIN_TAB, which can be accessed with one of + two methods: + - full table scan (representing SJ-Materialization-Scan strategy) + - eq_ref-like table lookup (representing SJ-Materialization-Lookup) + + Unlike regular base tables, non-merged semi-joins have: + - non-zero JOIN_TAB::startup_cost, and + - join_tab->table->is_filled_at_execution()==TRUE, which means one + cannot do const table detection or range analysis or other table data- + dependent inferences + // instead, get_delayed_table_estimates() runs optimization on the nest so that + // we get an idea about temptable size + + 3.2.2 Merged semi-joins and join optimization + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + - optimize_semijoin_nests() does pre-optimization + - during join optimization, the join has one JOIN_TAB (or is it POSITION?) + array, and suffix-based detection is used, see advance_sj_state() + - after join optimization is done, get_best_combination() switches + the data-structure to prefix-based, multiple JOIN_TAB ranges format. + + 3.3 Semi-joins and query execution + ---------------------------------- + * Join executor has hooks for all semi-join strategies. + TODO elaborate. + +*/ + + static bool subquery_types_allow_materialization(Item_in_subselect *in_subs); static bool replace_where_subcondition(JOIN *join, Item **expr, @@ -25,6 +180,8 @@ static bool replace_where_subcondition(JOIN *join, Item **expr, static int subq_sj_candidate_cmp(Item_in_subselect* const *el1, Item_in_subselect* const *el2); static bool convert_subq_to_sj(JOIN *parent_join, Item_in_subselect *subq_pred); +static bool convert_subq_to_jtbm(JOIN *parent_join, + Item_in_subselect *subq_pred, bool *remove); static TABLE_LIST *alloc_join_nest(THD *thd); static void fix_list_after_tbl_changes(SELECT_LEX *new_parent, List<TABLE_LIST> *tlist); @@ -50,21 +207,29 @@ static bool sj_table_is_included(JOIN *join, JOIN_TAB *join_tab); static Item *remove_additional_cond(Item* conds); static void remove_subq_pushed_predicates(JOIN *join, Item **where); +enum_nested_loop_state +end_sj_materialize(JOIN *join, JOIN_TAB *join_tab, bool end_of_records); + /* Check if we need JOIN::prepare()-phase subquery rewrites and if yes, do them + SYNOPSIS + check_and_do_in_subquery_rewrites() + join Subquery's join + DESCRIPTION Check if we need to do - - subquery->semi-join rewrite + - subquery -> mergeable semi-join rewrite - if the subquery can be handled with materialization - 'substitution' rewrite for table-less subqueries like "(select 1)" - - and mark appropriately + - IN->EXISTS rewrite + and, depending on the rewrite, either do it, or record it to be done at a + later phase. RETURN - 0 - OK - -1 - Some sort of query error + 0 - OK + Other - Some sort of query error */ int check_and_do_in_subquery_rewrites(JOIN *join) @@ -191,15 +356,15 @@ int check_and_do_in_subquery_rewrites(JOIN *join) (void)subquery_types_allow_materialization(in_subs); in_subs->emb_on_expr_nest= thd->thd_marker.emb_on_expr_nest; + in_subs->is_flattenable_semijoin= TRUE; /* Register the subquery for further processing in flatten_subqueries() */ select_lex-> outer_select()->join->sj_subselects.append(thd->mem_root, in_subs); - in_subs->expr_join_nest= thd->thd_marker.emb_on_expr_nest; } else { - DBUG_PRINT("info", ("Subquery can't be converted to semi-join")); + DBUG_PRINT("info", ("Subquery can't be converted to merged semi-join")); /* Test if the user has set a legal combination of optimizer switches. */ if (!optimizer_flag(thd, OPTIMIZER_SWITCH_IN_TO_EXISTS) && !optimizer_flag(thd, OPTIMIZER_SWITCH_MATERIALIZATION)) @@ -240,6 +405,10 @@ int check_and_do_in_subquery_rewrites(JOIN *join) (*) The subquery must be part of a SELECT statement. The current condition also excludes multi-table update statements. + A note about prepared statements: we want the if-branch to be taken on + PREPARE and each EXECUTE. The rewrites are only done once, but we need + join->sj_subselects list to be populated for every EXECUTE. + */ if (optimizer_flag(thd, OPTIMIZER_SWITCH_MATERIALIZATION) && // 0 !select_lex->is_part_of_union() && // 1 @@ -255,6 +424,19 @@ int check_and_do_in_subquery_rewrites(JOIN *join) !in_subs->is_correlated) //4 { in_subs->in_strategy|= SUBS_MATERIALIZATION; + + /* + If the subquery is an AND-part of WHERE register for being processed + with jtbm strategy + */ + if (thd->thd_marker.emb_on_expr_nest == NO_JOIN_NEST && + optimizer_flag(thd, OPTIMIZER_SWITCH_SEMIJOIN)) + { + in_subs->emb_on_expr_nest= thd->thd_marker.emb_on_expr_nest; + in_subs->is_flattenable_semijoin= FALSE; + select_lex->outer_select()-> + join->sj_subselects.append(thd->mem_root, in_subs); + } } /* @@ -402,6 +584,108 @@ bool JOIN::transform_max_min_subquery() /* + Finalize IN->EXISTS conversion in case we couldn't use materialization. + + DESCRIPTION Invoke the IN->EXISTS converter + Replace the Item_in_subselect with its wrapper Item_in_optimizer in WHERE. + + RETURN + FALSE - Ok + TRUE - Fatal error +*/ + +bool make_in_exists_conversion(THD *thd, JOIN *join, Item_in_subselect *item) +{ + DBUG_ENTER("make_in_exists_conversion"); + JOIN *child_join= item->unit->first_select()->join; + //Item_subselect::trans_res res; + bool res; + + /* + We're going to finalize IN->EXISTS conversion. + Normally, IN->EXISTS conversion takes place inside the + Item_subselect::fix_fields() call, where item_subselect->fixed==FALSE (as + fix_fields() haven't finished yet) and item_subselect->changed==FALSE (as + the conversion haven't been finalized) + + At the end of Item_subselect::fix_fields() we had to set fixed=TRUE, + changed=TRUE (the only other option would have been to return error). + + So, now we have to set these back for the duration of select_transformer() + call. + */ + item->changed= 0; + item->fixed= 0; + + SELECT_LEX *save_select_lex= thd->lex->current_select; + thd->lex->current_select= item->unit->first_select(); + + res= item->select_transformer(child_join); + + thd->lex->current_select= save_select_lex; + + //if (res == Item_subselect::RES_ERROR) + if (res) + DBUG_RETURN(TRUE); + + item->changed= 1; + item->fixed= 1; + + Item *substitute= item->substitution; + bool do_fix_fields= !item->substitution->fixed; + /* + The Item_subselect has already been wrapped with Item_in_optimizer, so we + should search for item->optimizer, not 'item'. + */ + Item *replace_me= item->optimizer; + DBUG_ASSERT(replace_me==substitute); + + Item **tree= (item->emb_on_expr_nest == NO_JOIN_NEST)? + &join->conds : &(item->emb_on_expr_nest->on_expr); + if (replace_where_subcondition(join, tree, replace_me, substitute, + do_fix_fields)) + DBUG_RETURN(TRUE); + item->substitution= NULL; + + /* + If this is a prepared statement, repeat the above operation for + prep_where (or prep_on_expr). + */ + if (!thd->stmt_arena->is_conventional()) + { + tree= (item->emb_on_expr_nest == (TABLE_LIST*)NO_JOIN_NEST)? + &join->select_lex->prep_where : + &(item->emb_on_expr_nest->prep_on_expr); + + if (replace_where_subcondition(join, tree, replace_me, substitute, + FALSE)) + DBUG_RETURN(TRUE); + } + DBUG_RETURN(FALSE); +} + + +bool check_for_outer_joins(List<TABLE_LIST> *join_list) +{ + TABLE_LIST *table; + NESTED_JOIN *nested_join; + List_iterator<TABLE_LIST> li(*join_list); + while ((table= li++)) + { + if ((nested_join= table->nested_join)) + { + if (check_for_outer_joins(&nested_join->join_list)) + return TRUE; + } + + if (table->outer_join) + return TRUE; + } + return FALSE; +} + + +/* Convert semi-join subquery predicates into semi-join join nests SYNOPSIS @@ -467,7 +751,7 @@ bool convert_join_subqueries_to_semijoins(JOIN *join) { st_select_lex *child_select= (*in_subq)->get_select_lex(); JOIN *child_join= child_select->join; - child_join->outer_tables = child_join->tables; + child_join->outer_tables = child_join->table_count; /* child_select->where contains only the WHERE predicate of the @@ -485,6 +769,7 @@ bool convert_join_subqueries_to_semijoins(JOIN *join) // Temporary measure: disable semi-joins when they are together with outer // joins. + /* for (TABLE_LIST *tbl= join->select_lex->leaf_tables; tbl; tbl=tbl->next_leaf) { TABLE_LIST *embedding= tbl->embedding; @@ -495,6 +780,12 @@ bool convert_join_subqueries_to_semijoins(JOIN *join) arena= thd->activate_stmt_arena_if_needed(&backup); goto skip_conversion; } + }*/ + if (check_for_outer_joins(join->join_list)) + { + in_subq= join->sj_subselects.front(); + arena= thd->activate_stmt_arena_if_needed(&backup); + goto skip_conversion; } //dump_TABLE_LIST_struct(select_lex, select_lex->leaf_tables); @@ -508,25 +799,36 @@ bool convert_join_subqueries_to_semijoins(JOIN *join) // #tables-in-parent-query + #tables-in-subquery < MAX_TABLES /* Replace all subqueries to be flattened with Item_int(1) */ arena= thd->activate_stmt_arena_if_needed(&backup); - for (in_subq= join->sj_subselects.front(); - in_subq != in_subq_end && - join->tables + (*in_subq)->unit->first_select()->join->tables < MAX_TABLES; - in_subq++) - { - Item **tree= ((*in_subq)->emb_on_expr_nest == (TABLE_LIST*)1)? - &join->conds : &((*in_subq)->emb_on_expr_nest->on_expr); - if (replace_where_subcondition(join, tree, *in_subq, new Item_int(1), - FALSE)) - DBUG_RETURN(TRUE); /* purecov: inspected */ - } for (in_subq= join->sj_subselects.front(); - in_subq != in_subq_end && - join->tables + (*in_subq)->unit->first_select()->join->tables < MAX_TABLES; + in_subq != in_subq_end; in_subq++) { - if (convert_subq_to_sj(join, *in_subq)) - DBUG_RETURN(TRUE); + bool remove_item= TRUE; + if ((*in_subq)->is_flattenable_semijoin) + { + if (join->table_count + + (*in_subq)->unit->first_select()->join->table_count >= MAX_TABLES) + break; + if (convert_subq_to_sj(join, *in_subq)) + DBUG_RETURN(TRUE); + } + else + { + if (join->table_count + 1 >= MAX_TABLES) + break; + if (convert_subq_to_jtbm(join, *in_subq, &remove_item)) + DBUG_RETURN(TRUE); + } + if (remove_item) + { + Item **tree= ((*in_subq)->emb_on_expr_nest == NO_JOIN_NEST)? + &join->conds : &((*in_subq)->emb_on_expr_nest->on_expr); + Item *replace_me= (*in_subq)->original_item(); + if (replace_where_subcondition(join, tree, replace_me, new Item_int(1), + FALSE)) + DBUG_RETURN(TRUE); /* purecov: inspected */ + } } skip_conversion: /* @@ -554,20 +856,26 @@ skip_conversion: Item *substitute= (*in_subq)->substitution; bool do_fix_fields= !(*in_subq)->substitution->fixed; - Item **tree= ((*in_subq)->emb_on_expr_nest == (TABLE_LIST*)1)? + Item **tree= ((*in_subq)->emb_on_expr_nest == NO_JOIN_NEST)? &join->conds : &((*in_subq)->emb_on_expr_nest->on_expr); - if (replace_where_subcondition(join, tree, *in_subq, substitute, + Item *replace_me= (*in_subq)->original_item(); + if (replace_where_subcondition(join, tree, replace_me, substitute, do_fix_fields)) DBUG_RETURN(TRUE); (*in_subq)->substitution= NULL; - + + /* + If this is a prepared statement, repeat the above operation for + prep_where (or prep_on_expr). Subquery-to-semijoin conversion is + done once for prepared statement. + */ if (!thd->stmt_arena->is_conventional()) { - tree= ((*in_subq)->emb_on_expr_nest == (TABLE_LIST*)1)? + tree= ((*in_subq)->emb_on_expr_nest == NO_JOIN_NEST)? &join->select_lex->prep_where : &((*in_subq)->emb_on_expr_nest->prep_on_expr); - if (replace_where_subcondition(join, tree, *in_subq, substitute, + if (replace_where_subcondition(join, tree, replace_me, substitute, FALSE)) DBUG_RETURN(TRUE); } @@ -588,6 +896,51 @@ skip_conversion: DBUG_RETURN(FALSE); } + +/* + Get #output_rows and scan_time estimates for a "delayed" table. + + SYNOPSIS + get_delayed_table_estimates() + table IN Table to get estimates for + out_rows OUT E(#rows in the table) + scan_time OUT E(scan_time). + startup_cost OUT cost to populate the table. + + DESCRIPTION + Get #output_rows and scan_time estimates for a "delayed" table. By + "delayed" here we mean that the table is filled at the start of query + execution. This means that the optimizer can't use table statistics to + get #rows estimate for it, it has to call this function instead. + + This function is expected to make different actions depending on the nature + of the table. At the moment there is only one kind of delayed tables, + non-flattenable semi-joins. +*/ + +void get_delayed_table_estimates(TABLE *table, + ha_rows *out_rows, + double *scan_time, + double *startup_cost) +{ + Item_in_subselect *item= table->pos_in_table_list->jtbm_subselect; + + DBUG_ASSERT(item->engine->engine_type() == + subselect_engine::HASH_SJ_ENGINE); + + subselect_hash_sj_engine *hash_sj_engine= + ((subselect_hash_sj_engine*)item->engine); + + *out_rows= (ha_rows)item->jtbm_record_count; + *startup_cost= item->jtbm_read_time; + + /* Calculate cost of scanning the temptable */ + double data_size= (*out_rows) * hash_sj_engine->tmp_table->s->reclength; + /* Do like in handler::read_time */ + *scan_time= data_size/IO_SIZE + 2; +} + + /** @brief Replaces an expression destructively inside the expression tree of the WHERE clase. @@ -605,6 +958,7 @@ skip_conversion: @return <code>true</code> if there was an error, <code>false</code> if successful. */ + static bool replace_where_subcondition(JOIN *join, Item **expr, Item *old_cond, Item *new_cond, bool do_fix_fields) @@ -686,9 +1040,9 @@ static bool convert_subq_to_sj(JOIN *parent_join, Item_in_subselect *subq_pred) 1. Find out where to put the predicate into. Note: for "t1 LEFT JOIN t2" this will be t2, a leaf. */ - if ((void*)subq_pred->expr_join_nest != (void*)1) + if ((void*)subq_pred->emb_on_expr_nest != (void*)NO_JOIN_NEST) { - if (subq_pred->expr_join_nest->nested_join) + if (subq_pred->emb_on_expr_nest->nested_join) { /* We're dealing with @@ -697,10 +1051,10 @@ static bool convert_subq_to_sj(JOIN *parent_join, Item_in_subselect *subq_pred) The sj-nest will be inserted into the brackets nest. */ - emb_tbl_nest= subq_pred->expr_join_nest; + emb_tbl_nest= subq_pred->emb_on_expr_nest; emb_join_list= &emb_tbl_nest->nested_join->join_list; } - else if (!subq_pred->expr_join_nest->outer_join) + else if (!subq_pred->emb_on_expr_nest->outer_join) { /* We're dealing with @@ -710,13 +1064,13 @@ static bool convert_subq_to_sj(JOIN *parent_join, Item_in_subselect *subq_pred) The sj-nest will be tblX's "sibling", i.e. another child of its parent. This is ok because tblX is joined as an inner join. */ - emb_tbl_nest= subq_pred->expr_join_nest->embedding; + emb_tbl_nest= subq_pred->emb_on_expr_nest->embedding; if (emb_tbl_nest) emb_join_list= &emb_tbl_nest->nested_join->join_list; } - else if (!subq_pred->expr_join_nest->nested_join) + else if (!subq_pred->emb_on_expr_nest->nested_join) { - TABLE_LIST *outer_tbl= subq_pred->expr_join_nest; + TABLE_LIST *outer_tbl= subq_pred->emb_on_expr_nest; TABLE_LIST *wrap_nest; /* We're dealing with @@ -843,8 +1197,8 @@ static bool convert_subq_to_sj(JOIN *parent_join, Item_in_subselect *subq_pred) subq_pred->in_strategy= SUBS_SEMI_JOIN; // for subsequent executions /*TODO: also reset the 'with_subselect' there. */ - /* n. Adjust the parent_join->tables counter */ - uint table_no= parent_join->tables; + /* n. Adjust the parent_join->table_count counter */ + uint table_no= parent_join->table_count; /* n. Walk through child's tables and adjust table->map */ for (tl= subq_lex->leaf_tables; tl; tl= tl->next_leaf, table_no++) { @@ -857,7 +1211,7 @@ static bool convert_subq_to_sj(JOIN *parent_join, Item_in_subselect *subq_pred) emb= emb->embedding) emb->select_lex= parent_join->select_lex; } - parent_join->tables += subq_lex->join->tables; + parent_join->table_count += subq_lex->join->table_count; /* Put the subquery's WHERE into semi-join's sj_on_expr @@ -964,6 +1318,146 @@ static bool convert_subq_to_sj(JOIN *parent_join, Item_in_subselect *subq_pred) DBUG_RETURN(FALSE); } + +const int SUBQERY_TEMPTABLE_NAME_MAX_LEN= 20; + +static void create_subquery_temptable_name(char *to, uint number) +{ + DBUG_ASSERT(number < 10000); + to= strmov(to, "<subquery"); + to= int10_to_str((int) number, to, 10); + to[0]= '>'; + to[1]= 0; +} + + +/* + Convert subquery predicate into non-mergeable semi-join nest. + + TODO: + why does this do IN-EXISTS conversion? Can't we unify it with mergeable + semi-joins? currently, convert_subq_to_sj() cannot fail to convert (unless + fatal errors) + + + RETURN + FALSE - Ok + TRUE - Fatal error +*/ + +static bool convert_subq_to_jtbm(JOIN *parent_join, + Item_in_subselect *subq_pred, + bool *remove_item) +{ + SELECT_LEX *parent_lex= parent_join->select_lex; + List<TABLE_LIST> *emb_join_list= &parent_lex->top_join_list; + TABLE_LIST *emb_tbl_nest= NULL; // will change when we learn to handle outer joins + TABLE_LIST *tl; + DBUG_ENTER("convert_subq_to_jtbm"); + + double rows; + double read_time; + + subq_pred->in_strategy &= ~SUBS_IN_TO_EXISTS; + subq_pred->optimize(&rows, &read_time); + + subq_pred->jtbm_read_time= read_time; + subq_pred->jtbm_record_count=rows; + subq_pred->is_jtbm_merged= TRUE; + + if (subq_pred->engine->engine_type() != subselect_engine::HASH_SJ_ENGINE) + { + *remove_item= FALSE; +/* + bool res; + res= make_in_exists_conversion(parent_join->thd, parent_join, subq_pred); + DBUG_RETURN(res); +*/ + DBUG_RETURN(FALSE); + } + + + *remove_item= TRUE; + + TABLE_LIST *jtbm; + char *tbl_alias; + if (!(tbl_alias= (char*)parent_join->thd->calloc(SUBQERY_TEMPTABLE_NAME_MAX_LEN)) || + !(jtbm= alloc_join_nest(parent_join->thd))) //todo: this is not a join nest! + { + DBUG_RETURN(TRUE); + } + + jtbm->join_list= emb_join_list; + jtbm->embedding= emb_tbl_nest; + jtbm->jtbm_subselect= subq_pred; + jtbm->nested_join= NULL; + + /* Nests do not participate in those 'chains', so: */ + /* jtbm->next_leaf= jtbm->next_local= jtbm->next_global == NULL*/ + emb_join_list->push_back(jtbm); + + /* + Inject the jtbm table into TABLE_LIST::next_leaf list, so that + make_join_statistics() and co. can find it. + */ + for (tl= parent_lex->leaf_tables; tl->next_leaf; tl= tl->next_leaf) + {} + tl->next_leaf= jtbm; + + /* + Same as above for TABLE_LIST::next_local chain + (a theory: a next_local chain always starts with ::leaf_tables + because view's tables are inserted after the view) + */ + for (tl= parent_lex->leaf_tables; tl->next_local; tl= tl->next_local) + {} + tl->next_local= jtbm; + + /* A theory: no need to re-connect the next_global chain */ + + subselect_hash_sj_engine *hash_sj_engine= + ((subselect_hash_sj_engine*)subq_pred->engine); + jtbm->table= hash_sj_engine->tmp_table; + + jtbm->table->tablenr= parent_join->table_count; + jtbm->table->map= table_map(1) << (parent_join->table_count); + jtbm->jtbm_table_no= jtbm->table->tablenr; + + parent_join->table_count++; + DBUG_ASSERT(parent_join->table_count < MAX_TABLES); + + Item *conds= hash_sj_engine->semi_join_conds; + conds->fix_after_pullout(parent_lex, &conds); + + DBUG_EXECUTE("where", print_where(conds,"SJ-EXPR", QT_ORDINARY);); + + create_subquery_temptable_name(tbl_alias, hash_sj_engine->materialize_join-> + select_lex->select_number); + jtbm->alias= tbl_alias; +#if 0 + /* Inject sj_on_expr into the parent's WHERE or ON */ + if (emb_tbl_nest) + { + DBUG_ASSERT(0); + /*emb_tbl_nest->on_expr= and_items(emb_tbl_nest->on_expr, + sj_nest->sj_on_expr); + emb_tbl_nest->on_expr->fix_fields(parent_join->thd, &emb_tbl_nest->on_expr); + */ + } + else + { + /* Inject into the WHERE */ + parent_join->conds= and_items(parent_join->conds, conds); + parent_join->conds->fix_fields(parent_join->thd, &parent_join->conds); + parent_join->select_lex->where= parent_join->conds; + } +#endif + /* Don't unlink the child subselect, as the subquery will be used. */ + + DBUG_RETURN(FALSE); +} + + static TABLE_LIST *alloc_join_nest(THD *thd) { TABLE_LIST *tbl; @@ -1244,8 +1738,16 @@ bool optimize_semijoin_nests(JOIN *join, table_map all_table_map) sjm->tables= n_tables; sjm->is_used= FALSE; double subjoin_out_rows, subjoin_read_time; - join->get_partial_join_cost(n_tables + join->const_tables, - &subjoin_read_time, &subjoin_out_rows); + + /* + join->get_partial_cost_and_fanout(n_tables + join->const_tables, + table_map(-1), + &subjoin_read_time, + &subjoin_out_rows); + */ + join->get_prefix_cost_and_fanout(n_tables, + &subjoin_read_time, + &subjoin_out_rows); sjm->materialization_cost.convert_from_cost(subjoin_read_time); sjm->rows= subjoin_out_rows; @@ -1266,6 +1768,8 @@ bool optimize_semijoin_nests(JOIN *join, table_map all_table_map) "oe IN (SELECT t.key ...)" it is trivial. - Functional dependencies between the tables in the semi-join nest (the payoff is probably less here?) + + See also get_post_group_estimate(). */ { for (uint i=0 ; i < join->const_tables + sjm->tables ; i++) @@ -1321,6 +1825,7 @@ bool optimize_semijoin_nests(JOIN *join, table_map all_table_map) DBUG_RETURN(FALSE); } + /* Get estimated record length for semi-join materialization temptable @@ -1422,7 +1927,6 @@ get_tmp_table_write_cost(THD *thd, double row_count, uint row_size) } -//psergey-todo: is the below a kind of table elimination?? /* Check if table's KEYUSE elements have an eq_ref(outer_tables) candidate @@ -1439,6 +1943,8 @@ get_tmp_table_write_cost(THD *thd, double row_count, uint row_size) Check again if it is feasible to factor common parts with constant table search + Also check if it's feasible to factor common parts with table elimination + RETURN TRUE - There exists an eq_ref(outer-tables) candidate FALSE - Otherwise @@ -1489,6 +1995,7 @@ bool find_eq_ref_candidate(TABLE *table, table_map sj_inner_tables) return FALSE; } + /* Do semi-join optimization step after we've added a new tab to join prefix @@ -2148,7 +2655,7 @@ at_sjmat_pos(const JOIN *join, table_map remaining_tables, const JOIN_TAB *tab, void fix_semijoin_strategies_for_picked_join_order(JOIN *join) { - uint table_count=join->tables; + uint table_count=join->table_count; uint tablenr; table_map remaining_tables= 0; table_map handled_tabs= 0; @@ -2309,9 +2816,11 @@ void fix_semijoin_strategies_for_picked_join_order(JOIN *join) remaining_tables |= s->table->map; //s->sj_strategy= pos->sj_strategy; join->join_tab[first].sj_strategy= join->best_positions[first].sj_strategy; + join->join_tab[first].n_sj_tables= join->best_positions[first].n_sj_tables; } } + /* Setup semi-join materialization strategy for one semi-join nest @@ -2333,10 +2842,11 @@ void fix_semijoin_strategies_for_picked_join_order(JOIN *join) TRUE Error */ -bool setup_sj_materialization(JOIN_TAB *tab) +bool setup_sj_materialization(JOIN_TAB *sjm_tab) { uint i; DBUG_ENTER("setup_sj_materialization"); + JOIN_TAB *tab= sjm_tab->bush_children->start; TABLE_LIST *emb_sj_nest= tab->table->pos_in_table_list->embedding; SJ_MATERIALIZATION_INFO *sjm= emb_sj_nest->sj_mat_info; THD *thd= tab->join->thd; @@ -2364,10 +2874,13 @@ bool setup_sj_materialization(JOIN_TAB *tab) DBUG_RETURN(TRUE); /* purecov: inspected */ sjm->table->file->extra(HA_EXTRA_WRITE_CACHE); sjm->table->file->extra(HA_EXTRA_IGNORE_DUP_KEY); + tab->join->sj_tmp_tables.push_back(sjm->table); tab->join->sjm_info_list.push_back(sjm); sjm->materialized= FALSE; + sjm_tab->table= sjm->table; + if (!sjm->is_sj_scan) { KEY *tmp_key; /* The only index on the temporary table. */ @@ -2380,8 +2893,7 @@ bool setup_sj_materialization(JOIN_TAB *tab) temptable. */ TABLE_REF *tab_ref; - if (!(tab_ref= (TABLE_REF*) thd->alloc(sizeof(TABLE_REF)))) - DBUG_RETURN(TRUE); /* purecov: inspected */ + tab_ref= &sjm_tab->ref; tab_ref->key= 0; /* The only temp table index. */ tab_ref->key_length= tmp_key->key_length; if (!(tab_ref->key_buff= @@ -2414,12 +2926,22 @@ bool setup_sj_materialization(JOIN_TAB *tab) use that information instead. */ cur_ref_buff + null_count, - null_count ? tab_ref->key_buff : 0, + null_count ? cur_ref_buff : 0, cur_key_part->length, tab_ref->items[i], FALSE); cur_ref_buff+= cur_key_part->store_length; } *ref_key= NULL; /* End marker. */ + + /* + We don't ever have guarded conditions for SJM tables, but code at SQL + layer depends on cond_guards array being alloced. + */ + if (!(tab_ref->cond_guards= (bool**) thd->calloc(sizeof(uint*)*tmp_key_parts))) + { + DBUG_RETURN(TRUE); + } + tab_ref->key_err= 1; tab_ref->key_parts= tmp_key_parts; sjm->tab_ref= tab_ref; @@ -2439,6 +2961,8 @@ bool setup_sj_materialization(JOIN_TAB *tab) if (!(sjm->in_equality= create_subq_in_equalities(thd, sjm, emb_sj_nest->sj_subq_pred))) DBUG_RETURN(TRUE); /* purecov: inspected */ + sjm_tab->type= JT_EQ_REF; + sjm_tab->select_cond= sjm->in_equality; } else { @@ -2491,9 +3015,11 @@ bool setup_sj_materialization(JOIN_TAB *tab) then substitute_for_best_equal_field() will change the conditions according to the join order: - it1 - it2 it1.col=it2.col - ot cond(it1.col) + table | attached condition + ------+-------------------- + it1 | + it2 | it1.col=it2.col + ot | cond(it1.col) although we've originally had "SELECT it2.col", conditions attached to subsequent outer tables will refer to it1.col, so SJM-Scan will @@ -2522,8 +3048,18 @@ bool setup_sj_materialization(JOIN_TAB *tab) /* The write_set for source tables must be set up to allow the copying */ bitmap_set_bit(copy_to->table->write_set, copy_to->field_index); } + sjm_tab->type= JT_ALL; + + /* Initialize full scan */ + sjm_tab->read_first_record= join_read_record_no_init; + sjm_tab->read_record.copy_field= sjm->copy_field; + sjm_tab->read_record.copy_field_end= sjm->copy_field + + sjm->sjm_table_cols.elements; + sjm_tab->read_record.read_record= rr_sequential_and_unpack; } + sjm_tab->bush_children->end[-1].next_select= end_sj_materialize; + DBUG_RETURN(FALSE); } @@ -3150,17 +3686,19 @@ int setup_semijoin_dups_elimination(JOIN *join, ulonglong options, uint i; THD *thd= join->thd; DBUG_ENTER("setup_semijoin_dups_elimination"); - - for (i= join->const_tables ; i < join->tables; ) + + POSITION *pos= join->best_positions + join->const_tables; + for (i= join->const_tables ; i < join->top_join_tab_count; ) { JOIN_TAB *tab=join->join_tab + i; - POSITION *pos= join->best_positions + i; + //POSITION *pos= join->best_positions + i; uint keylen, keyno; switch (pos->sj_strategy) { case SJ_OPT_MATERIALIZE: case SJ_OPT_MATERIALIZE_SCAN: /* Do nothing */ - i+= pos->n_sj_tables; + i+= 1;// It used to be pos->n_sj_tables, but now they are embedded in a nest + pos += pos->n_sj_tables; break; case SJ_OPT_LOOSE_SCAN: { @@ -3177,6 +3715,7 @@ int setup_semijoin_dups_elimination(JOIN *join, ulonglong options, if (pos->n_sj_tables > 1) tab[pos->n_sj_tables - 1].do_firstmatch= tab; i+= pos->n_sj_tables; + pos+= pos->n_sj_tables; break; } case SJ_OPT_DUPS_WEEDOUT: @@ -3274,6 +3813,7 @@ int setup_semijoin_dups_elimination(JOIN *join, ulonglong options, join->join_tab[i + pos->n_sj_tables - 1].check_weed_out_table= sjtbl; i+= pos->n_sj_tables; + pos+= pos->n_sj_tables; break; } case SJ_OPT_FIRST_MATCH: @@ -3296,10 +3836,12 @@ int setup_semijoin_dups_elimination(JOIN *join, ulonglong options, } j[-1].do_firstmatch= jump_to; i+= pos->n_sj_tables; + pos+= pos->n_sj_tables; break; } case SJ_OPT_NONE: i++; + pos++; break; } } @@ -3500,7 +4042,7 @@ int rewrite_to_index_subquery_engine(JOIN *join) if (!join->group_list && !join->order && join->unit->item && join->unit->item->substype() == Item_subselect::IN_SUBS && - join->tables == 1 && join->conds && + join->table_count == 1 && join->conds && !join->unit->is_union()) { if (!join->having) @@ -3637,6 +4179,8 @@ static void remove_subq_pushed_predicates(JOIN *join, Item **where) } + + /** Optimize all subqueries of a query that were not flattened into a semijoin. @@ -3659,6 +4203,80 @@ bool JOIN::optimize_unflattened_subqueries() } +/* + Join tab execution startup function. + + SYNOPSIS + join_tab_execution_startup() + tab Join tab to perform startup actions for + + DESCRIPTION + Join tab execution startup function. This is different from + tab->read_first_record in the regard that this has actions that are to be + done once per join execution. + + Currently there are only two possible startup functions, so we have them + both here inside if (...) branches. In future we could switch to function + pointers. + + RETURN + NESTED_LOOP_OK - OK + NESTED_LOOP_ERROR| NESTED_LOOP_KILLED - Error, abort the join execution +*/ + +enum_nested_loop_state join_tab_execution_startup(JOIN_TAB *tab) +{ + Item_in_subselect *in_subs; + DBUG_ENTER("join_tab_execution_startup"); + + if (tab->table->pos_in_table_list && + (in_subs= tab->table->pos_in_table_list->jtbm_subselect)) + { + /* It's a non-merged SJM nest */ + DBUG_ASSERT(in_subs->engine->engine_type() == + subselect_engine::HASH_SJ_ENGINE); + subselect_hash_sj_engine *hash_sj_engine= + ((subselect_hash_sj_engine*)in_subs->engine); + if (!hash_sj_engine->is_materialized) + { + hash_sj_engine->materialize_join->exec(); + hash_sj_engine->is_materialized= TRUE; + + if (hash_sj_engine->materialize_join->error || tab->join->thd->is_fatal_error) + DBUG_RETURN(NESTED_LOOP_ERROR); + } + } + else if (tab->bush_children) + { + /* It's a merged SJM nest */ + enum_nested_loop_state rc; + SJ_MATERIALIZATION_INFO *sjm= tab->bush_children->start->emb_sj_nest->sj_mat_info; + + if (!sjm->materialized) + { + JOIN *join= tab->join; + JOIN_TAB *join_tab= tab->bush_children->start; + JOIN_TAB *save_return_tab= join->return_tab; + /* + Now run the join for the inner tables. The first call is to run the + join, the second one is to signal EOF (this is essential for some + join strategies, e.g. it will make join buffering flush the records) + */ + if ((rc= sub_select(join, join_tab, FALSE/* no EOF */)) < 0 || + (rc= sub_select(join, join_tab, TRUE/* now EOF */)) < 0) + { + join->return_tab= save_return_tab; + DBUG_RETURN(rc); /* it's NESTED_LOOP_(ERROR|KILLED)*/ + } + join->return_tab= save_return_tab; + sjm->materialized= TRUE; + } + } + + DBUG_RETURN(NESTED_LOOP_OK); +} + + /** Choose an optimal strategy to execute an IN/ALL/ANY subquery predicate based on cost. @@ -3754,14 +4372,25 @@ bool JOIN::choose_subquery_plan(table_map join_tables) are evaluated for each complete row of the outer join. */ outer_partial_plan_len= (in_subs->get_join_tab_idx() == MAX_TABLES) ? - outer_join->tables : + outer_join->table_count : in_subs->get_join_tab_idx() + 1; - outer_join->get_partial_join_cost(outer_partial_plan_len, &dummy, - &outer_record_count); - if (outer_join->tables > outer_join->const_tables) + outer_join->get_partial_cost_and_fanout(outer_partial_plan_len, + table_map(-1), + &dummy, + &outer_record_count); + + if (outer_join->table_count > outer_join->const_tables) + { + outer_join->get_partial_cost_and_fanout(outer_partial_plan_len, + in_subs->used_tables(), + &dummy, + &outer_lookup_keys); + /* outer_lookup_keys= prev_record_reads(outer_join->best_positions, outer_partial_plan_len, in_subs->used_tables()); + */ + } else { /* If all tables are constant, positions is undefined. */ @@ -3793,7 +4422,7 @@ bool JOIN::choose_subquery_plan(table_map join_tables) inner_read_time_1= inner_join->best_read; inner_record_count_1= inner_join->record_count; - if (in_to_exists_where && const_tables != tables) + if (in_to_exists_where && const_tables != table_count) { /* Re-optimize and cost the subquery taking into account the IN-EXISTS @@ -3894,14 +4523,14 @@ bool JOIN::choose_subquery_plan(table_map join_tables) else if (in_subs->in_strategy & SUBS_IN_TO_EXISTS) { if (reopt_result == REOPT_NONE && in_to_exists_where && - const_tables != tables) + const_tables != table_count) { /* The subquery was not reoptimized either because the user allowed only the IN-EXISTS strategy, or because materialization was not possible based on semantic analysis. Clenup the original plan and reoptimize. */ - for (uint i= 0; i < tables; i++) + for (uint i= 0; i < table_count; i++) { join_tab[i].keyuse= NULL; join_tab[i].checked_keys.clear_all(); @@ -3939,7 +4568,7 @@ bool JOIN::choose_subquery_plan(table_map join_tables) bool JOIN::choose_tableless_subquery_plan() { - DBUG_ASSERT(!tables_list || !tables); + DBUG_ASSERT(!tables_list || !table_count); if (unit->item) { DBUG_ASSERT(unit->item->type() == Item::SUBSELECT_ITEM); diff --git a/sql/opt_subselect.h b/sql/opt_subselect.h index 47d85d5c38d..4d89609e1a8 100644 --- a/sql/opt_subselect.h +++ b/sql/opt_subselect.h @@ -1,4 +1,6 @@ -/* */ +/* + Semi-join subquery optimization code definitions +*/ #ifdef USE_PRAGMA_INTERFACE #pragma interface /* gcc class implementation */ @@ -366,4 +368,10 @@ int clear_sj_tmp_tables(JOIN *join); int rewrite_to_index_subquery_engine(JOIN *join); +void get_delayed_table_estimates(TABLE *table, + ha_rows *out_rows, + double *scan_time, + double *startup_cost); + +enum_nested_loop_state join_tab_execution_startup(JOIN_TAB *tab); diff --git a/sql/sql_base.cc b/sql/sql_base.cc index eb58b09fd00..63899242d06 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -7759,7 +7759,6 @@ bool setup_tables(THD *thd, Name_resolution_context *context, table_list= table_list->next_leaf, tablenr++) { TABLE *table= table_list->table; - table->pos_in_table_list= table_list; if (first_select_table && table_list->top_table() == first_select_table) { @@ -7767,9 +7766,19 @@ bool setup_tables(THD *thd, Name_resolution_context *context, first_select_table= 0; tablenr= 0; } - setup_table_map(table, table_list, tablenr); - if (table_list->process_index_hints(table)) - DBUG_RETURN(1); + + if (table_list->jtbm_subselect) + { + table_list->jtbm_table_no= tablenr; + } + else + { + table->pos_in_table_list= table_list; + setup_table_map(table, table_list, tablenr); + + if (table_list->process_index_hints(table)) + DBUG_RETURN(1); + } } if (tablenr > MAX_TABLES) { @@ -7796,6 +7805,17 @@ bool setup_tables(THD *thd, Name_resolution_context *context, if (res) DBUG_RETURN(1); } + + if (table_list->jtbm_subselect) + { + Item *item= table_list->jtbm_subselect->optimizer; + if (table_list->jtbm_subselect->optimizer->fix_fields(thd, &item)) + { + my_error(ER_TOO_MANY_TABLES,MYF(0),MAX_TABLES); /* psergey-todo: WHY ER_TOO_MANY_TABLES ???*/ + DBUG_RETURN(1); + } + DBUG_ASSERT(item == table_list->jtbm_subselect->optimizer); + } } /* Precompute and store the row types of NATURAL/USING joins. */ @@ -8217,7 +8237,7 @@ int setup_conds(THD *thd, TABLE_LIST *tables, TABLE_LIST *leaves, goto err_no_arena; } - thd->thd_marker.emb_on_expr_nest= (TABLE_LIST*)1; + thd->thd_marker.emb_on_expr_nest= NO_JOIN_NEST; if (*conds) { thd->where="where clause"; diff --git a/sql/sql_cursor.cc b/sql/sql_cursor.cc index 274d418dc19..ab5308affa9 100644 --- a/sql/sql_cursor.cc +++ b/sql/sql_cursor.cc @@ -363,7 +363,6 @@ Sensitive_cursor::open(JOIN *join_arg) join= join_arg; THD *thd= join->thd; /* First non-constant table */ - JOIN_TAB *join_tab= join->join_tab + join->const_tables; DBUG_ENTER("Sensitive_cursor::open"); join->change_result(result); @@ -381,26 +380,29 @@ Sensitive_cursor::open(JOIN *join_arg) /* Prepare JOIN for reading rows. */ join->tmp_table= 0; - join->join_tab[join->tables-1].next_select= setup_end_select_func(join); + join->join_tab[join->top_join_tab_count - 1].next_select= setup_end_select_func(join); join->send_records= 0; join->fetch_limit= join->unit->offset_limit_cnt; /* Disable JOIN CACHE as it is not working with cursors yet */ - for (JOIN_TAB *tab= join_tab; - tab != join->join_tab + join->tables - 1; - tab++) + for (JOIN_TAB *tab= first_linear_tab(join, WITHOUT_CONST_TABLES); + tab != join->join_tab + join->top_join_tab_count - 1; + tab= next_linear_tab(join, tab, WITH_BUSH_ROOTS)) { if (tab->next_select == sub_select_cache) tab->next_select= sub_select; } - DBUG_ASSERT(join_tab->table->reginfo.not_exists_optimize == 0); - DBUG_ASSERT(join_tab->not_used_in_distinct == 0); +#ifndef DBUG_OFF + JOIN_TAB *first_tab= first_linear_tab(join, WITHOUT_CONST_TABLES); + DBUG_ASSERT(first_tab->table->reginfo.not_exists_optimize == 0); + DBUG_ASSERT(first_tab->not_used_in_distinct == 0); /* null_row is set only if row not found and it's outer join: should never happen for the first table in join_tab list */ - DBUG_ASSERT(join_tab->table->null_row == 0); + DBUG_ASSERT(first_tab->table->null_row == 0); +#endif DBUG_RETURN(0); } diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc index 5b5565000da..d1183e29be5 100644 --- a/sql/sql_delete.cc +++ b/sql/sql_delete.cc @@ -681,9 +681,10 @@ multi_delete::initialize_tables(JOIN *join) walk= delete_tables; - for (JOIN_TAB *tab=join->join_tab, *end=join->join_tab+join->tables; - tab < end; - tab++) + + for (JOIN_TAB *tab= first_linear_tab(join, WITH_CONST_TABLES); + tab; + tab= next_linear_tab(join, tab, WITH_BUSH_ROOTS)) { if (tab->table->map & tables_to_delete_from) { diff --git a/sql/sql_join_cache.cc b/sql/sql_join_cache.cc index 44b953a27fb..64b82efd8ff 100644 --- a/sql/sql_join_cache.cc +++ b/sql/sql_join_cache.cc @@ -33,6 +33,7 @@ #define NO_MORE_RECORDS_IN_BUFFER (uint)(-1) +static void save_or_restore_used_tabs(JOIN_TAB *join_tab, bool save); /***************************************************************************** * Join cache module @@ -139,52 +140,6 @@ uint add_table_data_fields_to_join_cache(JOIN_TAB *tab, } /* - Get the next table whose records are stored in the join buffer of this cache - - SYNOPSIS - get_next_table() - tab the table for which the next table is to be returned - - DESCRIPTION - For a given table whose records are stored in this cache the function - returns the next such table if there is any. - The function takes into account that the tables whose records are - are stored in the same cache now can interleave with tables from - materialized semijoin subqueries. - - TODO - This function should be modified/simplified after the new code for - materialized semijoins is merged. - - RETURN - The next join table whose records are stored in the buffer of this cache - if such table exists, 0 - otherwise -*/ - -JOIN_TAB *JOIN_CACHE::get_next_table(JOIN_TAB *tab) -{ - - if (++tab == join_tab) - return NULL; - if (join_tab->first_sjm_sibling) - return tab; - uint i= tab-join->join_tab; - /* - Temporary measure before MWL#90 refactorings are there: if 'tab' is at upper - level (i.e. it's not inside an SJM nest), still include into the join buffer - the tables from within SJM nest. We might need the subquery's select list - columns, because SJ-Materialization-Scan upacks data to those. - - while (sj_is_materialize_strategy(join->best_positions[i].sj_strategy) && - i < join->tables) - i+= join->best_positions[i].n_sj_tables; - - */ - return join->join_tab+i < join_tab ? join->join_tab+i : NULL; -} - - -/* Determine different counters of fields associated with a record in the cache SYNOPSIS @@ -203,12 +158,55 @@ JOIN_TAB *JOIN_CACHE::get_next_table(JOIN_TAB *tab) void JOIN_CACHE::calc_record_fields() { - JOIN_TAB *tab = prev_cache ? prev_cache->join_tab : - (join_tab->first_sjm_sibling ? - join_tab->first_sjm_sibling : - join->join_tab+join->const_tables); - tables= join_tab-tab; + JOIN_TAB *tab; + + if (prev_cache) + tab= prev_cache->join_tab; + else + { + if (join_tab->bush_root_tab) + { + /* + --ot1--SJM1--------------ot2--... + | + | + +-it1--...--itN + ^____________ this->join_tab is somewhere here, + inside an sjm nest. + + The join buffer should store the values of it1.*, it2.*, .. + It should not store values of ot1.*. + */ + tab= join_tab->bush_root_tab->bush_children->start; + } + else + { + /* + -ot1--ot2--SJM1--SJM2--------------ot3--...--otN + | | ^ + | +-it21--...--it2N | + | \-- we're somewhere here, + +-it11--...--it1N at the top level + + The join buffer should store the values of + + ot1.*, ot2.*, it1{i}, it2{j}.*, ot3.*, ... + + that is, we should start from the first non-const top-level table. + + We will need to store columns of SJ-inner tables (it_X_Y.*), but we're + not interested in storing the columns of materialization tables + themselves. Beause of that, if the first non-const top-level table is a + materialized table, we move to its bush_children: + */ + tab= join->join_tab + join->const_tables; + if (tab->bush_children) + tab= tab->bush_children->start; + } + } + DBUG_ASSERT(!tab->bush_children); + start_tab= tab; fields= 0; blobs= 0; flag_fields= 0; @@ -216,7 +214,11 @@ void JOIN_CACHE::calc_record_fields() data_field_ptr_count= 0; referenced_fields= 0; - for ( ; tab ; tab= get_next_table(tab)) + /* + The following loop will get inside SJM nests, because data may be unpacked + to sjm-inner tables. + */ + for (; tab != join_tab ; tab= next_linear_tab(join, tab, WITHOUT_BUSH_ROOTS)) { tab->calc_used_field_length(FALSE); flag_fields+= test(tab->used_null_fields || tab->used_uneven_bit_fields); @@ -245,7 +247,8 @@ void JOIN_CACHE::calc_record_fields() that occur in the ref expressions and marks these fields in the bitmap tab->table->tmp_set. The function counts the number of them stored in this cache and the total number of them stored in the previous caches - and saves the results of the counting in 'local_key_arg_fields' and 'external_key_arg_fields' respectively. + and saves the results of the counting in 'local_key_arg_fields' and + 'external_key_arg_fields' respectively. NOTES The function does not do anything if no key is used to join the records @@ -269,8 +272,8 @@ void JOIN_CACHE::collect_info_on_key_args() cache= this; do { - for (tab= cache->join_tab-cache->tables; tab ; - tab= cache->get_next_table(tab)) + for (tab= cache->start_tab; tab != cache->join_tab; + tab= next_linear_tab(join, tab, WITHOUT_BUSH_ROOTS)) { uint key_args; bitmap_clear_all(&tab->table->tmp_set); @@ -386,7 +389,8 @@ void JOIN_CACHE::create_flag_fields() ©); /* Create fields for all null bitmaps and null row flags that are needed */ - for (tab= join_tab-tables; tab; tab= get_next_table(tab)) + for (tab= start_tab; tab != join_tab; + tab= next_linear_tab(join, tab, WITHOUT_BUSH_ROOTS)) { TABLE *table= tab->table; @@ -473,8 +477,8 @@ void JOIN_CACHE::create_key_arg_fields() while (ext_key_arg_cnt) { cache= cache->prev_cache; - for (tab= cache->join_tab-cache->tables; tab; - tab= cache->get_next_table(tab)) + for (tab= cache->start_tab; tab != cache->join_tab; + tab= next_linear_tab(join, tab, WITHOUT_BUSH_ROOTS)) { CACHE_FIELD *copy_end; MY_BITMAP *key_read_set= &tab->table->tmp_set; @@ -524,7 +528,8 @@ void JOIN_CACHE::create_key_arg_fields() /* Now create local fields that are used to build ref for this key access */ copy= field_descr+flag_fields; - for (tab= join_tab-tables; tab; tab= get_next_table(tab)) + for (tab= start_tab; tab != join_tab; + tab= next_linear_tab(join, tab, WITHOUT_BUSH_ROOTS)) { length+= add_table_data_fields_to_join_cache(tab, &tab->table->tmp_set, &data_field_count, ©, @@ -573,14 +578,15 @@ void JOIN_CACHE::create_key_arg_fields() none */ -void JOIN_CACHE:: create_remaining_fields() +void JOIN_CACHE::create_remaining_fields() { JOIN_TAB *tab; bool all_read_fields= !is_key_access(); CACHE_FIELD *copy= field_descr+flag_fields+data_field_count; CACHE_FIELD **copy_ptr= blob_ptr+data_field_ptr_count; - for (tab= join_tab-tables; tab; tab= get_next_table(tab)) + for (tab= start_tab; tab != join_tab; + tab= next_linear_tab(join, tab, WITHOUT_BUSH_ROOTS)) { MY_BITMAP *rem_field_set; TABLE *table= tab->table; @@ -737,8 +743,11 @@ ulong JOIN_CACHE::get_min_join_buffer_size() if (!min_buff_size) { size_t len= 0; - for (JOIN_TAB *tab= join_tab-tables; tab < join_tab; tab++) + for (JOIN_TAB *tab= start_tab; tab != join_tab; + tab= next_linear_tab(join, tab, WITHOUT_BUSH_ROOTS)) + { len+= tab->get_max_used_fieldlength(); + } len+= get_record_max_affix_length() + get_max_key_addon_space_per_record(); size_t min_sz= len*min_records; size_t add_sz= 0; @@ -790,8 +799,11 @@ ulong JOIN_CACHE::get_max_join_buffer_size(bool optimize_buff_size) size_t max_sz; size_t min_sz= get_min_join_buffer_size(); size_t len= 0; - for (JOIN_TAB *tab= join_tab-tables; tab < join_tab; tab++) + for (JOIN_TAB *tab= start_tab; tab != join_tab; + tab= next_linear_tab(join, tab, WITHOUT_BUSH_ROOTS)) + { len+= tab->get_used_fieldlength(); + } len+= get_record_max_affix_length(); avg_record_length= len; len+= get_max_key_addon_space_per_record() + avg_aux_buffer_incr; @@ -865,7 +877,9 @@ int JOIN_CACHE::alloc_buffer() set_if_bigger(max_records, 10); min_buff_size= get_min_join_buffer_size(); buff_size= get_max_join_buffer_size(optimize_buff_size); - for (tab= join->join_tab+join->const_tables; tab <= join_tab; tab++) + + for (tab= start_tab; tab!= join_tab; + tab= next_linear_tab(join, tab, WITHOUT_BUSH_ROOTS)) { cache= tab->cache; if (cache) @@ -2154,10 +2168,19 @@ enum_nested_loop_state JOIN_CACHE::join_matching_records(bool skip_last) join_tab->select->quick= 0; } - /* Prepare to retrieve all records of the joined table */ - if ((error= join_tab_scan->open())) - goto finish; /* psergey-note: if this returns error, we will assert in net_send_statement() */ + if ((rc= join_tab_execution_startup(join_tab)) < 0) + goto finish2; + /* Prepare to retrieve all records of the joined table */ + if ((error= join_tab_scan->open())) + { + /* + TODO: if we get here, we will assert in net_send_statement(). Add test + coverage and fix. + */ + goto finish; + } + while (!(error= join_tab_scan->next())) { if (join->thd->killed) @@ -2199,6 +2222,7 @@ enum_nested_loop_state JOIN_CACHE::join_matching_records(bool skip_last) finish: if (error) rc= error < 0 ? NESTED_LOOP_NO_MORE_ROWS: NESTED_LOOP_ERROR; +finish2: join_tab_scan->close(); return rc; } @@ -3219,12 +3243,7 @@ uint JOIN_CACHE_HASHED::get_next_key(uchar ** key) int JOIN_TAB_SCAN::open() { - JOIN_TAB *bound= join_tab-cache->tables; - for (JOIN_TAB *tab= join_tab-1; tab != bound && !tab->cache; tab--) - { - tab->status= tab->table->status; - tab->table->status= 0; - } + save_or_restore_used_tabs(join_tab, FALSE); is_first_record= TRUE; return join_init_read_record(join_tab); } @@ -3279,6 +3298,49 @@ int JOIN_TAB_SCAN::next() } +/* + Walk back in join order from join_tab until we encounter a join tab with + tab->cache!=NULL, and save/restore tab->table->status along the way. + + @param save TRUE save + FALSE restore +*/ + +static void save_or_restore_used_tabs(JOIN_TAB *join_tab, bool save) +{ + JOIN_TAB *first= join_tab->bush_root_tab? + join_tab->bush_root_tab->bush_children->start : + join_tab->join->join_tab + join_tab->join->const_tables; + + for (JOIN_TAB *tab= join_tab-1; tab != first && !tab->cache; tab--) + { + if (tab->bush_children) + { + for (JOIN_TAB *child= tab->bush_children->start; + child != tab->bush_children->end; + child++) + { + if (save) + child->table->status= child->status; + else + { + tab->status= tab->table->status; + tab->table->status= 0; + } + } + } + + if (save) + tab->table->status= tab->status; + else + { + tab->status= tab->table->status; + tab->table->status= 0; + } + } +} + + /* Perform finalizing actions for a scan over the table records @@ -3295,9 +3357,7 @@ int JOIN_TAB_SCAN::next() void JOIN_TAB_SCAN::close() { - JOIN_TAB *bound= join_tab-cache->tables; - for (JOIN_TAB *tab= join_tab-1; tab != bound && !tab->cache; tab--) - tab->table->status= tab->status; + save_or_restore_used_tabs(join_tab, TRUE); } @@ -3697,12 +3757,7 @@ int JOIN_TAB_SCAN_MRR::open() /* Dynamic range access is never used with BKA */ DBUG_ASSERT(join_tab->use_quick != 2); - JOIN_TAB *bound= join_tab-cache->tables; - for (JOIN_TAB *tab= join_tab-1; tab != bound && !tab->cache; tab--) - { - tab->status= tab->table->status; - tab->table->status= 0; - } + save_or_restore_used_tabs(join_tab, FALSE); init_mrr_buff(); diff --git a/sql/sql_join_cache.h b/sql/sql_join_cache.h index 920b7a507c1..f87cdcec649 100644 --- a/sql/sql_join_cache.h +++ b/sql/sql_join_cache.h @@ -121,11 +121,14 @@ protected: */ JOIN *join; - /* - Cardinality of the range of join tables whose fields can be put into the - cache. A table from the range not necessarily contributes to the cache. + /* + JOIN_TAB of the first table that can have it's fields in the join cache. + That is, tables in the [start_tab, tab) range can have their fields in the + join cache. + If a join tab in the range represents an SJM-nest, then all tables from the + nest can have their fields in the join cache, too. */ - uint tables; + JOIN_TAB *start_tab; /* The total number of flag and data fields that can appear in a record @@ -647,8 +650,6 @@ public: buff= 0; } - JOIN_TAB *get_next_table(JOIN_TAB *tab); - friend class JOIN_CACHE_HASHED; friend class JOIN_CACHE_BNL; friend class JOIN_CACHE_BKA; diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index b4da09c8108..a0699994cb7 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -3114,8 +3114,16 @@ bool st_select_lex::optimize_unflattened_subqueries() for (SELECT_LEX_UNIT *un= first_inner_unit(); un; un= un->next_unit()) { Item_subselect *subquery_predicate= un->item; + if (subquery_predicate) { + if (subquery_predicate->substype() == Item_subselect::IN_SUBS) + { + Item_in_subselect *in_subs=(Item_in_subselect*)subquery_predicate; + if (in_subs->is_jtbm_merged) + continue; + } + for (SELECT_LEX *sl= un->first_select(); sl; sl= sl->next_select()) { JOIN *inner_join= sl->join; diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 2830184e8c7..3f29782fbca 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -98,7 +98,7 @@ static void revise_cache_usage(JOIN_TAB *join_tab); static bool make_join_readinfo(JOIN *join, ulonglong options, uint no_jbuf_after); static bool only_eq_ref_tables(JOIN *join, ORDER *order, table_map tables); static void update_depend_map(JOIN *join); -static void update_depend_map(JOIN *join, ORDER *order); +static void update_depend_map_for_order(JOIN *join, ORDER *order); static ORDER *remove_const(JOIN *join,ORDER *first_order,COND *cond, bool change_list, bool *simple_order); static int return_zero_rows(JOIN *join, select_result *res,TABLE_LIST *tables, @@ -237,11 +237,11 @@ static void select_describe(JOIN *join, bool need_tmp_table,bool need_order, bool distinct, const char *message=NullS); static void add_group_and_distinct_keys(JOIN *join, JOIN_TAB *join_tab); static uint make_join_orderinfo(JOIN *join); -static int -join_read_record_no_init(JOIN_TAB *tab); Item_equal *find_item_equal(COND_EQUAL *cond_equal, Field *field, bool *inherited_fl); +JOIN_TAB *first_depth_first_tab(JOIN* join); +JOIN_TAB *next_depth_first_tab(JOIN* join, JOIN_TAB* tab); /** This handles SELECT with and without UNION. @@ -553,7 +553,7 @@ JOIN::prepare(Item ***rref_pointer_array, table_ptr; table_ptr= table_ptr->next_leaf) { - tables++; /* Count the number of tables in the join. */ + table_count++; /* Count the number of tables in the join. */ /* If the query uses implicit grouping where the select list contains both aggregate functions and non-aggregate fields, any non-aggregated field @@ -793,6 +793,56 @@ err: } +void +inject_jtbm_conds(JOIN *join, List<TABLE_LIST> *join_list, Item **join_where) +{ + TABLE_LIST *table; + NESTED_JOIN *nested_join; + List_iterator<TABLE_LIST> li(*join_list); + DBUG_ENTER("inject_jtbm_conds"); + + + while ((table= li++)) + { + Item_in_subselect *item; + + if ((item= table->jtbm_subselect)) + { + Item_in_subselect *subq_pred= item; + double rows; + double read_time; + + subq_pred->in_strategy &= ~SUBS_IN_TO_EXISTS; + subq_pred->optimize(&rows, &read_time); + + subq_pred->jtbm_read_time= read_time; + subq_pred->jtbm_record_count=rows; + subq_pred->is_jtbm_merged= TRUE; + + subselect_hash_sj_engine *hash_sj_engine= + ((subselect_hash_sj_engine*)item->engine); + + + //repeat of convert_subq_to_jtbm: + table->table= hash_sj_engine->tmp_table; + + setup_table_map(table->table, table, table->jtbm_table_no); + + Item *sj_conds= hash_sj_engine->semi_join_conds; + + (*join_where)= and_items(*join_where, sj_conds); + (*join_where)->fix_fields(join->thd, join_where); + //parent_join->select_lex->where= parent_join->conds; + } + + if ((nested_join= table->nested_join)) + { + inject_jtbm_conds(join, &nested_join->join_list, join_where); + } + } + DBUG_VOID_RETURN; +} + /** global select optimisation. @@ -884,6 +934,8 @@ JOIN::optimize() if (arena) thd->restore_active_arena(arena, &backup); } + + inject_jtbm_conds(this, join_list, &conds); conds= optimize_cond(this, conds, join_list, &cond_value, &cond_equal); if (thd->is_error()) @@ -913,7 +965,7 @@ JOIN::optimize() "Impossible HAVING" : "Impossible WHERE")); zero_result_cause= having_value == Item::COND_FALSE ? "Impossible HAVING" : "Impossible WHERE"; - tables= 0; + table_count= top_join_tab_count= 0; error= 0; goto setup_subq_exit; } @@ -964,7 +1016,7 @@ JOIN::optimize() { DBUG_PRINT("info",("No matching min/max row")); zero_result_cause= "No matching min/max row"; - tables= 0; + table_count= top_join_tab_count= 0; error=0; goto setup_subq_exit; } @@ -978,7 +1030,7 @@ JOIN::optimize() DBUG_PRINT("info",("Select tables optimized away")); zero_result_cause= "Select tables optimized away"; tables_list= 0; // All tables resolved - const_tables= tables; + const_tables= top_join_tab_count= table_count; /* Extract all table-independent conditions and replace the WHERE clause with them. All other conditions were computed by opt_sum_query @@ -1031,7 +1083,7 @@ JOIN::optimize() else { /* Remove distinct if only const tables */ - select_distinct= select_distinct && (const_tables != tables); + select_distinct= select_distinct && (const_tables != table_count); } thd_proc_info(thd, "preparing"); @@ -1098,8 +1150,9 @@ JOIN::optimize() /* Perform the optimization on fields evaluation mentioned above for all on expressions. - */ - for (JOIN_TAB *tab= join_tab + const_tables; tab < join_tab + tables ; tab++) + */ + for (JOIN_TAB *tab= first_linear_tab(this, WITHOUT_CONST_TABLES); tab; + tab= next_linear_tab(this, tab, WITH_BUSH_ROOTS)) { if (*tab->on_expr_ref) { @@ -1108,26 +1161,24 @@ JOIN::optimize() map2table); (*tab->on_expr_ref)->update_used_tables(); } - - } /* Perform the optimization on fields evaliation mentioned above for all used ref items. */ - for (JOIN_TAB *tab= join_tab + const_tables; tab < join_tab + tables; tab++) + for (JOIN_TAB *tab= first_linear_tab(this, WITHOUT_CONST_TABLES); tab; + tab= next_linear_tab(this, tab, WITH_BUSH_ROOTS)) { uint key_copy_index=0; for (uint i=0; i < tab->ref.key_parts; i++) { - Item **ref_item_ptr= tab->ref.items+i; Item *ref_item= *ref_item_ptr; if (!ref_item->used_tables() && !(select_options & SELECT_DESCRIBE)) continue; COND_EQUAL *equals= tab->first_inner ? tab->first_inner->cond_equal : - cond_equal; + cond_equal; ref_item= substitute_for_best_equal_field(ref_item, equals, map2table); ref_item->update_used_tables(); if (*ref_item_ptr != ref_item) @@ -1136,14 +1187,14 @@ JOIN::optimize() Item *item= ref_item->real_item(); store_key *key_copy= tab->ref.key_copy[key_copy_index]; if (key_copy->type() == store_key::FIELD_STORE_KEY) - { + { store_key_field *field_copy= ((store_key_field *)key_copy); field_copy->change_source_field((Item_field *) item); } } key_copy_index++; } - } + } if (conds && const_table_map != found_const_table_map && (select_options & SELECT_DESCRIBE)) @@ -1190,7 +1241,7 @@ JOIN::optimize() The FROM clause must contain a single non-constant table. */ - if (tables - const_tables == 1 && (group_list || select_distinct) && + if (table_count - const_tables == 1 && (group_list || select_distinct) && !tmp_table_param.sum_func_count && (!join_tab[const_tables].select || !join_tab[const_tables].select->quick || @@ -1241,7 +1292,7 @@ JOIN::optimize() if (! hidden_group_fields && rollup.state == ROLLUP::STATE_NONE) select_distinct=0; } - else if (select_distinct && tables - const_tables == 1 && + else if (select_distinct && table_count - const_tables == 1 && rollup.state == ROLLUP::STATE_NONE) { /* @@ -1370,7 +1421,7 @@ JOIN::optimize() When the WITH ROLLUP modifier is present, we cannot skip temporary table creation for the DISTINCT clause just because there are only const tables. */ - need_tmp= ((const_tables != tables && + need_tmp= ((const_tables != table_count && ((select_distinct || !simple_order || !simple_group) || (group_list && order) || test(select_options & OPTION_BUFFER_RESULT))) || @@ -1384,7 +1435,7 @@ JOIN::optimize() Yet the current implementation of FORCE INDEX hints does not allow us to do it in a clean manner. */ - no_jbuf_after= 1 ? tables : make_join_orderinfo(this); + no_jbuf_after= 1 ? table_count : make_join_orderinfo(this); select_opts_for_readinfo= (select_options & (SELECT_DESCRIBE | SELECT_NO_JOIN_CACHE)) | @@ -1415,13 +1466,16 @@ JOIN::optimize() */ if (need_tmp || select_distinct || group_list || order) { - for (uint i = const_tables; i < tables; i++) - join_tab[i].table->prepare_for_position(); + for (uint i= 0; i < table_count; i++) + { + if (!(table[i]->map & const_table_map)) + table[i]->prepare_for_position(); + } } DBUG_EXECUTE("info",TEST_join(this);); - if (const_tables != tables) + if (const_tables != table_count) { /* Because filesort always does a full table scan or a quick range scan @@ -1507,7 +1561,7 @@ JOIN::optimize() setup_subq_exit: /* Choose an execution strategy for this JOIN. */ - if (!tables_list || !tables) + if (!tables_list || !table_count) choose_tableless_subquery_plan(); /* Even with zero matching rows, subqueries in the HAVING clause may @@ -1627,7 +1681,7 @@ int JOIN::init_execution() if (exec_tmp_table1->distinct) { table_map used_tables= thd->used_tables; - JOIN_TAB *last_join_tab= join_tab+tables-1; + JOIN_TAB *last_join_tab= join_tab + top_join_tab_count - 1; do { if (used_tables & last_join_tab->table->map) @@ -1685,9 +1739,8 @@ bool JOIN::setup_subquery_caches() if (conds) conds= conds->transform(&Item::expr_cache_insert_transformer, (uchar*) thd); - for (JOIN_TAB *tab= join_tab + const_tables; - tab < join_tab + tables ; - tab++) + for (JOIN_TAB *tab= first_linear_tab(this, WITHOUT_CONST_TABLES); + tab; tab= next_linear_tab(this, tab, WITH_BUSH_ROOTS)) { if (tab->select_cond) tab->select_cond= @@ -1841,12 +1894,17 @@ JOIN::reinit() set_items_ref_array(items0); if (join_tab_save) - memcpy(join_tab, join_tab_save, sizeof(JOIN_TAB) * tables); + memcpy(join_tab, join_tab_save, sizeof(JOIN_TAB) * table_count); /* need to reset ref access state (see join_read_key) */ if (join_tab) - for (uint i= 0; i < tables; i++) - join_tab[i].ref.key_err= TRUE; + { + for (JOIN_TAB *tab= first_linear_tab(this, WITH_CONST_TABLES); tab; + tab= next_linear_tab(this, tab, WITH_BUSH_ROOTS)) + { + tab->ref.key_err= TRUE; + } + } if (tmp_join) restore_tmp(); @@ -1903,7 +1961,7 @@ JOIN::save_join_tab() if (!join_tab_save && select_lex->master_unit()->uncacheable) { if (!(join_tab_save= (JOIN_TAB*)thd->memdup((uchar*) join_tab, - sizeof(JOIN_TAB) * tables))) + sizeof(JOIN_TAB) * table_count))) return 1; } return 0; @@ -1943,7 +2001,7 @@ JOIN::exec() } (void) result->prepare2(); // Currently, this cannot fail. - if (!tables_list && (tables || !select_lex->with_sum_func)) + if (!tables_list && (table_count || !select_lex->with_sum_func)) { // Only test of functions if (select_options & SELECT_DESCRIBE) select_describe(this, FALSE, FALSE, FALSE, @@ -1996,7 +2054,7 @@ JOIN::exec() FOUND_ROWS() may be called. Never reset the examined row count here. It must be accumulated from all join iterations of all join parts. */ - if (tables) + if (table_count) thd->limit_found_rows= 0; /* @@ -2064,7 +2122,7 @@ JOIN::exec() } if (order && (order != group_list || !(select_options & SELECT_BIG_RESULT)) && - (const_tables == tables || + (const_tables == table_count || ((simple_order || skip_sort_order) && test_if_skip_sort_order(&join_tab[const_tables], order, select_limit, 0, @@ -2075,7 +2133,7 @@ JOIN::exec() select_describe(this, need_tmp, order != 0 && !skip_sort_order, select_distinct, - !tables ? "No tables used" : NullS); + !table_count ? "No tables used" : NullS); DBUG_VOID_RETURN; } @@ -2112,7 +2170,7 @@ JOIN::exec() thd_proc_info(thd, copy_to_tmp_table); DBUG_PRINT("info", ("%s", thd->proc_info)); if (!curr_join->sort_and_group && - curr_join->const_tables != curr_join->tables) + curr_join->const_tables != curr_join->table_count) { JOIN_TAB *first_tab= curr_join->join_tab + curr_join->const_tables; first_tab->sorted= test(first_tab->loosescan_match_tab); @@ -2287,7 +2345,7 @@ JOIN::exec() DBUG_VOID_RETURN; curr_join->group_list= 0; if (!curr_join->sort_and_group && - curr_join->const_tables != curr_join->tables) + curr_join->const_tables != curr_join->table_count) { JOIN_TAB *first_tab= curr_join->join_tab + curr_join->const_tables; first_tab->sorted= test(first_tab->loosescan_match_tab); @@ -2300,7 +2358,7 @@ JOIN::exec() DBUG_VOID_RETURN; } end_read_record(&curr_join->join_tab->read_record); - curr_join->const_tables= curr_join->tables; // Mark free for cleanup() + curr_join->const_tables= curr_join->table_count; // Mark free for cleanup() curr_join->join_tab[0].table= 0; // Table is freed // No sum funcs anymore @@ -2474,7 +2532,7 @@ JOIN::exec() WHERE clause for any tables after the sorted one. */ JOIN_TAB *curr_table= &curr_join->join_tab[curr_join->const_tables+1]; - JOIN_TAB *end_table= &curr_join->join_tab[curr_join->tables]; + JOIN_TAB *end_table= &curr_join->join_tab[curr_join->top_join_tab_count]; for (; curr_table < end_table ; curr_table++) { /* @@ -2512,7 +2570,7 @@ JOIN::exec() curr_join->group_list ? TRUE : FALSE)) DBUG_VOID_RETURN; sortorder= curr_join->sortorder; - if (curr_join->const_tables != curr_join->tables && + if (curr_join->const_tables != curr_join->table_count && !curr_join->join_tab[curr_join->const_tables].table->sort.io_cache) { /* @@ -2534,7 +2592,7 @@ JOIN::exec() curr_join->fields= curr_fields_list; curr_join->procedure= procedure; - if (is_top_level_join() && thd->cursor && tables != const_tables) + if (is_top_level_join() && thd->cursor && table_count != const_tables) { /* We are here if this is JOIN::exec for the last select of the main unit @@ -2601,9 +2659,11 @@ JOIN::destroy() { if (join_tab != tmp_join->join_tab) { - JOIN_TAB *tab, *end; - for (tab= join_tab, end= tab+tables ; tab != end ; tab++) + for (JOIN_TAB *tab= first_linear_tab(this, WITH_CONST_TABLES); tab; + tab= next_linear_tab(this, tab, WITH_BUSH_ROOTS)) + { tab->cleanup(); + } } tmp_join->tmp_join= 0; /* @@ -2884,8 +2944,10 @@ make_join_statistics(JOIN *join, TABLE_LIST *tables_arg, COND *conds, JOIN_TAB *stat_vector[MAX_TABLES+1]; DBUG_ENTER("make_join_statistics"); - table_count=join->tables; - stat=(JOIN_TAB*) join->thd->calloc(sizeof(JOIN_TAB)*table_count); + LINT_INIT(table); /* inited in all loops */ + table_count=join->table_count; + + stat=(JOIN_TAB*) join->thd->calloc(sizeof(JOIN_TAB)*(table_count)); stat_ref=(JOIN_TAB**) join->thd->alloc(sizeof(JOIN_TAB*)*MAX_TABLES); table_vector=(TABLE**) join->thd->alloc(sizeof(TABLE*)*(table_count*2)); if (!stat || !stat_ref || !table_vector) @@ -2922,10 +2984,8 @@ make_join_statistics(JOIN *join, TABLE_LIST *tables_arg, COND *conds, bzero((char*) table->const_key_parts, sizeof(key_part_map)*table->s->keys); all_table_map|= table->map; s->join=join; - s->info=0; // For describe s->dependent= tables->dep_tables; - s->key_dependent= 0; if (tables->schema_table) table->file->stats.records= 2; table->quick_condition_rows= table->file->stats.records; @@ -2935,9 +2995,11 @@ make_join_statistics(JOIN *join, TABLE_LIST *tables_arg, COND *conds, { /* s is the only inner table of an outer join */ #ifdef WITH_PARTITION_STORAGE_ENGINE - if ((!table->file->stats.records || table->no_partitions_used) && !embedding) + if (!table->is_filled_at_execution() && + (!table->file->stats.records || table->no_partitions_used) && !embedding) #else - if (!table->file->stats.records && !embedding) + if (!table->is_filled_at_execution() && + !table->file->stats.records && !embedding) #endif { // Empty table s->dependent= 0; // Ignore LEFT JOIN depend. @@ -2971,7 +3033,8 @@ make_join_statistics(JOIN *join, TABLE_LIST *tables_arg, COND *conds, #else const bool no_partitions_used= FALSE; #endif - if ((table->s->system || table->file->stats.records <= 1 || + if (!table->is_filled_at_execution() && + (table->s->system || table->file->stats.records <= 1 || no_partitions_used) && !s->dependent && (table->file->ha_table_flags() & HA_STATS_RECORDS_IS_EXACT) && @@ -3025,7 +3088,7 @@ make_join_statistics(JOIN *join, TABLE_LIST *tables_arg, COND *conds, { if (s->dependent & s->table->map) { - join->tables=0; // Don't use join->table + join->table_count=0; // Don't use join->table my_message(ER_WRONG_OUTER_JOIN, ER(ER_WRONG_OUTER_JOIN), MYF(0)); goto error; } @@ -3035,7 +3098,7 @@ make_join_statistics(JOIN *join, TABLE_LIST *tables_arg, COND *conds, if (conds || outer_join) { - if (update_ref_and_keys(join->thd, keyuse_array, stat, join->tables, + if (update_ref_and_keys(join->thd, keyuse_array, stat, join->table_count, conds, ~outer_join, join->select_lex, &sargables)) goto error; /* @@ -3097,6 +3160,9 @@ make_join_statistics(JOIN *join, TABLE_LIST *tables_arg, COND *conds, { table=s->table; + if (table->is_filled_at_execution()) + continue; + /* If equi-join condition by a key is null rejecting and after a substitution of a const table the key value happens to be null @@ -3254,15 +3320,34 @@ make_join_statistics(JOIN *join, TABLE_LIST *tables_arg, COND *conds, for (s=stat ; s < stat_end ; s++) { + s->startup_cost= 0; if (s->type == JT_SYSTEM || s->type == JT_CONST) { /* Only one matching row */ - s->found_records=s->records=s->read_time=1; s->worst_seeks=1.0; + s->found_records= s->records= 1; + s->read_time=1.0; + s->worst_seeks=1.0; continue; } /* Approximate found rows and time to read them */ - s->found_records=s->records=s->table->file->stats.records; - s->read_time=(ha_rows) s->table->file->scan_time(); + + if (s->table->is_filled_at_execution()) + { + get_delayed_table_estimates(s->table, &s->records, &s->read_time, + &s->startup_cost); + s->found_records= s->records; + table->quick_condition_rows=s->records; + } + else + { + s->found_records= s->records= s->table->file->stats.records; + s->read_time= s->table->file->scan_time(); + /* + table->quick_condition_rows has already been set to + table->file->stats.records + */ + } + /* Set a max range of how many seeks we can expect when using keys @@ -3285,10 +3370,11 @@ make_join_statistics(JOIN *join, TABLE_LIST *tables_arg, COND *conds, Don't do range analysis if we're on the inner side of an outer join (2). Do range analysis if we're on the inner side of a semi-join (3). */ - if (!s->const_keys.is_clear_all() && // (1) - (!s->table->pos_in_table_list->embedding || // (2) - (s->table->pos_in_table_list->embedding && // (3) - s->table->pos_in_table_list->embedding->sj_on_expr))) // (3) + if (!s->const_keys.is_clear_all() && // (1) + (!s->table->pos_in_table_list->embedding || // (2) + (s->table->pos_in_table_list->embedding && // (3) + s->table->pos_in_table_list->embedding->sj_on_expr)) && // (3) + !s->table->is_filled_at_execution()) { ha_rows records; SQL_SELECT *select; @@ -3326,7 +3412,7 @@ make_join_statistics(JOIN *join, TABLE_LIST *tables_arg, COND *conds, if (records != HA_POS_ERROR) { s->found_records=records; - s->read_time= (ha_rows) (s->quick ? s->quick->read_time : 0.0); + s->read_time= s->quick ? s->quick->read_time : 0.0; } delete select; } @@ -3337,18 +3423,18 @@ make_join_statistics(JOIN *join, TABLE_LIST *tables_arg, COND *conds, join->join_tab=stat; join->map2table=stat_ref; - join->table= join->all_tables=table_vector; + join->table= table_vector; join->const_tables=const_count; join->found_const_table_map=found_const_table_map; - if (join->const_tables != join->tables) + if (join->const_tables != join->table_count) optimize_keyuse(join, keyuse_array); if (optimize_semijoin_nests(join, all_table_map)) DBUG_RETURN(TRUE); /* purecov: inspected */ /* Find an optimal join order of the non-constant tables. */ - if (join->const_tables != join->tables) + if (join->const_tables != join->table_count) { if (choose_plan(join, all_table_map & ~join->const_table_map)) goto error; @@ -4595,7 +4681,7 @@ static void optimize_keyuse(JOIN *join, DYNAMIC_ARRAY *keyuse_array) for (tablenr=0 ; ! (map & 1) ; map>>=1, tablenr++) ; if (map == 1) // Only one table { - TABLE *tmp_table=join->all_tables[tablenr]; + TABLE *tmp_table=join->table[tablenr]; keyuse->ref_table_rows= max(tmp_table->file->stats.records, 100); } } @@ -5152,8 +5238,9 @@ best_access_path(JOIN *join, else tmp= best_time; // Do nothing } - loose_scan_opt.check_ref_access_part2(key, start_key, records, tmp); + tmp += s->startup_cost; + loose_scan_opt.check_ref_access_part2(key, start_key, records, tmp); } /* not ft_key */ if (tmp + 0.0001 < best_time - records/(double) TIME_FOR_COMPARE) { @@ -5230,13 +5317,19 @@ best_access_path(JOIN *join, Since we have a 'ref' access path, and FORCE INDEX instructs us to choose it over ALL/index, there is no need to consider a full table scan. + (5) Non-flattenable semi-joins: don't consider doing a scan of temporary + table if we had an option to make lookups into it. In real-world cases, + lookups are cheaper than full scans, but when the table is small, they + can be [considered to be] more expensive, which causes lookups not to + be used for cases with small datasets, which is annoying. */ if ((records >= s->found_records || best > s->read_time) && // (1) !(s->quick && best_key && s->quick->index == best_key->key && // (2) best_max_key_part >= s->table->quick_key_parts[best_key->key]) &&// (2) !((s->table->file->ha_table_flags() & HA_TABLE_SCAN_ON_INDEX) && // (3) ! s->table->covering_keys.is_clear_all() && best_key && !s->quick) &&// (3) - !(s->table->force_index && best_key && !s->quick)) // (4) + !(s->table->force_index && best_key && !s->quick) && // (4) + !(best_key && s->table->pos_in_table_list->jtbm_subselect)) // (5) { // Check full join ha_rows rnd_records= matching_candidates_in_table(s, found_constraint); @@ -5265,8 +5358,7 @@ best_access_path(JOIN *join, } else { - /* Estimate cost of reading table. */ - tmp= s->table->file->scan_time(); + tmp= s->read_time; if ((s->table->map & join->outer_join) || disable_jbuf) // Can't use join cache { /* @@ -5295,6 +5387,7 @@ best_access_path(JOIN *join, } } + tmp += s->startup_cost; /* We estimate the cost of evaluating WHERE clause for found records as record_count * rnd_records / TIME_FOR_COMPARE. This cost plus @@ -5318,7 +5411,7 @@ best_access_path(JOIN *join, join->outer_join))); } } - + /* Update the cost information for the current partial plan */ pos->records_read= records; pos->read_time= best; @@ -5396,7 +5489,7 @@ choose_plan(JOIN *join, table_map join_tables) jtab_sort_func= straight_join ? join_tab_cmp_straight : join_tab_cmp; } my_qsort2(join->best_ref + join->const_tables, - join->tables - join->const_tables, sizeof(JOIN_TAB*), + join->table_count - join->const_tables, sizeof(JOIN_TAB*), jtab_sort_func, (void*)join->emb_sjm_nest); join->cur_sj_inner_tables= 0; @@ -5572,7 +5665,7 @@ join_tab_cmp_embedded_first(const void *emb, const void* ptr1, const void* ptr2 static uint determine_search_depth(JOIN *join) { - uint table_count= join->tables - join->const_tables; + uint table_count= join->table_count - join->const_tables; uint search_depth; /* TODO: this value should be determined dynamically, based on statistics: */ uint max_tables_for_exhaustive_opt= 7; @@ -5850,10 +5943,11 @@ greedy_search(JOIN *join, @returns read_time_arg and record_count_arg contain the computed cost. */ - +#if 0 void JOIN::get_partial_join_cost(uint n_tables, double *read_time_arg, double *record_count_arg) { +#if 0 double record_count= 1; double read_time= 0.0; @@ -5869,6 +5963,117 @@ void JOIN::get_partial_join_cost(uint n_tables, } *read_time_arg= read_time;// + record_count / TIME_FOR_COMPARE; *record_count_arg= record_count; +#endif + DBUG_ASSERT(0); +} +#endif + +/* + Get partial join cost and fanout + + end_tab==NULL means get full join cost and fanout + + if end_tab belongs to a semi-join nest, get fanout within the scope of that + nest. +*/ + +void JOIN::get_partial_cost_and_fanout(uint end_tab_idx, + table_map filter_map, + double *read_time_arg, + double *record_count_arg) +{ + double record_count= 1; + double read_time= 0.0; + double sj_inner_fanout= 1.0; + JOIN_TAB *end_tab= NULL; + JOIN_TAB *tab; + uint i; + uint last_sj_table= MAX_TABLES; + + for (tab= first_depth_first_tab(this), i= const_tables; + tab; + tab= next_depth_first_tab(this, tab), i++) + { + end_tab= tab; + if (i == end_tab_idx) + break; + } + + for (tab= first_depth_first_tab(this), i= const_tables; + tab; + tab= next_depth_first_tab(this, tab), i++) + { + /* + We've entered the SJM nest that contains the end_tab. The caller is + actually + - interested in fanout inside the nest (because that's how many times + we'll invoke the attached WHERE conditions) + - not interested in cost + */ + if (end_tab->bush_root_tab && end_tab->bush_root_tab == tab) + { + /* Ok, end_tab is inside SJM nest and we're entering that nest now */ + record_count= 1.0; + read_time= 0.0; + } + + /* + Ignore fanout (but not cost) from sj-inner tables, as long as + the range that processes them finishes before the end_tab + */ + if (tab->sj_strategy != SJ_OPT_NONE) + { + sj_inner_fanout= 1.0; + last_sj_table= i + tab->n_sj_tables; + } + + if (tab->records_read && (tab->table->map & filter_map)) + { + record_count *= tab->records_read; + read_time += tab->read_time; + if (tab->emb_sj_nest) + sj_inner_fanout *= tab->records_read; + } + + if (i == last_sj_table) + { + record_count /= sj_inner_fanout; + sj_inner_fanout= 1.0; + last_sj_table= MAX_TABLES; + } + } + *read_time_arg= read_time;// + record_count / TIME_FOR_COMPARE; + *record_count_arg= record_count; +} + + +/* + Get prefix cost and fanout. This function is different from + get_partial_cost_and_fanout: + - it operates on a JOIN that haven't yet finished its optimization phase (in + particular, fix_semijoin_strategies_for_picked_join_order() and + get_best_combination() haven't been called) + - it assumes the the join prefix doesn't have any semi-join plans + + These assumptions are met by the caller of the function. +*/ + +void JOIN::get_prefix_cost_and_fanout(uint n_tables, + double *read_time_arg, + double *record_count_arg) +{ + double record_count= 1; + double read_time= 0.0; + for (uint i= const_tables; i < n_tables + const_tables ; i++) + { + if (best_positions[i].records_read) + { + record_count *= best_positions[i].records_read; + read_time += best_positions[i].read_time; + } + } + *read_time_arg= read_time;// + record_count / TIME_FOR_COMPARE; + *record_count_arg= record_count; } @@ -6280,8 +6485,8 @@ void JOIN_TAB::calc_used_field_length(bool max_fl) set_if_smaller(rec_length, table->file->stats.mean_rec_length); /* - psergey-todo: why we don't count here rowid that we might need to store - when using DuplicateElimination? + TODO: why we don't count here rowid that we might need to store when + using DuplicateElimination? */ used_fields=fields; used_fieldlength=rec_length; @@ -6315,7 +6520,15 @@ int JOIN_TAB::make_scan_filter() Item *cond= is_inner_table_of_outer_join() ? *get_first_inner_table()->on_expr_ref : join->conds; + + /* + psergey4timour: passing MAX_TABLES here is not correct. + The following make_cond_for_table call constructs a condition that will be + applied when reading a table with join buffering. This means that we're + nearly certain that this condition will be checked fewer than #{records in + join output} times. + */ if (cond && (tmp= make_cond_for_table(join->thd, cond, join->const_table_map | table->map, @@ -6466,6 +6679,210 @@ prev_record_reads(POSITION *positions, uint idx, table_map found_ref) /* + Enumerate join tabs in breadth-first fashion, including const tables. +*/ + +JOIN_TAB *first_breadth_first_tab(JOIN *join) +{ + return join->join_tab; /* There's always one (i.e. first) table */ +} + + +JOIN_TAB *next_breadth_first_tab(JOIN *join, JOIN_TAB *tab) +{ + 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) + return tab; + + /* No more top-level tabs. Switch to enumerating SJM nest children */ + tab= join->join_tab; + } + else + { + /* We're inside of an SJM nest */ + if (!tab->last_leaf_in_bush) + { + /* There's one more table in the nest, return it. */ + return ++tab; + } + else + { + /* + There are no more tables in this nest. Get out of it and then we'll + proceed to the next nest. + */ + tab= tab->bush_root_tab + 1; + } + } + + /* + 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++) + { + if (tab->bush_children) + return tab->bush_children->start; + } + return NULL; +} + + +JOIN_TAB *first_top_level_tab(JOIN *join, enum enum_with_const_tables with_const) +{ + JOIN_TAB *tab= join->join_tab; + if (with_const == WITH_CONST_TABLES) + { + if (join->const_tables == join->table_count) + return NULL; + tab += join->const_tables; + } + return tab; +} + + +JOIN_TAB *next_top_level_tab(JOIN *join, JOIN_TAB *tab) +{ + tab= next_breadth_first_tab(join, tab); + if (tab && tab->bush_root_tab) + tab= NULL; + return tab; +} + + +JOIN_TAB *first_linear_tab(JOIN *join, enum enum_with_const_tables const_tbls) +{ + JOIN_TAB *first= join->join_tab; + if (const_tbls == WITHOUT_CONST_TABLES) + first+= join->const_tables; + if (first < join->join_tab + join->top_join_tab_count) + return first; + return NULL; /* All tables were const tables */ +} + + +/* + A helper function to loop over all join's join_tab in sequential fashion + + DESCRIPTION + Depending on include_bush_roots parameter, JOIN_TABs that represent + SJM-scan/lookups are either returned or omitted. + + SJM-Bush children are returned right after (or in place of) their container + join tab (TODO: does anybody depend on this? A: make_join_readinfo() seems + to) + + For example, if we have this structure: + + ot1--ot2--sjm1----------------ot3-... + | + +--it1--it2--it3 + + calls to next_linear_tab( include_bush_roots=TRUE) will return: + + ot1 ot2 sjm1 it1 it2 it3 ot3 ... + + while calls to next_linear_tab( include_bush_roots=FALSE) will return: + + ot1 ot2 it1 it2 it3 ot3 ... + + (note that sjm1 won't be returned). +*/ + +JOIN_TAB *next_linear_tab(JOIN* join, JOIN_TAB* tab, + enum enum_with_bush_roots include_bush_roots) +{ + if (include_bush_roots == WITH_BUSH_ROOTS && tab->bush_children) + { + /* This JOIN_TAB is a SJM nest; Start from first table in nest */ + return tab->bush_children->start; + } + + DBUG_ASSERT(!tab->last_leaf_in_bush || tab->bush_root_tab); + + if (tab->bush_root_tab) /* Are we inside an SJM nest */ + { + /* Inside SJM nest */ + if (!tab->last_leaf_in_bush) + return tab+1; /* Return next in nest */ + /* Continue from the sjm on the top level */ + tab= tab->bush_root_tab; + } + + /* If no more JOIN_TAB's on the top level */ + if (++tab == join->join_tab + join->top_join_tab_count) + return NULL; + + if (include_bush_roots == WITHOUT_BUSH_ROOTS && tab->bush_children) + { + /* This JOIN_TAB is a SJM nest; Start from first table in nest */ + tab= tab->bush_children->start; + } + return tab; +} + + +/* + Start to iterate over all join tables in bush-children-first order, excluding + the const tables (see next_depth_first_tab() comment for details) +*/ + +JOIN_TAB *first_depth_first_tab(JOIN* join) +{ + JOIN_TAB* tab; + /* This means we're starting the enumeration */ + if (join->const_tables == join->top_join_tab_count) + return NULL; + + tab= join->join_tab + join->const_tables; + + return (tab->bush_children) ? tab->bush_children->start : tab; +} + + +/* + A helper function to iterate over all join tables in bush-children-first order + + DESCRIPTION + + For example, for this join plan + + ot1--ot2--sjm1------------ot3-... + | + | + it1--it2--it3 + + call to first_depth_first_tab() will return ot1, and subsequent calls to + next_depth_first_tab() will return: + + ot2 it1 it2 it3 sjm ot3 ... +*/ + +JOIN_TAB *next_depth_first_tab(JOIN* join, JOIN_TAB* tab) +{ + /* If we're inside SJM nest and have reached its end, get out */ + if (tab->last_leaf_in_bush) + return tab->bush_root_tab; + + /* Move to next tab in the array we're traversing */ + tab++; + + if (tab == join->join_tab +join->top_join_tab_count) + return NULL; /* Outside SJM nest and reached EOF */ + + if (tab->bush_children) + return tab->bush_children->start; + + return tab; +} + + +static Item * const null_ptr= NULL; + +/* Set up join struct according to the picked join order in SYNOPSIS @@ -6480,6 +6897,11 @@ prev_record_reads(POSITION *positions, uint idx, table_map found_ref) - create join->join_tab array and put there the JOIN_TABs in the join order - create data structures describing ref access methods. + NOTE + In this function we switch from pre-join-optimization JOIN_TABs to + post-join-optimization JOIN_TABs. This is achieved by copying the entire + JOIN_TAB objects. + RETURN FALSE OK TRUE Out of memory @@ -6488,7 +6910,7 @@ prev_record_reads(POSITION *positions, uint idx, table_map found_ref) static bool get_best_combination(JOIN *join) { - uint i,tablenr; + uint tablenr; table_map used_tables; JOIN_TAB *join_tab,*j; KEYUSE *keyuse; @@ -6496,7 +6918,7 @@ get_best_combination(JOIN *join) THD *thd=join->thd; DBUG_ENTER("get_best_combination"); - table_count=join->tables; + table_count=join->table_count; if (!(join->join_tab=join_tab= (JOIN_TAB*) thd->alloc(sizeof(JOIN_TAB)*table_count))) DBUG_RETURN(TRUE); @@ -6507,10 +6929,71 @@ get_best_combination(JOIN *join) fix_semijoin_strategies_for_picked_join_order(join); + JOIN_TAB_RANGE *root_range; + if (!(root_range= new JOIN_TAB_RANGE)) + DBUG_RETURN(TRUE); + root_range->start= join->join_tab; + /* root_range->end will be set later */ + join->join_tab_ranges.empty(); + + if (join->join_tab_ranges.push_back(root_range)) + DBUG_RETURN(TRUE); + + JOIN_TAB *sjm_nest_end= NULL; + JOIN_TAB *sjm_nest_root= NULL; + for (j=join_tab, tablenr=0 ; tablenr < table_count ; tablenr++,j++) { TABLE *form; + POSITION *cur_pos= &join->best_positions[tablenr]; + if (cur_pos->sj_strategy == SJ_OPT_MATERIALIZE || + cur_pos->sj_strategy == SJ_OPT_MATERIALIZE_SCAN) + { + /* + Ok, we've entered an SJ-Materialization semi-join (note that this can't + be done recursively, semi-joins are not allowed to be nested). + 1. Put into main join order a JOIN_TAB that represents a lookup or scan + in the temptable. + */ + bzero(j, sizeof(JOIN_TAB)); + j->join= join; + j->table= NULL; //temporary way to tell SJM tables from others. + j->ref.key = -1; + j->on_expr_ref= (Item**) &null_ptr; + j->keys= key_map(1); /* The unique index is always in 'possible keys' in EXPLAIN */ + + /* + 2. Proceed with processing SJM nest's join tabs, putting them into the + sub-order + */ + SJ_MATERIALIZATION_INFO *sjm= cur_pos->table->emb_sj_nest->sj_mat_info; + j->records= j->records_read= (ha_rows)(sjm->is_sj_scan? sjm->rows : 1); + JOIN_TAB *jt; + JOIN_TAB_RANGE *jt_range; + if (!(jt= (JOIN_TAB*)join->thd->alloc(sizeof(JOIN_TAB)*sjm->tables)) || + !(jt_range= new JOIN_TAB_RANGE)) + DBUG_RETURN(TRUE); + jt_range->start= jt; + jt_range->end= jt + sjm->tables; + join->join_tab_ranges.push_back(jt_range); + j->bush_children= jt_range; + sjm_nest_end= jt + sjm->tables; + sjm_nest_root= j; + + j= jt; + } + *j= *join->best_positions[tablenr].table; + +#if 0 +/* SJ-Materialization is represented with join tab ranges */ + if (j->sj_strategy == SJ_OPT_MATERIALIZE || + j->sj_strategy == SJ_OPT_MATERIALIZE) + j->sj_strategy= SJ_OPT_NONE; +#endif + + j->bush_root_tab= sjm_nest_root; + form=join->table[tablenr]=j->table; used_tables|= form->map; form->reginfo.join_tab=j; @@ -6518,14 +7001,14 @@ get_best_combination(JOIN *join) form->reginfo.not_exists_optimize=0; // Only with LEFT JOIN DBUG_PRINT("info",("type: %d", j->type)); if (j->type == JT_CONST) - continue; // Handled in make_join_stat.. + goto loop_end; // Handled in make_join_stat.. j->loosescan_match_tab= NULL; //non-nulls will be set later j->ref.key = -1; j->ref.key_parts=0; if (j->type == JT_SYSTEM) - continue; + goto loop_end; if ( !(keyuse= join->best_positions[tablenr].key) || (join->best_positions[tablenr].sj_strategy == SJ_OPT_LOOSE_SCAN)) { @@ -6536,10 +7019,27 @@ get_best_combination(JOIN *join) } else if (create_ref_for_key(join, j, keyuse, used_tables)) DBUG_RETURN(TRUE); // Something went wrong + loop_end: + /* + Save records_read in JOIN_TAB so that select_describe()/etc don't have + to access join->best_positions[]. + */ + j->records_read= (ha_rows)join->best_positions[tablenr].records_read; + join->map2table[j->table->tablenr]= j; + + /* If we've reached the end of sjm nest, switch back to main sequence */ + if (j + 1 == sjm_nest_end) + { + j->last_leaf_in_bush= TRUE; + j= sjm_nest_root; + sjm_nest_root= NULL; + sjm_nest_end= NULL; + } } + root_range->end= j; - for (i=0 ; i < table_count ; i++) - join->map2table[join->join_tab[i].table->tablenr]=join->join_tab+i; + join->top_join_tab_count= join->join_tab_ranges.head()->end - + join->join_tab_ranges.head()->start; update_depend_map(join); DBUG_RETURN(0); } @@ -6886,7 +7386,8 @@ JOIN::make_simple_join(JOIN *parent, TABLE *temp_table) join_tab= parent->join_tab_reexec; table= &parent->table_reexec[0]; parent->table_reexec[0]= temp_table; - tables= 1; + table_count= top_join_tab_count= 1; + const_tables= 0; const_table_map= 0; eliminated_tables= 0; @@ -6931,6 +7432,9 @@ JOIN::make_simple_join(JOIN *parent, TABLE *temp_table) join_tab->loosescan_match_tab= NULL; join_tab->emb_sj_nest= NULL; join_tab->pre_idx_push_select_cond= NULL; + join_tab->bush_root_tab= NULL; + join_tab->bush_children= NULL; + join_tab->last_leaf_in_bush= FALSE; bzero((char*) &join_tab->read_record,sizeof(join_tab->read_record)); temp_table->status=0; temp_table->null_row=0; @@ -7011,9 +7515,11 @@ inline void add_cond_and_fix(THD *thd, Item **e1, Item *e2) static void add_not_null_conds(JOIN *join) { DBUG_ENTER("add_not_null_conds"); - for (uint i=join->const_tables ; i < join->tables ; i++) + + for (JOIN_TAB *tab= first_linear_tab(join, WITHOUT_CONST_TABLES); + tab; + tab= next_linear_tab(join, tab, WITH_BUSH_ROOTS)) { - JOIN_TAB *tab=join->join_tab+i; if (tab->type == JT_REF || tab->type == JT_EQ_REF || tab->type == JT_REF_OR_NULL) { @@ -7142,14 +7648,21 @@ add_found_match_trig_cond(JOIN_TAB *tab, COND *cond, JOIN_TAB *root_tab) This function can be called only after the execution plan has been chosen. */ + static void make_outerjoin_info(JOIN *join) { DBUG_ENTER("make_outerjoin_info"); - for (uint i=join->const_tables ; i < join->tables ; i++) + for (JOIN_TAB *tab= first_linear_tab(join, WITHOUT_CONST_TABLES); tab; + tab= next_linear_tab(join, tab, WITHOUT_BUSH_ROOTS)) { - JOIN_TAB *tab=join->join_tab+i; TABLE *table=tab->table; + /* + psergey: The following is probably incorrect, fix it when we get + semi+outer joins processing to work: + */ + if (!table) + continue; TABLE_LIST *tbl= table->pos_in_table_list; TABLE_LIST *embedding= tbl->embedding; @@ -7220,9 +7733,9 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond) */ if (cond) /* Because of QUICK_GROUP_MIN_MAX_SELECT */ { /* there may be a select without a cond. */ - if (join->tables > 1) + if (join->table_count > 1) cond->update_used_tables(); // Tablenr may have changed - if (join->const_tables == join->tables && + if (join->const_tables == join->table_count && thd->lex->current_select->master_unit() == &thd->lex->unit) // not upper level SELECT join->const_table_map|=RAND_TABLE_BIT; @@ -7246,8 +7759,10 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond) DBUG_EXECUTE("where",print_where(join->exec_const_cond,"constants", QT_ORDINARY);); - for (JOIN_TAB *tab= join->join_tab+join->const_tables; - tab < join->join_tab+join->tables ; tab++) + uint linear_no= join->const_tables; + for (JOIN_TAB *tab= first_linear_tab(join, WITHOUT_CONST_TABLES); + tab; + tab= next_linear_tab(join, tab, WITHOUT_BUSH_ROOTS), linear_no++) { if (*tab->on_expr_ref) { @@ -7300,16 +7815,22 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond) OUTER_REF_TABLE_BIT | RAND_TABLE_BIT); JOIN_TAB *tab; table_map current_map; - for (uint i=join->const_tables ; i < join->tables ; i++) + uint i= join->const_tables; + for (tab= first_depth_first_tab(join); tab; + tab= next_depth_first_tab(join, tab), i++) { bool is_hj; - tab= join->join_tab+i; /* first_inner is the X in queries like: SELECT * FROM t1 LEFT OUTER JOIN (t2 JOIN t3) ON X */ - JOIN_TAB *first_inner_tab= tab->first_inner; - current_map= tab->table->map; + JOIN_TAB *first_inner_tab= tab->first_inner; + + if (tab->table) + current_map= tab->table->map; + else + current_map= tab->bush_children->start->emb_sj_nest->sj_inner_tables; + bool use_quick_range=0; COND *tmp; @@ -7332,7 +7853,7 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond) Following force including random expression in last table condition. It solve problem with select like SELECT * FROM t1 WHERE rand() > 0.5 */ - if (i == join->tables-1) + if (tab == join->join_tab + join->top_join_tab_count - 1) current_map|= OUTER_REF_TABLE_BIT | RAND_TABLE_BIT; used_tables|=current_map; @@ -7352,17 +7873,30 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond) We will use join cache here : prevent sorting of the first table only and sort at the end. */ - if (i != join->const_tables && join->tables > join->const_tables + 1) + if (i != join->const_tables && join->table_count > join->const_tables + 1) join->full_join= 1; } tmp= NULL; + if (cond) - tmp= make_cond_for_table(thd, cond, used_tables, current_map, i, - FALSE, FALSE); - /* Add conditions added by add_not_null_conds(). */ - if (tab->select_cond) - add_cond_and_fix(thd, &tmp, tab->select_cond); + { + if (tab->bush_children) + { + // Reached the materialization tab + tmp= make_cond_after_sjm(cond, cond, save_used_tables, used_tables); + used_tables= save_used_tables | used_tables; + save_used_tables= 0; + } + else + { + tmp= make_cond_for_table(thd, cond, used_tables, current_map, i, + FALSE, FALSE); + } + /* Add conditions added by add_not_null_conds(). */ + if (tab->select_cond) + add_cond_and_fix(thd, &tmp, tab->select_cond); + } is_hj= (tab->type == JT_REF || tab->type == JT_EQ_REF) && (join->allowed_join_cache_types & JOIN_CACHE_HASHED_BIT) && @@ -7401,9 +7935,9 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond) if (tmp || !cond || tab->type == JT_REF || tab->type == JT_REF_OR_NULL || tab->type == JT_EQ_REF || first_inner_tab) { - DBUG_EXECUTE("where", - print_where(tmp,tab->table->alias.c_ptr(), - QT_ORDINARY);); + DBUG_EXECUTE("where",print_where(tmp, + tab->table? tab->table->alias.c_ptr() :"sjm-nest", + QT_ORDINARY);); SQL_SELECT *sel= tab->select= ((SQL_SELECT*) thd->memdup((uchar*) select, sizeof(*select))); @@ -7427,16 +7961,20 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond) tab->set_select_cond(tmp, __LINE__); /* Push condition to storage engine if this is enabled and the condition is not guarded */ - if (thd->variables.engine_condition_pushdown && !first_inner_tab) + if (tab->table) { - COND *push_cond= + tab->table->file->pushed_cond= NULL; + if (thd->variables.engine_condition_pushdown && !first_inner_tab) + { + COND *push_cond= make_cond_for_table(thd, tmp, current_map, current_map, MAX_TABLES, FALSE, FALSE); - if (push_cond) - { - /* Push condition to handler */ - if (!tab->table->file->cond_push(push_cond)) - tab->table->file->pushed_cond= push_cond; + if (push_cond) + { + /* Push condition to handler */ + if (!tab->table->file->cond_push(push_cond)) + tab->table->file->pushed_cond= push_cond; + } } } } @@ -7448,7 +7986,9 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond) sel->head=tab->table; DBUG_EXECUTE("where", - print_where(tmp,tab->table->alias.c_ptr(), + print_where(tmp, + tab->table ? tab->table->alias.c_ptr() : + "(sjm-nest)", QT_ORDINARY);); if (tab->quick) { @@ -7469,7 +8009,7 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond) } tab->quick=0; } - uint ref_key=(uint) sel->head->reginfo.join_tab->ref.key+1; + uint ref_key= sel->head? (uint) sel->head->reginfo.join_tab->ref.key+1 : 0; if (i == join->const_tables && ref_key) { if (!tab->const_keys.is_clear_all() && @@ -7488,12 +8028,12 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond) the index if we are using limit and this is the first table */ - if ((cond && - (!tab->keys.is_subset(tab->const_keys) && i > 0)) || - (!tab->const_keys.is_clear_all() && i == join->const_tables && - join->unit->select_limit_cnt < - join->best_positions[i].records_read && - !(join->select_options & OPTION_FOUND_ROWS))) + if (!tab->table->is_filled_at_execution() && + ((cond && (!tab->keys.is_subset(tab->const_keys) && i > 0)) || + (!tab->const_keys.is_clear_all() && i == join->const_tables && + join->unit->select_limit_cnt < + join->best_positions[i].records_read && + !(join->select_options & OPTION_FOUND_ROWS)))) { /* Join with outer join condition */ COND *orig_cond=sel->cond; @@ -7567,7 +8107,7 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond) } /* - Push down conditions from all on expressions. + Push down conditions from all ON expressions. Each of these conditions are guarded by a variable that turns if off just before null complemented row for outer joins is formed. Thus, the condition from an @@ -7576,8 +8116,9 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond) */ /* First push down constant conditions from on expressions */ - for (JOIN_TAB *join_tab= join->join_tab+join->const_tables; - join_tab < join->join_tab+join->tables ; join_tab++) + for (JOIN_TAB *join_tab= first_linear_tab(join, WITHOUT_CONST_TABLES); + join_tab; + join_tab= next_linear_tab(join, join_tab, WITH_BUSH_ROOTS)) { if (*join_tab->on_expr_ref) { @@ -7602,11 +8143,11 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond) } } - /* Push down non-constant conditions from on expressions */ - JOIN_TAB *first_tab= join->join_tab+join->const_tables; + /* Push down non-constant conditions from ON expressions */ + //JOIN_TAB *first_tab= join->join_tab+join->const_tables; JOIN_TAB *last_tab= tab; while (first_inner_tab && first_inner_tab->last_inner == last_tab) - { + { /* Table tab is the last inner table of an outer join. An on expression is always attached to it. @@ -7615,12 +8156,26 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond) table_map used_tables2= (join->const_table_map | OUTER_REF_TABLE_BIT | RAND_TABLE_BIT); - for (tab= first_tab; tab <= last_tab ; tab++) + for (JOIN_TAB *tab= first_linear_tab(join, WITHOUT_CONST_TABLES); + tab; + tab= (tab == last_tab)? NULL: next_linear_tab(join, tab, + WITH_BUSH_ROOTS)) { + if (!tab->table) + { + /* + psergey-todo: this is probably incorrect, fix this when we get + correct processing for outer joins + semi joins + */ + continue; + } current_map= tab->table->map; used_tables2|= current_map; + /* + psergey: have put the MAX_TABLES below. It's bad, will need to fix it. + */ COND *tmp_cond= make_cond_for_table(thd, on_expr, used_tables2, - current_map, (tab - first_tab), + current_map, /*(tab - first_tab)*/ MAX_TABLES, FALSE, FALSE); if (tab == first_inner_tab && tab->on_precond) add_cond_and_fix(thd, &tmp_cond, tab->on_precond); @@ -7663,37 +8218,10 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond) (*sel_cond_ref)->update_used_tables(); if (cond_tab->select) cond_tab->select->cond= cond_tab->select_cond; - } + } } first_inner_tab= first_inner_tab->first_upper; } - - if (save_used_tables && !(used_tables & - ~(tab->emb_sj_nest->sj_inner_tables | - join->const_table_map | PSEUDO_TABLE_BITS))) - { - /* - We have reached the end of semi join nest. That is, the join order - looks like this: - - outer_tbl1 SJ-Materialize(inner_tbl1 ... inner_tblN) outer_tbl ... - ^ - \-we're here - At this point, we need to produce two conditions - - A condition that can be checked when we have all of the sj-inner - tables (inner_tbl1 ... inner_tblN). This will be used while doing - materialization. - - A condition that can be checked when we have all of the tables - in the prefix (both inner and outer). - */ - tab->emb_sj_nest->sj_mat_info->join_cond= - cond ? - make_cond_after_sjm(cond, cond, save_used_tables, used_tables): - NULL; - used_tables= save_used_tables | used_tables; - save_used_tables= 0; - } - } } DBUG_RETURN(0); @@ -7724,11 +8252,17 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond) static uint make_join_orderinfo(JOIN *join) { + /* + This function needs to be fixed to take into account that we now have SJM + nests. + */ + DBUG_ASSERT(0); + JOIN_TAB *tab; if (join->need_tmp) - return join->tables; + return join->table_count; tab= join->get_sort_by_join_tab(); - return tab ? tab-join->join_tab : join->tables; + return tab ? tab-join->join_tab : join->table_count; } /* @@ -7902,7 +8436,7 @@ void revise_cache_usage(JOIN_TAB *join_tab) SYNOPSIS end_sj_materialize() join The join - join_tab Last join table + join_tab Points to right after the last join_tab in materialization bush end_of_records FALSE <=> This call is made to pass another record combination TRUE <=> EOF (no action) @@ -7920,7 +8454,7 @@ void revise_cache_usage(JOIN_TAB *join_tab) NESTED_LOOP_ERROR */ -static enum_nested_loop_state +enum_nested_loop_state end_sj_materialize(JOIN *join, JOIN_TAB *join_tab, bool end_of_records) { int error; @@ -8095,6 +8629,7 @@ static uint check_join_cache_usage(JOIN_TAB *tab, ulonglong options, uint no_jbuf_after, + uint table_index, JOIN_TAB *prev_tab) { COST_VECT cost; @@ -8110,11 +8645,14 @@ uint check_join_cache_usage(JOIN_TAB *tab, !(join->allowed_join_cache_types & JOIN_CACHE_HASHED_BIT); bool no_bka_cache= !(join->allowed_join_cache_types & JOIN_CACHE_BKA_BIT); - uint i= tab - join->join_tab; join->return_tab= 0; - if (cache_level == 0 || i == join->const_tables || !prev_tab) + /* + Don't use join cache if @@join_cache_level==0 or this table is the first + one join suborder (either at top level or inside a bush) + */ + if (cache_level == 0 || !prev_tab) return 0; if (force_unlinked_cache && (cache_level%2 == 0)) @@ -8122,10 +8660,7 @@ uint check_join_cache_usage(JOIN_TAB *tab, if (options & SELECT_NO_JOIN_CACHE) goto no_join_cache; - /* - psergey-todo: why the below when execution code seems to handle the - "range checked for each record" case? - */ + if (tab->use_quick == 2) goto no_join_cache; @@ -8148,11 +8683,16 @@ uint check_join_cache_usage(JOIN_TAB *tab, } /* - Don't use join buffering if we're dictated not to by no_jbuf_after (this - ...) + Don't use join buffering if we're dictated not to by no_jbuf_after + (This is not meaningfully used currently) */ - if (!(i <= no_jbuf_after) || tab->loosescan_match_tab || - sj_is_materialize_strategy(join->best_positions[i].sj_strategy)) + if (table_index > no_jbuf_after) + goto no_join_cache; + + /* + TODO: BNL join buffer should be perfectly ok with tab->bush_children. + */ + if (tab->loosescan_match_tab || tab->bush_children) goto no_join_cache; for (JOIN_TAB *first_inner= tab->first_inner; first_inner; @@ -8282,6 +8822,9 @@ no_join_cache: join join whose tables are to be checked options options of the join no_jbuf_after don't use join buffering after table with this number + (The tables are assumed to be numbered in + first_linear_tab(join, WITHOUT_CONST_TABLES), + next_linear_tab(join, WITH_CONST_TABLES) order). DESCRIPTION For each table after the first non-constant table the function checks @@ -8305,29 +8848,36 @@ no_join_cache: void check_join_cache_usage_for_tables(JOIN *join, ulonglong options, uint no_jbuf_after) { - JOIN_TAB *first_sjm_table= NULL; - JOIN_TAB *last_sjm_table= NULL; + JOIN_TAB *tab; + JOIN_TAB *prev_tab; - for (uint i= join->const_tables; i < join->tables; i++) - join->join_tab[i].used_join_cache_level= join->max_allowed_join_cache_level; - - for (uint i= join->const_tables; i < join->tables; i++) + for (tab= first_linear_tab(join, WITHOUT_CONST_TABLES); + tab; + tab= next_linear_tab(join, tab, WITH_BUSH_ROOTS)) { - JOIN_TAB *tab= join->join_tab+i; - - if (sj_is_materialize_strategy(join->best_positions[i].sj_strategy)) - { - first_sjm_table= tab; - last_sjm_table= tab + join->best_positions[i].n_sj_tables; - for (JOIN_TAB *sjm_tab= first_sjm_table; - sjm_tab != last_sjm_table; sjm_tab++) - sjm_tab->first_sjm_sibling= first_sjm_table; - } - if (!(tab >= first_sjm_table && tab < last_sjm_table)) - tab->first_sjm_sibling= NULL; - + tab->used_join_cache_level= join->max_allowed_join_cache_level; + } + + uint idx= join->const_tables; + for (tab= first_linear_tab(join, WITHOUT_CONST_TABLES); + tab; + tab= next_linear_tab(join, tab, WITH_BUSH_ROOTS)) + { +restart: tab->icp_other_tables_ok= TRUE; tab->idx_cond_fact_out= TRUE; + + /* + Check if we have a preceding join_tab, as something that will feed us + records that we could buffer. We don't have it, if + - this is the first non-const table in the join order, + - this is the first table inside an SJM nest. + */ + prev_tab= tab - 1; + if (tab == join->join_tab + join->const_tables || + (tab->bush_root_tab && tab->bush_root_tab->bush_children->start == tab)) + prev_tab= NULL; + switch (tab->type) { case JT_SYSTEM: case JT_CONST: @@ -8337,16 +8887,25 @@ void check_join_cache_usage_for_tables(JOIN *join, ulonglong options, case JT_ALL: tab->used_join_cache_level= check_join_cache_usage(tab, options, no_jbuf_after, - tab == last_sjm_table ? - first_sjm_table : - tab-1); + idx, + prev_tab); tab->use_join_cache= test(tab->used_join_cache_level); + /* + psergey-merge: todo: raise the question that this is really stupid that + we can first allocate a join buffer, then decide not to use it and free + it. + */ if (join->return_tab) - i= join->return_tab-join->join_tab-1; // always >= 0 + { + tab= join->return_tab; + goto restart; + } break; default: tab->used_join_cache_level= 0; - } + } + if (!tab->bush_children) + idx++; } } @@ -8376,6 +8935,7 @@ void check_join_cache_usage_for_tables(JOIN *join, ulonglong options, static bool make_join_readinfo(JOIN *join, ulonglong options, uint no_jbuf_after) { + JOIN_TAB *tab; uint i; DBUG_ENTER("make_join_readinfo"); @@ -8385,11 +8945,15 @@ make_join_readinfo(JOIN *join, ulonglong options, uint no_jbuf_after) if (!join->select_lex->sj_nests.is_empty() && setup_semijoin_dups_elimination(join, options, no_jbuf_after)) DBUG_RETURN(TRUE); /* purecov: inspected */ + + /* For const tables, set partial_join_cardinality to 1. */ + for (tab= join->join_tab; tab != join->join_tab + join->const_tables; tab++) + tab->partial_join_cardinality= 1; - for (i= 0; i < join->const_tables; i++) - join->join_tab[i].partial_join_cardinality= 1; - - for (i=join->const_tables ; i < join->tables ; i++) + JOIN_TAB *prev_tab= NULL; + for (tab= first_linear_tab(join, WITHOUT_CONST_TABLES), i= join->const_tables; + tab; + prev_tab=tab, tab= next_linear_tab(join, tab, WITH_BUSH_ROOTS)) { /* The approximation below for partial join cardinality is not good because @@ -8398,24 +8962,50 @@ make_join_readinfo(JOIN *join, ulonglong options, uint no_jbuf_after) semi-joins. Later it should be improved. */ - JOIN_TAB *tab=join->join_tab+i; + + if (tab->bush_root_tab && tab->bush_root_tab->bush_children->start == tab) + prev_tab= NULL; + DBUG_ASSERT(tab->bush_children || tab->table == join->best_positions[i].table->table); + tab->partial_join_cardinality= join->best_positions[i].records_read * - (i ? (tab-1)->partial_join_cardinality : 1); + (prev_tab? prev_tab->partial_join_cardinality : 1); + if (!tab->bush_children) + i++; } check_join_cache_usage_for_tables(join, options, no_jbuf_after); - - for (i=join->const_tables ; i < join->tables ; i++) + + JOIN_TAB *first_tab; + for (tab= first_tab= first_linear_tab(join, WITHOUT_CONST_TABLES); + tab; + tab= next_linear_tab(join, tab, WITH_BUSH_ROOTS)) { - JOIN_TAB *tab=join->join_tab+i; + if (tab->bush_children) + { + if (setup_sj_materialization(tab)) + return TRUE; + } + TABLE *table=tab->table; uint jcl= tab->used_join_cache_level; tab->read_record.table= table; tab->read_record.file=table->file; tab->read_record.unlock_row= rr_unlock_row; - tab->next_select=sub_select; /* normal select */ tab->sorted= sorted; sorted= 0; // only first must be sorted + + + /* + We should not set tab->next_select for the last table in the + SMJ-nest, as setup_sj_materialization() has already set it to + end_sj_materialize. + */ + if (!(tab->bush_root_tab && + tab->bush_root_tab->bush_children->end == tab + 1)) + { + tab->next_select=sub_select; /* normal select */ + } + if (tab->loosescan_match_tab) { @@ -8424,20 +9014,6 @@ make_join_readinfo(JOIN *join, ulonglong options, uint no_jbuf_after) return TRUE; /* purecov: inspected */ tab->sorted= TRUE; } - - /* - SJ-Materialization - */ - if (sj_is_materialize_strategy(join->best_positions[i].sj_strategy)) - { - if (i == join->const_tables) - join->first_select= sub_select_sjm; - else - tab[-1].next_select= sub_select_sjm; - - if (setup_sj_materialization(tab)) - return TRUE; - } table->status=STATUS_NO_RECORD; pick_table_access_method (tab); @@ -8507,8 +9083,9 @@ make_join_readinfo(JOIN *join, ulonglong options, uint no_jbuf_after) } else { - tab->read_first_record= join_init_read_record; - if (i == join->const_tables) + if (!tab->bush_children) + tab->read_first_record= join_init_read_record; + if (tab == first_tab) { if (tab->select && tab->select->quick) { @@ -8589,15 +9166,18 @@ make_join_readinfo(JOIN *join, ulonglong options, uint no_jbuf_after) /* purecov: end */ } } - join->join_tab[join->tables-1].next_select=0; /* Set by do_select */ + uint n_top_tables= join->join_tab_ranges.head()->end - + join->join_tab_ranges.head()->start; + + join->join_tab[n_top_tables - 1].next_select=0; /* Set by do_select */ -/* + /* If a join buffer is used to join a table the ordering by an index for the first non-constant table cannot be employed anymore. */ - for (i=join->const_tables ; i < join->tables ; i++) + for (tab= join->join_tab + join->const_tables ; + tab != join->join_tab + n_top_tables ; tab++) { - JOIN_TAB *tab=join->join_tab+i; if (tab->use_join_cache) { JOIN_TAB *sort_by_tab= join->group && join->simple_group && @@ -8643,9 +9223,8 @@ make_join_readinfo(JOIN *join, ulonglong options, uint no_jbuf_after) bool error_if_full_join(JOIN *join) { - for (JOIN_TAB *tab=join->join_tab, *end=join->join_tab+join->tables; - tab < end; - tab++) + for (JOIN_TAB *tab=first_top_level_tab(join, WITH_CONST_TABLES); tab; + tab= next_top_level_tab(join, tab)) { if (tab->type == JT_ALL && (!tab->select || !tab->select->quick)) { @@ -8662,6 +9241,9 @@ bool error_if_full_join(JOIN *join) /** cleanup JOIN_TAB. + + DESCRIPTION + This is invoked when we've finished all join executions. */ void JOIN_TAB::cleanup() @@ -8684,6 +9266,15 @@ void JOIN_TAB::cleanup() { table->disable_keyread(); table->file->ha_index_or_rnd_end(); + + if (table->pos_in_table_list && + table->pos_in_table_list->jtbm_subselect) + { + end_read_record(&read_record); + //psergey-merge: + table->pos_in_table_list->jtbm_subselect->cleanup(); + DBUG_VOID_RETURN; + } /* We need to reset this for next select (Tested in part_of_refkey) @@ -8881,12 +9472,12 @@ void JOIN::cleanup(bool full) if (table) { - JOIN_TAB *tab,*end; + JOIN_TAB *tab; /* Only a sorted table may be cached. This sorted table is always the first non const table in join->table */ - if (tables > const_tables) // Test for not-const tables + if (table_count > const_tables) // Test for not-const tables { free_io_cache(table[const_tables]); filesort_free_buffers(table[const_tables],full); @@ -8894,13 +9485,17 @@ void JOIN::cleanup(bool full) if (full) { - for (tab= join_tab, end= tab+tables; tab != end; tab++) + for (tab= first_linear_tab(this, WITH_CONST_TABLES); tab; + tab= next_linear_tab(this, tab, WITH_BUSH_ROOTS)) + { tab->cleanup(); + } table= 0; } else { - for (tab= join_tab, end= tab+tables; tab != end; tab++) + for (tab= first_linear_tab(this, WITH_CONST_TABLES); tab; + tab= next_linear_tab(this, tab, WITH_BUSH_ROOTS)) { if (tab->table) { @@ -8969,6 +9564,8 @@ void JOIN::cleanup(bool full) SELECT * FROM t1,t2 WHERE t1.a=t2.a AND t1.b=t2.b ORDER BY t1.a,t2.c SELECT * FROM t1,t2 WHERE t1.a=t2.a ORDER BY t2.b,t1.a @endcode + + TODO: this function checks ORDER::used, which can only have a value of 0. */ static bool @@ -9042,9 +9639,8 @@ only_eq_ref_tables(JOIN *join,ORDER *order,table_map tables) static void update_depend_map(JOIN *join) { - JOIN_TAB *join_tab=join->join_tab, *end=join_tab+join->tables; - - for (; join_tab != end ; join_tab++) + for (JOIN_TAB *join_tab= first_linear_tab(join, WITH_CONST_TABLES); join_tab; + join_tab= next_linear_tab(join, join_tab, WITH_BUSH_ROOTS)) { TABLE_REF *ref= &join_tab->ref; table_map depend_map=0; @@ -9055,11 +9651,11 @@ static void update_depend_map(JOIN *join) ref->depend_map=depend_map & ~OUTER_REF_TABLE_BIT; depend_map&= ~OUTER_REF_TABLE_BIT; for (JOIN_TAB **tab=join->map2table; - depend_map ; - tab++,depend_map>>=1 ) + depend_map ; + tab++,depend_map>>=1 ) { if (depend_map & 1) - ref->depend_map|=(*tab)->ref.depend_map; + ref->depend_map|=(*tab)->ref.depend_map; } } } @@ -9067,7 +9663,7 @@ static void update_depend_map(JOIN *join) /** Update the dependency map for the sort order. */ -static void update_depend_map(JOIN *join, ORDER *order) +static void update_depend_map_for_order(JOIN *join, ORDER *order) { for (; order ; order=order->next) { @@ -9114,21 +9710,30 @@ static ORDER * remove_const(JOIN *join,ORDER *first_order, COND *cond, bool change_list, bool *simple_order) { - if (join->tables == join->const_tables) + if (join->table_count == join->const_tables) return change_list ? 0 : first_order; // No need to sort ORDER *order,**prev_ptr; - table_map first_table= join->join_tab[join->const_tables].table->map; + table_map first_table; table_map not_const_tables= ~join->const_table_map; table_map ref; + bool first_is_base_table= FALSE; DBUG_ENTER("remove_const"); + + LINT_INIT(first_table); /* protected by first_is_base_table */ + if (join->join_tab[join->const_tables].table) + { + first_table= join->join_tab[join->const_tables].table->map; + first_is_base_table= TRUE; + } + prev_ptr= &first_order; *simple_order= *join->join_tab[join->const_tables].on_expr_ref ? 0 : 1; /* NOTE: A variable of not_const_tables ^ first_table; breaks gcc 2.7 */ - update_depend_map(join, first_order); + update_depend_map_for_order(join, first_order); for (order=first_order; order ; order=order->next) { table_map order_tables=order->item[0]->used_tables(); @@ -9143,7 +9748,7 @@ remove_const(JOIN *join,ORDER *first_order, COND *cond, table for all queries containing more than one table, ROLLUP, and an outer join. */ - (join->tables > 1 && join->rollup.state == ROLLUP::STATE_INITED && + (join->table_count > 1 && join->rollup.state == ROLLUP::STATE_INITED && join->outer_join)) *simple_order=0; // Must do a temp table to sort else if (!(order_tables & not_const_tables)) @@ -9171,7 +9776,7 @@ remove_const(JOIN *join,ORDER *first_order, COND *cond, DBUG_PRINT("info",("removing: %s", order->item[0]->full_name())); continue; } - if ((ref=order_tables & (not_const_tables ^ first_table))) + if (first_is_base_table && (ref=order_tables & (not_const_tables ^ first_table))) { if (!(order_tables & first_table) && only_eq_ref_tables(join,first_order, ref)) @@ -9247,8 +9852,11 @@ static void clear_tables(JOIN *join) must clear only the non-const tables, as const tables are not re-calculated. */ - for (uint i=join->const_tables ; i < join->tables ; i++) - mark_as_null_row(join->table[i]); // All fields are NULL + for (uint i= 0 ; i < join->table_count ; i++) + { + if (!(join->table[i]->map & join->const_table_map)) + mark_as_null_row(join->table[i]); // All fields are NULL + } } /***************************************************************************** @@ -10086,7 +10694,25 @@ static int compare_fields_by_table_order(Item *field1, if (outer_ref) return cmp; JOIN_TAB **idx= (JOIN_TAB **) table_join_idx; - cmp= idx[f2->field->table->tablenr]-idx[f1->field->table->tablenr]; + + JOIN_TAB *tab1= idx[f1->field->table->tablenr]; + JOIN_TAB *tab2= idx[f2->field->table->tablenr]; + + /* + if one of the table is inside a merged SJM nest and another one isn't, + compare SJM bush roots of the tables. + */ + if (tab1->bush_root_tab != tab2->bush_root_tab) + { + if (tab1->bush_root_tab) + tab1= tab1->bush_root_tab; + + if (tab2->bush_root_tab) + tab2= tab2->bush_root_tab; + } + + cmp= tab2 - tab1; + if (!cmp) { JOIN_TAB *tab= idx[f1->field->table->tablenr]; @@ -10211,7 +10837,9 @@ Item *eliminate_item_equal(COND *cond, COND_EQUAL *upper_levels, /* Pick the "head" item: the constant one or the first in the join order - that's not inside some SJM nest. + (if the first in the join order happends to be inside an SJM nest, that's + ok, because this is where the value will be unpacked after + materialization). */ if (item_const) head= item_const; @@ -11323,6 +11951,20 @@ void optimize_wo_join_buffering(JOIN *join, uint first_tab, uint last_tab, *outer_rec_count= rec_count; for (i= first_tab; i <= last_tab; i++) reopt_remaining_tables |= join->positions[i].table->table->map; + + /* + best_access_path() optimization depends on the value of + join->cur_sj_inner_tables. Our goal in this function is to do a + re-optimization with disabled join buffering, but no other changes. + In order to achieve this, cur_sj_inner_tables needs have the same + value it had during the original invocations of best_access_path. + + We know that this function, optimize_wo_join_buffering() is called to + re-optimize semi-join join order range, which allows to conclude that + the "original" value of cur_sj_inner_tables was 0. + */ + table_map save_cur_sj_inner_tables= join->cur_sj_inner_tables; + join->cur_sj_inner_tables= 0; for (i= first_tab; i <= last_tab; i++) { @@ -11349,6 +11991,8 @@ void optimize_wo_join_buffering(JOIN *join, uint first_tab, uint last_tab, if (!rs->emb_sj_nest) *outer_rec_count *= pos.records_read; } + join->cur_sj_inner_tables= save_cur_sj_inner_tables; + *reopt_cost= cost; } @@ -12355,7 +12999,7 @@ create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List<Item> &fields, for distinct, as we want the distinct index to be usable in this case too. */ - item->marker == 4 || param->bit_fields_as_long, // psergey-feb17 + item->marker == 4 || param->bit_fields_as_long, force_copy_fields, param->convert_blob_length); @@ -12649,6 +13293,8 @@ create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List<Item> &fields, share->keys=1; share->uniques= test(using_unique_constraint); table->key_info= table->s->key_info= keyinfo; + table->keys_in_use_for_query.set_bit(0); + share->keys_in_use.set_bit(0); keyinfo->key_part=key_part_info; keyinfo->flags=HA_NOSAME; keyinfo->usable_key_parts=keyinfo->key_parts= param->group_parts; @@ -12664,6 +13310,8 @@ create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List<Item> &fields, bool maybe_null=(*cur_group->item)->maybe_null; key_part_info->null_bit=0; key_part_info->field= field; + if (cur_group == group) + field->key_start.set_bit(0); key_part_info->offset= field->offset(table->record[0]); key_part_info->length= (uint16) field->key_length(); key_part_info->type= (uint8) field->key_type(); @@ -12733,6 +13381,8 @@ create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List<Item> &fields, keyinfo->key_parts * sizeof(KEY_PART_INFO)))) goto err; bzero((void*) key_part_info, keyinfo->key_parts * sizeof(KEY_PART_INFO)); + table->keys_in_use_for_query.set_bit(0); + share->keys_in_use.set_bit(0); table->key_info= table->s->key_info= keyinfo; keyinfo->key_part=key_part_info; keyinfo->flags=HA_NOSAME | HA_NULL_ARE_EQUAL; @@ -12769,8 +13419,14 @@ create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List<Item> &fields, i < field_count; i++, reg_field++, key_part_info++) { - key_part_info->null_bit=0; key_part_info->field= *reg_field; + (*reg_field)->flags |= PART_KEY_FLAG; + if (key_part_info == keyinfo->key_part) + (*reg_field)->key_start.set_bit(0); + key_part_info->null_bit= (*reg_field)->null_bit; + key_part_info->null_offset= (uint) ((*reg_field)->null_ptr - + (uchar*) table->record[0]); + key_part_info->offset= (*reg_field)->offset(table->record[0]); key_part_info->length= (uint16) (*reg_field)->pack_length(); /* TODO: @@ -13587,14 +14243,13 @@ do_select(JOIN *join,List<Item> *fields,TABLE *table,Procedure *procedure) } /* Set up select_end */ Next_select_func end_select= setup_end_select_func(join); - if (join->tables) + if (join->table_count) { - join->join_tab[join->tables-1].next_select= end_select; - + join->join_tab[join->top_join_tab_count - 1].next_select= end_select; join_tab=join->join_tab+join->const_tables; } join->send_records=0; - if (join->tables == join->const_tables) + if (join->table_count == join->const_tables) { /* HAVING will be checked after processing aggregate functions, @@ -13634,13 +14289,13 @@ do_select(JOIN *join,List<Item> *fields,TABLE *table,Procedure *procedure) } else { - DBUG_ASSERT(join->tables); + DBUG_ASSERT(join->table_count); if (join->outer_ref_cond && !join->outer_ref_cond->val_int()) error= NESTED_LOOP_NO_MORE_ROWS; else - error= join->first_select(join,join_tab,0); + error= sub_select(join,join_tab,0); if (error == NESTED_LOOP_OK || error == NESTED_LOOP_NO_MORE_ROWS) - error= join->first_select(join,join_tab,1); + error= sub_select(join,join_tab,1); if (error == NESTED_LOOP_QUERY_LIMIT) error= NESTED_LOOP_OK; /* select_limit used */ } @@ -13707,143 +14362,6 @@ int rr_sequential_and_unpack(READ_RECORD *info) /* - Semi-join materialization join function - - SYNOPSIS - sub_select_sjm() - join The join - join_tab The first table in the materialization nest - end_of_records FALSE <=> This call is made to pass another record - combination - TRUE <=> EOF - - DESCRIPTION - This is a join execution function that does materialization of a join - suborder before joining it to the rest of the join. - - The table pointed by join_tab is the first of the materialized tables. - This function first creates the materialized table and then switches to - joining the materialized table with the rest of the join. - - The materialized table can be accessed in two ways: - - index lookups - - full table scan - - RETURN - One of enum_nested_loop_state values -*/ - -enum_nested_loop_state -sub_select_sjm(JOIN *join, JOIN_TAB *join_tab, bool end_of_records) -{ - int res; - enum_nested_loop_state rc; - - DBUG_ENTER("sub_select_sjm"); - - if (!join_tab->emb_sj_nest) - { - /* - We're handling GROUP BY/ORDER BY, this is the first table, and we've - actually executed the join already and now we're just reading the - result of the join from the temporary table. - Bypass to regular join handling. - Yes, it would be nicer if sub_select_sjm wasn't called at all in this - case but there's no easy way to arrange this. - */ - rc= sub_select(join, join_tab, end_of_records); - DBUG_RETURN(rc); - } - - SJ_MATERIALIZATION_INFO *sjm= join_tab->emb_sj_nest->sj_mat_info; - if (end_of_records) - { - rc= (*join_tab[sjm->tables - 1].next_select)(join, - join_tab + sjm->tables, - end_of_records); - DBUG_RETURN(rc); - } - if (!sjm->materialized) - { - /* - Do the materialization. First, put end_sj_materialize after the last - inner table so we can catch record combinations of sj-inner tables. - */ - Next_select_func next_func= join_tab[sjm->tables - 1].next_select; - join_tab[sjm->tables - 1].next_select= end_sj_materialize; - - /* - Now run the join for the inner tables. The first call is to run the - join, the second one is to signal EOF (this is essential for some - join strategies, e.g. it will make join buffering flush the records) - */ - if ((rc= sub_select(join, join_tab, FALSE)) < 0 || - (rc= sub_select(join, join_tab, TRUE/*EOF*/)) < 0) - { - join_tab[sjm->tables - 1].next_select= next_func; - DBUG_RETURN(rc); /* it's NESTED_LOOP_(ERROR|KILLED)*/ - } - join_tab[sjm->tables - 1].next_select= next_func; - - /* - Ok, materialization finished. Initialize the access to the temptable - */ - sjm->materialized= TRUE; - join_tab->read_record.read_record= join_no_more_records; - if (sjm->is_sj_scan) - { - /* Initialize full scan */ - JOIN_TAB *last_tab= join_tab + (sjm->tables - 1); - init_read_record(&last_tab->read_record, join->thd, - sjm->table, NULL, TRUE, TRUE, FALSE); - - DBUG_ASSERT(last_tab->read_record.read_record == rr_sequential); - last_tab->read_first_record= join_read_record_no_init; - last_tab->read_record.copy_field= sjm->copy_field; - last_tab->read_record.copy_field_end= sjm->copy_field + - sjm->sjm_table_cols.elements; - last_tab->read_record.read_record= rr_sequential_and_unpack; - } - } - else - { - if (sjm->is_sj_scan) - { - /* Reset the cursor for a new scan over the table */ - if (sjm->table->file->ha_rnd_init(TRUE)) - DBUG_RETURN(NESTED_LOOP_ERROR); - } - } - - if (sjm->is_sj_scan) - { - /* Do full scan of the materialized table */ - JOIN_TAB *last_tab= join_tab + (sjm->tables - 1); - - Item *save_cond= last_tab->select_cond; - last_tab->set_select_cond(sjm->join_cond, __LINE__); - - rc= sub_select(join, last_tab, end_of_records); - last_tab->set_select_cond(save_cond, __LINE__); - DBUG_RETURN(rc); - } - else - { - /* Do index lookup in the materialized table */ - if ((res= join_read_key2(join_tab->join->thd, join_tab, - sjm->table, sjm->tab_ref)) == 1) - DBUG_RETURN(NESTED_LOOP_ERROR); /* purecov: inspected */ - if (res || !sjm->in_equality->val_int()) - DBUG_RETURN(NESTED_LOOP_NO_MORE_ROWS); - } - rc= (*join_tab[sjm->tables - 1].next_select)(join, - join_tab + sjm->tables, - end_of_records); - DBUG_RETURN(rc); -} - - -/* Fill the join buffer with partial records, retrieve all full matches for them SYNOPSIS @@ -14068,7 +14586,7 @@ sub_select(JOIN *join,JOIN_TAB *join_tab,bool end_of_records) if (join->resume_nested_loop) { /* If not the last table, plunge down the nested loop */ - if (join_tab < join->join_tab + join->tables - 1) + if (join_tab < join->join_tab + join->top_join_tab_count - 1) rc= (*join_tab->next_select)(join, join_tab + 1, 0); else { @@ -14095,6 +14613,10 @@ sub_select(JOIN *join,JOIN_TAB *join_tab,bool end_of_records) } join->thd->row_count= 0; + if (rc != NESTED_LOOP_NO_MORE_ROWS && + (rc= join_tab_execution_startup(join_tab)) < 0) + DBUG_RETURN(rc); + if (join_tab->loosescan_match_tab) join_tab->loosescan_match_tab->found_match= FALSE; @@ -14881,13 +15403,28 @@ int join_init_read_record(JOIN_TAB *tab) return (*tab->read_record.read_record)(&tab->read_record); } -static int +int join_read_record_no_init(JOIN_TAB *tab) { + Copy_field *save_copy, *save_copy_end; + + /* + init_read_record resets all elements of tab->read_record(). + Remember things that we don't want to have reset. + */ + save_copy= tab->read_record.copy_field; + save_copy_end= tab->read_record.copy_field_end; + + init_read_record(&tab->read_record, tab->join->thd, tab->table, + tab->select,1,1, FALSE); + + tab->read_record.copy_field= save_copy; + tab->read_record.copy_field_end= save_copy_end; + tab->read_record.read_record= rr_sequential_and_unpack; + return (*tab->read_record.read_record)(&tab->read_record); } - static int join_read_first(JOIN_TAB *tab) { @@ -15086,7 +15623,7 @@ end_send(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)), if (join->select_options & OPTION_FOUND_ROWS) { JOIN_TAB *jt=join->join_tab; - if ((join->tables == 1) && !join->tmp_table && !join->sort_and_group + if ((join->table_count == 1) && !join->tmp_table && !join->sort_and_group && !join->send_group_parts && !join->having && !jt->select_cond && !(jt->select && jt->select->quick) && (jt->table->file->ha_table_flags() & HA_STATS_RECORDS_IS_EXACT) && @@ -15402,7 +15939,7 @@ end_update(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)), table->file->print_error(error, MYF(0));/* purecov: inspected */ DBUG_RETURN(NESTED_LOOP_ERROR); /* purecov: inspected */ } - join->join_tab[join->tables-1].next_select=end_unique_update; + join->join_tab[join->top_join_tab_count-1].next_select=end_unique_update; } join->send_records++; DBUG_RETURN(NESTED_LOOP_OK); @@ -15548,9 +16085,23 @@ end_write_group(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)), Check if "left_item=right_item" equality is guaranteed to be true by use of [eq]ref access on left_item->field->table. - @return - TRUE if right_item is used removable reference key on left_item - FALSE otherwise + SYNOPSIS + test_if_ref() + root_cond + left_item + right_item + + DESCRIPTION + Check if the given "left_item = right_item" equality is guaranteed to be + true by use of [eq_]ref access method. + + We need root_cond as we can't remove ON expressions even if employed ref + access guarantees that they are true. This is because TODO + + RETURN + TRUE if right_item is used removable reference key on left_item + FALSE Otherwise + */ bool test_if_ref(Item *root_cond, Item_field *left_item,Item *right_item) @@ -15612,8 +16163,10 @@ bool test_if_ref(Item *root_cond, Item_field *left_item,Item *right_item) @param cond Condition to analyze @param tables Tables for which "current field values" are available - @param used_table Table that we're extracting the condition for (may - also include PSEUDO_TABLE_BITS, and may be zero) + @param used_table Table that we're extracting the condition for + tables Tables for which "current field values" are available (this + includes used_table) + (may also include PSEUDO_TABLE_BITS, and may be zero) @param join_tab_idx_arg The index of the JOIN_TAB this Item is being extracted for. MAX_TABLES if there is no corresponding JOIN_TAB. @@ -15675,6 +16228,7 @@ make_cond_for_table_from_pred(THD *thd, Item *root_cond, Item *cond, { if (used_table && !(cond->used_tables() & used_table)) return (COND*) 0; // Already checked + if (cond->type() == Item::COND_ITEM) { if (((Item_cond*) cond)->functype() == Item_func::COND_AND_FUNC) @@ -15751,6 +16305,7 @@ make_cond_for_table_from_pred(THD *thd, Item *root_cond, Item *cond, if ((cond->marker == 3 && !retain_ref_cond) || (cond->used_tables() & ~tables)) return (COND*) 0; // Can't check this yet + if (cond->marker == 2 || cond->eq_cmp_result() == Item::COND_OK) { cond->set_join_tab_idx(join_tab_idx_arg); @@ -15781,13 +16336,28 @@ make_cond_for_table_from_pred(THD *thd, Item *root_cond, Item *cond, } +/* + The difference of this from make_cond_for_table() is that we're in the + following state: + 1. conditions referring to 'tables' have been checked + 2. conditions referring to sjm_tables have been checked, too + 3. We need condition that couldn't be checked in #1 or #2 but + can be checked when we get both (tables | sjm_tables). + +*/ static COND * make_cond_after_sjm(Item *root_cond, Item *cond, table_map tables, table_map sjm_tables) { + /* + We assume that conditions that refer to only join prefix tables or + sjm_tables have already been checked. + */ if ((!(cond->used_tables() & ~tables) || !(cond->used_tables() & ~sjm_tables))) return (COND*) 0; // Already checked + + /* AND/OR recursive descent */ if (cond->type() == Item::COND_ITEM) { if (((Item_cond*) cond)->functype() == Item_func::COND_AND_FUNC) @@ -16511,7 +17081,7 @@ test_if_skip_sort_order(JOIN_TAB *tab,ORDER *order,ha_rows select_limit_arg, ref_key_quick_rows= table->quick_rows[ref_key]; read_time= join->best_positions[tablenr].read_time; - for (uint i= tablenr+1; i < join->tables; i++) + for (uint i= tablenr+1; i < join->table_count; i++) fanout*= join->best_positions[i].records_read; // fanout is always >= 1 for (nr=0; nr < table->s->keys ; nr++) @@ -16691,7 +17261,7 @@ test_if_skip_sort_order(JOIN_TAB *tab,ORDER *order,ha_rows select_limit_arg, if (best_key < 0 || ((select_limit_arg >= table_records) && (tab->type == JT_ALL && - tab->join->tables > tab->join->const_tables + 1) && + tab->join->table_count > tab->join->const_tables + 1) && !(table->file->index_flags(best_key, 0, 1) & HA_CLUSTERED_INDEX))) goto use_filesort; @@ -16931,7 +17501,7 @@ create_sort_index(THD *thd, JOIN *join, ORDER *order, JOIN_TAB *tab; DBUG_ENTER("create_sort_index"); - if (join->tables == join->const_tables) + if (join->table_count == join->const_tables) DBUG_RETURN(0); // One row, no need to sort tab= join->join_tab + join->const_tables; table= tab->table; @@ -19434,16 +20004,19 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order, { table_map used_tables=0; - uchar sjm_nests[MAX_TABLES]; - uint sjm_nests_cur=0; - uint sjm_nests_end= 0; - uint end_table= join->tables; bool printing_materialize_nest= FALSE; uint select_id= join->select_lex->select_number; - for (uint i=0 ; i < end_table ; i++) + for (JOIN_TAB *tab= first_breadth_first_tab(join); tab; + tab= next_breadth_first_tab(join, tab)) { - JOIN_TAB *tab=join->join_tab+i; + if (tab->bush_root_tab) + { + JOIN_TAB *first_sibling= tab->bush_root_tab->bush_children->start; + select_id= first_sibling->emb_sj_nest->sj_subq_pred->get_identifier(); + printing_materialize_nest= TRUE; + } + TABLE *table=tab->table; TABLE_LIST *table_list= tab->table->pos_in_table_list; char buff[512]; @@ -19483,84 +20056,6 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order, join->select_lex->type; item_list.push_back(new Item_string(stype, strlen(stype), cs)); - /* - Special processing for SJ-Materialization nests: print the fake table - and delay printing of the SJM nest contents until later. - */ - uint sj_strategy= join->best_positions[i].sj_strategy; - if (sj_is_materialize_strategy(sj_strategy) && - !printing_materialize_nest) - { - /* table */ - int len= my_snprintf(table_name_buffer, - sizeof(table_name_buffer)-1, - "subselect%d", - tab->emb_sj_nest->sj_subq_pred->get_identifier()); - item_list.push_back(new Item_string(table_name_buffer, len, cs)); - /* partitions */ - if (join->thd->lex->describe & DESCRIBE_PARTITIONS) - item_list.push_back(item_null); - /* type */ - uint type= (sj_strategy == SJ_OPT_MATERIALIZE_SCAN)? JT_ALL : JT_EQ_REF; - item_list.push_back(new Item_string(join_type_str[type], - strlen(join_type_str[type]), - cs)); - /* possible_keys */ - item_list.push_back(new Item_string("unique_key", - strlen("unique_key"), cs)); - if (sj_strategy == SJ_OPT_MATERIALIZE_SCAN) - { - item_list.push_back(item_null); /* key */ - item_list.push_back(item_null); /* key_len */ - item_list.push_back(item_null); /* ref */ - } - else - { - /* key */ - item_list.push_back(new Item_string("unique_key", strlen("unique_key"), cs)); - /* key_len */ - uint klen= tab->emb_sj_nest->sj_mat_info->table->key_info[0].key_length; - uint buflen= longlong10_to_str(klen, keylen_str_buf, 10) - keylen_str_buf; - item_list.push_back(new Item_string(keylen_str_buf, buflen, cs)); - /* ref */ - item_list.push_back(new Item_string("func", strlen("func"), cs)); - } - /* rows */ - ha_rows rows= (ha_rows) ((sj_strategy == SJ_OPT_MATERIALIZE_SCAN)? - tab->emb_sj_nest->sj_mat_info->rows : 1.0); - item_list.push_back(new Item_int((longlong)rows, - MY_INT64_NUM_DECIMAL_DIGITS)); - /* filtered */ - if (join->thd->lex->describe & DESCRIBE_EXTENDED) - item_list.push_back(new Item_float(1.0, 2)); - - /* Extra */ - if (need_tmp_table) - { - need_tmp_table=0; - extra.append(STRING_WITH_LEN("; Using temporary")); - } - if (need_order) - { - need_order=0; - extra.append(STRING_WITH_LEN("; Using filesort")); - } - /* Skip initial "; "*/ - const char *str= extra.ptr(); - uint32 extra_len= extra.length(); - if (extra_len) - { - str += 2; - extra_len -= 2; - } - item_list.push_back(new Item_string(str, extra_len, cs)); - - /* Register the nest for further processing: */ - sjm_nests[sjm_nests_end++]= i; - i += join->best_positions[i].n_sj_tables-1; - goto loop_end; - } - if ((tab->type == JT_ALL || tab->type == JT_HASH) && tab->select && tab->select->quick) { @@ -19583,6 +20078,16 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order, table->derived_select_number); item_list.push_back(new Item_string(table_name_buffer, len, cs)); } + else if (tab->bush_children) + { + JOIN_TAB *ctab= tab->bush_children->start; + /* table */ + int len= my_snprintf(table_name_buffer, + sizeof(table_name_buffer)-1, + "<subquery%d>", + ctab->emb_sj_nest->sj_subq_pred->get_identifier()); + item_list.push_back(new Item_string(table_name_buffer, len, cs)); + } else { TABLE_LIST *real_table= table->pos_in_table_list; @@ -19698,7 +20203,8 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order, } else { - if (table_list->schema_table && + if (table_list && /* SJM bushes don't have table_list */ + table_list->schema_table && table_list->schema_table->i_s_requested_object & OPTIMIZE_I_S_TABLE) { const char *tmp_buff; @@ -19729,7 +20235,8 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order, } /* Add "rows" field to item_list. */ - if (table_list->schema_table) + if (table_list /* SJM bushes don't have table_list */ && + table_list->schema_table) { /* in_rows */ if (join->thd->lex->describe & DESCRIBE_EXTENDED) @@ -19748,12 +20255,23 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order, examined_rows= tab->limit; else { - tab->table->file->info(HA_STATUS_VARIABLE); - examined_rows= tab->table->file->stats.records; + if (!tab->table->pos_in_table_list || + tab->table->is_filled_at_execution()) // temporary, is_filled_at_execution + { + examined_rows= tab->records; + } + else + { + /* + handler->info(HA_STATUS_VARIABLE) has been called in + make_join_statistics() + */ + examined_rows= tab->table->file->stats.records; + } } } else - examined_rows=(ha_rows)join->best_positions[i].records_read; + examined_rows=(ha_rows)tab->records_read; item_list.push_back(new Item_int((longlong) (ulonglong) examined_rows, MY_INT64_NUM_DECIMAL_DIGITS)); @@ -19761,21 +20279,9 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order, /* Add "filtered" field to item_list. */ if (join->thd->lex->describe & DESCRIBE_EXTENDED) { - /* - psergey-todo: - in the code above, we cast to integer when asssigning to - examined_rows. - In the code below, we may divide original value but result of - conversion of the same value to integer, which may produce a - value that's greater than 100%, which looks very odd. - I'm not fixing this right away because that might trigger a wave - of small EXPLAIN EXTENDED output changes, which I don't have time - to deal with right now. - */ float f= 0.0; if (examined_rows) - f= (float) (100.0 * join->best_positions[i].records_read / - examined_rows); + f= (float) (100.0 * tab->records_read / examined_rows); item_list.push_back(new Item_float(f, 2)); } } @@ -19860,7 +20366,8 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order, extra.append(STRING_WITH_LEN("; Using where")); } } - if (table_list->schema_table && + if (table_list /* SJM bushes don't have table_list */ && + table_list->schema_table && table_list->schema_table->i_s_requested_object & OPTIMIZE_I_S_TABLE) { if (!table_list->table_open_method) @@ -19957,25 +20464,6 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order, } } - /* - if (sj_is_materialize_strategy(sj_strategy)) - { - if (join->best_positions[i].n_sj_tables == 1) - extra.append(STRING_WITH_LEN("; Materialize")); - else - { - last_sjm_table= i + join->best_positions[i].n_sj_tables - 1; - extra.append(STRING_WITH_LEN("; Start materialize")); - } - if (sj_strategy == SJ_OPT_MATERIALIZE_SCAN) - extra.append(STRING_WITH_LEN("; Scan")); - } - else if (last_sjm_table == i) - { - extra.append(STRING_WITH_LEN("; End materialize")); - } - */ - for (uint part= 0; part < tab->ref.key_parts; part++) { if (tab->ref.cond_guards[part]) @@ -20001,14 +20489,6 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order, } item_list.push_back(new Item_string(str, len, cs)); } - loop_end: - if (i+1 == end_table && sjm_nests_cur != sjm_nests_end) - { - printing_materialize_nest= TRUE; - i= sjm_nests[sjm_nests_cur++] - 1; - end_table= (i+1) + join->best_positions[i+1].n_sj_tables; - select_id= join->join_tab[i+1].emb_sj_nest->sj_subq_pred->get_identifier(); - } // For next iteration used_tables|=table->map; @@ -20246,6 +20726,14 @@ void TABLE_LIST::print(THD *thd, table_map eliminated_tables, String *str, print_join(thd, eliminated_tables, str, &nested_join->join_list, query_type); str->append(')'); } + else if (jtbm_subselect) + { + str->append(STRING_WITH_LEN(" <materialize> (")); + subselect_hash_sj_engine *hash_engine; + hash_engine= (subselect_hash_sj_engine*)jtbm_subselect->engine; + hash_engine->materialize_engine->print(str, query_type); + str->append(')'); + } else { const char *cmp_name; // Name to compare with alias @@ -20538,7 +21026,7 @@ void JOIN::save_query_plan(Join_plan_state *save_to) keyuse= save_to->keyuse; /* keyuse is reset to an empty array. */ save_to->keyuse= tmp_keyuse; - for (uint i= 0; i < tables; i++) + for (uint i= 0; i < table_count; i++) { save_to->join_tab_keyuse[i]= join_tab[i].keyuse; join_tab[i].keyuse= NULL; @@ -20547,8 +21035,8 @@ void JOIN::save_query_plan(Join_plan_state *save_to) } } memcpy((uchar*) save_to->best_positions, (uchar*) best_positions, - sizeof(POSITION) * (tables + 1)); - memset(best_positions, 0, sizeof(POSITION) * (tables + 1)); + sizeof(POSITION) * (table_count + 1)); + memset(best_positions, 0, sizeof(POSITION) * (table_count + 1)); } @@ -20567,7 +21055,7 @@ void JOIN::restore_query_plan(Join_plan_state *restore_from) keyuse= restore_from->keyuse; restore_from->keyuse= tmp_keyuse; - for (uint i= 0; i < tables; i++) + for (uint i= 0; i < table_count; i++) { join_tab[i].keyuse= restore_from->join_tab_keyuse[i]; join_tab[i].checked_keys= restore_from->join_tab_checked_keys[i]; @@ -20575,7 +21063,7 @@ void JOIN::restore_query_plan(Join_plan_state *restore_from) } memcpy((uchar*) best_positions, (uchar*) restore_from->best_positions, - sizeof(POSITION) * (tables + 1)); + sizeof(POSITION) * (table_count + 1)); } @@ -20614,7 +21102,7 @@ JOIN::reoptimize(Item *added_where, table_map join_tables, uint org_keyuse_elements; /* Re-run the REF optimizer to take into account the new conditions. */ - if (update_ref_and_keys(thd, &added_keyuse, join_tab, tables, added_where, + if (update_ref_and_keys(thd, &added_keyuse, join_tab, table_count, added_where, ~outer_join, select_lex, &sargables)) { delete_dynamic(&added_keyuse); diff --git a/sql/sql_select.h b/sql/sql_select.h index f67b79fff47..d3ad4f69949 100644 --- a/sql/sql_select.h +++ b/sql/sql_select.h @@ -165,13 +165,25 @@ enum enum_nested_loop_state typedef enum_nested_loop_state (*Next_select_func)(JOIN *, struct st_join_table *, bool); + +/* + Function prototype for reading first record for a join tab + + RETURN + 0 - OK + -1 - Record not found + Other - A fatal error +*/ typedef int (*Read_record_func)(struct st_join_table *tab); + Next_select_func setup_end_select_func(JOIN *join); int rr_sequential(READ_RECORD *info); +int rr_sequential_and_unpack(READ_RECORD *info); class JOIN_CACHE; class SJ_TMP_TABLE; +class JOIN_TAB_RANGE; typedef struct st_join_table { st_join_table() {} /* Remove gcc warning */ @@ -200,6 +212,21 @@ typedef struct st_join_table { st_join_table *last_inner; /**< last table table for embedding outer join */ st_join_table *first_upper; /**< first inner table for embedding outer join */ st_join_table *first_unmatched; /**< used for optimization purposes only */ + + /* + For join tabs that are inside an SJM bush: root of the bush + */ + st_join_table *bush_root_tab; + + /* TRUE <=> This join_tab is inside an SJM bush and is the last leaf tab here */ + bool last_leaf_in_bush; + + /* + ptr - this is a bush, and ptr points to description of child join_tab + range + NULL - this join tab has no bush children + */ + JOIN_TAB_RANGE *bush_children; /* Special content for EXPLAIN 'Extra' column or NULL if none */ const char *info; @@ -237,7 +264,13 @@ typedef struct st_join_table { method (but not 'index' for some reason), i.e. this matches method which E(#records) is in found_records. */ - ha_rows read_time; + double read_time; + + /* psergey-todo: make the below have type double, like POSITION::records_read? */ + ha_rows records_read; + + /* Startup cost for execution */ + double startup_cost; double partial_join_cardinality; @@ -330,11 +363,11 @@ typedef struct st_join_table { /* Semi-join strategy to be used for this join table. This is a copy of POSITION::sj_strategy field. This field is set up by the - fix_semijion_strategies_for_picked_join_order. + fix_semijoin_strategies_for_picked_join_order. */ uint sj_strategy; - struct st_join_table *first_sjm_sibling; + uint n_sj_tables; void cleanup(); inline bool is_using_loose_index_scan() @@ -450,9 +483,6 @@ enum_nested_loop_state sub_select_cache(JOIN *join, JOIN_TAB *join_tab, bool end_of_records); enum_nested_loop_state sub_select(JOIN *join,JOIN_TAB *join_tab, bool end_of_records); -enum_nested_loop_state sub_select_sjm(JOIN *join, JOIN_TAB *join_tab, - bool end_of_records); - enum_nested_loop_state end_send_group(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)), bool end_of_records); @@ -600,6 +630,13 @@ inline bool sj_is_materialize_strategy(uint strategy) return strategy >= SJ_OPT_MATERIALIZE; } +class JOIN_TAB_RANGE: public Sql_alloc +{ +public: + JOIN_TAB *start; + JOIN_TAB *end; +}; + class JOIN :public Sql_alloc { @@ -652,20 +689,40 @@ protected: bool choose_tableless_subquery_plan(); public: - JOIN_TAB *join_tab,**best_ref; + JOIN_TAB *join_tab, **best_ref; JOIN_TAB **map2table; ///< mapping between table indexes and JOIN_TABs JOIN_TAB *join_tab_save; ///< saved join_tab for subquery reexecution + + List<JOIN_TAB_RANGE> join_tab_ranges; + + /* + Base tables participating in the join. After join optimization is done, the + tables are stored in the join order (but the only really important part is + that const tables are first). + */ TABLE **table; - TABLE **all_tables; /** The table which has an index that allows to produce the requried ordering. A special value of 0x1 means that the ordering will be produced by passing 1st non-const table to filesort(). NULL means no such table exists. */ TABLE *sort_by_table; - uint tables; /**< Number of tables in the join */ + /* + Number of tables in the join. + (In MySQL, it is named 'tables' and is also the number of elements in + join->join_tab array. In MariaDB, the latter is not true, so we've renamed + the variable) + */ + uint table_count; uint outer_tables; /**< Number of tables that are not inside semijoin */ uint const_tables; + /* + Number of tables in the top join_tab array. Normally this matches + (join_tab_ranges.head()->end - join_tab_ranges.head()->start). + + We keep it here so that it is saved/restored with JOIN::restore_tmp. + */ + uint top_join_tab_count; uint send_group_parts; bool group; /**< If query contains GROUP BY clause */ /** @@ -692,7 +749,8 @@ public: /* Tables removed by table elimination. Set to 0 before the elimination. */ table_map eliminated_tables; /* - Bitmap of all inner tables from outer joins + Bitmap of all inner tables from outer joins (set at start of + make_join_statistics) */ table_map outer_join; ha_rows send_records,found_records,examined_rows,row_limit, select_limit; @@ -741,7 +799,6 @@ public: /* We also maintain a stack of join optimization states in * join->positions[] */ /******* Join optimization state members end *******/ - Next_select_func first_select; /* The cost of best complete join plan found so far during optimization, after optimization phase - cost of picked join order (not taking into @@ -893,6 +950,10 @@ public: bool optimized; ///< flag to avoid double optimization in EXPLAIN bool initialized; ///< flag to avoid double init_execution calls + /* + Subqueries that will need to be converted to semi-join nests, including + those converted to jtbm nests. The list is emptied when conversion is done. + */ Array<Item_in_subselect> sj_subselects; /* Additional WHERE and HAVING predicates to be considered for IN=>EXISTS @@ -900,7 +961,7 @@ public: */ Item *in_to_exists_where; Item *in_to_exists_having; - + /* Temporary tables used to weed-out semi-join duplicates */ List<TABLE> sj_tmp_tables; /* SJM nests that are executed with SJ-Materialization strategy */ @@ -933,7 +994,8 @@ public: { join_tab= join_tab_save= 0; table= 0; - tables= 0; + table_count= 0; + top_join_tab_count= 0; const_tables= 0; eliminated_tables= 0; join_list= 0; @@ -990,7 +1052,6 @@ public: rollup.state= ROLLUP::STATE_NONE; no_const_tables= FALSE; - first_select= sub_select; outer_ref_cond= 0; in_to_exists_where= NULL; in_to_exists_having= NULL; @@ -1056,7 +1117,7 @@ public: } inline table_map all_tables_map() { - return (table_map(1) << tables) - 1; + return (table_map(1) << table_count) - 1; } /* Return the table for which an index scan can be used to satisfy @@ -1079,8 +1140,13 @@ public: max_allowed_join_cache_level > JOIN_CACHE_HASHED_BIT; } bool choose_subquery_plan(table_map join_tables); - void get_partial_join_cost(uint n_tables, - double *read_time_arg, double *record_count_arg); + void get_partial_cost_and_fanout(uint end_tab_idx, + table_map filter_map, + double *read_time_arg, + double *record_count_arg); + void get_prefix_cost_and_fanout(uint n_tables, + double *read_time_arg, + double *record_count_arg); /* defined in opt_subselect.cc */ bool transform_max_min_subquery(); /* True if this JOIN is a subquery under an IN predicate. */ @@ -1088,7 +1154,6 @@ public: { return (unit->item && unit->item->is_in_predicate()); } - private: /** TRUE if the query contains an aggregate function but has no GROUP @@ -1099,6 +1164,15 @@ private: void cleanup_item_list(List<Item> &items) const; }; +enum enum_with_bush_roots { WITH_BUSH_ROOTS, WITHOUT_BUSH_ROOTS}; +enum enum_with_const_tables { WITH_CONST_TABLES, WITHOUT_CONST_TABLES}; + +JOIN_TAB *first_linear_tab(JOIN *join, enum enum_with_const_tables const_tbls); +JOIN_TAB *next_linear_tab(JOIN* join, JOIN_TAB* tab, + enum enum_with_bush_roots include_bush_roots); + +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); typedef struct st_select_check { uint const_ref,reg_ref; @@ -1351,6 +1425,7 @@ int safe_index_read(JOIN_TAB *tab); COND *remove_eq_conds(THD *thd, COND *cond, Item::cond_result *cond_value); int test_if_item_cache_changed(List<Cached_item> &list); int join_init_read_record(JOIN_TAB *tab); +int join_read_record_no_init(JOIN_TAB *tab); void set_position(JOIN *join,uint idx,JOIN_TAB *table,KEYUSE *key); inline Item * and_items(Item* cond, Item *item) { diff --git a/sql/sql_show.cc b/sql/sql_show.cc index 2eecae26506..d63fe87deae 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -6637,14 +6637,15 @@ int make_schema_select(THD *thd, SELECT_LEX *sel, bool get_schema_tables_result(JOIN *join, enum enum_schema_table_state executed_place) { - JOIN_TAB *tmp_join_tab= join->join_tab+join->tables; THD *thd= join->thd; LEX *lex= thd->lex; bool result= 0; DBUG_ENTER("get_schema_tables_result"); thd->no_warnings_for_error= 1; - for (JOIN_TAB *tab= join->join_tab; tab < tmp_join_tab; tab++) + for (JOIN_TAB *tab= first_linear_tab(join, WITH_CONST_TABLES); + tab; + tab= next_linear_tab(join, tab, WITHOUT_BUSH_ROOTS)) { if (!tab->table || !tab->table->pos_in_table_list) break; diff --git a/sql/sql_test.cc b/sql/sql_test.cc index af89b3cd525..cdab2ee8a6f 100644 --- a/sql/sql_test.cc +++ b/sql/sql_test.cc @@ -166,58 +166,67 @@ void TEST_filesort(SORT_FIELD *sortorder,uint s_length) void TEST_join(JOIN *join) { - uint i,ref; + uint ref; + int i; + List_iterator<JOIN_TAB_RANGE> it(join->join_tab_ranges); + JOIN_TAB_RANGE *jt_range; DBUG_ENTER("TEST_join"); - /* - Assemble results of all the calls to full_name() first, - in order not to garble the tabular output below. - */ - String ref_key_parts[MAX_TABLES]; - for (i= 0; i < join->tables; i++) - { - JOIN_TAB *tab= join->join_tab + i; - for (ref= 0; ref < tab->ref.key_parts; ref++) - { - ref_key_parts[i].append(tab->ref.items[ref]->full_name()); - ref_key_parts[i].append(" "); - } - } - DBUG_LOCK_FILE; VOID(fputs("\nInfo about JOIN\n",DBUG_FILE)); - for (i=0 ; i < join->tables ; i++) + + while ((jt_range= it++)) { - JOIN_TAB *tab=join->join_tab+i; - TABLE *form=tab->table; - char key_map_buff[128]; - fprintf(DBUG_FILE,"%-16.16s type: %-7s q_keys: %s refs: %d key: %d len: %d\n", - form->alias.c_ptr(), - join_type_str[tab->type], - tab->keys.print(key_map_buff), - tab->ref.key_parts, - tab->ref.key, - tab->ref.key_length); - if (tab->select) + /* + Assemble results of all the calls to full_name() first, + in order not to garble the tabular output below. + */ + String ref_key_parts[MAX_TABLES]; + int tables_in_range= jt_range->end - jt_range->start; + for (i= 0; i < tables_in_range; i++) { - char buf[MAX_KEY/8+1]; - if (tab->use_quick == 2) - fprintf(DBUG_FILE, - " quick select checked for each record (keys: %s)\n", - tab->select->quick_keys.print(buf)); - else if (tab->select->quick) + JOIN_TAB *tab= jt_range->start + i; + for (ref= 0; ref < tab->ref.key_parts; ref++) { - fprintf(DBUG_FILE, " quick select used:\n"); - tab->select->quick->dbug_dump(18, FALSE); + ref_key_parts[i].append(tab->ref.items[ref]->full_name()); + ref_key_parts[i].append(" "); } - else - VOID(fputs(" select used\n",DBUG_FILE)); } - if (tab->ref.key_parts) + + for (i= 0; i < tables_in_range; i++) { - fprintf(DBUG_FILE, + JOIN_TAB *tab= jt_range->start + i; + TABLE *form=tab->table; + char key_map_buff[128]; + fprintf(DBUG_FILE,"%-16.16s type: %-7s q_keys: %s refs: %d key: %d len: %d\n", + form->alias.c_ptr(), + join_type_str[tab->type], + tab->keys.print(key_map_buff), + tab->ref.key_parts, + tab->ref.key, + tab->ref.key_length); + if (tab->select) + { + char buf[MAX_KEY/8+1]; + if (tab->use_quick == 2) + fprintf(DBUG_FILE, + " quick select checked for each record (keys: %s)\n", + tab->select->quick_keys.print(buf)); + else if (tab->select->quick) + { + fprintf(DBUG_FILE, " quick select used:\n"); + tab->select->quick->dbug_dump(18, FALSE); + } + else + VOID(fputs(" select used\n",DBUG_FILE)); + } + if (tab->ref.key_parts) + { + fprintf(DBUG_FILE, " refs: %s\n", ref_key_parts[i].c_ptr_safe()); + } } + VOID(fputs("\n",DBUG_FILE)); } DBUG_UNLOCK_FILE; DBUG_VOID_RETURN; diff --git a/sql/sql_union.cc b/sql/sql_union.cc index 192ea31c610..a8c829c1c5d 100644 --- a/sql/sql_union.cc +++ b/sql/sql_union.cc @@ -137,6 +137,11 @@ select_union::create_result_table(THD *thd_arg, List<Item> *column_types, (ORDER*) 0, is_union_distinct, 1, options, HA_POS_ERROR, (char*) alias))) return TRUE; + //psergey-merge-fix: + table->keys_in_use_for_query.clear_all(); + for (uint i=0; i < table->s->fields; i++) + table->field[i]->flags &= ~PART_KEY_FLAG; + table->file->extra(HA_EXTRA_WRITE_CACHE); table->file->extra(HA_EXTRA_IGNORE_DUP_KEY); return FALSE; @@ -712,7 +717,8 @@ bool st_select_lex_unit::cleanup() if ((join= fake_select_lex->join)) { join->tables_list= 0; - join->tables= 0; + join->table_count= 0; + join->top_join_tab_count= 0; } error|= fake_select_lex->cleanup(); /* diff --git a/sql/table.cc b/sql/table.cc index 5548c220897..ca78b7baa87 100644 --- a/sql/table.cc +++ b/sql/table.cc @@ -5345,6 +5345,19 @@ bool st_table::is_children_attached(void) (parent && parent->children_attached)); } + +/* + Return TRUE if the table is filled at execution phase + + (and so, the optimizer must not do anything that depends on the contents of + the table, like range analysis or constant table detection) +*/ + +bool st_table::is_filled_at_execution() +{ + return test(pos_in_table_list->jtbm_subselect); +} + /* Cleanup this table for re-execution. diff --git a/sql/table.h b/sql/table.h index 4278c3c9c35..4e198116fe2 100644 --- a/sql/table.h +++ b/sql/table.h @@ -66,7 +66,8 @@ typedef struct st_order { bool counter_used; /* parameter was counter of columns */ Field *field; /* If tmp-table group */ char *buff; /* If tmp-table group */ - table_map used, depend_map; + table_map used; /* NOTE: the below is only set to 0 but is still used by eq_ref_table */ + table_map depend_map; } ORDER; /** @@ -966,6 +967,12 @@ struct st_table { file->extra(HA_EXTRA_KEYREAD); DBUG_VOID_RETURN; } + /* + Returns TRUE if the table is filled at execution phase (and so, the + optimizer must not do anything that depends on the contents of the table, + like range analysis or constant table detection) + */ + bool is_filled_at_execution(); inline void disable_keyread() { DBUG_ENTER("disable_keyread"); @@ -1194,7 +1201,7 @@ class Item_in_subselect; 1) table (TABLE_LIST::view == NULL) - base table (TABLE_LIST::derived == NULL) - - subquery - TABLE_LIST::table is a temp table + - FROM-clause subquery - TABLE_LIST::table is a temp table (TABLE_LIST::derived != NULL) - information schema table (TABLE_LIST::schema_table != NULL) @@ -1213,6 +1220,8 @@ class Item_in_subselect; (TABLE_LIST::natural_join != NULL) - JOIN ... USING (TABLE_LIST::join_using_fields != NULL) + - semi-join nest (sj_on_expr!= NULL && sj_subq_pred!=NULL) + 4) jtbm semi-join (jtbm_subselect != NULL) */ class Index_hint; @@ -1255,8 +1264,15 @@ struct TABLE_LIST */ table_map sj_inner_tables; /* Number of IN-compared expressions */ - uint sj_in_exprs; + uint sj_in_exprs; + + /* If this is a non-jtbm semi-join nest: corresponding subselect predicate */ Item_in_subselect *sj_subq_pred; + + /* If this is a jtbm semi-join object: corresponding subselect predicate */ + Item_in_subselect *jtbm_subselect; + uint jtbm_table_no; + SJ_MATERIALIZATION_INFO *sj_mat_info; /* |