Commit 5bc7d596 authored by sergefp@pylon.mylan's avatar sergefp@pylon.mylan

Merge mysql.com:/home/psergey/mysql-5.0-bug8804-r12

into  mysql.com:/home/psergey/mysql-5.1-bug8804-r12-merge
parents 0e949c6a cced6255
...@@ -12,7 +12,7 @@ insert into t4 values (1,10), (2,10), (3,30), (4, 30); ...@@ -12,7 +12,7 @@ insert into t4 values (1,10), (2,10), (3,30), (4, 30);
explain select * from t2 where p NOT IN (select p from t1); explain select * from t2 where p NOT IN (select p from t1);
id select_type table type possible_keys key key_len ref rows Extra id select_type table type possible_keys key key_len ref rows Extra
1 PRIMARY t2 ALL NULL NULL NULL NULL # Using where 1 PRIMARY t2 ALL NULL NULL NULL NULL # Using where
2 DEPENDENT SUBQUERY t1 unique_subquery PRIMARY PRIMARY 4 func # Using index 2 DEPENDENT SUBQUERY t1 unique_subquery PRIMARY PRIMARY 4 func #
select * from t2 where p NOT IN (select p from t1) order by p; select * from t2 where p NOT IN (select p from t1) order by p;
p u o p u o
4 4 4 4 4 4
......
...@@ -744,7 +744,7 @@ id select_type table type possible_keys key key_len ref rows filtered Extra ...@@ -744,7 +744,7 @@ id select_type table type possible_keys key key_len ref rows filtered Extra
3 DEPENDENT UNION NULL NULL NULL NULL NULL NULL NULL NULL No tables used 3 DEPENDENT UNION NULL NULL NULL NULL NULL NULL NULL NULL No tables used
NULL UNION RESULT <union2,3> ALL NULL NULL NULL NULL NULL NULL NULL UNION RESULT <union2,3> ALL NULL NULL NULL NULL NULL NULL
Warnings: Warnings:
Note 1003 select `test`.`t2`.`id` AS `id` from `test`.`t2` where <in_optimizer>(`test`.`t2`.`id`,<exists>(select 1 AS `1` having trigcond((<cache>(`test`.`t2`.`id`) = <ref_null_helper>(1))) union select 3 AS `3` having trigcond((<cache>(`test`.`t2`.`id`) = <ref_null_helper>(3))))) Note 1003 select `test`.`t2`.`id` AS `id` from `test`.`t2` where <in_optimizer>(`test`.`t2`.`id`,<exists>(select 1 AS `1` having (<cache>(`test`.`t2`.`id`) = <ref_null_helper>(1)) union select 3 AS `3` having (<cache>(`test`.`t2`.`id`) = <ref_null_helper>(3))))
SELECT * FROM t2 WHERE id IN (SELECT 5 UNION SELECT 3); SELECT * FROM t2 WHERE id IN (SELECT 5 UNION SELECT 3);
id id
SELECT * FROM t2 WHERE id IN (SELECT 5 UNION SELECT 2); SELECT * FROM t2 WHERE id IN (SELECT 5 UNION SELECT 2);
...@@ -892,7 +892,7 @@ id select_type table type possible_keys key key_len ref rows filtered Extra ...@@ -892,7 +892,7 @@ id select_type table type possible_keys key key_len ref rows filtered Extra
1 PRIMARY t1 index NULL PRIMARY 4 NULL 4 100.00 Using index 1 PRIMARY t1 index NULL PRIMARY 4 NULL 4 100.00 Using index
2 DEPENDENT SUBQUERY t2 index_subquery a a 5 func 2 100.00 Using index 2 DEPENDENT SUBQUERY t2 index_subquery a a 5 func 2 100.00 Using index
Warnings: Warnings:
Note 1003 select `test`.`t1`.`a` AS `a`,<in_optimizer>(`test`.`t1`.`a`,<exists>(<index_lookup>(<cache>(`test`.`t1`.`a`) in t2 on a checking NULL))) AS `t1.a in (select t2.a from t2)` from `test`.`t1` Note 1003 select `test`.`t1`.`a` AS `a`,<in_optimizer>(`test`.`t1`.`a`,<exists>(<index_lookup>(<cache>(`test`.`t1`.`a`) in t2 on a checking NULL having <is_not_null_test>(`test`.`t2`.`a`)))) AS `t1.a in (select t2.a from t2)` from `test`.`t1`
CREATE TABLE t3 (a int(11) default '0'); CREATE TABLE t3 (a int(11) default '0');
INSERT INTO t3 VALUES (1),(2),(3); INSERT INTO t3 VALUES (1),(2),(3);
SELECT t1.a, t1.a in (select t2.a from t2,t3 where t3.a=t2.a) FROM t1; SELECT t1.a, t1.a in (select t2.a from t2,t3 where t3.a=t2.a) FROM t1;
...@@ -907,7 +907,7 @@ id select_type table type possible_keys key key_len ref rows filtered Extra ...@@ -907,7 +907,7 @@ id select_type table type possible_keys key key_len ref rows filtered Extra
2 DEPENDENT SUBQUERY t2 ref_or_null a a 5 func 2 100.00 Using where; Using index 2 DEPENDENT SUBQUERY t2 ref_or_null a a 5 func 2 100.00 Using where; Using index
2 DEPENDENT SUBQUERY t3 ALL NULL NULL NULL NULL 3 100.00 Using where 2 DEPENDENT SUBQUERY t3 ALL NULL NULL NULL NULL 3 100.00 Using where
Warnings: Warnings:
Note 1003 select `test`.`t1`.`a` AS `a`,<in_optimizer>(`test`.`t1`.`a`,<exists>(select 1 AS `Not_used` from `test`.`t2` join `test`.`t3` where ((`test`.`t3`.`a` = `test`.`t2`.`a`) and trigcond(((<cache>(`test`.`t1`.`a`) = `test`.`t2`.`a`) or isnull(`test`.`t2`.`a`)))) having trigcond(<is_not_null_test>(`test`.`t2`.`a`)))) AS `t1.a in (select t2.a from t2,t3 where t3.a=t2.a)` from `test`.`t1` Note 1003 select `test`.`t1`.`a` AS `a`,<in_optimizer>(`test`.`t1`.`a`,<exists>(select 1 AS `Not_used` from `test`.`t2` join `test`.`t3` where ((`test`.`t3`.`a` = `test`.`t2`.`a`) and ((<cache>(`test`.`t1`.`a`) = `test`.`t2`.`a`) or isnull(`test`.`t2`.`a`))) having <is_not_null_test>(`test`.`t2`.`a`))) AS `t1.a in (select t2.a from t2,t3 where t3.a=t2.a)` from `test`.`t1`
drop table t1,t2,t3; drop table t1,t2,t3;
create table t1 (a float); create table t1 (a float);
select 10.5 IN (SELECT * from t1 LIMIT 1); select 10.5 IN (SELECT * from t1 LIMIT 1);
...@@ -1303,9 +1303,9 @@ a ...@@ -1303,9 +1303,9 @@ a
2 2
4 4
explain extended select * from t2 where t2.a in (select a from t1 where t1.b <> 30); explain extended select * from t2 where t2.a in (select a from t1 where t1.b <> 30);
id select_type table type possible_keys key key_len ref rows filtered Extra id select_type table type possible_keys key key_len ref rows Extra
1 PRIMARY t2 index NULL PRIMARY 4 NULL 4 100.00 Using where; Using index 1 PRIMARY t2 index NULL PRIMARY 4 NULL 4 Using where; Using index
2 DEPENDENT SUBQUERY t1 unique_subquery PRIMARY PRIMARY 4 func 1 100.00 Using index; Using where 2 DEPENDENT SUBQUERY t1 unique_subquery PRIMARY PRIMARY 4 func 1 Using where
Warnings: Warnings:
Note 1003 select `test`.`t2`.`a` AS `a` from `test`.`t2` where <in_optimizer>(`test`.`t2`.`a`,<exists>(<primary_index_lookup>(<cache>(`test`.`t2`.`a`) in t1 on PRIMARY where (`test`.`t1`.`b` <> 30)))) Note 1003 select `test`.`t2`.`a` AS `a` from `test`.`t2` where <in_optimizer>(`test`.`t2`.`a`,<exists>(<primary_index_lookup>(<cache>(`test`.`t2`.`a`) in t1 on PRIMARY where (`test`.`t1`.`b` <> 30))))
select * from t2 where t2.a in (select t1.a from t1,t3 where t1.b=t3.a); select * from t2 where t2.a in (select t1.a from t1,t3 where t1.b=t3.a);
...@@ -1460,29 +1460,29 @@ a1 0 ...@@ -1460,29 +1460,29 @@ a1 0
a2 1 a2 1
a3 1 a3 1
explain extended select s1, s1 NOT IN (SELECT s1 FROM t2) from t1; explain extended select s1, s1 NOT IN (SELECT s1 FROM t2) from t1;
id select_type table type possible_keys key key_len ref rows filtered Extra id select_type table type possible_keys key key_len ref rows Extra
1 PRIMARY t1 index NULL s1 6 NULL 3 100.00 Using index 1 PRIMARY t1 index NULL s1 6 NULL 3 Using index
2 DEPENDENT SUBQUERY t2 index_subquery s1 s1 6 func 2 100.00 Using index 2 DEPENDENT SUBQUERY t2 index_subquery s1 s1 6 func 2 Using index; Full scan on NULL key
Warnings: Warnings:
Note 1003 select `test`.`t1`.`s1` AS `s1`,(not(<in_optimizer>(`test`.`t1`.`s1`,<exists>(<index_lookup>(<cache>(`test`.`t1`.`s1`) in t2 on s1 checking NULL))))) AS `s1 NOT IN (SELECT s1 FROM t2)` from `test`.`t1` Note 1003 select `test`.`t1`.`s1` AS `s1`,(not(<in_optimizer>(`test`.`t1`.`s1`,<exists>(<index_lookup>(<cache>(`test`.`t1`.`s1`) in t2 on s1 checking NULL having trigcond(<is_not_null_test>(`test`.`t2`.`s1`))))))) AS `s1 NOT IN (SELECT s1 FROM t2)` from `test`.`t1`
explain extended select s1, s1 = ANY (SELECT s1 FROM t2) from t1; explain extended select s1, s1 = ANY (SELECT s1 FROM t2) from t1;
id select_type table type possible_keys key key_len ref rows filtered Extra id select_type table type possible_keys key key_len ref rows Extra
1 PRIMARY t1 index NULL s1 6 NULL 3 100.00 Using index 1 PRIMARY t1 index NULL s1 6 NULL 3 Using index
2 DEPENDENT SUBQUERY t2 index_subquery s1 s1 6 func 2 100.00 Using index 2 DEPENDENT SUBQUERY t2 index_subquery s1 s1 6 func 2 Using index; Full scan on NULL key
Warnings: Warnings:
Note 1003 select `test`.`t1`.`s1` AS `s1`,<in_optimizer>(`test`.`t1`.`s1`,<exists>(<index_lookup>(<cache>(`test`.`t1`.`s1`) in t2 on s1 checking NULL))) AS `s1 = ANY (SELECT s1 FROM t2)` from `test`.`t1` Note 1003 select `test`.`t1`.`s1` AS `s1`,<in_optimizer>(`test`.`t1`.`s1`,<exists>(<index_lookup>(<cache>(`test`.`t1`.`s1`) in t2 on s1 checking NULL having trigcond(<is_not_null_test>(`test`.`t2`.`s1`))))) AS `s1 = ANY (SELECT s1 FROM t2)` from `test`.`t1`
explain extended select s1, s1 <> ALL (SELECT s1 FROM t2) from t1; explain extended select s1, s1 <> ALL (SELECT s1 FROM t2) from t1;
id select_type table type possible_keys key key_len ref rows filtered Extra id select_type table type possible_keys key key_len ref rows Extra
1 PRIMARY t1 index NULL s1 6 NULL 3 100.00 Using index 1 PRIMARY t1 index NULL s1 6 NULL 3 Using index
2 DEPENDENT SUBQUERY t2 index_subquery s1 s1 6 func 2 100.00 Using index 2 DEPENDENT SUBQUERY t2 index_subquery s1 s1 6 func 2 Using index; Full scan on NULL key
Warnings: Warnings:
Note 1003 select `test`.`t1`.`s1` AS `s1`,(not(<in_optimizer>(`test`.`t1`.`s1`,<exists>(<index_lookup>(<cache>(`test`.`t1`.`s1`) in t2 on s1 checking NULL))))) AS `s1 <> ALL (SELECT s1 FROM t2)` from `test`.`t1` Note 1003 select `test`.`t1`.`s1` AS `s1`,(not(<in_optimizer>(`test`.`t1`.`s1`,<exists>(<index_lookup>(<cache>(`test`.`t1`.`s1`) in t2 on s1 checking NULL having trigcond(<is_not_null_test>(`test`.`t2`.`s1`))))))) AS `s1 <> ALL (SELECT s1 FROM t2)` from `test`.`t1`
explain extended select s1, s1 NOT IN (SELECT s1 FROM t2 WHERE s1 < 'a2') from t1; explain extended select s1, s1 NOT IN (SELECT s1 FROM t2 WHERE s1 < 'a2') from t1;
id select_type table type possible_keys key key_len ref rows filtered Extra id select_type table type possible_keys key key_len ref rows Extra
1 PRIMARY t1 index NULL s1 6 NULL 3 100.00 Using index 1 PRIMARY t1 index NULL s1 6 NULL 3 Using index
2 DEPENDENT SUBQUERY t2 index_subquery s1 s1 6 func 2 100.00 Using index; Using where 2 DEPENDENT SUBQUERY t2 index_subquery s1 s1 6 func 2 Using index; Using where; Full scan on NULL key
Warnings: Warnings:
Note 1003 select `test`.`t1`.`s1` AS `s1`,(not(<in_optimizer>(`test`.`t1`.`s1`,<exists>(<index_lookup>(<cache>(`test`.`t1`.`s1`) in t2 on s1 checking NULL where (`test`.`t2`.`s1` < _latin1'a2')))))) AS `s1 NOT IN (SELECT s1 FROM t2 WHERE s1 < 'a2')` from `test`.`t1` Note 1003 select `test`.`t1`.`s1` AS `s1`,(not(<in_optimizer>(`test`.`t1`.`s1`,<exists>(<index_lookup>(<cache>(`test`.`t1`.`s1`) in t2 on s1 checking NULL where (`test`.`t2`.`s1` < _latin1'a2') having trigcond(<is_not_null_test>(`test`.`t2`.`s1`))))))) AS `s1 NOT IN (SELECT s1 FROM t2 WHERE s1 < 'a2')` from `test`.`t1`
drop table t1,t2; drop table t1,t2;
create table t2 (a int, b int); create table t2 (a int, b int);
create table t3 (a int); create table t3 (a int);
...@@ -2816,19 +2816,19 @@ id select_type table type possible_keys key key_len ref rows filtered Extra ...@@ -2816,19 +2816,19 @@ id select_type table type possible_keys key key_len ref rows filtered Extra
1 PRIMARY t1 ALL NULL NULL NULL NULL 8 100.00 1 PRIMARY t1 ALL NULL NULL NULL NULL 8 100.00
2 DEPENDENT SUBQUERY t2 ALL NULL NULL NULL NULL 9 100.00 Using where 2 DEPENDENT SUBQUERY t2 ALL NULL NULL NULL NULL 9 100.00 Using where
Warnings: Warnings:
Note 1003 select `test`.`t1`.`one` AS `one`,`test`.`t1`.`two` AS `two`,<in_optimizer>((`test`.`t1`.`one`,`test`.`t1`.`two`),<exists>(select `test`.`t2`.`one` AS `one`,`test`.`t2`.`two` AS `two` from `test`.`t2` where ((`test`.`t2`.`flag` = _latin1'0') and trigcond((((<cache>(`test`.`t1`.`one`) = `test`.`t2`.`one`) or isnull(`test`.`t2`.`one`)) and ((<cache>(`test`.`t1`.`two`) = `test`.`t2`.`two`) or isnull(`test`.`t2`.`two`))))) having trigcond((<is_not_null_test>(`test`.`t2`.`one`) and <is_not_null_test>(`test`.`t2`.`two`))))) AS `test` from `test`.`t1` Note 1003 select `test`.`t1`.`one` AS `one`,`test`.`t1`.`two` AS `two`,<in_optimizer>((`test`.`t1`.`one`,`test`.`t1`.`two`),<exists>(select `test`.`t2`.`one` AS `one`,`test`.`t2`.`two` AS `two` from `test`.`t2` where ((`test`.`t2`.`flag` = _latin1'0') and trigcond(((<cache>(`test`.`t1`.`one`) = `test`.`t2`.`one`) or isnull(`test`.`t2`.`one`))) and trigcond(((<cache>(`test`.`t1`.`two`) = `test`.`t2`.`two`) or isnull(`test`.`t2`.`two`)))) having (trigcond(<is_not_null_test>(`test`.`t2`.`one`)) and trigcond(<is_not_null_test>(`test`.`t2`.`two`))))) AS `test` from `test`.`t1`
explain extended SELECT one,two from t1 where ROW(one,two) IN (SELECT one,two FROM t2 WHERE flag = 'N'); explain extended SELECT one,two from t1 where ROW(one,two) IN (SELECT one,two FROM t2 WHERE flag = 'N');
id select_type table type possible_keys key key_len ref rows filtered Extra id select_type table type possible_keys key key_len ref rows filtered Extra
1 PRIMARY t1 ALL NULL NULL NULL NULL 8 100.00 Using where 1 PRIMARY t1 ALL NULL NULL NULL NULL 8 100.00 Using where
2 DEPENDENT SUBQUERY t2 ALL NULL NULL NULL NULL 9 100.00 Using where 2 DEPENDENT SUBQUERY t2 ALL NULL NULL NULL NULL 9 100.00 Using where
Warnings: Warnings:
Note 1003 select `test`.`t1`.`one` AS `one`,`test`.`t1`.`two` AS `two` from `test`.`t1` where <in_optimizer>((`test`.`t1`.`one`,`test`.`t1`.`two`),<exists>(select `test`.`t2`.`one` AS `one`,`test`.`t2`.`two` AS `two` from `test`.`t2` where ((`test`.`t2`.`flag` = _latin1'N') and trigcond(((<cache>(`test`.`t1`.`one`) = `test`.`t2`.`one`) and (<cache>(`test`.`t1`.`two`) = `test`.`t2`.`two`)))))) Note 1003 select `test`.`t1`.`one` AS `one`,`test`.`t1`.`two` AS `two` from `test`.`t1` where <in_optimizer>((`test`.`t1`.`one`,`test`.`t1`.`two`),<exists>(select `test`.`t2`.`one` AS `one`,`test`.`t2`.`two` AS `two` from `test`.`t2` where ((`test`.`t2`.`flag` = _latin1'N') and (<cache>(`test`.`t1`.`one`) = `test`.`t2`.`one`) and (<cache>(`test`.`t1`.`two`) = `test`.`t2`.`two`))))
explain extended SELECT one,two,ROW(one,two) IN (SELECT one,two FROM t2 WHERE flag = '0' group by one,two) as 'test' from t1; explain extended SELECT one,two,ROW(one,two) IN (SELECT one,two FROM t2 WHERE flag = '0' group by one,two) as 'test' from t1;
id select_type table type possible_keys key key_len ref rows filtered Extra id select_type table type possible_keys key key_len ref rows filtered Extra
1 PRIMARY t1 ALL NULL NULL NULL NULL 8 100.00 1 PRIMARY t1 ALL NULL NULL NULL NULL 8 100.00
2 DEPENDENT SUBQUERY t2 ALL NULL NULL NULL NULL 9 100.00 Using where; Using temporary; Using filesort 2 DEPENDENT SUBQUERY t2 ALL NULL NULL NULL NULL 9 100.00 Using where; Using temporary; Using filesort
Warnings: Warnings:
Note 1003 select `test`.`t1`.`one` AS `one`,`test`.`t1`.`two` AS `two`,<in_optimizer>((`test`.`t1`.`one`,`test`.`t1`.`two`),<exists>(select `test`.`t2`.`one` AS `one`,`test`.`t2`.`two` AS `two` from `test`.`t2` where (`test`.`t2`.`flag` = _latin1'0') group by `test`.`t2`.`one`,`test`.`t2`.`two` having trigcond((((<cache>(`test`.`t1`.`one`) = `test`.`t2`.`one`) or isnull(`test`.`t2`.`one`)) and ((<cache>(`test`.`t1`.`two`) = `test`.`t2`.`two`) or isnull(`test`.`t2`.`two`)) and <is_not_null_test>(`test`.`t2`.`one`) and <is_not_null_test>(`test`.`t2`.`two`))))) AS `test` from `test`.`t1` Note 1003 select `test`.`t1`.`one` AS `one`,`test`.`t1`.`two` AS `two`,<in_optimizer>((`test`.`t1`.`one`,`test`.`t1`.`two`),<exists>(select `test`.`t2`.`one` AS `one`,`test`.`t2`.`two` AS `two` from `test`.`t2` where (`test`.`t2`.`flag` = _latin1'0') group by `test`.`t2`.`one`,`test`.`t2`.`two` having (trigcond(((<cache>(`test`.`t1`.`one`) = `test`.`t2`.`one`) or isnull(`test`.`t2`.`one`))) and trigcond(((<cache>(`test`.`t1`.`two`) = `test`.`t2`.`two`) or isnull(`test`.`t2`.`two`))) and trigcond(<is_not_null_test>(`test`.`t2`.`one`)) and trigcond(<is_not_null_test>(`test`.`t2`.`two`))))) AS `test` from `test`.`t1`
DROP TABLE t1,t2; DROP TABLE t1,t2;
CREATE TABLE t1 (a char(5), b char(5)); CREATE TABLE t1 (a char(5), b char(5));
INSERT INTO t1 VALUES (NULL,'aaa'), ('aaa','aaa'); INSERT INTO t1 VALUES (NULL,'aaa'), ('aaa','aaa');
...@@ -3009,7 +3009,7 @@ INSERT INTO t2 VALUES (1),(2),(3); ...@@ -3009,7 +3009,7 @@ INSERT INTO t2 VALUES (1),(2),(3);
EXPLAIN SELECT a, a IN (SELECT a FROM t1) FROM t2; EXPLAIN SELECT a, a IN (SELECT a FROM t1) FROM t2;
id select_type table type possible_keys key key_len ref rows Extra id select_type table type possible_keys key key_len ref rows Extra
1 PRIMARY t2 ALL NULL NULL NULL NULL 3 1 PRIMARY t2 ALL NULL NULL NULL NULL 3
2 DEPENDENT SUBQUERY t1 index_subquery a a 5 func 2 Using index 2 DEPENDENT SUBQUERY t1 index_subquery a a 5 func 2 Using index; Full scan on NULL key
SELECT a, a IN (SELECT a FROM t1) FROM t2; SELECT a, a IN (SELECT a FROM t1) FROM t2;
a a IN (SELECT a FROM t1) a a IN (SELECT a FROM t1)
1 1 1 1
......
...@@ -126,11 +126,11 @@ id select_type table type possible_keys key key_len ref rows Extra ...@@ -126,11 +126,11 @@ id select_type table type possible_keys key key_len ref rows Extra
1 PRIMARY t2 ALL DDOCTYPEID_IDX NULL NULL NULL 9 Using where 1 PRIMARY t2 ALL DDOCTYPEID_IDX NULL NULL NULL 9 Using where
1 PRIMARY t1 eq_ref PRIMARY PRIMARY 34 test.t2.DOCID 1 1 PRIMARY t1 eq_ref PRIMARY PRIMARY 34 test.t2.DOCID 1
1 PRIMARY t4 eq_ref PRIMARY PRIMARY 34 test.t2.DOCTYPEID 1 1 PRIMARY t4 eq_ref PRIMARY PRIMARY 34 test.t2.DOCTYPEID 1
2 DEPENDENT SUBQUERY t3 unique_subquery PRIMARY,FFOLDERID_IDX PRIMARY 34 func 1 Using index; Using where 2 DEPENDENT SUBQUERY t3 unique_subquery PRIMARY,FFOLDERID_IDX PRIMARY 34 func 1 Using where
3 DEPENDENT SUBQUERY t3 unique_subquery PRIMARY,FFOLDERID_IDX PRIMARY 34 func 1 Using index; Using where 3 DEPENDENT SUBQUERY t3 unique_subquery PRIMARY,FFOLDERID_IDX PRIMARY 34 func 1 Using where
4 DEPENDENT SUBQUERY t3 unique_subquery PRIMARY,FFOLDERID_IDX PRIMARY 34 func 1 Using index; Using where 4 DEPENDENT SUBQUERY t3 unique_subquery PRIMARY,FFOLDERID_IDX PRIMARY 34 func 1 Using where
5 DEPENDENT SUBQUERY t3 unique_subquery PRIMARY,FFOLDERID_IDX PRIMARY 34 func 1 Using index; Using where 5 DEPENDENT SUBQUERY t3 unique_subquery PRIMARY,FFOLDERID_IDX PRIMARY 34 func 1 Using where
6 DEPENDENT SUBQUERY t3 unique_subquery PRIMARY,FFOLDERID_IDX,CMFLDRPARNT_IDX PRIMARY 34 func 1 Using index; Using where 6 DEPENDENT SUBQUERY t3 unique_subquery PRIMARY,FFOLDERID_IDX,CMFLDRPARNT_IDX PRIMARY 34 func 1 Using where
drop table t1, t2, t3, t4; drop table t1, t2, t3, t4;
CREATE TABLE t1 (a int(10) , PRIMARY KEY (a)) Engine=InnoDB; CREATE TABLE t1 (a int(10) , PRIMARY KEY (a)) Engine=InnoDB;
INSERT INTO t1 VALUES (1),(2); INSERT INTO t1 VALUES (1),(2);
......
...@@ -15,9 +15,8 @@ insert into t2 values ...@@ -15,9 +15,8 @@ insert into t2 values
(4, NULL), (4, NULL),
(2, NULL); (2, NULL);
select a, oref, a in (select max(ie) select a, oref, a in (select max(ie)
from t1 where oref=t2.oref group by grp) from t2; from t1 where oref=t2.oref group by grp) Z from t2;
a oref a in (select max(ie) a oref Z
from t1 where oref=t2.oref group by grp)
1 1 1 1 1 1
2 2 0 2 2 0
3 3 NULL 3 3 NULL
...@@ -25,14 +24,13 @@ NULL 4 0 ...@@ -25,14 +24,13 @@ NULL 4 0
NULL 2 NULL NULL 2 NULL
explain extended explain extended
select a, oref, a in (select max(ie) select a, oref, a in (select max(ie)
from t1 where oref=t2.oref group by grp) from t2; from t1 where oref=t2.oref group by grp) Z from t2;
id select_type table type possible_keys key key_len ref rows filtered Extra id select_type table type possible_keys key key_len ref rows filtered Extra
1 PRIMARY t2 ALL NULL NULL NULL NULL 5 100.00 1 PRIMARY t2 ALL NULL NULL NULL NULL 5 100.00
2 DEPENDENT SUBQUERY t1 ALL NULL NULL NULL NULL 6 100.00 Using where; Using temporary; Using filesort 2 DEPENDENT SUBQUERY t1 ALL NULL NULL NULL NULL 6 100.00 Using where; Using temporary; Using filesort
Warnings: Warnings:
Note 1276 Field or reference 't2.oref' of SELECT #2 was resolved in SELECT #1 Note 1276 Field or reference 't2.oref' of SELECT #2 was resolved in SELECT #1
Note 1003 select `test`.`t2`.`a` AS `a`,`test`.`t2`.`oref` AS `oref`,<in_optimizer>(`test`.`t2`.`a`,<exists>(select max(`test`.`t1`.`ie`) AS `max(ie)` from `test`.`t1` where (`test`.`t1`.`oref` = `test`.`t2`.`oref`) group by `test`.`t1`.`grp` having trigcond((<cache>(`test`.`t2`.`a`) = <ref_null_helper>(max(`test`.`t1`.`ie`)))))) AS `a in (select max(ie) Note 1003 select `test`.`t2`.`a` AS `a`,`test`.`t2`.`oref` AS `oref`,<in_optimizer>(`test`.`t2`.`a`,<exists>(select max(`test`.`t1`.`ie`) AS `max(ie)` from `test`.`t1` where (`test`.`t1`.`oref` = `test`.`t2`.`oref`) group by `test`.`t1`.`grp` having trigcond((<cache>(`test`.`t2`.`a`) = <ref_null_helper>(max(`test`.`t1`.`ie`)))))) AS `Z` from `test`.`t2`
from t1 where oref=t2.oref group by grp)` from `test`.`t2`
explain extended explain extended
select a, oref from t2 select a, oref from t2
where a in (select max(ie) from t1 where oref=t2.oref group by grp); where a in (select max(ie) from t1 where oref=t2.oref group by grp);
...@@ -42,6 +40,16 @@ id select_type table type possible_keys key key_len ref rows filtered Extra ...@@ -42,6 +40,16 @@ id select_type table type possible_keys key key_len ref rows filtered Extra
Warnings: Warnings:
Note 1276 Field or reference 't2.oref' of SELECT #2 was resolved in SELECT #1 Note 1276 Field or reference 't2.oref' of SELECT #2 was resolved in SELECT #1
Note 1003 select `test`.`t2`.`a` AS `a`,`test`.`t2`.`oref` AS `oref` from `test`.`t2` where <in_optimizer>(`test`.`t2`.`a`,<exists>(select max(`test`.`t1`.`ie`) AS `max(ie)` from `test`.`t1` where (`test`.`t1`.`oref` = `test`.`t2`.`oref`) group by `test`.`t1`.`grp` having (<cache>(`test`.`t2`.`a`) = <ref_null_helper>(max(`test`.`t1`.`ie`))))) Note 1003 select `test`.`t2`.`a` AS `a`,`test`.`t2`.`oref` AS `oref` from `test`.`t2` where <in_optimizer>(`test`.`t2`.`a`,<exists>(select max(`test`.`t1`.`ie`) AS `max(ie)` from `test`.`t1` where (`test`.`t1`.`oref` = `test`.`t2`.`oref`) group by `test`.`t1`.`grp` having (<cache>(`test`.`t2`.`a`) = <ref_null_helper>(max(`test`.`t1`.`ie`)))))
select a, oref, a in (
select max(ie) from t1 where oref=t2.oref group by grp union
select max(ie) from t1 where oref=t2.oref group by grp
) Z from t2;
a oref Z
1 1 1
2 2 0
3 3 NULL
NULL 4 0
NULL 2 NULL
create table t3 (a int); create table t3 (a int);
insert into t3 values (NULL), (NULL); insert into t3 values (NULL), (NULL);
flush status; flush status;
...@@ -79,12 +87,12 @@ oref a Z ...@@ -79,12 +87,12 @@ oref a Z
4 NULL 0 4 NULL 0
explain extended explain extended
select oref, a, a in (select a from t1 where oref=t2.oref) Z from t2; select oref, a, a in (select a from t1 where oref=t2.oref) Z from t2;
id select_type table type possible_keys key key_len ref rows filtered Extra id select_type table type possible_keys key key_len ref rows Extra
1 PRIMARY t2 ALL NULL NULL NULL NULL 4 100.00 1 PRIMARY t2 ALL NULL NULL NULL NULL 4
2 DEPENDENT SUBQUERY t1 index_subquery a a 5 func 2 100.00 Using index; Using where 2 DEPENDENT SUBQUERY t1 index_subquery a a 5 func 2 Using where; Full scan on NULL key
Warnings: Warnings:
Note 1276 Field or reference 't2.oref' of SELECT #2 was resolved in SELECT #1 Note 1276 Field or reference 't2.oref' of SELECT #2 was resolved in SELECT #1
Note 1003 select `test`.`t2`.`oref` AS `oref`,`test`.`t2`.`a` AS `a`,<in_optimizer>(`test`.`t2`.`a`,<exists>(<index_lookup>(<cache>(`test`.`t2`.`a`) in t1 on a checking NULL where (`test`.`t1`.`oref` = `test`.`t2`.`oref`)))) AS `Z` from `test`.`t2` Note 1003 select `test`.`t2`.`oref` AS `oref`,`test`.`t2`.`a` AS `a`,<in_optimizer>(`test`.`t2`.`a`,<exists>(<index_lookup>(<cache>(`test`.`t2`.`a`) in t1 on a checking NULL where (`test`.`t1`.`oref` = `test`.`t2`.`oref`) having trigcond(<is_not_null_test>(`test`.`t1`.`a`))))) AS `Z` from `test`.`t2`
flush status; flush status;
select oref, a from t2 where a in (select a from t1 where oref=t2.oref); select oref, a from t2 where a in (select a from t1 where oref=t2.oref);
oref a oref a
...@@ -143,11 +151,480 @@ explain extended ...@@ -143,11 +151,480 @@ explain extended
select a, oref, select a, oref,
t3.a in (select t1.a from t1, t2 where t1.b=t2.a and t2.b=t3.oref) Z t3.a in (select t1.a from t1, t2 where t1.b=t2.a and t2.b=t3.oref) Z
from t3; from t3;
id select_type table type possible_keys key key_len ref rows filtered Extra id select_type table type possible_keys key key_len ref rows Extra
1 PRIMARY t3 ALL NULL NULL NULL NULL 3 100.00 1 PRIMARY t3 ALL NULL NULL NULL NULL 3
2 DEPENDENT SUBQUERY t1 ref_or_null a a 5 func 4 100.00 Using where 2 DEPENDENT SUBQUERY t2 ref a a 5 test.t1.b 1 Using where
2 DEPENDENT SUBQUERY t2 ref a a 5 test.t1.b 1 100.00 Using where
Warnings: Warnings:
Note 1276 Field or reference 't3.oref' of SELECT #2 was resolved in SELECT #1 Note 1276 Field or reference 't3.oref' of SELECT #2 was resolved in SELECT #1
Note 1003 select `test`.`t3`.`a` AS `a`,`test`.`t3`.`oref` AS `oref`,<in_optimizer>(`test`.`t3`.`a`,<exists>(select 1 AS `Not_used` from `test`.`t1` join `test`.`t2` where ((`test`.`t2`.`a` = `test`.`t1`.`b`) and (`test`.`t2`.`b` = `test`.`t3`.`oref`) and trigcond(((<cache>(`test`.`t3`.`a`) = `test`.`t1`.`a`) or isnull(`test`.`t1`.`a`)))) having trigcond(<is_not_null_test>(`test`.`t1`.`a`)))) AS `Z` from `test`.`t3` Note 1003 select `test`.`t3`.`a` AS `a`,`test`.`t3`.`oref` AS `oref`,<in_optimizer>(`test`.`t3`.`a`,<exists>(select 1 AS `Not_used` from `test`.`t1` join `test`.`t2` where ((`test`.`t2`.`a` = `test`.`t1`.`b`) and (`test`.`t2`.`b` = `test`.`t3`.`oref`) and trigcond(((<cache>(`test`.`t3`.`a`) = `test`.`t1`.`a`) or isnull(`test`.`t1`.`a`)))) having trigcond(<is_not_null_test>(`test`.`t1`.`a`)))) AS `Z` from `test`.`t3`
drop table t1, t2, t3; drop table t1, t2, t3;
create table t1 (a int NOT NULL, b int NOT NULL, key(a));
insert into t1 values
(0,0),(1,1),(2,2),(3,3),(4,4),(5,5),(6,6),(7,7),(8,8),(9,9);
create table t2 like t1;
insert into t2 select * from t1;
update t2 set b=1;
create table t3 (a int, oref int);
insert into t3 values (1, 1), (NULL,1), (NULL,0);
select a, oref,
t3.a in (select t1.a from t1, t2 where t1.b=t2.a and t2.b=t3.oref) Z
from t3;
a oref Z
1 1 1
NULL 1 NULL
NULL 0 0
This must show a trig_cond:
explain extended
select a, oref,
t3.a in (select t1.a from t1, t2 where t1.b=t2.a and t2.b=t3.oref) Z
from t3;
id select_type table type possible_keys key key_len ref rows Extra
1 PRIMARY t3 ALL NULL NULL NULL NULL 3
2 DEPENDENT SUBQUERY t1 ref a a 4 func 2 Using where; Full scan on NULL key
2 DEPENDENT SUBQUERY t2 ref a a 4 test.t1.b 1 Using where
Warnings:
Note 1276 Field or reference 't3.oref' of SELECT #2 was resolved in SELECT #1
Note 1003 select `test`.`t3`.`a` AS `a`,`test`.`t3`.`oref` AS `oref`,<in_optimizer>(`test`.`t3`.`a`,<exists>(select 1 AS `Not_used` from `test`.`t1` join `test`.`t2` where ((`test`.`t2`.`a` = `test`.`t1`.`b`) and (`test`.`t2`.`b` = `test`.`t3`.`oref`) and trigcond((<cache>(`test`.`t3`.`a`) = `test`.`t1`.`a`))))) AS `Z` from `test`.`t3`
drop table t1,t2,t3;
create table t1 (oref int, grp int);
insert into t1 (oref, grp) values
(1, 1),
(1, 1);
create table t2 (oref int, a int);
insert into t2 values
(1, NULL),
(2, NULL);
select a, oref,
a in (select count(*) from t1 group by grp having grp=t2.oref) Z from t2;
a oref Z
NULL 1 NULL
NULL 2 0
This must show a trig_cond:
explain extended
select a, oref,
a in (select count(*) from t1 group by grp having grp=t2.oref) Z from t2;
id select_type table type possible_keys key key_len ref rows Extra
1 PRIMARY t2 ALL NULL NULL NULL NULL 2
2 DEPENDENT SUBQUERY t1 ALL NULL NULL NULL NULL 2 Using temporary; Using filesort
Warnings:
Note 1276 Field or reference 't2.oref' of SELECT #2 was resolved in SELECT #1
Note 1003 select `test`.`t2`.`a` AS `a`,`test`.`t2`.`oref` AS `oref`,<in_optimizer>(`test`.`t2`.`a`,<exists>(select count(0) AS `count(*)` from `test`.`t1` group by `test`.`t1`.`grp` having ((`test`.`t1`.`grp` = `test`.`t2`.`oref`) and trigcond((<cache>(`test`.`t2`.`a`) = <ref_null_helper>(count(0))))))) AS `Z` from `test`.`t2`
drop table t1, t2;
create table t1 (a int, b int, primary key (a));
insert into t1 values (1,1), (3,1),(100,1);
create table t2 (a int, b int);
insert into t2 values (1,1),(2,1),(NULL,1),(NULL,0);
select a,b, a in (select a from t1 where t1.b = t2.b union select a from
t1 where t1.b = t2.b) Z from t2 ;
a b Z
1 1 1
2 1 0
NULL 1 NULL
NULL 0 0
select a,b, a in (select a from t1 where t1.b = t2.b) Z from t2 ;
a b Z
1 1 1
2 1 0
NULL 1 NULL
NULL 0 0
drop table t1, t2;
create table t3 (a int);
insert into t3 values (0),(1),(2),(3),(4),(5),(6),(7),(8),(9);
create table t2 (a int, b int, oref int);
insert into t2 values (NULL,1, 100), (NULL,2, 100);
create table t1 (a int, b int, c int, key(a,b));
insert into t1 select 2*A, 2*A, 100 from t3;
explain extended select a,b, oref, (a,b) in (select a,b from t1 where c=t2.oref) Z from t2;
id select_type table type possible_keys key key_len ref rows Extra
1 PRIMARY t2 ALL NULL NULL NULL NULL 2
2 DEPENDENT SUBQUERY t1 index_subquery a a 5 func 2 Using where; Full scan on NULL key
Warnings:
Note 1276 Field or reference 't2.oref' of SELECT #2 was resolved in SELECT #1
Note 1003 select `test`.`t2`.`a` AS `a`,`test`.`t2`.`b` AS `b`,`test`.`t2`.`oref` AS `oref`,<in_optimizer>((`test`.`t2`.`a`,`test`.`t2`.`b`),<exists>(<index_lookup>(<cache>(`test`.`t2`.`a`) in t1 on a checking NULL where ((`test`.`t1`.`c` = `test`.`t2`.`oref`) and trigcond(((<cache>(`test`.`t2`.`a`) = `test`.`t1`.`a`) or isnull(`test`.`t1`.`a`))) and trigcond(((<cache>(`test`.`t2`.`b`) = `test`.`t1`.`b`) or isnull(`test`.`t1`.`b`)))) having (trigcond(<is_not_null_test>(`test`.`t1`.`a`)) and trigcond(<is_not_null_test>(`test`.`t1`.`b`)))))) AS `Z` from `test`.`t2`
select a,b, oref, (a,b) in (select a,b from t1 where c=t2.oref) Z from t2;
a b oref Z
NULL 1 100 0
NULL 2 100 NULL
create table t4 (x int);
insert into t4 select A.a + 10*B.a from t1 A, t1 B;
explain extended
select a,b, oref,
(a,b) in (select a,b from t1,t4 where c=t2.oref) Z
from t2;
id select_type table type possible_keys key key_len ref rows Extra
1 PRIMARY t2 ALL NULL NULL NULL NULL 2
2 DEPENDENT SUBQUERY t1 ref_or_null a a 5 func 2 Using where; Full scan on NULL key
2 DEPENDENT SUBQUERY t4 ALL NULL NULL NULL NULL 100 Using where
Warnings:
Note 1276 Field or reference 't2.oref' of SELECT #2 was resolved in SELECT #1
Note 1003 select `test`.`t2`.`a` AS `a`,`test`.`t2`.`b` AS `b`,`test`.`t2`.`oref` AS `oref`,<in_optimizer>((`test`.`t2`.`a`,`test`.`t2`.`b`),<exists>(select `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b` from `test`.`t1` join `test`.`t4` where ((`test`.`t1`.`c` = `test`.`t2`.`oref`) and trigcond(((<cache>(`test`.`t2`.`a`) = `test`.`t1`.`a`) or isnull(`test`.`t1`.`a`))) and trigcond(((<cache>(`test`.`t2`.`b`) = `test`.`t1`.`b`) or isnull(`test`.`t1`.`b`)))) having (trigcond(<is_not_null_test>(`test`.`t1`.`a`)) and trigcond(<is_not_null_test>(`test`.`t1`.`b`))))) AS `Z` from `test`.`t2`
select a,b, oref,
(a,b) in (select a,b from t1,t4 where c=t2.oref) Z
from t2;
a b oref Z
NULL 1 100 0
NULL 2 100 NULL
drop table t1,t2,t3,t4;
create table t1 (oref char(4), grp int, ie1 int, ie2 int);
insert into t1 (oref, grp, ie1, ie2) values
('aa', 10, 2, 1),
('aa', 10, 1, 1),
('aa', 20, 2, 1),
('bb', 10, 3, 1),
('cc', 10, 4, 2),
('cc', 20, 3, 2),
('ee', 10, 2, 1),
('ee', 10, 1, 2),
('ff', 20, 2, 2),
('ff', 20, 1, 2);
create table t2 (oref char(4), a int, b int);
insert into t2 values
('ee', NULL, 1),
('bb', 2, 1),
('ff', 2, 2),
('cc', 3, NULL),
('bb', NULL, NULL),
('aa', 1, 1),
('dd', 1, NULL);
alter table t1 add index idx(ie1,ie2);
select oref, a, b, (a,b) in (select ie1,ie2 from t1 where oref=t2.oref) Z from t2 where a=3 and b is null ;
oref a b Z
cc 3 NULL NULL
insert into t2 values ('new1', 10,10);
insert into t1 values ('new1', 1234, 10, NULL);
select oref, a, b, (a,b) in (select ie1,ie2 from t1 where oref=t2.oref) Z from t2 where a=10 and b=10;
oref a b Z
new1 10 10 NULL
explain extended
select oref, a, b, (a,b) in (select ie1,ie2 from t1 where oref=t2.oref) Z from t2 where a=10 and b=10;
id select_type table type possible_keys key key_len ref rows Extra
1 PRIMARY t2 ALL NULL NULL NULL NULL 8 Using where
2 DEPENDENT SUBQUERY t1 index_subquery idx idx 5 func 4 Using where; Full scan on NULL key
Warnings:
Note 1276 Field or reference 't2.oref' of SELECT #2 was resolved in SELECT #1
Note 1003 select `test`.`t2`.`oref` AS `oref`,`test`.`t2`.`a` AS `a`,`test`.`t2`.`b` AS `b`,<in_optimizer>((`test`.`t2`.`a`,`test`.`t2`.`b`),<exists>(<index_lookup>(<cache>(`test`.`t2`.`a`) in t1 on idx checking NULL where ((`test`.`t1`.`oref` = `test`.`t2`.`oref`) and trigcond(((<cache>(`test`.`t2`.`a`) = `test`.`t1`.`ie1`) or isnull(`test`.`t1`.`ie1`))) and trigcond(((<cache>(`test`.`t2`.`b`) = `test`.`t1`.`ie2`) or isnull(`test`.`t1`.`ie2`)))) having (trigcond(<is_not_null_test>(`test`.`t1`.`ie1`)) and trigcond(<is_not_null_test>(`test`.`t1`.`ie2`)))))) AS `Z` from `test`.`t2` where ((`test`.`t2`.`b` = 10) and (`test`.`t2`.`a` = 10))
drop table t1, t2;
create table t1 (oref char(4), grp int, ie int);
insert into t1 (oref, grp, ie) values
('aa', 10, 2),
('aa', 10, 1),
('aa', 20, NULL),
('bb', 10, 3),
('cc', 10, 4),
('cc', 20, NULL),
('ee', 10, NULL),
('ee', 10, NULL),
('ff', 20, 2),
('ff', 20, 1);
create table t2 (oref char(4), a int);
insert into t2 values
('ee', NULL),
('bb', 2),
('ff', 2),
('cc', 3),
('aa', 1),
('dd', NULL),
('bb', NULL);
select oref, a, a in (select ie from t1 where oref=t2.oref) Z from t2;
oref a Z
ee NULL NULL
bb 2 0
ff 2 1
cc 3 NULL
aa 1 1
dd NULL 0
bb NULL NULL
select oref, a from t2 where a in (select ie from t1 where oref=t2.oref);
oref a
ff 2
aa 1
select oref, a from t2 where a not in (select ie from t1 where oref=t2.oref);
oref a
bb 2
dd NULL
select oref, a, a in (select min(ie) from t1 where oref=t2.oref group by grp) Z from t2;
oref a Z
ee NULL NULL
bb 2 0
ff 2 0
cc 3 NULL
aa 1 1
dd NULL 0
bb NULL NULL
select oref, a from t2 where
a in (select min(ie) from t1 where oref=t2.oref group by grp);
oref a
aa 1
select oref, a from t2 where
a not in (select min(ie) from t1 where oref=t2.oref group by grp);
oref a
bb 2
ff 2
dd NULL
update t1 set ie=3 where oref='ff' and ie=1;
select oref, a, a in (select min(ie) from t1 where oref=t2.oref group by
grp) Z from t2;
oref a Z
ee NULL NULL
bb 2 0
ff 2 1
cc 3 NULL
aa 1 1
dd NULL 0
bb NULL NULL
select oref, a from t2 where a in (select min(ie) from t1 where
oref=t2.oref group by grp);
oref a
ff 2
aa 1
select oref, a from t2 where a not in (select min(ie) from t1 where
oref=t2.oref group by grp);
oref a
bb 2
dd NULL
select oref, a, a in (select min(ie) from t1 where oref=t2.oref group by
grp having min(ie) > 1) Z from t2;
oref a Z
ee NULL 0
bb 2 0
ff 2 1
cc 3 0
aa 1 0
dd NULL 0
bb NULL NULL
select oref, a from t2 where a in (select min(ie) from t1 where
oref=t2.oref group by grp having min(ie) > 1);
oref a
ff 2
select oref, a from t2 where a not in (select min(ie) from t1 where
oref=t2.oref group by grp having min(ie) > 1);
oref a
ee NULL
bb 2
cc 3
aa 1
dd NULL
alter table t1 add index idx(ie);
explain select oref, a, a in (select ie from t1 where oref=t2.oref) Z from t2;
id select_type table type possible_keys key key_len ref rows Extra
1 PRIMARY t2 ALL NULL NULL NULL NULL 7
2 DEPENDENT SUBQUERY t1 index_subquery idx idx 5 func 4 Using where; Full scan on NULL key
select oref, a, a in (select ie from t1 where oref=t2.oref) Z from t2;
oref a Z
ee NULL NULL
bb 2 0
ff 2 1
cc 3 NULL
aa 1 1
dd NULL 0
bb NULL NULL
select oref, a from t2 where a in (select ie from t1 where oref=t2.oref);
oref a
ff 2
aa 1
select oref, a from t2 where a not in (select ie from t1 where oref=t2.oref);
oref a
bb 2
dd NULL
alter table t1 drop index idx;
alter table t1 add index idx(oref,ie);
explain select oref, a, a in (select ie from t1 where oref=t2.oref) Z from t2;
id select_type table type possible_keys key key_len ref rows Extra
1 PRIMARY t2 ALL NULL NULL NULL NULL 7
2 DEPENDENT SUBQUERY t1 ref_or_null idx idx 10 test.t2.oref,func 4 Using where; Using index; Full scan on NULL key
select oref, a, a in (select ie from t1 where oref=t2.oref) Z from t2;
oref a Z
ee NULL NULL
bb 2 0
ff 2 1
cc 3 NULL
aa 1 1
dd NULL 0
bb NULL NULL
select oref, a from t2 where a in (select ie from t1 where oref=t2.oref);
oref a
ff 2
aa 1
select oref, a from t2 where a not in (select ie from t1 where oref=t2.oref);
oref a
bb 2
dd NULL
explain
select oref, a,
a in (select min(ie) from t1 where oref=t2.oref
group by grp having min(ie) > 1) Z
from t2;
id select_type table type possible_keys key key_len ref rows Extra
1 PRIMARY t2 ALL NULL NULL NULL NULL 7
2 DEPENDENT SUBQUERY t1 ref idx idx 5 test.t2.oref 2 Using where; Using temporary; Using filesort
select oref, a,
a in (select min(ie) from t1 where oref=t2.oref
group by grp having min(ie) > 1) Z
from t2;
oref a Z
ee NULL 0
bb 2 0
ff 2 1
cc 3 0
aa 1 0
dd NULL 0
bb NULL NULL
select oref, a from t2 where a in (select min(ie) from t1 where oref=t2.oref
group by grp having min(ie) > 1);
oref a
ff 2
select oref, a from t2 where a not in (select min(ie) from t1 where oref=t2.oref
group by grp having min(ie) > 1);
oref a
ee NULL
bb 2
cc 3
aa 1
dd NULL
drop table t1,t2;
create table t1 (oref char(4), grp int, ie1 int, ie2 int);
insert into t1 (oref, grp, ie1, ie2) values
('aa', 10, 2, 1),
('aa', 10, 1, 1),
('aa', 20, 2, 1),
('bb', 10, 3, 1),
('cc', 10, 4, 2),
('cc', 20, 3, 2),
('ee', 10, 2, 1),
('ee', 10, 1, 2),
('ff', 20, 2, 2),
('ff', 20, 1, 2);
create table t2 (oref char(4), a int, b int);
insert into t2 values
('ee', NULL, 1),
('bb', 2, 1),
('ff', 2, 2),
('cc', 3, NULL),
('bb', NULL, NULL),
('aa', 1, 1),
('dd', 1, NULL);
select oref, a, b, (a,b) in (select ie1,ie2 from t1 where oref=t2.oref) Z from t2;
oref a b Z
ee NULL 1 NULL
bb 2 1 0
ff 2 2 1
cc 3 NULL NULL
bb NULL NULL NULL
aa 1 1 1
dd 1 NULL 0
select oref, a, b from t2 where (a,b) in (select ie1,ie2 from t1 where oref=t2.oref);
oref a b
ff 2 2
aa 1 1
select oref, a, b from t2 where (a,b) not in (select ie1,ie2 from t1 where oref=t2.oref);
oref a b
bb 2 1
dd 1 NULL
select oref, a, b,
(a,b) in (select min(ie1),max(ie2) from t1
where oref=t2.oref group by grp) Z
from t2;
oref a b Z
ee NULL 1 0
bb 2 1 0
ff 2 2 0
cc 3 NULL NULL
bb NULL NULL NULL
aa 1 1 1
dd 1 NULL 0
select oref, a, b from t2 where
(a,b) in (select min(ie1), max(ie2) from t1 where oref=t2.oref group by grp);
oref a b
aa 1 1
select oref, a, b from t2 where
(a,b) not in (select min(ie1), max(ie2) from t1 where oref=t2.oref group by grp);
oref a b
ee NULL 1
bb 2 1
ff 2 2
dd 1 NULL
alter table t1 add index idx(ie1,ie2);
explain select oref, a, b, (a,b) in (select ie1,ie2 from t1 where oref=t2.oref) Z from t2;
id select_type table type possible_keys key key_len ref rows Extra
1 PRIMARY t2 ALL NULL NULL NULL NULL 7
2 DEPENDENT SUBQUERY t1 index_subquery idx idx 5 func 4 Using where; Full scan on NULL key
select oref, a, b, (a,b) in (select ie1,ie2 from t1 where oref=t2.oref) Z from t2;
oref a b Z
ee NULL 1 NULL
bb 2 1 0
ff 2 2 1
cc 3 NULL NULL
bb NULL NULL NULL
aa 1 1 1
dd 1 NULL 0
select oref, a, b from t2 where (a,b) in (select ie1,ie2 from t1 where oref=t2.oref);
oref a b
ff 2 2
aa 1 1
select oref, a, b from t2 where (a,b) not in (select ie1,ie2 from t1 where oref=t2.oref);
oref a b
bb 2 1
dd 1 NULL
explain extended
select oref, a, b, (a,b) in (select ie1,ie2 from t1 where oref=t2.oref) Z from t2;
id select_type table type possible_keys key key_len ref rows Extra
1 PRIMARY t2 ALL NULL NULL NULL NULL 7
2 DEPENDENT SUBQUERY t1 index_subquery idx idx 5 func 4 Using where; Full scan on NULL key
Warnings:
Note 1276 Field or reference 't2.oref' of SELECT #2 was resolved in SELECT #1
Note 1003 select `test`.`t2`.`oref` AS `oref`,`test`.`t2`.`a` AS `a`,`test`.`t2`.`b` AS `b`,<in_optimizer>((`test`.`t2`.`a`,`test`.`t2`.`b`),<exists>(<index_lookup>(<cache>(`test`.`t2`.`a`) in t1 on idx checking NULL where ((`test`.`t1`.`oref` = `test`.`t2`.`oref`) and trigcond(((<cache>(`test`.`t2`.`a`) = `test`.`t1`.`ie1`) or isnull(`test`.`t1`.`ie1`))) and trigcond(((<cache>(`test`.`t2`.`b`) = `test`.`t1`.`ie2`) or isnull(`test`.`t1`.`ie2`)))) having (trigcond(<is_not_null_test>(`test`.`t1`.`ie1`)) and trigcond(<is_not_null_test>(`test`.`t1`.`ie2`)))))) AS `Z` from `test`.`t2`
drop table t1,t2;
create table t1 (oref char(4), grp int, ie int primary key);
insert into t1 (oref, grp, ie) values
('aa', 10, 2),
('aa', 10, 1),
('bb', 10, 3),
('cc', 10, 4),
('cc', 20, 5),
('cc', 10, 6);
create table t2 (oref char(4), a int);
insert into t2 values
('ee', NULL),
('bb', 2),
('cc', 5),
('cc', 2),
('cc', NULL),
('aa', 1),
('bb', NULL);
explain select oref, a, a in (select ie from t1 where oref=t2.oref) Z from t2;
id select_type table type possible_keys key key_len ref rows Extra
1 PRIMARY t2 ALL NULL NULL NULL NULL 7
2 DEPENDENT SUBQUERY t1 unique_subquery PRIMARY PRIMARY 4 func 1 Using where; Full scan on NULL key
select oref, a, a in (select ie from t1 where oref=t2.oref) Z from t2;
oref a Z
ee NULL 0
bb 2 0
cc 5 1
cc 2 0
cc NULL NULL
aa 1 1
bb NULL NULL
select oref, a from t2 where a in (select ie from t1 where oref=t2.oref);
oref a
cc 5
aa 1
select oref, a from t2 where a not in (select ie from t1 where oref=t2.oref);
oref a
ee NULL
bb 2
cc 2
explain
select oref, a, a in (select min(ie) from t1 where oref=t2.oref group by grp) Z from t2;
id select_type table type possible_keys key key_len ref rows Extra
1 PRIMARY t2 ALL NULL NULL NULL NULL 7
2 DEPENDENT SUBQUERY t1 ALL NULL NULL NULL NULL 6 Using where; Using temporary; Using filesort
select oref, a, a in (select min(ie) from t1 where oref=t2.oref group by grp) Z from t2;
oref a Z
ee NULL 0
bb 2 0
cc 5 1
cc 2 0
cc NULL NULL
aa 1 1
bb NULL NULL
drop table t1,t2;
...@@ -16,13 +16,14 @@ insert into t1 (oref, grp, ie) values ...@@ -16,13 +16,14 @@ insert into t1 (oref, grp, ie) values
(3, 1, 4), (3, 1, 4),
(3, 2, NULL); (3, 2, NULL);
# Ok, for # Ok, for
# select max(ie) from t1 where oref=PARAM group by grp # select max(ie) from t1 where oref=PARAM group by grp
# we'll have: # we'll have:
# 1 -> (1, NULL) matching + NULL # PARAM subquery result
# 2 -> (3) non-matching # 1 -> {(1), (NULL)} matching + NULL
# 3 -> (3, NULL) non-matching + NULL # 2 -> {(3)} non-matching
# 4 -> () nothing. # 3 -> {(3), (NULL)} non-matching + NULL
# 4 -> {} empty set
create table t2 (oref int, a int); create table t2 (oref int, a int);
insert into t2 values insert into t2 values
...@@ -34,18 +35,21 @@ insert into t2 values ...@@ -34,18 +35,21 @@ insert into t2 values
# true, false, null, false, null # true, false, null, false, null
select a, oref, a in (select max(ie) select a, oref, a in (select max(ie)
from t1 where oref=t2.oref group by grp) from t2; from t1 where oref=t2.oref group by grp) Z from t2;
# This must have a trigcond # This must have a trigcond
explain extended explain extended
select a, oref, a in (select max(ie) select a, oref, a in (select max(ie)
from t1 where oref=t2.oref group by grp) from t2; from t1 where oref=t2.oref group by grp) Z from t2;
# This must not have a trigcond: # This must not have a trigcond:
explain extended explain extended
select a, oref from t2 select a, oref from t2
where a in (select max(ie) from t1 where oref=t2.oref group by grp); where a in (select max(ie) from t1 where oref=t2.oref group by grp);
select a, oref, a in (
select max(ie) from t1 where oref=t2.oref group by grp union
select max(ie) from t1 where oref=t2.oref group by grp
) Z from t2;
# Non-correlated subquery, 2 NULL evaluations # Non-correlated subquery, 2 NULL evaluations
create table t3 (a int); create table t3 (a int);
...@@ -135,3 +139,336 @@ from t3; ...@@ -135,3 +139,336 @@ from t3;
drop table t1, t2, t3; drop table t1, t2, t3;
#
# BUG#24085: Wrong query result for "NULL IN (SELECT ... UNION SELECT ...)"
#
# case 1: NULL IN (SELECT not_null_val FROM ...) w/o HAVING/GROUP-BY/etc
create table t1 (a int NOT NULL, b int NOT NULL, key(a));
insert into t1 values
(0,0),(1,1),(2,2),(3,3),(4,4),(5,5),(6,6),(7,7),(8,8),(9,9);
create table t2 like t1;
insert into t2 select * from t1;
update t2 set b=1;
create table t3 (a int, oref int);
insert into t3 values (1, 1), (NULL,1), (NULL,0);
select a, oref,
t3.a in (select t1.a from t1, t2 where t1.b=t2.a and t2.b=t3.oref) Z
from t3;
--echo This must show a trig_cond:
explain extended
select a, oref,
t3.a in (select t1.a from t1, t2 where t1.b=t2.a and t2.b=t3.oref) Z
from t3;
drop table t1,t2,t3;
# case 2: NULL IN (SELECT not_null_val FROM) where SELECT has GROUP BY
create table t1 (oref int, grp int);
insert into t1 (oref, grp) values
(1, 1),
(1, 1);
# Ok, for
# select count(*) from t1 group by grp having grp=PARAM
# we'll have:
# PARAM subuqery result
# 1 -> {(2)}
# 2 -> {} - empty set
create table t2 (oref int, a int);
insert into t2 values
(1, NULL),
(2, NULL);
select a, oref,
a in (select count(*) from t1 group by grp having grp=t2.oref) Z from t2;
--echo This must show a trig_cond:
explain extended
select a, oref,
a in (select count(*) from t1 group by grp having grp=t2.oref) Z from t2;
drop table t1, t2;
create table t1 (a int, b int, primary key (a));
insert into t1 values (1,1), (3,1),(100,1);
create table t2 (a int, b int);
insert into t2 values (1,1),(2,1),(NULL,1),(NULL,0);
select a,b, a in (select a from t1 where t1.b = t2.b union select a from
t1 where t1.b = t2.b) Z from t2 ;
select a,b, a in (select a from t1 where t1.b = t2.b) Z from t2 ;
drop table t1, t2;
#
# BUG#24127: Incorrect results of row-based subqueries with NULLs on the left side.
#
create table t3 (a int);
insert into t3 values (0),(1),(2),(3),(4),(5),(6),(7),(8),(9);
create table t2 (a int, b int, oref int);
insert into t2 values (NULL,1, 100), (NULL,2, 100);
create table t1 (a int, b int, c int, key(a,b));
insert into t1 select 2*A, 2*A, 100 from t3;
# First test index subquery engine
explain extended select a,b, oref, (a,b) in (select a,b from t1 where c=t2.oref) Z from t2;
select a,b, oref, (a,b) in (select a,b from t1 where c=t2.oref) Z from t2;
# Then check that we do turn off 'ref' scans in the subquery
create table t4 (x int);
insert into t4 select A.a + 10*B.a from t1 A, t1 B;
explain extended
select a,b, oref,
(a,b) in (select a,b from t1,t4 where c=t2.oref) Z
from t2;
select a,b, oref,
(a,b) in (select a,b from t1,t4 where c=t2.oref) Z
from t2;
drop table t1,t2,t3,t4;
# More tests for tricky multi-column cases, where some of pushed-down
# equalities are used for index lookups and some arent.
create table t1 (oref char(4), grp int, ie1 int, ie2 int);
insert into t1 (oref, grp, ie1, ie2) values
('aa', 10, 2, 1),
('aa', 10, 1, 1),
('aa', 20, 2, 1),
('bb', 10, 3, 1),
('cc', 10, 4, 2),
('cc', 20, 3, 2),
('ee', 10, 2, 1),
('ee', 10, 1, 2),
('ff', 20, 2, 2),
('ff', 20, 1, 2);
create table t2 (oref char(4), a int, b int);
insert into t2 values
('ee', NULL, 1),
('bb', 2, 1),
('ff', 2, 2),
('cc', 3, NULL),
('bb', NULL, NULL),
('aa', 1, 1),
('dd', 1, NULL);
alter table t1 add index idx(ie1,ie2);
--cc 3 NULL NULL
select oref, a, b, (a,b) in (select ie1,ie2 from t1 where oref=t2.oref) Z from t2 where a=3 and b is null ;
insert into t2 values ('new1', 10,10);
insert into t1 values ('new1', 1234, 10, NULL);
-- new1, 10, 10, NULL,
select oref, a, b, (a,b) in (select ie1,ie2 from t1 where oref=t2.oref) Z from t2 where a=10 and b=10;
explain extended
select oref, a, b, (a,b) in (select ie1,ie2 from t1 where oref=t2.oref) Z from t2 where a=10 and b=10;
drop table t1, t2;
# Now test different column types:
create table t1 (oref char(4), grp int, ie int);
insert into t1 (oref, grp, ie) values
('aa', 10, 2),
('aa', 10, 1),
('aa', 20, NULL),
('bb', 10, 3),
('cc', 10, 4),
('cc', 20, NULL),
('ee', 10, NULL),
('ee', 10, NULL),
('ff', 20, 2),
('ff', 20, 1);
create table t2 (oref char(4), a int);
insert into t2 values
('ee', NULL),
('bb', 2),
('ff', 2),
('cc', 3),
('aa', 1),
('dd', NULL),
('bb', NULL);
select oref, a, a in (select ie from t1 where oref=t2.oref) Z from t2;
select oref, a from t2 where a in (select ie from t1 where oref=t2.oref);
select oref, a from t2 where a not in (select ie from t1 where oref=t2.oref);
select oref, a, a in (select min(ie) from t1 where oref=t2.oref group by grp) Z from t2;
select oref, a from t2 where
a in (select min(ie) from t1 where oref=t2.oref group by grp);
select oref, a from t2 where
a not in (select min(ie) from t1 where oref=t2.oref group by grp);
#
update t1 set ie=3 where oref='ff' and ie=1;
select oref, a, a in (select min(ie) from t1 where oref=t2.oref group by
grp) Z from t2;
select oref, a from t2 where a in (select min(ie) from t1 where
oref=t2.oref group by grp);
select oref, a from t2 where a not in (select min(ie) from t1 where
oref=t2.oref group by grp);
select oref, a, a in (select min(ie) from t1 where oref=t2.oref group by
grp having min(ie) > 1) Z from t2;
select oref, a from t2 where a in (select min(ie) from t1 where
oref=t2.oref group by grp having min(ie) > 1);
select oref, a from t2 where a not in (select min(ie) from t1 where
oref=t2.oref group by grp having min(ie) > 1);
#
alter table t1 add index idx(ie);
explain select oref, a, a in (select ie from t1 where oref=t2.oref) Z from t2;
select oref, a, a in (select ie from t1 where oref=t2.oref) Z from t2;
select oref, a from t2 where a in (select ie from t1 where oref=t2.oref);
select oref, a from t2 where a not in (select ie from t1 where oref=t2.oref);
alter table t1 drop index idx;
alter table t1 add index idx(oref,ie);
explain select oref, a, a in (select ie from t1 where oref=t2.oref) Z from t2;
select oref, a, a in (select ie from t1 where oref=t2.oref) Z from t2;
select oref, a from t2 where a in (select ie from t1 where oref=t2.oref);
select oref, a from t2 where a not in (select ie from t1 where oref=t2.oref);
explain
select oref, a,
a in (select min(ie) from t1 where oref=t2.oref
group by grp having min(ie) > 1) Z
from t2;
select oref, a,
a in (select min(ie) from t1 where oref=t2.oref
group by grp having min(ie) > 1) Z
from t2;
select oref, a from t2 where a in (select min(ie) from t1 where oref=t2.oref
group by grp having min(ie) > 1);
select oref, a from t2 where a not in (select min(ie) from t1 where oref=t2.oref
group by grp having min(ie) > 1);
drop table t1,t2;
create table t1 (oref char(4), grp int, ie1 int, ie2 int);
insert into t1 (oref, grp, ie1, ie2) values
('aa', 10, 2, 1),
('aa', 10, 1, 1),
('aa', 20, 2, 1),
('bb', 10, 3, 1),
('cc', 10, 4, 2),
('cc', 20, 3, 2),
('ee', 10, 2, 1),
('ee', 10, 1, 2),
('ff', 20, 2, 2),
('ff', 20, 1, 2);
create table t2 (oref char(4), a int, b int);
insert into t2 values
('ee', NULL, 1),
('bb', 2, 1),
('ff', 2, 2),
('cc', 3, NULL),
('bb', NULL, NULL),
('aa', 1, 1),
('dd', 1, NULL);
select oref, a, b, (a,b) in (select ie1,ie2 from t1 where oref=t2.oref) Z from t2;
select oref, a, b from t2 where (a,b) in (select ie1,ie2 from t1 where oref=t2.oref);
select oref, a, b from t2 where (a,b) not in (select ie1,ie2 from t1 where oref=t2.oref);
select oref, a, b,
(a,b) in (select min(ie1),max(ie2) from t1
where oref=t2.oref group by grp) Z
from t2;
select oref, a, b from t2 where
(a,b) in (select min(ie1), max(ie2) from t1 where oref=t2.oref group by grp);
select oref, a, b from t2 where
(a,b) not in (select min(ie1), max(ie2) from t1 where oref=t2.oref group by grp);
alter table t1 add index idx(ie1,ie2);
explain select oref, a, b, (a,b) in (select ie1,ie2 from t1 where oref=t2.oref) Z from t2;
select oref, a, b, (a,b) in (select ie1,ie2 from t1 where oref=t2.oref) Z from t2;
select oref, a, b from t2 where (a,b) in (select ie1,ie2 from t1 where oref=t2.oref);
select oref, a, b from t2 where (a,b) not in (select ie1,ie2 from t1 where oref=t2.oref);
explain extended
select oref, a, b, (a,b) in (select ie1,ie2 from t1 where oref=t2.oref) Z from t2;
drop table t1,t2;
create table t1 (oref char(4), grp int, ie int primary key);
insert into t1 (oref, grp, ie) values
('aa', 10, 2),
('aa', 10, 1),
('bb', 10, 3),
('cc', 10, 4),
('cc', 20, 5),
('cc', 10, 6);
create table t2 (oref char(4), a int);
insert into t2 values
('ee', NULL),
('bb', 2),
('cc', 5),
('cc', 2),
('cc', NULL),
('aa', 1),
('bb', NULL);
explain select oref, a, a in (select ie from t1 where oref=t2.oref) Z from t2;
select oref, a, a in (select ie from t1 where oref=t2.oref) Z from t2;
select oref, a from t2 where a in (select ie from t1 where oref=t2.oref);
select oref, a from t2 where a not in (select ie from t1 where oref=t2.oref);
explain
select oref, a, a in (select min(ie) from t1 where oref=t2.oref group by grp) Z from t2;
select oref, a, a in (select min(ie) from t1 where oref=t2.oref group by grp) Z from t2;
drop table t1,t2;
...@@ -877,11 +877,35 @@ longlong Item_in_optimizer::val_int() ...@@ -877,11 +877,35 @@ longlong Item_in_optimizer::val_int()
We disable the predicates we've pushed down into subselect, run the We disable the predicates we've pushed down into subselect, run the
subselect and see if it has produced any rows. subselect and see if it has produced any rows.
*/ */
((Item_in_subselect*)args[1])->enable_pushed_conds= FALSE; Item_in_subselect *item_subs=(Item_in_subselect*)args[1];
longlong tmp= args[1]->val_bool_result(); if (cache->cols() == 1)
result_for_null_param= null_value= {
!((Item_in_subselect*)args[1])->engine->no_rows(); item_subs->set_cond_guard_var(0, FALSE);
((Item_in_subselect*)args[1])->enable_pushed_conds= TRUE; longlong tmp= args[1]->val_bool_result();
result_for_null_param= null_value= !item_subs->engine->no_rows();
item_subs->set_cond_guard_var(0, TRUE);
}
else
{
uint i;
uint ncols= cache->cols();
/*
Turn off the predicates that are based on column compares for
which the left part is currently NULL
*/
for (i= 0; i < ncols; i++)
{
if (cache->el(i)->null_value)
item_subs->set_cond_guard_var(i, FALSE);
}
longlong tmp= args[1]->val_bool_result();
result_for_null_param= null_value= !item_subs->engine->no_rows();
/* Turn all predicates back on */
for (i= 0; i < ncols; i++)
item_subs->set_cond_guard_var(i, TRUE);
}
} }
} }
return 0; return 0;
......
...@@ -314,6 +314,7 @@ class Item_func_trig_cond: public Item_bool_func ...@@ -314,6 +314,7 @@ class Item_func_trig_cond: public Item_bool_func
enum Functype functype() const { return TRIG_COND_FUNC; }; enum Functype functype() const { return TRIG_COND_FUNC; };
const char *func_name() const { return "trigcond"; }; const char *func_name() const { return "trigcond"; };
bool const_item() const { return FALSE; } bool const_item() const { return FALSE; }
bool *get_trig_var() { return trig_var; }
}; };
class Item_func_not_all :public Item_func_not class Item_func_not_all :public Item_func_not
......
...@@ -234,16 +234,16 @@ bool Item_subselect::walk(Item_processor processor, bool walk_subquery, ...@@ -234,16 +234,16 @@ bool Item_subselect::walk(Item_processor processor, bool walk_subquery,
} }
bool Item_subselect::exec(bool full_scan) bool Item_subselect::exec()
{ {
int res; int res;
res= engine->exec(full_scan); res= engine->exec();
if (engine_changed) if (engine_changed)
{ {
engine_changed= 0; engine_changed= 0;
return exec(full_scan); return exec();
} }
return (res); return (res);
} }
...@@ -491,13 +491,13 @@ bool Item_singlerow_subselect::null_inside() ...@@ -491,13 +491,13 @@ bool Item_singlerow_subselect::null_inside()
void Item_singlerow_subselect::bring_value() void Item_singlerow_subselect::bring_value()
{ {
exec(FALSE); exec();
} }
double Item_singlerow_subselect::val_real() double Item_singlerow_subselect::val_real()
{ {
DBUG_ASSERT(fixed == 1); DBUG_ASSERT(fixed == 1);
if (!exec(FALSE) && !value->null_value) if (!exec() && !value->null_value)
{ {
null_value= 0; null_value= 0;
return value->val_real(); return value->val_real();
...@@ -512,7 +512,7 @@ double Item_singlerow_subselect::val_real() ...@@ -512,7 +512,7 @@ double Item_singlerow_subselect::val_real()
longlong Item_singlerow_subselect::val_int() longlong Item_singlerow_subselect::val_int()
{ {
DBUG_ASSERT(fixed == 1); DBUG_ASSERT(fixed == 1);
if (!exec(FALSE) && !value->null_value) if (!exec() && !value->null_value)
{ {
null_value= 0; null_value= 0;
return value->val_int(); return value->val_int();
...@@ -526,7 +526,7 @@ longlong Item_singlerow_subselect::val_int() ...@@ -526,7 +526,7 @@ longlong Item_singlerow_subselect::val_int()
String *Item_singlerow_subselect::val_str(String *str) String *Item_singlerow_subselect::val_str(String *str)
{ {
if (!exec(FALSE) && !value->null_value) if (!exec() && !value->null_value)
{ {
null_value= 0; null_value= 0;
return value->val_str(str); return value->val_str(str);
...@@ -541,7 +541,7 @@ String *Item_singlerow_subselect::val_str(String *str) ...@@ -541,7 +541,7 @@ String *Item_singlerow_subselect::val_str(String *str)
my_decimal *Item_singlerow_subselect::val_decimal(my_decimal *decimal_value) my_decimal *Item_singlerow_subselect::val_decimal(my_decimal *decimal_value)
{ {
if (!exec(FALSE) && !value->null_value) if (!exec() && !value->null_value)
{ {
null_value= 0; null_value= 0;
return value->val_decimal(decimal_value); return value->val_decimal(decimal_value);
...@@ -556,7 +556,7 @@ my_decimal *Item_singlerow_subselect::val_decimal(my_decimal *decimal_value) ...@@ -556,7 +556,7 @@ my_decimal *Item_singlerow_subselect::val_decimal(my_decimal *decimal_value)
bool Item_singlerow_subselect::val_bool() bool Item_singlerow_subselect::val_bool()
{ {
if (!exec(FALSE) && !value->null_value) if (!exec() && !value->null_value)
{ {
null_value= 0; null_value= 0;
return value->val_bool(); return value->val_bool();
...@@ -608,7 +608,7 @@ bool Item_in_subselect::test_limit(SELECT_LEX_UNIT *unit) ...@@ -608,7 +608,7 @@ bool Item_in_subselect::test_limit(SELECT_LEX_UNIT *unit)
Item_in_subselect::Item_in_subselect(Item * left_exp, Item_in_subselect::Item_in_subselect(Item * left_exp,
st_select_lex *select_lex): st_select_lex *select_lex):
Item_exists_subselect(), optimizer(0), transformed(0), Item_exists_subselect(), optimizer(0), transformed(0),
enable_pushed_conds(TRUE), upper_item(0) pushed_cond_guards(NULL), upper_item(0)
{ {
DBUG_ENTER("Item_in_subselect::Item_in_subselect"); DBUG_ENTER("Item_in_subselect::Item_in_subselect");
left_expr= left_exp; left_expr= left_exp;
...@@ -653,7 +653,7 @@ void Item_exists_subselect::fix_length_and_dec() ...@@ -653,7 +653,7 @@ void Item_exists_subselect::fix_length_and_dec()
double Item_exists_subselect::val_real() double Item_exists_subselect::val_real()
{ {
DBUG_ASSERT(fixed == 1); DBUG_ASSERT(fixed == 1);
if (exec(FALSE)) if (exec())
{ {
reset(); reset();
return 0; return 0;
...@@ -664,7 +664,7 @@ double Item_exists_subselect::val_real() ...@@ -664,7 +664,7 @@ double Item_exists_subselect::val_real()
longlong Item_exists_subselect::val_int() longlong Item_exists_subselect::val_int()
{ {
DBUG_ASSERT(fixed == 1); DBUG_ASSERT(fixed == 1);
if (exec(FALSE)) if (exec())
{ {
reset(); reset();
return 0; return 0;
...@@ -675,7 +675,7 @@ longlong Item_exists_subselect::val_int() ...@@ -675,7 +675,7 @@ longlong Item_exists_subselect::val_int()
String *Item_exists_subselect::val_str(String *str) String *Item_exists_subselect::val_str(String *str)
{ {
DBUG_ASSERT(fixed == 1); DBUG_ASSERT(fixed == 1);
if (exec(FALSE)) if (exec())
{ {
reset(); reset();
return 0; return 0;
...@@ -688,7 +688,7 @@ String *Item_exists_subselect::val_str(String *str) ...@@ -688,7 +688,7 @@ String *Item_exists_subselect::val_str(String *str)
my_decimal *Item_exists_subselect::val_decimal(my_decimal *decimal_value) my_decimal *Item_exists_subselect::val_decimal(my_decimal *decimal_value)
{ {
DBUG_ASSERT(fixed == 1); DBUG_ASSERT(fixed == 1);
if (exec(FALSE)) if (exec())
{ {
reset(); reset();
return 0; return 0;
...@@ -701,7 +701,7 @@ my_decimal *Item_exists_subselect::val_decimal(my_decimal *decimal_value) ...@@ -701,7 +701,7 @@ my_decimal *Item_exists_subselect::val_decimal(my_decimal *decimal_value)
bool Item_exists_subselect::val_bool() bool Item_exists_subselect::val_bool()
{ {
DBUG_ASSERT(fixed == 1); DBUG_ASSERT(fixed == 1);
if (exec(FALSE)) if (exec())
{ {
reset(); reset();
return 0; return 0;
...@@ -719,7 +719,7 @@ double Item_in_subselect::val_real() ...@@ -719,7 +719,7 @@ double Item_in_subselect::val_real()
DBUG_ASSERT(0); DBUG_ASSERT(0);
DBUG_ASSERT(fixed == 1); DBUG_ASSERT(fixed == 1);
null_value= 0; null_value= 0;
if (exec(!enable_pushed_conds)) if (exec())
{ {
reset(); reset();
null_value= 1; null_value= 1;
...@@ -740,7 +740,7 @@ longlong Item_in_subselect::val_int() ...@@ -740,7 +740,7 @@ longlong Item_in_subselect::val_int()
DBUG_ASSERT(0); DBUG_ASSERT(0);
DBUG_ASSERT(fixed == 1); DBUG_ASSERT(fixed == 1);
null_value= 0; null_value= 0;
if (exec(!enable_pushed_conds)) if (exec())
{ {
reset(); reset();
null_value= 1; null_value= 1;
...@@ -761,7 +761,7 @@ String *Item_in_subselect::val_str(String *str) ...@@ -761,7 +761,7 @@ String *Item_in_subselect::val_str(String *str)
DBUG_ASSERT(0); DBUG_ASSERT(0);
DBUG_ASSERT(fixed == 1); DBUG_ASSERT(fixed == 1);
null_value= 0; null_value= 0;
if (exec(!enable_pushed_conds)) if (exec())
{ {
reset(); reset();
null_value= 1; null_value= 1;
...@@ -781,7 +781,7 @@ bool Item_in_subselect::val_bool() ...@@ -781,7 +781,7 @@ bool Item_in_subselect::val_bool()
{ {
DBUG_ASSERT(fixed == 1); DBUG_ASSERT(fixed == 1);
null_value= 0; null_value= 0;
if (exec(!enable_pushed_conds)) if (exec())
{ {
reset(); reset();
null_value= 1; null_value= 1;
...@@ -801,7 +801,7 @@ my_decimal *Item_in_subselect::val_decimal(my_decimal *decimal_value) ...@@ -801,7 +801,7 @@ my_decimal *Item_in_subselect::val_decimal(my_decimal *decimal_value)
DBUG_ASSERT(0); DBUG_ASSERT(0);
null_value= 0; null_value= 0;
DBUG_ASSERT(fixed == 1); DBUG_ASSERT(fixed == 1);
if (exec(!enable_pushed_conds)) if (exec())
{ {
reset(); reset();
null_value= 1; null_value= 1;
...@@ -965,7 +965,7 @@ Item_in_subselect::single_value_transformer(JOIN *join, ...@@ -965,7 +965,7 @@ Item_in_subselect::single_value_transformer(JOIN *join,
if (!substitution) if (!substitution)
{ {
//first call for this unit /* We're invoked for the 1st (or the only) SELECT in the subquery UNION */
SELECT_LEX_UNIT *unit= select_lex->master_unit(); SELECT_LEX_UNIT *unit= select_lex->master_unit();
substitution= optimizer; substitution= optimizer;
...@@ -991,19 +991,14 @@ Item_in_subselect::single_value_transformer(JOIN *join, ...@@ -991,19 +991,14 @@ Item_in_subselect::single_value_transformer(JOIN *join,
unit->uncacheable|= UNCACHEABLE_DEPENDENT; unit->uncacheable|= UNCACHEABLE_DEPENDENT;
} }
if (!abort_on_null && left_expr->maybe_null && !pushed_cond_guards)
{
if (!(pushed_cond_guards= (bool*)join->thd->alloc(sizeof(bool))))
DBUG_RETURN(RES_ERROR);
pushed_cond_guards[0]= TRUE;
}
select_lex->uncacheable|= UNCACHEABLE_DEPENDENT; select_lex->uncacheable|= UNCACHEABLE_DEPENDENT;
/*
Add the left part of a subselect to a WHERE or HAVING clause of
the right part, e.g.
SELECT 1 IN (SELECT a FROM t1) =>
SELECT Item_in_optimizer(1, SELECT a FROM t1 WHERE a=1)
HAVING is used only if the right part contains a SUM function, a GROUP
BY or a HAVING clause.
*/
if (join->having || select_lex->with_sum_func || if (join->having || select_lex->with_sum_func ||
select_lex->group_list.elements) select_lex->group_list.elements)
{ {
...@@ -1015,13 +1010,13 @@ Item_in_subselect::single_value_transformer(JOIN *join, ...@@ -1015,13 +1010,13 @@ Item_in_subselect::single_value_transformer(JOIN *join,
ref_pointer_array, ref_pointer_array,
(char *)"<ref>", (char *)"<ref>",
this->full_name())); this->full_name()));
if (!abort_on_null && ((Item*)select_lex->item_list.head())->maybe_null) if (!abort_on_null && left_expr->maybe_null)
{ {
/* /*
We can encounter "NULL IN (SELECT ...)". Wrap the added condition We can encounter "NULL IN (SELECT ...)". Wrap the added condition
within a trigger. within a trig_cond.
*/ */
item= new Item_func_trig_cond(item, &enable_pushed_conds); item= new Item_func_trig_cond(item, get_cond_guard(0));
} }
/* /*
...@@ -1030,6 +1025,8 @@ Item_in_subselect::single_value_transformer(JOIN *join, ...@@ -1030,6 +1025,8 @@ Item_in_subselect::single_value_transformer(JOIN *join,
argument (reference) to fix_fields() argument (reference) to fix_fields()
*/ */
select_lex->having= join->having= and_items(join->having, item); select_lex->having= join->having= and_items(join->having, item);
if (join->having == item)
item->name= (char*)in_having_cond;
select_lex->having_fix_field= 1; select_lex->having_fix_field= 1;
/* /*
we do not check join->having->fixed, because Item_and (from and_items) we do not check join->having->fixed, because Item_and (from and_items)
...@@ -1056,14 +1053,19 @@ Item_in_subselect::single_value_transformer(JOIN *join, ...@@ -1056,14 +1053,19 @@ Item_in_subselect::single_value_transformer(JOIN *join,
item= func->create(expr, item); item= func->create(expr, item);
if (!abort_on_null && orig_item->maybe_null) if (!abort_on_null && orig_item->maybe_null)
{ {
having= having= new Item_is_not_null_test(this, having);
new Item_func_trig_cond(new Item_is_not_null_test(this, having), if (left_expr->maybe_null)
&enable_pushed_conds); {
if (!(having= new Item_func_trig_cond(having,
get_cond_guard(0))))
DBUG_RETURN(RES_ERROR);
}
/* /*
Item_is_not_null_test can't be changed during fix_fields() Item_is_not_null_test can't be changed during fix_fields()
we can assign select_lex->having here, and pass 0 as last we can assign select_lex->having here, and pass 0 as last
argument (reference) to fix_fields() argument (reference) to fix_fields()
*/ */
having->name= (char*)in_having_cond;
select_lex->having= join->having= having; select_lex->having= join->having= having;
select_lex->having_fix_field= 1; select_lex->having_fix_field= 1;
/* /*
...@@ -1075,17 +1077,25 @@ Item_in_subselect::single_value_transformer(JOIN *join, ...@@ -1075,17 +1077,25 @@ Item_in_subselect::single_value_transformer(JOIN *join,
select_lex->having_fix_field= 0; select_lex->having_fix_field= 0;
if (tmp) if (tmp)
DBUG_RETURN(RES_ERROR); DBUG_RETURN(RES_ERROR);
/*
NOTE: It is important that we add this "IS NULL" here, even when
orig_item can't be NULL. This is needed so that this predicate is
only used by ref[_or_null] analyzer (and, e.g. is not used by const
propagation).
*/
item= new Item_cond_or(item, item= new Item_cond_or(item,
new Item_func_isnull(orig_item)); new Item_func_isnull(orig_item));
item= new Item_func_trig_cond(item, &enable_pushed_conds);
} }
/*
If we may encounter NULL IN (SELECT ...) and care whether subquery
result is NULL or FALSE, wrap condition in a trig_cond.
*/
if (!abort_on_null && left_expr->maybe_null)
{
if (!(item= new Item_func_trig_cond(item, get_cond_guard(0))))
DBUG_RETURN(RES_ERROR);
}
/*
TODO: figure out why the following is done here in
single_value_transformer but there is no corresponding action in
row_value_transformer?
*/
item->name= (char *)in_additional_cond; item->name= (char *)in_additional_cond;
/* /*
AND can't be changed during fix_fields() AND can't be changed during fix_fields()
we can assign select_lex->having here, and pass 0 as last we can assign select_lex->having here, and pass 0 as last
...@@ -1116,10 +1126,16 @@ Item_in_subselect::single_value_transformer(JOIN *join, ...@@ -1116,10 +1126,16 @@ Item_in_subselect::single_value_transformer(JOIN *join,
select_lex->ref_pointer_array, select_lex->ref_pointer_array,
(char *)"<no matter>", (char *)"<no matter>",
(char *)"<result>")); (char *)"<result>"));
new_having= new Item_func_trig_cond(new_having, &enable_pushed_conds); if (!abort_on_null && left_expr->maybe_null)
{
if (!(new_having= new Item_func_trig_cond(new_having,
get_cond_guard(0))))
DBUG_RETURN(RES_ERROR);
}
new_having->name= (char*)in_having_cond;
select_lex->having= join->having= new_having; select_lex->having= join->having= new_having;
select_lex->having_fix_field= 1; select_lex->having_fix_field= 1;
/* /*
we do not check join->having->fixed, because comparison function we do not check join->having->fixed, because comparison function
(from func->create) can't be fixed after creation (from func->create) can't be fixed after creation
...@@ -1189,6 +1205,15 @@ Item_in_subselect::row_value_transformer(JOIN *join) ...@@ -1189,6 +1205,15 @@ Item_in_subselect::row_value_transformer(JOIN *join)
thd->lex->current_select= current; thd->lex->current_select= current;
unit->uncacheable|= UNCACHEABLE_DEPENDENT; unit->uncacheable|= UNCACHEABLE_DEPENDENT;
if (!abort_on_null && left_expr->maybe_null && !pushed_cond_guards)
{
if (!(pushed_cond_guards= (bool*)join->thd->alloc(sizeof(bool) *
left_expr->cols())))
DBUG_RETURN(RES_ERROR);
for (uint i= 0; i < cols_num; i++)
pushed_cond_guards[i]= TRUE;
}
} }
select_lex->uncacheable|= UNCACHEABLE_DEPENDENT; select_lex->uncacheable|= UNCACHEABLE_DEPENDENT;
...@@ -1205,6 +1230,7 @@ Item_in_subselect::row_value_transformer(JOIN *join) ...@@ -1205,6 +1230,7 @@ Item_in_subselect::row_value_transformer(JOIN *join)
is_not_null_test(v3)) is_not_null_test(v3))
where is_not_null_test used to register nulls in case if we have where is_not_null_test used to register nulls in case if we have
not found matching to return correct NULL value not found matching to return correct NULL value
TODO: say here explicitly if the order of AND parts matters or not.
*/ */
Item *item_having_part2= 0; Item *item_having_part2= 0;
for (uint i= 0; i < cols_num; i++) for (uint i= 0; i < cols_num; i++)
...@@ -1233,21 +1259,28 @@ Item_in_subselect::row_value_transformer(JOIN *join) ...@@ -1233,21 +1259,28 @@ Item_in_subselect::row_value_transformer(JOIN *join)
(char *)"<no matter>", (char *)"<no matter>",
(char *)"<list ref>") (char *)"<list ref>")
); );
having_item= Item *col_item= new Item_cond_or(item_eq, item_isnull);
and_items(having_item, if (!abort_on_null && left_expr->el(i)->maybe_null)
new Item_cond_or(item_eq, item_isnull)); {
item_having_part2= if (!(col_item= new Item_func_trig_cond(col_item, get_cond_guard(i))))
and_items(item_having_part2, DBUG_RETURN(RES_ERROR);
new }
Item_is_not_null_test(this, having_item= and_items(having_item, col_item);
new
Item_ref(&select_lex->context, Item *item_nnull_test=
select_lex-> new Item_is_not_null_test(this,
ref_pointer_array + i, new Item_ref(&select_lex->context,
(char *)"<no matter>", select_lex->
(char *)"<list ref>") ref_pointer_array + i,
) (char *)"<no matter>",
); (char *)"<list ref>"));
if (!abort_on_null && left_expr->el(i)->maybe_null)
{
if (!(item_nnull_test=
new Item_func_trig_cond(item_nnull_test, get_cond_guard(i))))
DBUG_RETURN(RES_ERROR);
}
item_having_part2= and_items(item_having_part2, item_nnull_test);
item_having_part2->top_level_item(); item_having_part2->top_level_item();
} }
having_item= and_items(having_item, item_having_part2); having_item= and_items(having_item, item_having_part2);
...@@ -1296,18 +1329,15 @@ Item_in_subselect::row_value_transformer(JOIN *join) ...@@ -1296,18 +1329,15 @@ Item_in_subselect::row_value_transformer(JOIN *join)
); );
if (!abort_on_null) if (!abort_on_null)
{ {
having_item= Item *having_col_item=
and_items(having_item, new Item_is_not_null_test(this,
new new
Item_is_not_null_test(this, Item_ref(&select_lex->context,
new select_lex->ref_pointer_array + i,
Item_ref(&select_lex->context, (char *)"<no matter>",
select_lex-> (char *)"<list ref>"));
ref_pointer_array + i,
(char *)"<no matter>",
(char *)"<list ref>")
)
);
item_isnull= new item_isnull= new
Item_func_isnull(new Item_func_isnull(new
Item_direct_ref(&select_lex->context, Item_direct_ref(&select_lex->context,
...@@ -1316,14 +1346,23 @@ Item_in_subselect::row_value_transformer(JOIN *join) ...@@ -1316,14 +1346,23 @@ Item_in_subselect::row_value_transformer(JOIN *join)
(char *)"<no matter>", (char *)"<no matter>",
(char *)"<list ref>") (char *)"<list ref>")
); );
item= new Item_cond_or(item, item_isnull); item= new Item_cond_or(item, item_isnull);
/*
TODO: why we create the above for cases where the right part
cant be NULL?
*/
if (left_expr->el(i)->maybe_null)
{
if (!(item= new Item_func_trig_cond(item, get_cond_guard(i))))
DBUG_RETURN(RES_ERROR);
if (!(having_col_item=
new Item_func_trig_cond(having_col_item, get_cond_guard(i))))
DBUG_RETURN(RES_ERROR);
}
having_item= and_items(having_item, having_col_item);
} }
where_item= and_items(where_item, item); where_item= and_items(where_item, item);
} }
if (where_item)
where_item= new Item_func_trig_cond(where_item, &enable_pushed_conds);
/* /*
AND can't be changed during fix_fields() AND can't be changed during fix_fields()
we can assign select_lex->where here, and pass 0 as last we can assign select_lex->where here, and pass 0 as last
...@@ -1337,9 +1376,9 @@ Item_in_subselect::row_value_transformer(JOIN *join) ...@@ -1337,9 +1376,9 @@ Item_in_subselect::row_value_transformer(JOIN *join)
if (having_item) if (having_item)
{ {
bool res; bool res;
having_item= new Item_func_trig_cond(having_item, &enable_pushed_conds);
select_lex->having= join->having= and_items(join->having, having_item); select_lex->having= join->having= and_items(join->having, having_item);
if (having_item == select_lex->having)
having_item->name= (char*)in_having_cond;
select_lex->having->top_level_item(); select_lex->having->top_level_item();
/* /*
AND can't be changed during fix_fields() AND can't be changed during fix_fields()
...@@ -1725,7 +1764,7 @@ int init_read_record_seq(JOIN_TAB *tab); ...@@ -1725,7 +1764,7 @@ int init_read_record_seq(JOIN_TAB *tab);
int join_read_always_key_or_null(JOIN_TAB *tab); int join_read_always_key_or_null(JOIN_TAB *tab);
int join_read_next_same_or_null(READ_RECORD *info); int join_read_next_same_or_null(READ_RECORD *info);
int subselect_single_select_engine::exec(bool full_scan) int subselect_single_select_engine::exec()
{ {
DBUG_ENTER("subselect_single_select_engine::exec"); DBUG_ENTER("subselect_single_select_engine::exec");
char const *save_where= thd->where; char const *save_where= thd->where;
...@@ -1763,9 +1802,13 @@ int subselect_single_select_engine::exec(bool full_scan) ...@@ -1763,9 +1802,13 @@ int subselect_single_select_engine::exec(bool full_scan)
if (!executed) if (!executed)
{ {
item->reset_value_registration(); item->reset_value_registration();
if (full_scan) bool have_changed_access= FALSE;
JOIN_TAB *changed_tabs[MAX_TABLES];
JOIN_TAB **last_changed_tab= changed_tabs;
if (item->have_guarded_conds())
{ {
/* /*
For at least one of the pushed predicates the following is true:
We should not apply optimizations based on the condition that was We should not apply optimizations based on the condition that was
pushed down into the subquery. Those optimizations are ref[_or_null] pushed down into the subquery. Those optimizations are ref[_or_null]
acceses. Change them to be full table scans. acceses. Change them to be full table scans.
...@@ -1773,32 +1816,36 @@ int subselect_single_select_engine::exec(bool full_scan) ...@@ -1773,32 +1816,36 @@ int subselect_single_select_engine::exec(bool full_scan)
for (uint i=join->const_tables ; i < join->tables ; i++) for (uint i=join->const_tables ; i < join->tables ; i++)
{ {
JOIN_TAB *tab=join->join_tab+i; JOIN_TAB *tab=join->join_tab+i;
if (tab->keyuse && tab->keyuse->outer_ref) if (tab && tab->keyuse)
{ {
tab->read_first_record= init_read_record_seq; for (uint i= 0; i < tab->ref.key_parts; i++)
tab->read_record.record= tab->table->record[0]; {
tab->read_record.thd= join->thd; bool *cond_guard= tab->ref.cond_guards[i];
tab->read_record.ref_length= tab->table->file->ref_length; if (cond_guard && !*cond_guard)
{
/* Change the access method to full table scan */
tab->read_first_record= init_read_record_seq;
tab->read_record.record= tab->table->record[0];
tab->read_record.thd= join->thd;
tab->read_record.ref_length= tab->table->file->ref_length;
*(last_changed_tab++)= tab;
break;
}
}
} }
} }
} }
join->exec(); join->exec();
if (full_scan) /* Enable the optimizations back */
for (JOIN_TAB **ptab= changed_tabs; ptab != last_changed_tab; ptab++)
{ {
/* Enable the optimizations back */ JOIN_TAB *tab= *ptab;
for (uint i=join->const_tables ; i < join->tables ; i++) tab->read_record.record= 0;
{ tab->read_record.ref_length= 0;
JOIN_TAB *tab=join->join_tab+i; tab->read_first_record= join_read_always_key_or_null;
if (tab->keyuse && tab->keyuse->outer_ref) tab->read_record.read_record= join_read_next_same_or_null;
{
tab->read_record.record= 0;
tab->read_record.ref_length= 0;
tab->read_first_record= join_read_always_key_or_null;
tab->read_record.read_record= join_read_next_same_or_null;
}
}
} }
executed= 1; executed= 1;
thd->where= save_where; thd->where= save_where;
...@@ -1810,13 +1857,9 @@ int subselect_single_select_engine::exec(bool full_scan) ...@@ -1810,13 +1857,9 @@ int subselect_single_select_engine::exec(bool full_scan)
DBUG_RETURN(0); DBUG_RETURN(0);
} }
int subselect_union_engine::exec(bool full_scan) int subselect_union_engine::exec()
{ {
char const *save_where= thd->where; char const *save_where= thd->where;
/*
Ignore the full_scan parameter: the pushed down predicates are only used
for filtering, and the caller has disabled them if necessary.
*/
int res= unit->exec(); int res= unit->exec();
thd->where= save_where; thd->where= save_where;
return res; return res;
...@@ -1824,7 +1867,7 @@ int subselect_union_engine::exec(bool full_scan) ...@@ -1824,7 +1867,7 @@ int subselect_union_engine::exec(bool full_scan)
/* /*
Search for at least on row satisfying select condition Search for at least one row satisfying select condition
SYNOPSIS SYNOPSIS
subselect_uniquesubquery_engine::scan_table() subselect_uniquesubquery_engine::scan_table()
...@@ -1833,8 +1876,8 @@ int subselect_union_engine::exec(bool full_scan) ...@@ -1833,8 +1876,8 @@ int subselect_union_engine::exec(bool full_scan)
Scan the table using sequential access until we find at least one row Scan the table using sequential access until we find at least one row
satisfying select condition. satisfying select condition.
The result of this function (info about whether a row was found) is The caller must set this->empty_result_set=FALSE before calling this
stored in this->empty_result_set. function. This function will set it to TRUE if it finds a matching row.
RETURN RETURN
FALSE - OK FALSE - OK
...@@ -1846,7 +1889,6 @@ int subselect_uniquesubquery_engine::scan_table() ...@@ -1846,7 +1889,6 @@ int subselect_uniquesubquery_engine::scan_table()
int error; int error;
TABLE *table= tab->table; TABLE *table= tab->table;
DBUG_ENTER("subselect_uniquesubquery_engine::scan_table"); DBUG_ENTER("subselect_uniquesubquery_engine::scan_table");
empty_result_set= TRUE;
if (table->file->inited) if (table->file->inited)
table->file->ha_index_end(); table->file->ha_index_end();
...@@ -1939,10 +1981,13 @@ bool subselect_uniquesubquery_engine::copy_ref_key() ...@@ -1939,10 +1981,13 @@ bool subselect_uniquesubquery_engine::copy_ref_key()
- FALSE otherwise. - FALSE otherwise.
In some cases (IN subselect is a top level item, i.e. abort_on_null==TRUE) In some cases (IN subselect is a top level item, i.e. abort_on_null==TRUE)
the caller doesn't distinguish between NULL and FALSE result and we just the caller doesn't distinguish between NULL and FALSE result and we just
return FALSE. return FALSE.
Otherwise we make a full table scan to see if there is at least one matching row. Otherwise we make a full table scan to see if there is at least one
matching row.
The result of this function (info about whether a row was found) is
stored in this->empty_result_set.
NOTE NOTE
RETURN RETURN
...@@ -1950,11 +1995,12 @@ bool subselect_uniquesubquery_engine::copy_ref_key() ...@@ -1950,11 +1995,12 @@ bool subselect_uniquesubquery_engine::copy_ref_key()
TRUE - an error occured while scanning TRUE - an error occured while scanning
*/ */
int subselect_uniquesubquery_engine::exec(bool full_scan) int subselect_uniquesubquery_engine::exec()
{ {
DBUG_ENTER("subselect_uniquesubquery_engine::exec"); DBUG_ENTER("subselect_uniquesubquery_engine::exec");
int error; int error;
TABLE *table= tab->table; TABLE *table= tab->table;
empty_result_set= TRUE;
/* TODO: change to use of 'full_scan' here? */ /* TODO: change to use of 'full_scan' here? */
if (copy_ref_key()) if (copy_ref_key())
...@@ -1975,9 +2021,13 @@ int subselect_uniquesubquery_engine::exec(bool full_scan) ...@@ -1975,9 +2021,13 @@ int subselect_uniquesubquery_engine::exec(bool full_scan)
{ {
error= 0; error= 0;
table->null_row= 0; table->null_row= 0;
((Item_in_subselect *) item)->value= (!table->status && if (!table->status && (!cond || cond->val_int()))
(!cond || cond->val_int()) ? 1 : {
0); ((Item_in_subselect *) item)->value= 1;
empty_result_set= FALSE;
}
else
((Item_in_subselect *) item)->value= 0;
} }
DBUG_RETURN(error != 0); DBUG_RETURN(error != 0);
...@@ -2043,7 +2093,7 @@ subselect_uniquesubquery_engine::~subselect_uniquesubquery_engine() ...@@ -2043,7 +2093,7 @@ subselect_uniquesubquery_engine::~subselect_uniquesubquery_engine()
1 1
*/ */
int subselect_indexsubquery_engine::exec(bool full_scan) int subselect_indexsubquery_engine::exec()
{ {
DBUG_ENTER("subselect_indexsubquery_engine::exec"); DBUG_ENTER("subselect_indexsubquery_engine::exec");
int error; int error;
...@@ -2084,8 +2134,9 @@ int subselect_indexsubquery_engine::exec(bool full_scan) ...@@ -2084,8 +2134,9 @@ int subselect_indexsubquery_engine::exec(bool full_scan)
table->null_row= 0; table->null_row= 0;
if (!table->status) if (!table->status)
{ {
if (!cond || cond->val_int()) if ((!cond || cond->val_int()) && (!having || having->val_int()))
{ {
empty_result_set= FALSE;
if (null_finding) if (null_finding)
((Item_in_subselect *) item)->was_null= 1; ((Item_in_subselect *) item)->was_null= 1;
else else
...@@ -2228,11 +2279,16 @@ void subselect_indexsubquery_engine::print(String *str) ...@@ -2228,11 +2279,16 @@ void subselect_indexsubquery_engine::print(String *str)
str->append(key_info->name); str->append(key_info->name);
if (check_null) if (check_null)
str->append(STRING_WITH_LEN(" checking NULL")); str->append(STRING_WITH_LEN(" checking NULL"));
if (cond) if (cond)
{ {
str->append(STRING_WITH_LEN(" where ")); str->append(STRING_WITH_LEN(" where "));
cond->print(str); cond->print(str);
} }
if (having)
{
str->append(STRING_WITH_LEN(" having "));
having->print(str);
}
str->append(')'); str->append(')');
} }
......
...@@ -94,7 +94,7 @@ class Item_subselect :public Item_result_field ...@@ -94,7 +94,7 @@ class Item_subselect :public Item_result_field
return null_value; return null_value;
} }
bool fix_fields(THD *thd, Item **ref); bool fix_fields(THD *thd, Item **ref);
virtual bool exec(bool full_scan); virtual bool exec();
virtual void fix_length_and_dec(); virtual void fix_length_and_dec();
table_map used_tables() const; table_map used_tables() const;
table_map not_null_tables() const { return 0; } table_map not_null_tables() const { return 0; }
...@@ -104,6 +104,7 @@ class Item_subselect :public Item_result_field ...@@ -104,6 +104,7 @@ class Item_subselect :public Item_result_field
Item *get_tmp_table_item(THD *thd); Item *get_tmp_table_item(THD *thd);
void update_used_tables(); void update_used_tables();
void print(String *str); void print(String *str);
virtual bool have_guarded_conds() { return FALSE; }
bool change_engine(subselect_engine *eng) bool change_engine(subselect_engine *eng)
{ {
old_engine= engine; old_engine= engine;
...@@ -250,13 +251,21 @@ class Item_in_subselect :public Item_exists_subselect ...@@ -250,13 +251,21 @@ class Item_in_subselect :public Item_exists_subselect
bool transformed; bool transformed;
public: public:
/* Used to trigger on/off conditions that were pushed down to subselect */ /* Used to trigger on/off conditions that were pushed down to subselect */
bool enable_pushed_conds; bool *pushed_cond_guards;
bool *get_cond_guard(int i)
{
return pushed_cond_guards ? pushed_cond_guards + i : NULL;
}
void set_cond_guard_var(int i, bool v) { pushed_cond_guards[i]= v; }
bool have_guarded_conds() { return test(pushed_cond_guards); }
Item_func_not_all *upper_item; // point on NOT/NOP before ALL/SOME subquery Item_func_not_all *upper_item; // point on NOT/NOP before ALL/SOME subquery
Item_in_subselect(Item * left_expr, st_select_lex *select_lex); Item_in_subselect(Item * left_expr, st_select_lex *select_lex);
Item_in_subselect() Item_in_subselect()
:Item_exists_subselect(), optimizer(0), abort_on_null(0), transformed(0), :Item_exists_subselect(), optimizer(0), abort_on_null(0), transformed(0),
enable_pushed_conds(TRUE), upper_item(0) pushed_cond_guards(NULL), upper_item(0)
{} {}
subs_type substype() { return IN_SUBS; } subs_type substype() { return IN_SUBS; }
...@@ -341,23 +350,22 @@ class subselect_engine: public Sql_alloc ...@@ -341,23 +350,22 @@ class subselect_engine: public Sql_alloc
SYNOPSIS SYNOPSIS
exec() exec()
full_scan TRUE - Pushed-down predicates are disabled, the engine
must disable made based on those predicates.
FALSE - Pushed-down predicates are in effect.
DESCRIPTION DESCRIPTION
Execute the engine. The result of execution is subquery value that is Execute the engine. The result of execution is subquery value that is
either captured by previously set up select_result-based 'sink' or either captured by previously set up select_result-based 'sink' or
stored somewhere by the exec() method itself. stored somewhere by the exec() method itself.
A required side effect: if full_scan==TRUE, subselect_engine->no_rows() A required side effect: If at least one pushed-down predicate is
should return correct result. disabled, subselect_engine->no_rows() must return correct result after
the exec() call.
RETURN RETURN
0 - OK 0 - OK
1 - Either an execution error, or the engine was be "changed", and 1 - Either an execution error, or the engine was "changed", and the
caller should call exec() again for the new engine. caller should call exec() again for the new engine.
*/ */
virtual int exec(bool full_scan)= 0; virtual int exec()= 0;
virtual uint cols()= 0; /* return number of columns in select */ virtual uint cols()= 0; /* return number of columns in select */
virtual uint8 uncacheable()= 0; /* query is uncacheable */ virtual uint8 uncacheable()= 0; /* query is uncacheable */
enum Item_result type() { return res_type; } enum Item_result type() { return res_type; }
...@@ -392,7 +400,7 @@ class subselect_single_select_engine: public subselect_engine ...@@ -392,7 +400,7 @@ class subselect_single_select_engine: public subselect_engine
void cleanup(); void cleanup();
int prepare(); int prepare();
void fix_length_and_dec(Item_cache** row); void fix_length_and_dec(Item_cache** row);
int exec(bool full_scan); int exec();
uint cols(); uint cols();
uint8 uncacheable(); uint8 uncacheable();
void exclude(); void exclude();
...@@ -416,7 +424,7 @@ class subselect_union_engine: public subselect_engine ...@@ -416,7 +424,7 @@ class subselect_union_engine: public subselect_engine
void cleanup(); void cleanup();
int prepare(); int prepare();
void fix_length_and_dec(Item_cache** row); void fix_length_and_dec(Item_cache** row);
int exec(bool full_scan); int exec();
uint cols(); uint cols();
uint8 uncacheable(); uint8 uncacheable();
void exclude(); void exclude();
...@@ -430,11 +438,30 @@ class subselect_union_engine: public subselect_engine ...@@ -430,11 +438,30 @@ class subselect_union_engine: public subselect_engine
struct st_join_table; struct st_join_table;
/*
A subquery execution engine that evaluates the subquery by doing one index
lookup in a unique index.
This engine is used to resolve subqueries in forms
outer_expr IN (SELECT tbl.unique_key FROM tbl WHERE subq_where)
or, tuple-based:
(oe1, .. oeN) IN (SELECT uniq_key_part1, ... uniq_key_partK
FROM tbl WHERE subqwhere)
i.e. the subquery is a single table SELECT without GROUP BY, aggregate
functions, etc.
*/
class subselect_uniquesubquery_engine: public subselect_engine class subselect_uniquesubquery_engine: public subselect_engine
{ {
protected: protected:
st_join_table *tab; st_join_table *tab;
Item *cond; Item *cond; /* The WHERE condition of subselect */
/* /*
TRUE<=> last execution produced empty set. Valid only when left TRUE<=> last execution produced empty set. Valid only when left
expression is NULL. expression is NULL.
...@@ -454,7 +481,7 @@ class subselect_uniquesubquery_engine: public subselect_engine ...@@ -454,7 +481,7 @@ class subselect_uniquesubquery_engine: public subselect_engine
void cleanup(); void cleanup();
int prepare(); int prepare();
void fix_length_and_dec(Item_cache** row); void fix_length_and_dec(Item_cache** row);
int exec(bool full_scan); int exec();
uint cols() { return 1; } uint cols() { return 1; }
uint8 uncacheable() { return UNCACHEABLE_DEPENDENT; } uint8 uncacheable() { return UNCACHEABLE_DEPENDENT; }
void exclude(); void exclude();
...@@ -472,16 +499,47 @@ class subselect_indexsubquery_engine: public subselect_uniquesubquery_engine ...@@ -472,16 +499,47 @@ class subselect_indexsubquery_engine: public subselect_uniquesubquery_engine
{ {
/* FALSE for 'ref', TRUE for 'ref-or-null'. */ /* FALSE for 'ref', TRUE for 'ref-or-null'. */
bool check_null; bool check_null;
/*
The "having" clause. This clause (further reffered to as "artificial
having") was inserted by subquery transformation code. It contains
Item(s) that have a side-effect: they record whether the subquery has
produced a row with NULL certain components. We need to use it for cases
like
(oe1, oe2) IN (SELECT t.key, t.no_key FROM t1)
where we do index lookup on t.key=oe1 but need also to check if there
was a row such that t.no_key IS NULL.
NOTE: This is currently here and not in the uniquesubquery_engine. Ideally
it should have been in uniquesubquery_engine in order to allow execution of
subqueries like
(oe1, oe2) IN (SELECT primary_key, non_key_maybe_null_field FROM tbl)
We could use uniquesubquery_engine for the first component and let
Item_is_not_null_test( non_key_maybe_null_field) to handle the second.
However, subqueries like the above are currently not handled by index
lookup-based subquery engines, the engine applicability check misses
them: it doesn't switch the engine for case of artificial having and
[eq_]ref access (only for artifical having + ref_or_null or no having).
The above example subquery is handled as a full-blown SELECT with eq_ref
access to one table.
Due to this limitation, the "artificial having" currently needs to be
checked by only in indexsubquery_engine.
*/
Item *having;
public: public:
// constructor can assign THD because it will be called after JOIN::prepare // constructor can assign THD because it will be called after JOIN::prepare
subselect_indexsubquery_engine(THD *thd, st_join_table *tab_arg, subselect_indexsubquery_engine(THD *thd, st_join_table *tab_arg,
Item_subselect *subs, Item *where, Item_subselect *subs, Item *where,
bool chk_null) Item *having_arg, bool chk_null)
:subselect_uniquesubquery_engine(thd, tab_arg, subs, where), :subselect_uniquesubquery_engine(thd, tab_arg, subs, where),
check_null(chk_null) check_null(chk_null),
having(having_arg)
{} {}
int exec(bool full_scan); int exec();
void print (String *str); void print (String *str);
}; };
......
...@@ -1532,7 +1532,7 @@ extern const LEX_STRING command_name[]; ...@@ -1532,7 +1532,7 @@ extern const LEX_STRING command_name[];
extern const char *first_keyword, *my_localhost, *delayed_user, *binary_keyword; extern const char *first_keyword, *my_localhost, *delayed_user, *binary_keyword;
extern const char **errmesg; /* Error messages */ extern const char **errmesg; /* Error messages */
extern const char *myisam_recover_options_str; extern const char *myisam_recover_options_str;
extern const char *in_left_expr_name, *in_additional_cond; extern const char *in_left_expr_name, *in_additional_cond, *in_having_cond;
extern const char * const triggers_file_ext; extern const char * const triggers_file_ext;
extern const char * const trigname_file_ext; extern const char * const trigname_file_ext;
extern Eq_creator eq_creator; extern Eq_creator eq_creator;
......
...@@ -524,10 +524,13 @@ char *mysqld_unix_port, *opt_mysql_tmpdir; ...@@ -524,10 +524,13 @@ char *mysqld_unix_port, *opt_mysql_tmpdir;
const char **errmesg; /* Error messages */ const char **errmesg; /* Error messages */
const char *myisam_recover_options_str="OFF"; const char *myisam_recover_options_str="OFF";
const char *myisam_stats_method_str="nulls_unequal"; const char *myisam_stats_method_str="nulls_unequal";
/* name of reference on left espression in rewritten IN subquery */ /* name of reference on left espression in rewritten IN subquery */
const char *in_left_expr_name= "<left expr>"; const char *in_left_expr_name= "<left expr>";
/* name of additional condition */ /* name of additional condition */
const char *in_additional_cond= "<IN COND>"; const char *in_additional_cond= "<IN COND>";
const char *in_having_cond= "<IN HAVING>";
my_decimal decimal_zero; my_decimal decimal_zero;
/* classes for comparation parsing/processing */ /* classes for comparation parsing/processing */
Eq_creator eq_creator; Eq_creator eq_creator;
......
...@@ -504,7 +504,7 @@ class st_select_lex_unit: public st_select_lex_node { ...@@ -504,7 +504,7 @@ class st_select_lex_unit: public st_select_lex_node {
void set_thd(THD *thd_arg) { thd= thd_arg; } void set_thd(THD *thd_arg) { thd= thd_arg; }
friend void lex_start(THD *thd, const uchar *buf, uint length); friend void lex_start(THD *thd, const uchar *buf, uint length);
friend int subselect_union_engine::exec(bool); friend int subselect_union_engine::exec();
List<Item> *get_unit_column_types(); List<Item> *get_unit_column_types();
}; };
......
...@@ -506,72 +506,88 @@ JOIN::prepare(Item ***rref_pointer_array, ...@@ -506,72 +506,88 @@ JOIN::prepare(Item ***rref_pointer_array,
/* /*
test if it is known for optimisation IN subquery Remove the predicates pushed down into the subquery
SYNOPSIS SYNOPSIS
JOIN::test_in_subselect() JOIN::remove_subq_pushed_predicates()
where - pointer for variable in which conditions should be where IN Must be NULL
stored if subquery is known OUT The remaining WHERE condition, or NULL
RETURN DESCRIPTION
1 - known Given that this join will be executed using (unique|index)_subquery,
0 - unknown without "checking NULL", remove the predicates that were pushed down
into the subquery.
We can remove the equalities that will be guaranteed to be true by the
fact that subquery engine will be using index lookup.
If the subquery compares scalar values, we can remove the condition that
was wrapped into trig_cond (it will be checked when needed by the subquery
engine)
If the subquery compares row values, we need to keep the wrapped
equalities in the WHERE clause: when the left (outer) tuple has both NULL
and non-NULL values, we'll do a full table scan and will rely on the
equalities corresponding to non-NULL parts of left tuple to filter out
non-matching records.
*/ */
bool JOIN::test_in_subselect(Item **where) void JOIN::remove_subq_pushed_predicates(Item **where)
{ {
if (conds->type() == Item::FUNC_ITEM && if (conds->type() == Item::FUNC_ITEM &&
((Item_func *)this->conds)->functype() == Item_func::EQ_FUNC && ((Item_func *)this->conds)->functype() == Item_func::EQ_FUNC &&
((Item_func *)conds)->arguments()[0]->type() == Item::REF_ITEM && ((Item_func *)conds)->arguments()[0]->type() == Item::REF_ITEM &&
((Item_func *)conds)->arguments()[1]->type() == Item::FIELD_ITEM) ((Item_func *)conds)->arguments()[1]->type() == Item::FIELD_ITEM)
{ {
join_tab->info= "Using index";
*where= 0; *where= 0;
return 1; return;
} }
if (conds->type() == Item::COND_ITEM && if (conds->type() == Item::COND_ITEM &&
((class Item_func *)this->conds)->functype() == ((class Item_func *)this->conds)->functype() ==
Item_func::COND_AND_FUNC) Item_func::COND_AND_FUNC)
{ {
if ((*where= remove_additional_cond(conds))) *where= remove_additional_cond(conds);
join_tab->info= "Using index; Using where";
else
join_tab->info= "Using index";
return 1;
} }
return 0;
} }
/* /*
Check if the passed HAVING clause is a clause added by subquery optimizer Index lookup-based subquery: save some flags for EXPLAIN output
SYNOPSIS SYNOPSIS
is_having_subq_predicates() save_index_subquery_explain_info()
having Having clause join_tab Subquery's join tab (there is only one as index lookup is
only used for subqueries that are single-table SELECTs)
where Subquery's WHERE clause
RETURN DESCRIPTION
TRUE The passed HAVING clause was added by the subquery optimizer For index lookup-based subquery (i.e. one executed with
FALSE Otherwise subselect_uniquesubquery_engine or subselect_indexsubquery_engine),
check its EXPLAIN output row should contain
"Using index" (TAB_INFO_FULL_SCAN_ON_NULL)
"Using Where" (TAB_INFO_USING_WHERE)
"Full scan on NULL key" (TAB_INFO_FULL_SCAN_ON_NULL)
and set appropriate flags in join_tab->packed_info.
*/ */
bool is_having_subq_predicates(Item *having) static void save_index_subquery_explain_info(JOIN_TAB *join_tab, Item* where)
{ {
if (having->type() == Item::FUNC_ITEM) join_tab->packed_info= TAB_INFO_HAVE_VALUE;
if (join_tab->table->used_keys.is_set(join_tab->ref.key))
join_tab->packed_info |= TAB_INFO_USING_INDEX;
if (where)
join_tab->packed_info |= TAB_INFO_USING_WHERE;
for (uint i = 0; i < join_tab->ref.key_parts; i++)
{ {
if (((Item_func *) having)->functype() == Item_func::ISNOTNULLTEST_FUNC) if (join_tab->ref.cond_guards[i])
return TRUE;
if (((Item_func *) having)->functype() == Item_func::TRIG_COND_FUNC)
{ {
having= ((Item_func*)having)->arguments()[0]; join_tab->packed_info |= TAB_INFO_FULL_SCAN_ON_NULL;
if (((Item_func *) having)->functype() == Item_func::ISNOTNULLTEST_FUNC) break;
return TRUE;
} }
return TRUE;
} }
return FALSE;
} }
/* /*
global select optimisation. global select optimisation.
return 0 - success return 0 - success
...@@ -1027,51 +1043,47 @@ JOIN::optimize() ...@@ -1027,51 +1043,47 @@ JOIN::optimize()
if (join_tab[0].type == JT_EQ_REF && if (join_tab[0].type == JT_EQ_REF &&
join_tab[0].ref.items[0]->name == in_left_expr_name) join_tab[0].ref.items[0]->name == in_left_expr_name)
{ {
if (test_in_subselect(&where)) remove_subq_pushed_predicates(&where);
{ save_index_subquery_explain_info(join_tab, where);
join_tab[0].type= JT_UNIQUE_SUBQUERY; join_tab[0].type= JT_UNIQUE_SUBQUERY;
error= 0; error= 0;
DBUG_RETURN(unit->item-> DBUG_RETURN(unit->item->
change_engine(new change_engine(new
subselect_uniquesubquery_engine(thd, subselect_uniquesubquery_engine(thd,
join_tab, join_tab,
unit->item, unit->item,
where))); where)));
}
} }
else if (join_tab[0].type == JT_REF && else if (join_tab[0].type == JT_REF &&
join_tab[0].ref.items[0]->name == in_left_expr_name) join_tab[0].ref.items[0]->name == in_left_expr_name)
{ {
if (test_in_subselect(&where)) remove_subq_pushed_predicates(&where);
{ save_index_subquery_explain_info(join_tab, where);
join_tab[0].type= JT_INDEX_SUBQUERY; join_tab[0].type= JT_INDEX_SUBQUERY;
error= 0; error= 0;
DBUG_RETURN(unit->item-> DBUG_RETURN(unit->item->
change_engine(new change_engine(new
subselect_indexsubquery_engine(thd, subselect_indexsubquery_engine(thd,
join_tab, join_tab,
unit->item, unit->item,
where, where,
0))); NULL,
} 0)));
} }
} else if (join_tab[0].type == JT_REF_OR_NULL && } else if (join_tab[0].type == JT_REF_OR_NULL &&
join_tab[0].ref.items[0]->name == in_left_expr_name && join_tab[0].ref.items[0]->name == in_left_expr_name &&
is_having_subq_predicates(having)) having->name == in_having_cond)
{ {
join_tab[0].type= JT_INDEX_SUBQUERY; join_tab[0].type= JT_INDEX_SUBQUERY;
error= 0; error= 0;
conds= remove_additional_cond(conds);
if ((conds= remove_additional_cond(conds))) save_index_subquery_explain_info(join_tab, conds);
join_tab->info= "Using index; Using where";
else
join_tab->info= "Using index";
DBUG_RETURN(unit->item-> DBUG_RETURN(unit->item->
change_engine(new subselect_indexsubquery_engine(thd, change_engine(new subselect_indexsubquery_engine(thd,
join_tab, join_tab,
unit->item, unit->item,
conds, conds,
having,
1))); 1)));
} }
...@@ -2585,9 +2597,7 @@ typedef struct key_field_t { // Used when finding key fields ...@@ -2585,9 +2597,7 @@ typedef struct key_field_t { // Used when finding key fields
when val IS NULL. when val IS NULL.
*/ */
bool null_rejecting; bool null_rejecting;
bool *cond_guard; /* See KEYUSE::cond_guard */
/* TRUE<=> This ref access is an outer subquery reference access */
bool outer_ref;
} KEY_FIELD; } KEY_FIELD;
/* Values in optimize */ /* Values in optimize */
...@@ -2889,7 +2899,7 @@ add_key_field(KEY_FIELD **key_fields,uint and_level, Item_func *cond, ...@@ -2889,7 +2899,7 @@ add_key_field(KEY_FIELD **key_fields,uint and_level, Item_func *cond,
cond->functype() == Item_func::MULT_EQUAL_FUNC) && cond->functype() == Item_func::MULT_EQUAL_FUNC) &&
((*value)->type() == Item::FIELD_ITEM) && ((*value)->type() == Item::FIELD_ITEM) &&
((Item_field*)*value)->field->maybe_null()); ((Item_field*)*value)->field->maybe_null());
(*key_fields)->outer_ref= FALSE; (*key_fields)->cond_guard= NULL;
(*key_fields)++; (*key_fields)++;
} }
...@@ -2986,8 +2996,9 @@ add_key_fields(JOIN *join, KEY_FIELD **key_fields, uint *and_level, ...@@ -2986,8 +2996,9 @@ add_key_fields(JOIN *join, KEY_FIELD **key_fields, uint *and_level,
} }
/* /*
Subquery optimization: check if the encountered condition is one Subquery optimization: Conditions that are pushed down into subqueries
added by condition push down into subquery. are wrapped into Item_func_trig_cond. We process the wrapped condition
but need to set cond_guard for KEYUSE elements generated from it.
*/ */
{ {
if (cond->type() == Item::FUNC_ITEM && if (cond->type() == Item::FUNC_ITEM &&
...@@ -3004,7 +3015,7 @@ add_key_fields(JOIN *join, KEY_FIELD **key_fields, uint *and_level, ...@@ -3004,7 +3015,7 @@ add_key_fields(JOIN *join, KEY_FIELD **key_fields, uint *and_level,
sargables); sargables);
// Indicate that this ref access candidate is for subquery lookup: // Indicate that this ref access candidate is for subquery lookup:
for (; save != *key_fields; save++) for (; save != *key_fields; save++)
save->outer_ref= TRUE; save->cond_guard= ((Item_func_trig_cond*)cond)->get_trig_var();
} }
return; return;
} }
...@@ -3184,7 +3195,7 @@ add_key_part(DYNAMIC_ARRAY *keyuse_array,KEY_FIELD *key_field) ...@@ -3184,7 +3195,7 @@ add_key_part(DYNAMIC_ARRAY *keyuse_array,KEY_FIELD *key_field)
keyuse.used_tables=key_field->val->used_tables(); keyuse.used_tables=key_field->val->used_tables();
keyuse.optimize= key_field->optimize & KEY_OPTIMIZE_REF_OR_NULL; keyuse.optimize= key_field->optimize & KEY_OPTIMIZE_REF_OR_NULL;
keyuse.null_rejecting= key_field->null_rejecting; keyuse.null_rejecting= key_field->null_rejecting;
keyuse.outer_ref= key_field->outer_ref; keyuse.cond_guard= key_field->cond_guard;
VOID(insert_dynamic(keyuse_array,(gptr) &keyuse)); VOID(insert_dynamic(keyuse_array,(gptr) &keyuse));
} }
} }
...@@ -5144,7 +5155,8 @@ static bool create_ref_for_key(JOIN *join, JOIN_TAB *j, KEYUSE *org_keyuse, ...@@ -5144,7 +5155,8 @@ static bool create_ref_for_key(JOIN *join, JOIN_TAB *j, KEYUSE *org_keyuse,
if (!(j->ref.key_buff= (byte*) thd->calloc(ALIGN_SIZE(length)*2)) || if (!(j->ref.key_buff= (byte*) thd->calloc(ALIGN_SIZE(length)*2)) ||
!(j->ref.key_copy= (store_key**) thd->alloc((sizeof(store_key*) * !(j->ref.key_copy= (store_key**) thd->alloc((sizeof(store_key*) *
(keyparts+1)))) || (keyparts+1)))) ||
!(j->ref.items= (Item**) thd->alloc(sizeof(Item*)*keyparts))) !(j->ref.items= (Item**) thd->alloc(sizeof(Item*)*keyparts)) ||
!(j->ref.cond_guards= (bool**) thd->alloc(sizeof(uint*)*keyparts)))
{ {
DBUG_RETURN(TRUE); DBUG_RETURN(TRUE);
} }
...@@ -5159,6 +5171,8 @@ static bool create_ref_for_key(JOIN *join, JOIN_TAB *j, KEYUSE *org_keyuse, ...@@ -5159,6 +5171,8 @@ static bool create_ref_for_key(JOIN *join, JOIN_TAB *j, KEYUSE *org_keyuse,
if (ftkey) if (ftkey)
{ {
j->ref.items[0]=((Item_func*)(keyuse->val))->key_item(); j->ref.items[0]=((Item_func*)(keyuse->val))->key_item();
/* Predicates pushed down into subquery can't be used FT access */
j->ref.cond_guards[0]= NULL;
if (keyuse->used_tables) if (keyuse->used_tables)
DBUG_RETURN(TRUE); // not supported yet. SerG DBUG_RETURN(TRUE); // not supported yet. SerG
...@@ -5175,6 +5189,7 @@ static bool create_ref_for_key(JOIN *join, JOIN_TAB *j, KEYUSE *org_keyuse, ...@@ -5175,6 +5189,7 @@ static bool create_ref_for_key(JOIN *join, JOIN_TAB *j, KEYUSE *org_keyuse,
uint maybe_null= test(keyinfo->key_part[i].null_bit); uint maybe_null= test(keyinfo->key_part[i].null_bit);
j->ref.items[i]=keyuse->val; // Save for cond removal j->ref.items[i]=keyuse->val; // Save for cond removal
j->ref.cond_guards[i]= keyuse->cond_guard;
if (keyuse->null_rejecting) if (keyuse->null_rejecting)
j->ref.null_rejecting |= 1 << i; j->ref.null_rejecting |= 1 << i;
keyuse_uses_no_tables= keyuse_uses_no_tables && !keyuse->used_tables; keyuse_uses_no_tables= keyuse_uses_no_tables && !keyuse->used_tables;
...@@ -7832,7 +7847,7 @@ change_cond_ref_to_const(THD *thd, I_List<COND_CMP> *save_list, ...@@ -7832,7 +7847,7 @@ change_cond_ref_to_const(THD *thd, I_List<COND_CMP> *save_list,
SYNOPSIS SYNOPSIS
remove_additional_cond() remove_additional_cond()
conds - condition for processing conds Condition for processing
RETURN VALUES RETURN VALUES
new conditions new conditions
...@@ -11145,7 +11160,9 @@ int rr_sequential(READ_RECORD *info); ...@@ -11145,7 +11160,9 @@ int rr_sequential(READ_RECORD *info);
int init_read_record_seq(JOIN_TAB *tab) int init_read_record_seq(JOIN_TAB *tab)
{ {
tab->read_record.read_record= rr_sequential; tab->read_record.read_record= rr_sequential;
return tab->read_record.file->ha_rnd_init(1); if (tab->read_record.file->ha_rnd_init(1))
return 1;
return (*tab->read_record.read_record)(&tab->read_record);
} }
static int static int
...@@ -15160,6 +15177,24 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order, ...@@ -15160,6 +15177,24 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order,
if (tab->info) if (tab->info)
item_list.push_back(new Item_string(tab->info,strlen(tab->info),cs)); item_list.push_back(new Item_string(tab->info,strlen(tab->info),cs));
else if (tab->packed_info & TAB_INFO_HAVE_VALUE)
{
if (tab->packed_info & TAB_INFO_USING_INDEX)
extra.append(STRING_WITH_LEN("; Using index"));
if (tab->packed_info & TAB_INFO_USING_WHERE)
extra.append(STRING_WITH_LEN("; Using where"));
if (tab->packed_info & TAB_INFO_FULL_SCAN_ON_NULL)
extra.append(STRING_WITH_LEN("; Full scan on NULL key"));
/* Skip initial "; "*/
const char *str= extra.ptr();
uint32 len= extra.length();
if (len)
{
str += 2;
len -= 2;
}
item_list.push_back(new Item_string(str, len, cs));
}
else else
{ {
if (quick_type == QUICK_SELECT_I::QS_TYPE_ROR_UNION || if (quick_type == QUICK_SELECT_I::QS_TYPE_ROR_UNION ||
...@@ -15218,6 +15253,15 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order, ...@@ -15218,6 +15253,15 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order,
} }
if (distinct & test_all_bits(used_tables,thd->used_tables)) if (distinct & test_all_bits(used_tables,thd->used_tables))
extra.append(STRING_WITH_LEN("; Distinct")); extra.append(STRING_WITH_LEN("; Distinct"));
for (uint part= 0; part < tab->ref.key_parts; part++)
{
if (tab->ref.cond_guards[part])
{
extra.append(STRING_WITH_LEN("; Full scan on NULL key"));
break;
}
}
/* Skip initial "; "*/ /* Skip initial "; "*/
const char *str= extra.ptr(); const char *str= extra.ptr();
......
...@@ -35,8 +35,17 @@ typedef struct keyuse_t { ...@@ -35,8 +35,17 @@ typedef struct keyuse_t {
satisfied if val has NULL 'value'. satisfied if val has NULL 'value'.
*/ */
bool null_rejecting; bool null_rejecting;
/* TRUE<=> This ref access is an outer subquery reference access */ /*
bool outer_ref; !NULL - This KEYUSE was created from an equality that was wrapped into
an Item_func_trig_cond. This means the equality (and validity of
this KEYUSE element) can be turned on and off. The on/off state
is indicted by the pointed value:
*cond_guard == TRUE <=> equality condition is on
*cond_guard == FALSE <=> equality condition is off
NULL - Otherwise (the source equality can't be turned off)
*/
bool *cond_guard;
} KEYUSE; } KEYUSE;
class store_key; class store_key;
...@@ -51,6 +60,18 @@ typedef struct st_table_ref ...@@ -51,6 +60,18 @@ typedef struct st_table_ref
byte *key_buff2; // key_buff+key_length byte *key_buff2; // key_buff+key_length
store_key **key_copy; // store_key **key_copy; //
Item **items; // val()'s for each keypart Item **items; // val()'s for each keypart
/*
Array of pointers to trigger variables. Some/all of the pointers may be
NULL. The ref access can be used iff
for each used key part i, (!cond_guards[i] || *cond_guards[i])
This array is used by subquery code. The subquery code may inject
triggered conditions, i.e. conditions that can be 'switched off'. A ref
access created from such condition is not valid when at least one of the
underlying conditions is switched off (see subquery code for more details)
*/
bool **cond_guards;
/* /*
(null_rejecting & (1<<i)) means the condition is '=' and no matching (null_rejecting & (1<<i)) means the condition is '=' and no matching
rows will be produced if items[i] IS NULL (see add_not_null_conds()) rows will be produced if items[i] IS NULL (see add_not_null_conds())
...@@ -99,6 +120,13 @@ enum enum_nested_loop_state ...@@ -99,6 +120,13 @@ enum enum_nested_loop_state
NESTED_LOOP_QUERY_LIMIT= 3, NESTED_LOOP_CURSOR_LIMIT= 4 NESTED_LOOP_QUERY_LIMIT= 3, NESTED_LOOP_CURSOR_LIMIT= 4
}; };
/* Values for JOIN_TAB::packed_info */
#define TAB_INFO_HAVE_VALUE 1
#define TAB_INFO_USING_INDEX 2
#define TAB_INFO_USING_WHERE 4
#define TAB_INFO_FULL_SCAN_ON_NULL 8
typedef enum_nested_loop_state typedef enum_nested_loop_state
(*Next_select_func)(JOIN *, struct st_join_table *, bool); (*Next_select_func)(JOIN *, struct st_join_table *, bool);
typedef int (*Read_record_func)(struct st_join_table *tab); typedef int (*Read_record_func)(struct st_join_table *tab);
...@@ -120,7 +148,15 @@ typedef struct st_join_table { ...@@ -120,7 +148,15 @@ typedef struct st_join_table {
st_join_table *last_inner; /* last table table for embedding outer join */ 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_upper; /* first inner table for embedding outer join */
st_join_table *first_unmatched; /* used for optimization purposes only */ st_join_table *first_unmatched; /* used for optimization purposes only */
/* Special content for EXPLAIN 'Extra' column or NULL if none */
const char *info; const char *info;
/*
Bitmap of TAB_INFO_* bits that encodes special line for EXPLAIN 'Extra'
column, or 0 if there is no info.
*/
uint packed_info;
Read_record_func read_first_record; Read_record_func read_first_record;
Next_select_func next_select; Next_select_func next_select;
READ_RECORD read_record; READ_RECORD read_record;
...@@ -425,7 +461,7 @@ class JOIN :public Sql_alloc ...@@ -425,7 +461,7 @@ class JOIN :public Sql_alloc
Item_sum ***func); Item_sum ***func);
int rollup_send_data(uint idx); int rollup_send_data(uint idx);
int rollup_write_data(uint idx, TABLE *table); int rollup_write_data(uint idx, TABLE *table);
bool test_in_subselect(Item **where); void remove_subq_pushed_predicates(Item **where);
/* /*
Release memory and, if possible, the open tables held by this execution Release memory and, if possible, the open tables held by this execution
plan (and nested plans). It's used to release some tables before plan (and nested plans). It's used to release some tables before
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment