List:Commits« Previous MessageNext Message »
From:Guilhem Bichot Date:February 9 2012 2:21pm
Subject:bzr push into mysql-trunk branch (guilhem.bichot:3870)
View as plain text  
 3870 Guilhem Bichot	2012-02-09 [merge]
      merge from trunk

    modified:
      mysql-test/r/events_restart.result
      mysql-test/t/events_restart.test
      sql/events.cc
=== modified file 'mysql-test/include/icp_tests.inc'
--- a/mysql-test/include/icp_tests.inc	2012-02-01 09:59:13 +0000
+++ b/mysql-test/include/icp_tests.inc	2012-02-08 15:25:17 +0000
@@ -862,6 +862,15 @@ CREATE TABLE t2 (  
 
 INSERT INTO t2 VALUES (1,7,'f');
 
+# Bug was specific of IN->EXISTS:
+set @old_opt_switch=@@optimizer_switch;
+--disable_query_log
+if (`select locate('materialization', @@optimizer_switch) > 0`) 
+{
+  set optimizer_switch='materialization=off';
+}
+--enable_query_log
+
 let query=
 SELECT t1.i1
 FROM t1
@@ -877,6 +886,7 @@ WHERE t1.i1 NOT IN
 eval EXPLAIN $query;
 eval $query;
 
+set @@optimizer_switch=@old_opt_switch;
 DROP TABLE t1,t2;
 
 --echo #
@@ -909,6 +919,15 @@ CREATE TABLE t3 (  
 
 INSERT INTO t3 VALUES (NULL,'w'), (1,NULL), (2,'d');
 
+# Bug was specific of IN->EXISTS:
+set @old_opt_switch=@@optimizer_switch;
+--disable_query_log
+if (`select locate('materialization', @@optimizer_switch) > 0`) 
+{
+  set optimizer_switch='materialization=off';
+}
+--enable_query_log
+
 let query=
 SELECT i1
 FROM t3
@@ -923,6 +942,7 @@ WHERE c1 IN
 eval EXPLAIN $query;
 eval $query;
 
+set @@optimizer_switch=@old_opt_switch;
 DROP TABLE t1, t2, t3;
 
 --echo #

=== modified file 'mysql-test/include/join_cache.inc'
--- a/mysql-test/include/join_cache.inc	2012-01-26 15:42:39 +0000
+++ b/mysql-test/include/join_cache.inc	2012-02-08 15:25:17 +0000
@@ -1713,6 +1713,15 @@ CREATE TABLE t3 (
 
 INSERT INTO t3 VALUES (NULL), (NULL);
 
+# Bug was specific of IN->EXISTS:
+set @old_opt_switch=@@optimizer_switch;
+--disable_query_log
+if (`select locate('materialization', @@optimizer_switch) > 0`) 
+{
+  set optimizer_switch='materialization=off';
+}
+--enable_query_log
+
 --echo
 
 let query_in=
@@ -1752,6 +1761,7 @@ eval $query_in_toplevel;
 
 
 --echo
+set @@optimizer_switch=@old_opt_switch;
 DROP TABLE t1, t2, t3;
 
 set @@join_buffer_size=default;

=== modified file 'mysql-test/include/subquery.inc'
--- a/mysql-test/include/subquery.inc	2012-02-07 14:50:31 +0000
+++ b/mysql-test/include/subquery.inc	2012-02-08 15:25:17 +0000
@@ -3223,7 +3223,7 @@ INSERT INTO t1 VALUES (10,1), (14,1);
 CREATE TABLE t2 (pk int PRIMARY KEY, int_key int);
 INSERT INTO t2 VALUES (3,3), (5,NULL), (7,3);
 
---echo # should have eq_ref for t1
+--echo # should have eq_ref for t1, unless subquery materialization is used
 --replace_column 1 x 2 x 5 x 6 x 7 x 8 x 9 x 10 x
 EXPLAIN
 SELECT * FROM t2 outr
@@ -5804,3 +5804,21 @@ eval EXPLAIN SELECT * FROM ( $subq ) AS 
 eval SELECT * FROM ( $subq ) AS alias3;
 
 DROP TABLE t1,t2;
+
+--echo #
+--echo # Test that indexsubquery_engine only does one lookup if
+--echo # the technique is unique_subquery: does not try to read the
+--echo # next row if the first row failed the subquery's WHERE
+--echo # condition (here: b=3).
+--echo #
+
+create table t1(a int);
+insert into t1 values(1),(2);
+create table t2(a int primary key, b int);
+insert into t2 values(1,10),(2,10);
+let $query=select * from t1 where a in (select a from t2 where b=3);
+eval explain $query;
+flush status;
+eval $query;
+show status like "handler_read%";
+drop table t1,t2;

=== modified file 'mysql-test/include/subquery_mat.inc'
--- a/mysql-test/include/subquery_mat.inc	2012-01-20 09:07:08 +0000
+++ b/mysql-test/include/subquery_mat.inc	2012-02-08 15:25:17 +0000
@@ -910,7 +910,6 @@ DROP TABLE t1;
 DROP TABLE t2;
 DROP TABLE t3; 
 
-
 --echo #
 --echo # Bug#13419028 - SUBQUERY MATERIALIZATION NOT USED IN CREATE
 --echo # SELECT
@@ -1022,4 +1021,326 @@ DROP TABLE t1, t2;
 
 --echo # End of test for bug#13607423.
 
+--echo
+--echo Test of WL#6094 "Allow subquery materialization in NOT IN if all
+--echo columns are not nullable"
+--echo
+
+# We want to test WL#6094 only, not WL#6095, so we use 2 columns.
+
+create table t1(a int not null);
+create table t2(a int not null);
+insert into t1 values(1),(2);
+insert into t2 values(1),(2);
+
+--echo Test in SELECT list
+
+--echo
+let $query=select a, (a,a) in (select a,a from t2) from t1;
+
+--echo cols not nullable => subq materialization
+eval explain extended $query;
+eval $query;
+
+--echo
+let $query=select t1.a, t2.a, (t1.a,t1.a) in (select a,a from t2 as t3)
+from t1 join t2 on t1.a+t2.a=1000;
+--echo cols not nullable => subq materialization
+eval explain extended $query;
+eval $query;
+
+--echo
+let $query=select t1.a, t2.a, (t2.a,t2.a) in (select a,a from t2 as t3)
+from t1 left join t2 on t1.a+t2.a=1000;
+
+--echo t2.a is not nullable, but in the query it may appear as NULL
+--echo as it's in an outer join. So, no materialization.
+eval explain extended $query;
+eval $query;
+
+--echo
+alter table t2 modify a int;
+let $query=select t1.a, t2.a, (t1.a,t1.a) in (select a,a from t2 as t3)
+from t1 join t2 on t1.a+t2.a=1000;
+--echo two nullable inner cols => no subq materialization
+eval explain extended $query;
+eval $query;
+alter table t2 modify a int not null;
+
+--echo
+--echo Test in WHERE
+--echo
+let $query=select t1.a, t2.a
+from t1 join t2 on t1.a+t2.a=3
+where (t2.a,t2.a) in (select a,a from t2 as t3);
+--echo top-level => subq materialization. With one exception: if
+--echo semijoin is enabled in @@optimizer_switch, semijoin is chosen,
+--echo then rejected (due to outer join), and in that case, the
+--echo fallback is IN->EXISTS, subq-materialization is not tried...
+eval explain extended $query;
+eval $query;
+
+--echo
+let $query=select t1.a, t2.a
+from t1 join t2 on t1.a+t2.a=3
+where (t2.a,t2.a) not in (select a,a from t2 as t3);
+--echo cols not nullable => subq materialization
+eval explain extended $query;
+eval $query;
+
+drop table t1,t2;
+
+--echo
+--echo Test of WL6095 "Allow subquery materialization in NOT IN if
+--echo single-column subquery"
+--echo
+
+# We want to test WL#6095 only, not WL#6094, so we use nullable columns.
+
+create table t1(a int null);
+create table t2(a int null);
+insert into t1 values(1),(2);
+insert into t2 values(1),(2);
+
+--echo
+let $query=select a, a in (select a from t2) from t1;
+
+--echo one col => subq materialization
+eval explain extended $query;
+eval $query;
+
+--echo
+let $query=select t1.a, t2.a, t2.a in (select * from t2 as t3)
+from t1 left join t2 on t1.a+t2.a=1000;
+
+--echo t2.a is not nullable, but in the query it may appear as NULL
+--echo as it's in an outer join. But there is only one inner column so
+--echo materialization is possible
+eval explain extended $query;
+eval $query;
+
+--echo
+let $query=select t1.a, t2.a, (t2.a,t2.a) in (select a,a from t2 as t3)
+from t1 left join t2 on t1.a+t2.a=1000;
+
+--echo _two_ outer columns, nullable => no materialization
+eval explain extended $query;
+eval $query;
+
+drop table t1,t2;
+
+--echo
+--echo Test in HAVING
+
+create table t1(a int, b int);
+create table t2(a int);
+insert into t1 values(1,1),(1,2),(1,3),(2,1),(2,2),(2,3);
+insert into t2 values(10),(20);
+
+let $query=select t1.a as z, sum(t1.b) from t1 group by t1.a
+having (z in (select * from t2)) is null;
+
+--echo no NULLs.
+
+eval explain extended $query;
+eval $query;
+
+--echo one outer NULL
+insert into t1 values(null,null);
+
+eval explain extended $query;
+eval $query;
+
+--echo one outer NULL and one inner NULL
+insert into t2 values(null);
+
+eval explain extended $query;
+eval $query;
+
+--echo one inner NULL
+delete from t1 where a is null;
+
+eval explain extended $query;
+eval $query;
+
+drop table t1,t2;
+
+--echo
+--echo Verify that an inner NULL is looked up only once (result is
+--echo cached).
+
+create table t1(a int);
+create table t2(a int);
+insert into t1 values(1),(2),(3),(4),(5),(6);
+insert into t1 select * from t1; # t1 has 12 rows
+insert into t2 values(10),(20),(NULL);
+
+let $query=select a, (a in (select * from t2)) from t1;
+
+eval explain extended $query;
+flush status;
+eval $query;
+--echo There will be one look-up in the temporary table for each row
+--echo of t1 (12), plus one additional look-up to check whether table
+--echo contains a NULL value.
+show status like "handler_read_key";
+
+drop table t1,t2;
+
+--echo #
+--echo # Bug#13495157 - SUBQUERY MATERIALIZATION NOT USED FOR CERTAIN
+--echo # STATEMENTS
+--echo #
+
+CREATE TABLE t1(a INT);
+INSERT INTO t1 VALUES(1),(2),(3);
+CREATE TABLE t2(a INT);
+INSERT INTO t2 VALUES(1),(2),(4);
+
+--echo # subquery materialization used for SELECT:
+EXPLAIN SELECT * FROM t1 WHERE a IN (SELECT * FROM t2 WHERE a <> 2);
+SELECT * FROM t1 WHERE a IN (SELECT * FROM t2 WHERE a <> 2);
+
+--echo # Also used for INSERT SELECT:
+# a) all different tables:
+CREATE TABLE t3 SELECT * FROM t1;
+EXPLAIN INSERT INTO t3 SELECT * FROM t1 WHERE a IN (SELECT * FROM t2 WHERE a <> 2);
+INSERT INTO t3 SELECT * FROM t1 WHERE a IN (SELECT * FROM t2 WHERE a <> 2);
+--sorted_result
+SELECT * FROM t3;
+
+# b) insert into subquery's selected table
+EXPLAIN INSERT INTO t2 SELECT * FROM t1 WHERE a IN (SELECT * FROM t2 WHERE a <> 2);
+INSERT INTO t2 SELECT * FROM t1 WHERE a IN (SELECT * FROM t2 WHERE a <> 2);
+--sorted_result
+SELECT * FROM t2;
+
+# c) insert into subquery's and query's selected table
+EXPLAIN INSERT INTO t2 SELECT * FROM t2 WHERE a IN (SELECT * FROM t2 WHERE a <> 2);
+INSERT INTO t2 SELECT * FROM t2 WHERE a IN (SELECT * FROM t2 WHERE a <> 2);
+--sorted_result
+SELECT * FROM t2;
+
+--echo # Not used for single-table UPDATE, DELETE:
+# a) all different tables
+EXPLAIN SELECT * FROM t2 WHERE a IN (SELECT * FROM t1);
+EXPLAIN UPDATE t2 SET a=a-1 WHERE a IN (SELECT * FROM t1);
+UPDATE t2 SET a=a-1 WHERE a IN (SELECT * FROM t1);
+--sorted_result
+SELECT * FROM t2;
+EXPLAIN DELETE FROM t2 WHERE a IN (SELECT * FROM t1);
+DELETE FROM t2 WHERE a IN (SELECT * FROM t1);
+--sorted_result
+SELECT * FROM t2;
+
+# b) update/delete in subquery's selected table: forbidden
+--error ER_UPDATE_TABLE_USED
+EXPLAIN UPDATE t2 SET a=a-1 WHERE a IN (SELECT * FROM t2);
+--error ER_UPDATE_TABLE_USED
+EXPLAIN DELETE FROM t2 WHERE a IN (SELECT * FROM t2);
+
+# Put some content so that future queries have rows to modify:
+UPDATE t2 SET a=3 WHERE a=0;
+
+--echo # Used for multi-table UPDATE, DELETE:
+
+# a) all different tables
+EXPLAIN SELECT * FROM t2,t3 WHERE t2.a IN (SELECT * FROM t1 WHERE a <> 2);
+EXPLAIN UPDATE t2,t3 SET t2.a=t2.a-2 WHERE t2.a IN (SELECT * FROM t1 WHERE a <> 2);
+UPDATE t2,t3 SET t2.a=t2.a-2 WHERE t2.a IN (SELECT * FROM t1 WHERE a <> 2);
+--sorted_result
+SELECT * FROM t2;
+EXPLAIN DELETE t2.* FROM t2,t3 WHERE t2.a IN (SELECT * FROM t1 WHERE a <> 2);
+DELETE t2.* FROM t2,t3 WHERE t2.a IN (SELECT * FROM t1 WHERE a <> 2);
+--sorted_result
+SELECT * FROM t2;
+
+# b) update/delete in subquery's selected table: forbidden
+--error ER_UPDATE_TABLE_USED
+EXPLAIN UPDATE t2,t3 SET t2.a=10 WHERE t2.a IN (SELECT * FROM t2 WHERE a <> 2);
+--error ER_UPDATE_TABLE_USED
+EXPLAIN DELETE t2.* FROM t2,t3 WHERE t2.a IN (SELECT * FROM t2 WHERE a <> 2);
+
+DROP TABLE t1,t2,t3;
+
+--echo #
+--echo # Test that subquery materialization only does one lookup: does
+--echo # not try to read the next row if the first row failed the
+--echo # subquery's WHERE. We use a case where index lookup is not
+--echo # enough to satisfy IN(), because index has length two when the
+--echo # outer value has length three, and thus the post-filtering
+--echo # WHERE added by subselect_hash_sj_engine::setup() makes the
+--echo # decision.
+--echo #
+create table t1 (a varchar(3));
+create table t2 (a varchar(2));
+insert into t1 values('aaa'), ('aaa');
+insert into t2 values('aa'), ('aa');
+let $query=select * from t1 where a in (select a from t2);
+eval explain $query;
+flush status;
+eval $query;
+show status like "handler_read%";
+drop table t1,t2;
+
+--echo #
+--echo # Bug#13655791 DIFFERENT RESULT WITH WL6094 ON QUERY WITH XOR
+--echo # IN WHERE CLAUSE + MYISAM
+--echo #
+
+CREATE TABLE t1 (
+  pk int NOT NULL,
+  col_varchar_nokey varchar(1) DEFAULT NULL,
+  PRIMARY KEY (pk)
+);
+
+INSERT INTO t1 VALUES (10,'x');
+
+CREATE TABLE t2 (
+  pk int NOT NULL,
+  col_varchar_nokey varchar(1) DEFAULT NULL,
+  PRIMARY KEY (pk)
+);
+
+INSERT INTO t2 VALUES (1,'v'), (5,'x'), (11,'z'), (12,'c'), (15,'y');
+
+CREATE TABLE t3 (
+  pk int NOT NULL,
+  col_int_key int DEFAULT NULL,
+  PRIMARY KEY (pk),
+  KEY col_int_key (col_int_key)
+);
+
+INSERT INTO t3 VALUES (10,8);
+
+CREATE TABLE t4 (
+  pk int NOT NULL,
+  col_varchar_nokey varchar(1) DEFAULT NULL,
+  PRIMARY KEY (pk)
+);
+
+INSERT INTO t4 VALUES (1,'x');
+
+let $query=
+SELECT OUTR.pk, OUTR.col_varchar_nokey, OUTR2.col_varchar_nokey
+FROM t2 AS OUTR2
+  JOIN t4 AS OUTR
+  ON (OUTR2.col_varchar_nokey > OUTR.col_varchar_nokey)
+WHERE
+  OUTR.col_varchar_nokey IN (
+    SELECT INNR.col_varchar_nokey
+    FROM t3 AS INNR2
+      LEFT JOIN t1 AS INNR
+      ON (INNR2.col_int_key >= INNR.pk)
+  )
+  XOR OUTR.pk < 6
+;
+
+eval EXPLAIN $query;
+FLUSH STATUS;
+eval $query;
+SHOW STATUS LIKE "HANDLER_READ%";
+
+DROP TABLE t1,t2,t3,t4;
+
 --echo # End of 5.6 tests

=== modified file 'mysql-test/r/innodb_icp.result'
--- a/mysql-test/r/innodb_icp.result	2012-02-01 09:59:13 +0000
+++ b/mysql-test/r/innodb_icp.result	2012-02-08 15:25:17 +0000
@@ -794,6 +794,7 @@ PRIMARY KEY (pk),
 KEY col_int_key (i1)
 );
 INSERT INTO t2 VALUES (1,7,'f');
+set @old_opt_switch=@@optimizer_switch;
 EXPLAIN SELECT t1.i1
 FROM t1
 WHERE t1.i1 NOT IN
@@ -821,6 +822,7 @@ OR SUBQUERY_t2.c1 = 'a'
 i1
 NULL
 133
+set @@optimizer_switch=@old_opt_switch;
 DROP TABLE t1,t2;
 #
 # Bug#11876420 "MISSING ROW IN RESULT WITH SUBQUERY + IN + XOR + 
@@ -845,6 +847,7 @@ c1 VARCHAR(1),
 KEY col_varchar_key (c1)
 ) ENGINE=InnoDB;
 INSERT INTO t3 VALUES (NULL,'w'), (1,NULL), (2,'d');
+set @old_opt_switch=@@optimizer_switch;
 EXPLAIN SELECT i1
 FROM t3
 WHERE c1 IN
@@ -870,6 +873,7 @@ XOR i1;
 i1
 1
 2
+set @@optimizer_switch=@old_opt_switch;
 DROP TABLE t1, t2, t3;
 #
 # Bug#12355958 "FAILING ASSERTION: TRX->LOCK.N_ACTIVE_THRS == 1"

=== modified file 'mysql-test/r/innodb_icp_all.result'
--- a/mysql-test/r/innodb_icp_all.result	2012-02-01 09:59:13 +0000
+++ b/mysql-test/r/innodb_icp_all.result	2012-02-08 15:25:17 +0000
@@ -794,6 +794,7 @@ PRIMARY KEY (pk),
 KEY col_int_key (i1)
 );
 INSERT INTO t2 VALUES (1,7,'f');
+set @old_opt_switch=@@optimizer_switch;
 EXPLAIN SELECT t1.i1
 FROM t1
 WHERE t1.i1 NOT IN
@@ -821,6 +822,7 @@ OR SUBQUERY_t2.c1 = 'a'
 i1
 NULL
 133
+set @@optimizer_switch=@old_opt_switch;
 DROP TABLE t1,t2;
 #
 # Bug#11876420 "MISSING ROW IN RESULT WITH SUBQUERY + IN + XOR + 
@@ -845,6 +847,7 @@ c1 VARCHAR(1),
 KEY col_varchar_key (c1)
 ) ENGINE=InnoDB;
 INSERT INTO t3 VALUES (NULL,'w'), (1,NULL), (2,'d');
+set @old_opt_switch=@@optimizer_switch;
 EXPLAIN SELECT i1
 FROM t3
 WHERE c1 IN
@@ -870,6 +873,7 @@ XOR i1;
 i1
 1
 2
+set @@optimizer_switch=@old_opt_switch;
 DROP TABLE t1, t2, t3;
 #
 # Bug#12355958 "FAILING ASSERTION: TRX->LOCK.N_ACTIVE_THRS == 1"

=== modified file 'mysql-test/r/innodb_icp_none.result'
--- a/mysql-test/r/innodb_icp_none.result	2012-02-01 09:59:13 +0000
+++ b/mysql-test/r/innodb_icp_none.result	2012-02-08 15:25:17 +0000
@@ -793,6 +793,7 @@ PRIMARY KEY (pk),
 KEY col_int_key (i1)
 );
 INSERT INTO t2 VALUES (1,7,'f');
+set @old_opt_switch=@@optimizer_switch;
 EXPLAIN SELECT t1.i1
 FROM t1
 WHERE t1.i1 NOT IN
@@ -820,6 +821,7 @@ OR SUBQUERY_t2.c1 = 'a'
 i1
 NULL
 133
+set @@optimizer_switch=@old_opt_switch;
 DROP TABLE t1,t2;
 #
 # Bug#11876420 "MISSING ROW IN RESULT WITH SUBQUERY + IN + XOR + 
@@ -844,6 +846,7 @@ c1 VARCHAR(1),
 KEY col_varchar_key (c1)
 ) ENGINE=InnoDB;
 INSERT INTO t3 VALUES (NULL,'w'), (1,NULL), (2,'d');
+set @old_opt_switch=@@optimizer_switch;
 EXPLAIN SELECT i1
 FROM t3
 WHERE c1 IN
@@ -869,6 +872,7 @@ XOR i1;
 i1
 1
 2
+set @@optimizer_switch=@old_opt_switch;
 DROP TABLE t1, t2, t3;
 #
 # Bug#12355958 "FAILING ASSERTION: TRX->LOCK.N_ACTIVE_THRS == 1"

=== modified file 'mysql-test/r/join_cache_bka.result'
--- a/mysql-test/r/join_cache_bka.result	2012-01-30 13:13:15 +0000
+++ b/mysql-test/r/join_cache_bka.result	2012-02-08 15:25:17 +0000
@@ -2380,6 +2380,7 @@ c1 INTEGER,
 KEY k1 (c1)
 ) ENGINE=InnoDB;
 INSERT INTO t3 VALUES (NULL), (NULL);
+set @old_opt_switch=@@optimizer_switch;
 
 explain SELECT t3.c1 FROM t3
 WHERE t3.c1 = SOME (SELECT t1.c2_key FROM t2 JOIN t1 ON t2.pk < t1.c1)
@@ -2411,9 +2412,9 @@ id	select_type	table	type	possible_keys	
 explain SELECT t3.c1 FROM t3
 WHERE t3.c1 IN (SELECT t1.c2_key FROM t2 JOIN t1 ON t2.pk < t1.c1);
 id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
-1	PRIMARY	t2	index	PRIMARY	PRIMARY	4	NULL	1	Using index; Start materialize; Scan
-1	PRIMARY	t1	ALL	col_int_key	NULL	NULL	NULL	1	Using where; End materialize; Using join buffer (Block Nested Loop)
-1	PRIMARY	t3	ref	k1	k1	5	test.t1.c2_key	1	Using index
+1	PRIMARY	t2	index	PRIMARY	PRIMARY	4	NULL	1	Using index; Start temporary
+1	PRIMARY	t1	ALL	col_int_key	NULL	NULL	NULL	1	Using where; Using join buffer (Block Nested Loop)
+1	PRIMARY	t3	ref	k1	k1	5	test.t1.c2_key	1	Using index; End temporary
 SELECT t3.c1 FROM t3
 WHERE t3.c1 = SOME (SELECT t1.c2_key FROM t2 JOIN t1 ON t2.pk < t1.c1)
 XOR TRUE;
@@ -2433,6 +2434,7 @@ SELECT t3.c1 FROM t3
 WHERE t3.c1 IN (SELECT t1.c2_key FROM t2 JOIN t1 ON t2.pk < t1.c1);
 c1
 
+set @@optimizer_switch=@old_opt_switch;
 DROP TABLE t1, t2, t3;
 set @@join_buffer_size=default;
 

=== modified file 'mysql-test/r/join_cache_bka_nixbnl.result'
--- a/mysql-test/r/join_cache_bka_nixbnl.result	2012-01-30 13:13:15 +0000
+++ b/mysql-test/r/join_cache_bka_nixbnl.result	2012-02-08 15:25:17 +0000
@@ -2380,6 +2380,7 @@ c1 INTEGER,
 KEY k1 (c1)
 ) ENGINE=InnoDB;
 INSERT INTO t3 VALUES (NULL), (NULL);
+set @old_opt_switch=@@optimizer_switch;
 
 explain SELECT t3.c1 FROM t3
 WHERE t3.c1 = SOME (SELECT t1.c2_key FROM t2 JOIN t1 ON t2.pk < t1.c1)
@@ -2411,9 +2412,9 @@ id	select_type	table	type	possible_keys	
 explain SELECT t3.c1 FROM t3
 WHERE t3.c1 IN (SELECT t1.c2_key FROM t2 JOIN t1 ON t2.pk < t1.c1);
 id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
-1	PRIMARY	t2	index	PRIMARY	PRIMARY	4	NULL	1	Using index; Start materialize; Scan
-1	PRIMARY	t1	ALL	col_int_key	NULL	NULL	NULL	1	Using where; End materialize
-1	PRIMARY	t3	ref	k1	k1	5	test.t1.c2_key	1	Using index
+1	PRIMARY	t2	index	PRIMARY	PRIMARY	4	NULL	1	Using index; Start temporary
+1	PRIMARY	t1	ALL	col_int_key	NULL	NULL	NULL	1	Using where
+1	PRIMARY	t3	ref	k1	k1	5	test.t1.c2_key	1	Using index; End temporary
 SELECT t3.c1 FROM t3
 WHERE t3.c1 = SOME (SELECT t1.c2_key FROM t2 JOIN t1 ON t2.pk < t1.c1)
 XOR TRUE;
@@ -2433,6 +2434,7 @@ SELECT t3.c1 FROM t3
 WHERE t3.c1 IN (SELECT t1.c2_key FROM t2 JOIN t1 ON t2.pk < t1.c1);
 c1
 
+set @@optimizer_switch=@old_opt_switch;
 DROP TABLE t1, t2, t3;
 set @@join_buffer_size=default;
 

=== modified file 'mysql-test/r/join_cache_bkaunique.result'
--- a/mysql-test/r/join_cache_bkaunique.result	2012-01-30 13:13:15 +0000
+++ b/mysql-test/r/join_cache_bkaunique.result	2012-02-08 15:25:17 +0000
@@ -2381,6 +2381,7 @@ c1 INTEGER,
 KEY k1 (c1)
 ) ENGINE=InnoDB;
 INSERT INTO t3 VALUES (NULL), (NULL);
+set @old_opt_switch=@@optimizer_switch;
 
 explain SELECT t3.c1 FROM t3
 WHERE t3.c1 = SOME (SELECT t1.c2_key FROM t2 JOIN t1 ON t2.pk < t1.c1)
@@ -2412,9 +2413,9 @@ id	select_type	table	type	possible_keys	
 explain SELECT t3.c1 FROM t3
 WHERE t3.c1 IN (SELECT t1.c2_key FROM t2 JOIN t1 ON t2.pk < t1.c1);
 id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
-1	PRIMARY	t2	index	PRIMARY	PRIMARY	4	NULL	1	Using index; Start materialize; Scan
-1	PRIMARY	t1	ALL	col_int_key	NULL	NULL	NULL	1	Using where; End materialize; Using join buffer (Block Nested Loop)
-1	PRIMARY	t3	ref	k1	k1	5	test.t1.c2_key	1	Using index
+1	PRIMARY	t2	index	PRIMARY	PRIMARY	4	NULL	1	Using index; Start temporary
+1	PRIMARY	t1	ALL	col_int_key	NULL	NULL	NULL	1	Using where; Using join buffer (Block Nested Loop)
+1	PRIMARY	t3	ref	k1	k1	5	test.t1.c2_key	1	Using index; End temporary
 SELECT t3.c1 FROM t3
 WHERE t3.c1 = SOME (SELECT t1.c2_key FROM t2 JOIN t1 ON t2.pk < t1.c1)
 XOR TRUE;
@@ -2434,6 +2435,7 @@ SELECT t3.c1 FROM t3
 WHERE t3.c1 IN (SELECT t1.c2_key FROM t2 JOIN t1 ON t2.pk < t1.c1);
 c1
 
+set @@optimizer_switch=@old_opt_switch;
 DROP TABLE t1, t2, t3;
 set @@join_buffer_size=default;
 

=== modified file 'mysql-test/r/join_cache_bnl.result'
--- a/mysql-test/r/join_cache_bnl.result	2012-01-30 13:13:15 +0000
+++ b/mysql-test/r/join_cache_bnl.result	2012-02-08 15:25:17 +0000
@@ -2381,6 +2381,7 @@ c1 INTEGER,
 KEY k1 (c1)
 ) ENGINE=InnoDB;
 INSERT INTO t3 VALUES (NULL), (NULL);
+set @old_opt_switch=@@optimizer_switch;
 
 explain SELECT t3.c1 FROM t3
 WHERE t3.c1 = SOME (SELECT t1.c2_key FROM t2 JOIN t1 ON t2.pk < t1.c1)
@@ -2412,9 +2413,9 @@ id	select_type	table	type	possible_keys	
 explain SELECT t3.c1 FROM t3
 WHERE t3.c1 IN (SELECT t1.c2_key FROM t2 JOIN t1 ON t2.pk < t1.c1);
 id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
-1	PRIMARY	t2	index	PRIMARY	PRIMARY	4	NULL	1	Using index; Start materialize; Scan
-1	PRIMARY	t1	ALL	col_int_key	NULL	NULL	NULL	1	Using where; End materialize; Using join buffer (Block Nested Loop)
-1	PRIMARY	t3	ref	k1	k1	5	test.t1.c2_key	1	Using index
+1	PRIMARY	t2	index	PRIMARY	PRIMARY	4	NULL	1	Using index; Start temporary
+1	PRIMARY	t1	ALL	col_int_key	NULL	NULL	NULL	1	Using where; Using join buffer (Block Nested Loop)
+1	PRIMARY	t3	ref	k1	k1	5	test.t1.c2_key	1	Using index; End temporary
 SELECT t3.c1 FROM t3
 WHERE t3.c1 = SOME (SELECT t1.c2_key FROM t2 JOIN t1 ON t2.pk < t1.c1)
 XOR TRUE;
@@ -2434,6 +2435,7 @@ SELECT t3.c1 FROM t3
 WHERE t3.c1 IN (SELECT t1.c2_key FROM t2 JOIN t1 ON t2.pk < t1.c1);
 c1
 
+set @@optimizer_switch=@old_opt_switch;
 DROP TABLE t1, t2, t3;
 set @@join_buffer_size=default;
 

=== modified file 'mysql-test/r/join_cache_nojb.result'
--- a/mysql-test/r/join_cache_nojb.result	2012-02-01 09:59:13 +0000
+++ b/mysql-test/r/join_cache_nojb.result	2012-02-08 15:25:17 +0000
@@ -2381,6 +2381,7 @@ c1 INTEGER,
 KEY k1 (c1)
 ) ENGINE=InnoDB;
 INSERT INTO t3 VALUES (NULL), (NULL);
+set @old_opt_switch=@@optimizer_switch;
 
 explain SELECT t3.c1 FROM t3
 WHERE t3.c1 = SOME (SELECT t1.c2_key FROM t2 JOIN t1 ON t2.pk < t1.c1)
@@ -2412,9 +2413,9 @@ id	select_type	table	type	possible_keys	
 explain SELECT t3.c1 FROM t3
 WHERE t3.c1 IN (SELECT t1.c2_key FROM t2 JOIN t1 ON t2.pk < t1.c1);
 id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
-1	PRIMARY	t2	index	PRIMARY	PRIMARY	4	NULL	1	Using index; Start materialize; Scan
-1	PRIMARY	t1	ALL	col_int_key	NULL	NULL	NULL	1	Using where; End materialize
-1	PRIMARY	t3	ref	k1	k1	5	test.t1.c2_key	1	Using index
+1	PRIMARY	t2	index	PRIMARY	PRIMARY	4	NULL	1	Using index; Start temporary
+1	PRIMARY	t1	ALL	col_int_key	NULL	NULL	NULL	1	Using where
+1	PRIMARY	t3	ref	k1	k1	5	test.t1.c2_key	1	Using index; End temporary
 SELECT t3.c1 FROM t3
 WHERE t3.c1 = SOME (SELECT t1.c2_key FROM t2 JOIN t1 ON t2.pk < t1.c1)
 XOR TRUE;
@@ -2434,6 +2435,7 @@ SELECT t3.c1 FROM t3
 WHERE t3.c1 IN (SELECT t1.c2_key FROM t2 JOIN t1 ON t2.pk < t1.c1);
 c1
 
+set @@optimizer_switch=@old_opt_switch;
 DROP TABLE t1, t2, t3;
 set @@join_buffer_size=default;
 

=== modified file 'mysql-test/r/myisam_icp.result'
--- a/mysql-test/r/myisam_icp.result	2012-02-01 09:59:13 +0000
+++ b/mysql-test/r/myisam_icp.result	2012-02-08 15:25:17 +0000
@@ -792,6 +792,7 @@ PRIMARY KEY (pk),
 KEY col_int_key (i1)
 );
 INSERT INTO t2 VALUES (1,7,'f');
+set @old_opt_switch=@@optimizer_switch;
 EXPLAIN SELECT t1.i1
 FROM t1
 WHERE t1.i1 NOT IN
@@ -819,6 +820,7 @@ OR SUBQUERY_t2.c1 = 'a'
 i1
 NULL
 133
+set @@optimizer_switch=@old_opt_switch;
 DROP TABLE t1,t2;
 #
 # Bug#11876420 "MISSING ROW IN RESULT WITH SUBQUERY + IN + XOR + 
@@ -843,6 +845,7 @@ c1 VARCHAR(1),
 KEY col_varchar_key (c1)
 ) ENGINE=InnoDB;
 INSERT INTO t3 VALUES (NULL,'w'), (1,NULL), (2,'d');
+set @old_opt_switch=@@optimizer_switch;
 EXPLAIN SELECT i1
 FROM t3
 WHERE c1 IN
@@ -867,6 +870,7 @@ XOR i1;
 i1
 1
 2
+set @@optimizer_switch=@old_opt_switch;
 DROP TABLE t1, t2, t3;
 #
 # Bug#12355958 "FAILING ASSERTION: TRX->LOCK.N_ACTIVE_THRS == 1"

=== modified file 'mysql-test/r/myisam_icp_all.result'
--- a/mysql-test/r/myisam_icp_all.result	2012-02-01 09:59:13 +0000
+++ b/mysql-test/r/myisam_icp_all.result	2012-02-08 15:25:17 +0000
@@ -792,6 +792,7 @@ PRIMARY KEY (pk),
 KEY col_int_key (i1)
 );
 INSERT INTO t2 VALUES (1,7,'f');
+set @old_opt_switch=@@optimizer_switch;
 EXPLAIN SELECT t1.i1
 FROM t1
 WHERE t1.i1 NOT IN
@@ -819,6 +820,7 @@ OR SUBQUERY_t2.c1 = 'a'
 i1
 NULL
 133
+set @@optimizer_switch=@old_opt_switch;
 DROP TABLE t1,t2;
 #
 # Bug#11876420 "MISSING ROW IN RESULT WITH SUBQUERY + IN + XOR + 
@@ -843,6 +845,7 @@ c1 VARCHAR(1),
 KEY col_varchar_key (c1)
 ) ENGINE=InnoDB;
 INSERT INTO t3 VALUES (NULL,'w'), (1,NULL), (2,'d');
+set @old_opt_switch=@@optimizer_switch;
 EXPLAIN SELECT i1
 FROM t3
 WHERE c1 IN
@@ -867,6 +870,7 @@ XOR i1;
 i1
 1
 2
+set @@optimizer_switch=@old_opt_switch;
 DROP TABLE t1, t2, t3;
 #
 # Bug#12355958 "FAILING ASSERTION: TRX->LOCK.N_ACTIVE_THRS == 1"

=== modified file 'mysql-test/r/myisam_icp_none.result'
--- a/mysql-test/r/myisam_icp_none.result	2012-02-01 09:59:13 +0000
+++ b/mysql-test/r/myisam_icp_none.result	2012-02-08 15:25:17 +0000
@@ -791,6 +791,7 @@ PRIMARY KEY (pk),
 KEY col_int_key (i1)
 );
 INSERT INTO t2 VALUES (1,7,'f');
+set @old_opt_switch=@@optimizer_switch;
 EXPLAIN SELECT t1.i1
 FROM t1
 WHERE t1.i1 NOT IN
@@ -818,6 +819,7 @@ OR SUBQUERY_t2.c1 = 'a'
 i1
 NULL
 133
+set @@optimizer_switch=@old_opt_switch;
 DROP TABLE t1,t2;
 #
 # Bug#11876420 "MISSING ROW IN RESULT WITH SUBQUERY + IN + XOR + 
@@ -842,6 +844,7 @@ c1 VARCHAR(1),
 KEY col_varchar_key (c1)
 ) ENGINE=InnoDB;
 INSERT INTO t3 VALUES (NULL,'w'), (1,NULL), (2,'d');
+set @old_opt_switch=@@optimizer_switch;
 EXPLAIN SELECT i1
 FROM t3
 WHERE c1 IN
@@ -866,6 +869,7 @@ XOR i1;
 i1
 1
 2
+set @@optimizer_switch=@old_opt_switch;
 DROP TABLE t1, t2, t3;
 #
 # Bug#12355958 "FAILING ASSERTION: TRX->LOCK.N_ACTIVE_THRS == 1"

=== modified file 'mysql-test/r/subquery_all.result'
--- a/mysql-test/r/subquery_all.result	2012-02-07 14:50:31 +0000
+++ b/mysql-test/r/subquery_all.result	2012-02-08 15:25:17 +0000
@@ -890,9 +890,9 @@ a	t1.a in (select t2.a from t2)
 explain extended SELECT t1.a, t1.a in (select t2.a from t2) FROM t1;
 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
-2	DEPENDENT SUBQUERY	t2	index_subquery	a	a	5	func	2	100.00	Using index
+2	SUBQUERY	t2	index	NULL	a	5	NULL	3	100.00	Using index
 Warnings:
-Note	1003	/* select#1 */ 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`
+Note	1003	/* select#1 */ select `test`.`t1`.`a` AS `a`,<in_optimizer>(`test`.`t1`.`a`,`test`.`t1`.`a` in ( <materialize> (/* select#2 */ select `test`.`t2`.`a` from `test`.`t2` ), <primary_index_lookup>(`test`.`t1`.`a` in <temporary table> on distinct_key where ((`test`.`t1`.`a` = `materialized subselect`.`a`))))) AS `t1.a in (select t2.a from t2)` from `test`.`t1`
 CREATE TABLE t3 (a int(11) default '0');
 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;
@@ -904,10 +904,10 @@ a	t1.a in (select t2.a from t2,t3 where 
 explain extended SELECT t1.a, t1.a in (select t2.a from t2,t3 where t3.a=t2.a) FROM t1;
 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
-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; Using join buffer (Block Nested Loop)
+2	SUBQUERY	t2	index	a	a	5	NULL	3	100.00	Using index
+2	SUBQUERY	t3	ALL	NULL	NULL	NULL	NULL	3	100.00	Using where; Using join buffer (Block Nested Loop)
 Warnings:
-Note	1003	/* select#1 */ select `test`.`t1`.`a` AS `a`,<in_optimizer>(`test`.`t1`.`a`,<exists>(/* select#2 */ select 1 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`
+Note	1003	/* select#1 */ select `test`.`t1`.`a` AS `a`,<in_optimizer>(`test`.`t1`.`a`,`test`.`t1`.`a` in ( <materialize> (/* select#2 */ select `test`.`t2`.`a` from `test`.`t2` join `test`.`t3` where (`test`.`t3`.`a` = `test`.`t2`.`a`) ), <primary_index_lookup>(`test`.`t1`.`a` in <temporary table> on distinct_key where ((`test`.`t1`.`a` = `materialized subselect`.`a`))))) AS `t1.a in (select t2.a from t2,t3 where t3.a=t2.a)` from `test`.`t1`
 drop table t1,t2,t3;
 create table t1 (a float);
 select 10.5 IN (SELECT * from t1 LIMIT 1);
@@ -1368,27 +1368,27 @@ a3	1
 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
 1	PRIMARY	t1	index	NULL	s1	6	NULL	3	100.00	Using index
-2	DEPENDENT SUBQUERY	t2	index_subquery	s1	s1	6	func	2	100.00	Using index; Full scan on NULL key
+2	SUBQUERY	t2	index	NULL	s1	6	NULL	2	100.00	Using index
 Warnings:
-Note	1003	/* select#1 */ 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_if(outer_field_is_not_null, <is_not_null_test>(`test`.`t2`.`s1`), true)))))) AS `s1 NOT IN (SELECT s1 FROM t2)` from `test`.`t1`
+Note	1003	/* select#1 */ select `test`.`t1`.`s1` AS `s1`,(not(<in_optimizer>(`test`.`t1`.`s1`,`test`.`t1`.`s1` in ( <materialize> (/* select#2 */ select `test`.`t2`.`s1` from `test`.`t2` ), <primary_index_lookup>(`test`.`t1`.`s1` in <temporary table> on distinct_key where ((`test`.`t1`.`s1` = `materialized subselect`.`s1`))))))) AS `s1 NOT IN (SELECT s1 FROM t2)` from `test`.`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
 1	PRIMARY	t1	index	NULL	s1	6	NULL	3	100.00	Using index
-2	DEPENDENT SUBQUERY	t2	index_subquery	s1	s1	6	func	2	100.00	Using index; Full scan on NULL key
+2	SUBQUERY	t2	index	NULL	s1	6	NULL	2	100.00	Using index
 Warnings:
-Note	1003	/* select#1 */ 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_if(outer_field_is_not_null, <is_not_null_test>(`test`.`t2`.`s1`), true)))) AS `s1 = ANY (SELECT s1 FROM t2)` from `test`.`t1`
+Note	1003	/* select#1 */ select `test`.`t1`.`s1` AS `s1`,<in_optimizer>(`test`.`t1`.`s1`,`test`.`t1`.`s1` in ( <materialize> (/* select#2 */ select `test`.`t2`.`s1` from `test`.`t2` ), <primary_index_lookup>(`test`.`t1`.`s1` in <temporary table> on distinct_key where ((`test`.`t1`.`s1` = `materialized subselect`.`s1`))))) AS `s1 = ANY (SELECT s1 FROM t2)` from `test`.`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
 1	PRIMARY	t1	index	NULL	s1	6	NULL	3	100.00	Using index
-2	DEPENDENT SUBQUERY	t2	index_subquery	s1	s1	6	func	2	100.00	Using index; Full scan on NULL key
+2	SUBQUERY	t2	index	NULL	s1	6	NULL	2	100.00	Using index
 Warnings:
-Note	1003	/* select#1 */ 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_if(outer_field_is_not_null, <is_not_null_test>(`test`.`t2`.`s1`), true)))))) AS `s1 <> ALL (SELECT s1 FROM t2)` from `test`.`t1`
+Note	1003	/* select#1 */ select `test`.`t1`.`s1` AS `s1`,(not(<in_optimizer>(`test`.`t1`.`s1`,`test`.`t1`.`s1` in ( <materialize> (/* select#2 */ select `test`.`t2`.`s1` from `test`.`t2` ), <primary_index_lookup>(`test`.`t1`.`s1` in <temporary table> on distinct_key where ((`test`.`t1`.`s1` = `materialized subselect`.`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;
 id	select_type	table	type	possible_keys	key	key_len	ref	rows	filtered	Extra
 1	PRIMARY	t1	index	NULL	s1	6	NULL	3	100.00	Using index
-2	DEPENDENT SUBQUERY	t2	index_subquery	s1	s1	6	func	2	100.00	Using index; Using where; Full scan on NULL key
+2	SUBQUERY	t2	index	s1	s1	6	NULL	2	50.00	Using where; Using index
 Warnings:
-Note	1003	/* select#1 */ 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` < 'a2') having trigcond_if(outer_field_is_not_null, <is_not_null_test>(`test`.`t2`.`s1`), true)))))) AS `s1 NOT IN (SELECT s1 FROM t2 WHERE s1 < 'a2')` from `test`.`t1`
+Note	1003	/* select#1 */ select `test`.`t1`.`s1` AS `s1`,(not(<in_optimizer>(`test`.`t1`.`s1`,`test`.`t1`.`s1` in ( <materialize> (/* select#2 */ select `test`.`t2`.`s1` from `test`.`t2` where (`test`.`t2`.`s1` < 'a2') ), <primary_index_lookup>(`test`.`t1`.`s1` in <temporary table> on distinct_key where ((`test`.`t1`.`s1` = `materialized subselect`.`s1`))))))) AS `s1 NOT IN (SELECT s1 FROM t2 WHERE s1 < 'a2')` from `test`.`t1`
 drop table t1,t2;
 create table t2 (a int, b int);
 create table t3 (a int);
@@ -1641,9 +1641,9 @@ id	text
 explain extended select * from t1 where id not in (select id from t1 where id < 8);
 id	select_type	table	type	possible_keys	key	key_len	ref	rows	filtered	Extra
 1	PRIMARY	t1	ALL	NULL	NULL	NULL	NULL	12	100.00	Using where
-2	DEPENDENT SUBQUERY	t1	unique_subquery	PRIMARY	PRIMARY	4	func	1	100.00	Using index; Using where
+2	SUBQUERY	t1	range	PRIMARY	PRIMARY	4	NULL	7	100.00	Using where; Using index
 Warnings:
-Note	1003	/* select#1 */ select `test`.`t1`.`id` AS `id`,`test`.`t1`.`text` AS `text` from `test`.`t1` where (not(<in_optimizer>(`test`.`t1`.`id`,<exists>(<primary_index_lookup>(<cache>(`test`.`t1`.`id`) in t1 on PRIMARY where ((`test`.`t1`.`id` < 8) and (<cache>(`test`.`t1`.`id`) = `test`.`t1`.`id`)))))))
+Note	1003	/* select#1 */ select `test`.`t1`.`id` AS `id`,`test`.`t1`.`text` AS `text` from `test`.`t1` where (not(<in_optimizer>(`test`.`t1`.`id`,`test`.`t1`.`id` in ( <materialize> (/* select#2 */ select `test`.`t1`.`id` from `test`.`t1` where (`test`.`t1`.`id` < 8) ), <primary_index_lookup>(`test`.`t1`.`id` in <temporary table> on distinct_key where ((`test`.`t1`.`id` = `materialized subselect`.`id`)))))))
 explain extended select * from t1 as tt where not exists (select id from t1 where id < 8 and (id = tt.id or id is null) having id is not null);
 id	select_type	table	type	possible_keys	key	key_len	ref	rows	filtered	Extra
 1	PRIMARY	tt	ALL	NULL	NULL	NULL	NULL	12	100.00	Using where
@@ -2829,7 +2829,7 @@ INSERT INTO t2 VALUES (1),(2),(3);
 EXPLAIN SELECT a, a IN (SELECT a FROM t1) FROM t2;
 id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
 1	PRIMARY	t2	ALL	NULL	NULL	NULL	NULL	3	
-2	DEPENDENT SUBQUERY	t1	index_subquery	a	a	5	func	2	Using index; Full scan on NULL key
+2	SUBQUERY	t1	index	NULL	a	5	NULL	5	Using index
 SELECT a, a IN (SELECT a FROM t1) FROM t2;
 a	a IN (SELECT a FROM t1)
 1	1
@@ -4180,14 +4180,14 @@ CREATE TABLE t1 (pk int PRIMARY KEY, int
 INSERT INTO t1 VALUES (10,1), (14,1);
 CREATE TABLE t2 (pk int PRIMARY KEY, int_key int);
 INSERT INTO t2 VALUES (3,3), (5,NULL), (7,3);
-# should have eq_ref for t1
+# should have eq_ref for t1, unless subquery materialization is used
 EXPLAIN
 SELECT * FROM t2 outr
 WHERE outr.int_key NOT IN (SELECT t1.pk FROM t1, t2)  
 ORDER BY outr.pk;
 id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
 x	x	outr	ALL	x	x	x	x	x	x
-x	x	t1	eq_ref	x	x	x	x	x	x
+x	x	t1	index	x	x	x	x	x	x
 x	x	t2	index	x	x	x	x	x	x
 # should not crash on debug binaries
 SELECT * FROM t2 outr
@@ -4264,9 +4264,9 @@ Z
 explain extended select a in (select max(ie) from t1 where oref=4 group by grp) from t3;
 id	select_type	table	type	possible_keys	key	key_len	ref	rows	filtered	Extra
 1	PRIMARY	t3	ALL	NULL	NULL	NULL	NULL	2	100.00	
-2	DEPENDENT SUBQUERY	t1	ALL	NULL	NULL	NULL	NULL	6	100.00	Using where; Using temporary; Using filesort
+2	SUBQUERY	t1	ALL	NULL	NULL	NULL	NULL	6	100.00	Using where; Using temporary; Using filesort
 Warnings:
-Note	1003	/* select#1 */ select <in_optimizer>(`test`.`t3`.`a`,<exists>(/* select#2 */ select max(`test`.`t1`.`ie`) from `test`.`t1` where (`test`.`t1`.`oref` = 4) group by `test`.`t1`.`grp` having trigcond_if(outer_field_is_not_null, (<cache>(`test`.`t3`.`a`) = <ref_null_helper>(max(`test`.`t1`.`ie`))), true))) AS `a in (select max(ie) from t1 where oref=4 group by grp)` from `test`.`t3`
+Note	1003	/* select#1 */ select <in_optimizer>(`test`.`t3`.`a`,`test`.`t3`.`a` in ( <materialize> (/* select#2 */ select max(`test`.`t1`.`ie`) from `test`.`t1` where (`test`.`t1`.`oref` = 4) group by `test`.`t1`.`grp` ), <primary_index_lookup>(`test`.`t3`.`a` in <temporary table> on distinct_key where ((`test`.`t3`.`a` = `materialized subselect`.`max(ie)`))))) AS `a in (select max(ie) from t1 where oref=4 group by grp)` from `test`.`t3`
 drop table t1, t2, t3;
 create table t1 (a int, oref int, key(a));
 insert into t1 values 
@@ -4900,9 +4900,9 @@ SELECT a FROM t1, t2 WHERE a=b AND (b NO
 id	select_type	table	type	possible_keys	key	key_len	ref	rows	filtered	Extra
 1	PRIMARY	t1	ALL	NULL	NULL	NULL	NULL	3	100.00	Using where
 1	PRIMARY	t2	eq_ref	PRIMARY	PRIMARY	4	test.t1.a	1	100.00	Using index
-2	DEPENDENT SUBQUERY	t1	ALL	NULL	NULL	NULL	NULL	3	100.00	Using where
+2	SUBQUERY	t1	ALL	NULL	NULL	NULL	NULL	3	100.00	
 Warnings:
-Note	1003	/* select#1 */ select `test`.`t1`.`a` AS `a` from `test`.`t1` join `test`.`t2` where ((`test`.`t2`.`b` = `test`.`t1`.`a`) and (not(<in_optimizer>(`test`.`t1`.`a`,<exists>(/* select#2 */ select 1 from `test`.`t1` where ((<cache>(`test`.`t2`.`b`) = `test`.`t1`.`a`) or isnull(`test`.`t1`.`a`)) having <is_not_null_test>(`test`.`t1`.`a`))))))
+Note	1003	/* select#1 */ select `test`.`t1`.`a` AS `a` from `test`.`t1` join `test`.`t2` where ((`test`.`t2`.`b` = `test`.`t1`.`a`) and (not(<in_optimizer>(`test`.`t1`.`a`,`test`.`t1`.`a` in ( <materialize> (/* select#2 */ select `test`.`t1`.`a` from `test`.`t1` ), <primary_index_lookup>(`test`.`t1`.`a` in <temporary table> on distinct_key where ((`test`.`t1`.`a` = `materialized subselect`.`a`))))))))
 SELECT a FROM t1, t2 WHERE a=b AND (b NOT IN (SELECT a FROM t1));
 a
 SELECT a FROM t1, t2 WHERE a=b AND (b NOT IN (SELECT a FROM t1 WHERE a > 4));
@@ -4922,8 +4922,8 @@ WHERE t1.id NOT IN (SELECT t2.id FROM t2
 WHERE t3.name='xxx' AND t2.id=t3.id);
 id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
 1	PRIMARY	t1	ALL	NULL	NULL	NULL	NULL	4	Using where
-2	DEPENDENT SUBQUERY	t2	eq_ref	PRIMARY	PRIMARY	4	func	1	Using where; Using index; Full scan on NULL key
-2	DEPENDENT SUBQUERY	t3	eq_ref	PRIMARY	PRIMARY	4	func	1	Using where; Full scan on NULL key
+2	SUBQUERY	t2	index	PRIMARY	PRIMARY	4	NULL	3	Using index
+2	SUBQUERY	t3	eq_ref	PRIMARY	PRIMARY	4	test.t2.id	1	Using where
 SELECT * FROM t1
 WHERE t1.id NOT IN (SELECT t2.id FROM t2,t3 
 WHERE t3.name='xxx' AND t2.id=t3.id);
@@ -4956,7 +4956,7 @@ a
 EXPLAIN SELECT a FROM t1 WHERE a NOT IN (SELECT a FROM t2);
 id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
 1	PRIMARY	t1	ALL	NULL	NULL	NULL	NULL	4	Using where
-2	DEPENDENT SUBQUERY	t2	unique_subquery	PRIMARY	PRIMARY	4	func	1	Using index; Using where
+2	SUBQUERY	t2	index	NULL	PRIMARY	4	NULL	2	Using index
 DROP TABLE t1, t2;
 CREATE TABLE t1 (a INT);
 INSERT INTO t1 VALUES(1);
@@ -6280,8 +6280,8 @@ INSERT INTO t2 VALUES (1), (2);
 EXPLAIN  
 SELECT i FROM t1 WHERE (1) NOT IN (SELECT i FROM t2);
 id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
-1	PRIMARY	NULL	NULL	NULL	NULL	NULL	NULL	NULL	Impossible WHERE noticed after reading const tables
-2	DEPENDENT SUBQUERY	t2	index_subquery	k	k	5	const	2	Using index
+1	PRIMARY	t1	system	NULL	NULL	NULL	NULL	1	
+2	SUBQUERY	t2	index	NULL	k	5	NULL	2	Using index
 DROP TABLE t2;
 DROP TABLE t1;
 #
@@ -7045,7 +7045,7 @@ WHERE (c_sq1_alias1.col_int_nokey != @va
 OR c_sq1_alias1.pk != @var3));
 id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
 1	PRIMARY	NULL	NULL	NULL	NULL	NULL	NULL	NULL	Impossible WHERE noticed after reading const tables
-2	DEPENDENT SUBQUERY	c_sq1_alias1	system	PRIMARY,col_varchar_key	NULL	NULL	NULL	1	
+2	UNCACHEABLE SUBQUERY	c_sq1_alias1	system	PRIMARY	NULL	NULL	NULL	1	
 SELECT sq4_alias1.*
 FROM t1 AS sq4_alias1
 WHERE (sq4_alias1.col_varchar_key , sq4_alias1.col_varchar_nokey)
@@ -7068,7 +7068,7 @@ OR c_sq1_alias1.pk != @var3)) ) AS alias
 id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
 1	PRIMARY	<derived2>	system	NULL	NULL	NULL	NULL	0	const row not found
 2	DERIVED	NULL	NULL	NULL	NULL	NULL	NULL	NULL	no matching row in const table
-3	DEPENDENT SUBQUERY	c_sq1_alias1	system	PRIMARY,col_varchar_key	NULL	NULL	NULL	1	
+3	UNCACHEABLE SUBQUERY	c_sq1_alias1	system	PRIMARY	NULL	NULL	NULL	1	
 SELECT * FROM ( SELECT sq4_alias1.*
 FROM t1 AS sq4_alias1
 WHERE (sq4_alias1.col_varchar_key , sq4_alias1.col_varchar_nokey)
@@ -7080,4 +7080,31 @@ WHERE (c_sq1_alias1.col_int_nokey != @va
 OR c_sq1_alias1.pk != @var3)) ) AS alias3;
 pk	col_int_nokey	col_int_key	col_time_key	col_varchar_key	col_varchar_nokey
 DROP TABLE t1,t2;
+#
+# Test that indexsubquery_engine only does one lookup if
+# the technique is unique_subquery: does not try to read the
+# next row if the first row failed the subquery's WHERE
+# condition (here: b=3).
+#
+create table t1(a int);
+insert into t1 values(1),(2);
+create table t2(a int primary key, b int);
+insert into t2 values(1,10),(2,10);
+explain select * from t1 where a in (select a from t2 where b=3);
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	PRIMARY	t1	ALL	NULL	NULL	NULL	NULL	2	Using where
+1	PRIMARY	t2	eq_ref	PRIMARY	PRIMARY	4	test.t1.a	1	Using where
+flush status;
+select * from t1 where a in (select a from t2 where b=3);
+a
+show status like "handler_read%";
+Variable_name	Value
+Handler_read_first	0
+Handler_read_key	2
+Handler_read_last	0
+Handler_read_next	0
+Handler_read_prev	0
+Handler_read_rnd	0
+Handler_read_rnd_next	3
+drop table t1,t2;
 set optimizer_switch=default;

=== modified file 'mysql-test/r/subquery_all_bka.result'
--- a/mysql-test/r/subquery_all_bka.result	2012-02-07 14:50:31 +0000
+++ b/mysql-test/r/subquery_all_bka.result	2012-02-08 15:25:17 +0000
@@ -891,9 +891,9 @@ a	t1.a in (select t2.a from t2)
 explain extended SELECT t1.a, t1.a in (select t2.a from t2) FROM t1;
 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
-2	DEPENDENT SUBQUERY	t2	index_subquery	a	a	5	func	2	100.00	Using index
+2	SUBQUERY	t2	index	NULL	a	5	NULL	3	100.00	Using index
 Warnings:
-Note	1003	/* select#1 */ 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`
+Note	1003	/* select#1 */ select `test`.`t1`.`a` AS `a`,<in_optimizer>(`test`.`t1`.`a`,`test`.`t1`.`a` in ( <materialize> (/* select#2 */ select `test`.`t2`.`a` from `test`.`t2` ), <primary_index_lookup>(`test`.`t1`.`a` in <temporary table> on distinct_key where ((`test`.`t1`.`a` = `materialized subselect`.`a`))))) AS `t1.a in (select t2.a from t2)` from `test`.`t1`
 CREATE TABLE t3 (a int(11) default '0');
 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;
@@ -905,10 +905,10 @@ a	t1.a in (select t2.a from t2,t3 where 
 explain extended SELECT t1.a, t1.a in (select t2.a from t2,t3 where t3.a=t2.a) FROM t1;
 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
-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; Using join buffer (Block Nested Loop)
+2	SUBQUERY	t2	index	a	a	5	NULL	3	100.00	Using index
+2	SUBQUERY	t3	ALL	NULL	NULL	NULL	NULL	3	100.00	Using where; Using join buffer (Block Nested Loop)
 Warnings:
-Note	1003	/* select#1 */ select `test`.`t1`.`a` AS `a`,<in_optimizer>(`test`.`t1`.`a`,<exists>(/* select#2 */ select 1 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`
+Note	1003	/* select#1 */ select `test`.`t1`.`a` AS `a`,<in_optimizer>(`test`.`t1`.`a`,`test`.`t1`.`a` in ( <materialize> (/* select#2 */ select `test`.`t2`.`a` from `test`.`t2` join `test`.`t3` where (`test`.`t3`.`a` = `test`.`t2`.`a`) ), <primary_index_lookup>(`test`.`t1`.`a` in <temporary table> on distinct_key where ((`test`.`t1`.`a` = `materialized subselect`.`a`))))) AS `t1.a in (select t2.a from t2,t3 where t3.a=t2.a)` from `test`.`t1`
 drop table t1,t2,t3;
 create table t1 (a float);
 select 10.5 IN (SELECT * from t1 LIMIT 1);
@@ -1369,27 +1369,27 @@ a3	1
 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
 1	PRIMARY	t1	index	NULL	s1	6	NULL	3	100.00	Using index
-2	DEPENDENT SUBQUERY	t2	index_subquery	s1	s1	6	func	2	100.00	Using index; Full scan on NULL key
+2	SUBQUERY	t2	index	NULL	s1	6	NULL	2	100.00	Using index
 Warnings:
-Note	1003	/* select#1 */ 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_if(outer_field_is_not_null, <is_not_null_test>(`test`.`t2`.`s1`), true)))))) AS `s1 NOT IN (SELECT s1 FROM t2)` from `test`.`t1`
+Note	1003	/* select#1 */ select `test`.`t1`.`s1` AS `s1`,(not(<in_optimizer>(`test`.`t1`.`s1`,`test`.`t1`.`s1` in ( <materialize> (/* select#2 */ select `test`.`t2`.`s1` from `test`.`t2` ), <primary_index_lookup>(`test`.`t1`.`s1` in <temporary table> on distinct_key where ((`test`.`t1`.`s1` = `materialized subselect`.`s1`))))))) AS `s1 NOT IN (SELECT s1 FROM t2)` from `test`.`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
 1	PRIMARY	t1	index	NULL	s1	6	NULL	3	100.00	Using index
-2	DEPENDENT SUBQUERY	t2	index_subquery	s1	s1	6	func	2	100.00	Using index; Full scan on NULL key
+2	SUBQUERY	t2	index	NULL	s1	6	NULL	2	100.00	Using index
 Warnings:
-Note	1003	/* select#1 */ 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_if(outer_field_is_not_null, <is_not_null_test>(`test`.`t2`.`s1`), true)))) AS `s1 = ANY (SELECT s1 FROM t2)` from `test`.`t1`
+Note	1003	/* select#1 */ select `test`.`t1`.`s1` AS `s1`,<in_optimizer>(`test`.`t1`.`s1`,`test`.`t1`.`s1` in ( <materialize> (/* select#2 */ select `test`.`t2`.`s1` from `test`.`t2` ), <primary_index_lookup>(`test`.`t1`.`s1` in <temporary table> on distinct_key where ((`test`.`t1`.`s1` = `materialized subselect`.`s1`))))) AS `s1 = ANY (SELECT s1 FROM t2)` from `test`.`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
 1	PRIMARY	t1	index	NULL	s1	6	NULL	3	100.00	Using index
-2	DEPENDENT SUBQUERY	t2	index_subquery	s1	s1	6	func	2	100.00	Using index; Full scan on NULL key
+2	SUBQUERY	t2	index	NULL	s1	6	NULL	2	100.00	Using index
 Warnings:
-Note	1003	/* select#1 */ 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_if(outer_field_is_not_null, <is_not_null_test>(`test`.`t2`.`s1`), true)))))) AS `s1 <> ALL (SELECT s1 FROM t2)` from `test`.`t1`
+Note	1003	/* select#1 */ select `test`.`t1`.`s1` AS `s1`,(not(<in_optimizer>(`test`.`t1`.`s1`,`test`.`t1`.`s1` in ( <materialize> (/* select#2 */ select `test`.`t2`.`s1` from `test`.`t2` ), <primary_index_lookup>(`test`.`t1`.`s1` in <temporary table> on distinct_key where ((`test`.`t1`.`s1` = `materialized subselect`.`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;
 id	select_type	table	type	possible_keys	key	key_len	ref	rows	filtered	Extra
 1	PRIMARY	t1	index	NULL	s1	6	NULL	3	100.00	Using index
-2	DEPENDENT SUBQUERY	t2	index_subquery	s1	s1	6	func	2	100.00	Using index; Using where; Full scan on NULL key
+2	SUBQUERY	t2	index	s1	s1	6	NULL	2	50.00	Using where; Using index
 Warnings:
-Note	1003	/* select#1 */ 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` < 'a2') having trigcond_if(outer_field_is_not_null, <is_not_null_test>(`test`.`t2`.`s1`), true)))))) AS `s1 NOT IN (SELECT s1 FROM t2 WHERE s1 < 'a2')` from `test`.`t1`
+Note	1003	/* select#1 */ select `test`.`t1`.`s1` AS `s1`,(not(<in_optimizer>(`test`.`t1`.`s1`,`test`.`t1`.`s1` in ( <materialize> (/* select#2 */ select `test`.`t2`.`s1` from `test`.`t2` where (`test`.`t2`.`s1` < 'a2') ), <primary_index_lookup>(`test`.`t1`.`s1` in <temporary table> on distinct_key where ((`test`.`t1`.`s1` = `materialized subselect`.`s1`))))))) AS `s1 NOT IN (SELECT s1 FROM t2 WHERE s1 < 'a2')` from `test`.`t1`
 drop table t1,t2;
 create table t2 (a int, b int);
 create table t3 (a int);
@@ -1642,9 +1642,9 @@ id	text
 explain extended select * from t1 where id not in (select id from t1 where id < 8);
 id	select_type	table	type	possible_keys	key	key_len	ref	rows	filtered	Extra
 1	PRIMARY	t1	ALL	NULL	NULL	NULL	NULL	12	100.00	Using where
-2	DEPENDENT SUBQUERY	t1	unique_subquery	PRIMARY	PRIMARY	4	func	1	100.00	Using index; Using where
+2	SUBQUERY	t1	range	PRIMARY	PRIMARY	4	NULL	7	100.00	Using where; Using index
 Warnings:
-Note	1003	/* select#1 */ select `test`.`t1`.`id` AS `id`,`test`.`t1`.`text` AS `text` from `test`.`t1` where (not(<in_optimizer>(`test`.`t1`.`id`,<exists>(<primary_index_lookup>(<cache>(`test`.`t1`.`id`) in t1 on PRIMARY where ((`test`.`t1`.`id` < 8) and (<cache>(`test`.`t1`.`id`) = `test`.`t1`.`id`)))))))
+Note	1003	/* select#1 */ select `test`.`t1`.`id` AS `id`,`test`.`t1`.`text` AS `text` from `test`.`t1` where (not(<in_optimizer>(`test`.`t1`.`id`,`test`.`t1`.`id` in ( <materialize> (/* select#2 */ select `test`.`t1`.`id` from `test`.`t1` where (`test`.`t1`.`id` < 8) ), <primary_index_lookup>(`test`.`t1`.`id` in <temporary table> on distinct_key where ((`test`.`t1`.`id` = `materialized subselect`.`id`)))))))
 explain extended select * from t1 as tt where not exists (select id from t1 where id < 8 and (id = tt.id or id is null) having id is not null);
 id	select_type	table	type	possible_keys	key	key_len	ref	rows	filtered	Extra
 1	PRIMARY	tt	ALL	NULL	NULL	NULL	NULL	12	100.00	Using where
@@ -2830,7 +2830,7 @@ INSERT INTO t2 VALUES (1),(2),(3);
 EXPLAIN SELECT a, a IN (SELECT a FROM t1) FROM t2;
 id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
 1	PRIMARY	t2	ALL	NULL	NULL	NULL	NULL	3	
-2	DEPENDENT SUBQUERY	t1	index_subquery	a	a	5	func	2	Using index; Full scan on NULL key
+2	SUBQUERY	t1	index	NULL	a	5	NULL	5	Using index
 SELECT a, a IN (SELECT a FROM t1) FROM t2;
 a	a IN (SELECT a FROM t1)
 1	1
@@ -4181,14 +4181,14 @@ CREATE TABLE t1 (pk int PRIMARY KEY, int
 INSERT INTO t1 VALUES (10,1), (14,1);
 CREATE TABLE t2 (pk int PRIMARY KEY, int_key int);
 INSERT INTO t2 VALUES (3,3), (5,NULL), (7,3);
-# should have eq_ref for t1
+# should have eq_ref for t1, unless subquery materialization is used
 EXPLAIN
 SELECT * FROM t2 outr
 WHERE outr.int_key NOT IN (SELECT t1.pk FROM t1, t2)  
 ORDER BY outr.pk;
 id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
 x	x	outr	ALL	x	x	x	x	x	x
-x	x	t1	eq_ref	x	x	x	x	x	x
+x	x	t1	index	x	x	x	x	x	x
 x	x	t2	index	x	x	x	x	x	x
 # should not crash on debug binaries
 SELECT * FROM t2 outr
@@ -4265,9 +4265,9 @@ Z
 explain extended select a in (select max(ie) from t1 where oref=4 group by grp) from t3;
 id	select_type	table	type	possible_keys	key	key_len	ref	rows	filtered	Extra
 1	PRIMARY	t3	ALL	NULL	NULL	NULL	NULL	2	100.00	
-2	DEPENDENT SUBQUERY	t1	ALL	NULL	NULL	NULL	NULL	6	100.00	Using where; Using temporary; Using filesort
+2	SUBQUERY	t1	ALL	NULL	NULL	NULL	NULL	6	100.00	Using where; Using temporary; Using filesort
 Warnings:
-Note	1003	/* select#1 */ select <in_optimizer>(`test`.`t3`.`a`,<exists>(/* select#2 */ select max(`test`.`t1`.`ie`) from `test`.`t1` where (`test`.`t1`.`oref` = 4) group by `test`.`t1`.`grp` having trigcond_if(outer_field_is_not_null, (<cache>(`test`.`t3`.`a`) = <ref_null_helper>(max(`test`.`t1`.`ie`))), true))) AS `a in (select max(ie) from t1 where oref=4 group by grp)` from `test`.`t3`
+Note	1003	/* select#1 */ select <in_optimizer>(`test`.`t3`.`a`,`test`.`t3`.`a` in ( <materialize> (/* select#2 */ select max(`test`.`t1`.`ie`) from `test`.`t1` where (`test`.`t1`.`oref` = 4) group by `test`.`t1`.`grp` ), <primary_index_lookup>(`test`.`t3`.`a` in <temporary table> on distinct_key where ((`test`.`t3`.`a` = `materialized subselect`.`max(ie)`))))) AS `a in (select max(ie) from t1 where oref=4 group by grp)` from `test`.`t3`
 drop table t1, t2, t3;
 create table t1 (a int, oref int, key(a));
 insert into t1 values 
@@ -4901,9 +4901,9 @@ SELECT a FROM t1, t2 WHERE a=b AND (b NO
 id	select_type	table	type	possible_keys	key	key_len	ref	rows	filtered	Extra
 1	PRIMARY	t1	ALL	NULL	NULL	NULL	NULL	3	100.00	Using where
 1	PRIMARY	t2	eq_ref	PRIMARY	PRIMARY	4	test.t1.a	1	100.00	Using index
-2	DEPENDENT SUBQUERY	t1	ALL	NULL	NULL	NULL	NULL	3	100.00	Using where
+2	SUBQUERY	t1	ALL	NULL	NULL	NULL	NULL	3	100.00	
 Warnings:
-Note	1003	/* select#1 */ select `test`.`t1`.`a` AS `a` from `test`.`t1` join `test`.`t2` where ((`test`.`t2`.`b` = `test`.`t1`.`a`) and (not(<in_optimizer>(`test`.`t1`.`a`,<exists>(/* select#2 */ select 1 from `test`.`t1` where ((<cache>(`test`.`t2`.`b`) = `test`.`t1`.`a`) or isnull(`test`.`t1`.`a`)) having <is_not_null_test>(`test`.`t1`.`a`))))))
+Note	1003	/* select#1 */ select `test`.`t1`.`a` AS `a` from `test`.`t1` join `test`.`t2` where ((`test`.`t2`.`b` = `test`.`t1`.`a`) and (not(<in_optimizer>(`test`.`t1`.`a`,`test`.`t1`.`a` in ( <materialize> (/* select#2 */ select `test`.`t1`.`a` from `test`.`t1` ), <primary_index_lookup>(`test`.`t1`.`a` in <temporary table> on distinct_key where ((`test`.`t1`.`a` = `materialized subselect`.`a`))))))))
 SELECT a FROM t1, t2 WHERE a=b AND (b NOT IN (SELECT a FROM t1));
 a
 SELECT a FROM t1, t2 WHERE a=b AND (b NOT IN (SELECT a FROM t1 WHERE a > 4));
@@ -4923,8 +4923,8 @@ WHERE t1.id NOT IN (SELECT t2.id FROM t2
 WHERE t3.name='xxx' AND t2.id=t3.id);
 id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
 1	PRIMARY	t1	ALL	NULL	NULL	NULL	NULL	4	Using where
-2	DEPENDENT SUBQUERY	t2	eq_ref	PRIMARY	PRIMARY	4	func	1	Using where; Using index; Full scan on NULL key
-2	DEPENDENT SUBQUERY	t3	eq_ref	PRIMARY	PRIMARY	4	func	1	Using where; Full scan on NULL key
+2	SUBQUERY	t2	index	PRIMARY	PRIMARY	4	NULL	3	Using index
+2	SUBQUERY	t3	eq_ref	PRIMARY	PRIMARY	4	test.t2.id	1	Using where; Using join buffer (Batched Key Access)
 SELECT * FROM t1
 WHERE t1.id NOT IN (SELECT t2.id FROM t2,t3 
 WHERE t3.name='xxx' AND t2.id=t3.id);
@@ -4957,7 +4957,7 @@ a
 EXPLAIN SELECT a FROM t1 WHERE a NOT IN (SELECT a FROM t2);
 id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
 1	PRIMARY	t1	ALL	NULL	NULL	NULL	NULL	4	Using where
-2	DEPENDENT SUBQUERY	t2	unique_subquery	PRIMARY	PRIMARY	4	func	1	Using index; Using where
+2	SUBQUERY	t2	index	NULL	PRIMARY	4	NULL	2	Using index
 DROP TABLE t1, t2;
 CREATE TABLE t1 (a INT);
 INSERT INTO t1 VALUES(1);
@@ -6281,8 +6281,8 @@ INSERT INTO t2 VALUES (1), (2);
 EXPLAIN  
 SELECT i FROM t1 WHERE (1) NOT IN (SELECT i FROM t2);
 id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
-1	PRIMARY	NULL	NULL	NULL	NULL	NULL	NULL	NULL	Impossible WHERE noticed after reading const tables
-2	DEPENDENT SUBQUERY	t2	index_subquery	k	k	5	const	2	Using index
+1	PRIMARY	t1	system	NULL	NULL	NULL	NULL	1	
+2	SUBQUERY	t2	index	NULL	k	5	NULL	2	Using index
 DROP TABLE t2;
 DROP TABLE t1;
 #
@@ -7046,7 +7046,7 @@ WHERE (c_sq1_alias1.col_int_nokey != @va
 OR c_sq1_alias1.pk != @var3));
 id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
 1	PRIMARY	NULL	NULL	NULL	NULL	NULL	NULL	NULL	Impossible WHERE noticed after reading const tables
-2	DEPENDENT SUBQUERY	c_sq1_alias1	system	PRIMARY,col_varchar_key	NULL	NULL	NULL	1	
+2	UNCACHEABLE SUBQUERY	c_sq1_alias1	system	PRIMARY	NULL	NULL	NULL	1	
 SELECT sq4_alias1.*
 FROM t1 AS sq4_alias1
 WHERE (sq4_alias1.col_varchar_key , sq4_alias1.col_varchar_nokey)
@@ -7069,7 +7069,7 @@ OR c_sq1_alias1.pk != @var3)) ) AS alias
 id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
 1	PRIMARY	<derived2>	system	NULL	NULL	NULL	NULL	0	const row not found
 2	DERIVED	NULL	NULL	NULL	NULL	NULL	NULL	NULL	no matching row in const table
-3	DEPENDENT SUBQUERY	c_sq1_alias1	system	PRIMARY,col_varchar_key	NULL	NULL	NULL	1	
+3	UNCACHEABLE SUBQUERY	c_sq1_alias1	system	PRIMARY	NULL	NULL	NULL	1	
 SELECT * FROM ( SELECT sq4_alias1.*
 FROM t1 AS sq4_alias1
 WHERE (sq4_alias1.col_varchar_key , sq4_alias1.col_varchar_nokey)
@@ -7081,5 +7081,32 @@ WHERE (c_sq1_alias1.col_int_nokey != @va
 OR c_sq1_alias1.pk != @var3)) ) AS alias3;
 pk	col_int_nokey	col_int_key	col_time_key	col_varchar_key	col_varchar_nokey
 DROP TABLE t1,t2;
+#
+# Test that indexsubquery_engine only does one lookup if
+# the technique is unique_subquery: does not try to read the
+# next row if the first row failed the subquery's WHERE
+# condition (here: b=3).
+#
+create table t1(a int);
+insert into t1 values(1),(2);
+create table t2(a int primary key, b int);
+insert into t2 values(1,10),(2,10);
+explain select * from t1 where a in (select a from t2 where b=3);
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	PRIMARY	t1	ALL	NULL	NULL	NULL	NULL	2	Using where
+1	PRIMARY	t2	eq_ref	PRIMARY	PRIMARY	4	test.t1.a	1	Using where; Using join buffer (Batched Key Access)
+flush status;
+select * from t1 where a in (select a from t2 where b=3);
+a
+show status like "handler_read%";
+Variable_name	Value
+Handler_read_first	0
+Handler_read_key	2
+Handler_read_last	0
+Handler_read_next	2
+Handler_read_prev	0
+Handler_read_rnd	2
+Handler_read_rnd_next	3
+drop table t1,t2;
 set optimizer_switch=default;
 set optimizer_switch=default;

=== modified file 'mysql-test/r/subquery_all_bka_nixbnl.result'
--- a/mysql-test/r/subquery_all_bka_nixbnl.result	2012-02-07 14:50:31 +0000
+++ b/mysql-test/r/subquery_all_bka_nixbnl.result	2012-02-08 15:25:17 +0000
@@ -891,9 +891,9 @@ a	t1.a in (select t2.a from t2)
 explain extended SELECT t1.a, t1.a in (select t2.a from t2) FROM t1;
 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
-2	DEPENDENT SUBQUERY	t2	index_subquery	a	a	5	func	2	100.00	Using index
+2	SUBQUERY	t2	index	NULL	a	5	NULL	3	100.00	Using index
 Warnings:
-Note	1003	/* select#1 */ 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`
+Note	1003	/* select#1 */ select `test`.`t1`.`a` AS `a`,<in_optimizer>(`test`.`t1`.`a`,`test`.`t1`.`a` in ( <materialize> (/* select#2 */ select `test`.`t2`.`a` from `test`.`t2` ), <primary_index_lookup>(`test`.`t1`.`a` in <temporary table> on distinct_key where ((`test`.`t1`.`a` = `materialized subselect`.`a`))))) AS `t1.a in (select t2.a from t2)` from `test`.`t1`
 CREATE TABLE t3 (a int(11) default '0');
 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;
@@ -905,10 +905,10 @@ a	t1.a in (select t2.a from t2,t3 where 
 explain extended SELECT t1.a, t1.a in (select t2.a from t2,t3 where t3.a=t2.a) FROM t1;
 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
-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	SUBQUERY	t3	ALL	NULL	NULL	NULL	NULL	3	100.00	Using where
+2	SUBQUERY	t2	ref	a	a	5	test.t3.a	2	100.00	Using index
 Warnings:
-Note	1003	/* select#1 */ select `test`.`t1`.`a` AS `a`,<in_optimizer>(`test`.`t1`.`a`,<exists>(/* select#2 */ select 1 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`
+Note	1003	/* select#1 */ select `test`.`t1`.`a` AS `a`,<in_optimizer>(`test`.`t1`.`a`,`test`.`t1`.`a` in ( <materialize> (/* select#2 */ select `test`.`t2`.`a` from `test`.`t2` join `test`.`t3` where (`test`.`t2`.`a` = `test`.`t3`.`a`) ), <primary_index_lookup>(`test`.`t1`.`a` in <temporary table> on distinct_key where ((`test`.`t1`.`a` = `materialized subselect`.`a`))))) AS `t1.a in (select t2.a from t2,t3 where t3.a=t2.a)` from `test`.`t1`
 drop table t1,t2,t3;
 create table t1 (a float);
 select 10.5 IN (SELECT * from t1 LIMIT 1);
@@ -1369,27 +1369,27 @@ a3	1
 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
 1	PRIMARY	t1	index	NULL	s1	6	NULL	3	100.00	Using index
-2	DEPENDENT SUBQUERY	t2	index_subquery	s1	s1	6	func	2	100.00	Using index; Full scan on NULL key
+2	SUBQUERY	t2	index	NULL	s1	6	NULL	2	100.00	Using index
 Warnings:
-Note	1003	/* select#1 */ 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_if(outer_field_is_not_null, <is_not_null_test>(`test`.`t2`.`s1`), true)))))) AS `s1 NOT IN (SELECT s1 FROM t2)` from `test`.`t1`
+Note	1003	/* select#1 */ select `test`.`t1`.`s1` AS `s1`,(not(<in_optimizer>(`test`.`t1`.`s1`,`test`.`t1`.`s1` in ( <materialize> (/* select#2 */ select `test`.`t2`.`s1` from `test`.`t2` ), <primary_index_lookup>(`test`.`t1`.`s1` in <temporary table> on distinct_key where ((`test`.`t1`.`s1` = `materialized subselect`.`s1`))))))) AS `s1 NOT IN (SELECT s1 FROM t2)` from `test`.`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
 1	PRIMARY	t1	index	NULL	s1	6	NULL	3	100.00	Using index
-2	DEPENDENT SUBQUERY	t2	index_subquery	s1	s1	6	func	2	100.00	Using index; Full scan on NULL key
+2	SUBQUERY	t2	index	NULL	s1	6	NULL	2	100.00	Using index
 Warnings:
-Note	1003	/* select#1 */ 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_if(outer_field_is_not_null, <is_not_null_test>(`test`.`t2`.`s1`), true)))) AS `s1 = ANY (SELECT s1 FROM t2)` from `test`.`t1`
+Note	1003	/* select#1 */ select `test`.`t1`.`s1` AS `s1`,<in_optimizer>(`test`.`t1`.`s1`,`test`.`t1`.`s1` in ( <materialize> (/* select#2 */ select `test`.`t2`.`s1` from `test`.`t2` ), <primary_index_lookup>(`test`.`t1`.`s1` in <temporary table> on distinct_key where ((`test`.`t1`.`s1` = `materialized subselect`.`s1`))))) AS `s1 = ANY (SELECT s1 FROM t2)` from `test`.`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
 1	PRIMARY	t1	index	NULL	s1	6	NULL	3	100.00	Using index
-2	DEPENDENT SUBQUERY	t2	index_subquery	s1	s1	6	func	2	100.00	Using index; Full scan on NULL key
+2	SUBQUERY	t2	index	NULL	s1	6	NULL	2	100.00	Using index
 Warnings:
-Note	1003	/* select#1 */ 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_if(outer_field_is_not_null, <is_not_null_test>(`test`.`t2`.`s1`), true)))))) AS `s1 <> ALL (SELECT s1 FROM t2)` from `test`.`t1`
+Note	1003	/* select#1 */ select `test`.`t1`.`s1` AS `s1`,(not(<in_optimizer>(`test`.`t1`.`s1`,`test`.`t1`.`s1` in ( <materialize> (/* select#2 */ select `test`.`t2`.`s1` from `test`.`t2` ), <primary_index_lookup>(`test`.`t1`.`s1` in <temporary table> on distinct_key where ((`test`.`t1`.`s1` = `materialized subselect`.`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;
 id	select_type	table	type	possible_keys	key	key_len	ref	rows	filtered	Extra
 1	PRIMARY	t1	index	NULL	s1	6	NULL	3	100.00	Using index
-2	DEPENDENT SUBQUERY	t2	index_subquery	s1	s1	6	func	2	100.00	Using index; Using where; Full scan on NULL key
+2	SUBQUERY	t2	index	s1	s1	6	NULL	2	50.00	Using where; Using index
 Warnings:
-Note	1003	/* select#1 */ 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` < 'a2') having trigcond_if(outer_field_is_not_null, <is_not_null_test>(`test`.`t2`.`s1`), true)))))) AS `s1 NOT IN (SELECT s1 FROM t2 WHERE s1 < 'a2')` from `test`.`t1`
+Note	1003	/* select#1 */ select `test`.`t1`.`s1` AS `s1`,(not(<in_optimizer>(`test`.`t1`.`s1`,`test`.`t1`.`s1` in ( <materialize> (/* select#2 */ select `test`.`t2`.`s1` from `test`.`t2` where (`test`.`t2`.`s1` < 'a2') ), <primary_index_lookup>(`test`.`t1`.`s1` in <temporary table> on distinct_key where ((`test`.`t1`.`s1` = `materialized subselect`.`s1`))))))) AS `s1 NOT IN (SELECT s1 FROM t2 WHERE s1 < 'a2')` from `test`.`t1`
 drop table t1,t2;
 create table t2 (a int, b int);
 create table t3 (a int);
@@ -1642,9 +1642,9 @@ id	text
 explain extended select * from t1 where id not in (select id from t1 where id < 8);
 id	select_type	table	type	possible_keys	key	key_len	ref	rows	filtered	Extra
 1	PRIMARY	t1	ALL	NULL	NULL	NULL	NULL	12	100.00	Using where
-2	DEPENDENT SUBQUERY	t1	unique_subquery	PRIMARY	PRIMARY	4	func	1	100.00	Using index; Using where
+2	SUBQUERY	t1	range	PRIMARY	PRIMARY	4	NULL	7	100.00	Using where; Using index
 Warnings:
-Note	1003	/* select#1 */ select `test`.`t1`.`id` AS `id`,`test`.`t1`.`text` AS `text` from `test`.`t1` where (not(<in_optimizer>(`test`.`t1`.`id`,<exists>(<primary_index_lookup>(<cache>(`test`.`t1`.`id`) in t1 on PRIMARY where ((`test`.`t1`.`id` < 8) and (<cache>(`test`.`t1`.`id`) = `test`.`t1`.`id`)))))))
+Note	1003	/* select#1 */ select `test`.`t1`.`id` AS `id`,`test`.`t1`.`text` AS `text` from `test`.`t1` where (not(<in_optimizer>(`test`.`t1`.`id`,`test`.`t1`.`id` in ( <materialize> (/* select#2 */ select `test`.`t1`.`id` from `test`.`t1` where (`test`.`t1`.`id` < 8) ), <primary_index_lookup>(`test`.`t1`.`id` in <temporary table> on distinct_key where ((`test`.`t1`.`id` = `materialized subselect`.`id`)))))))
 explain extended select * from t1 as tt where not exists (select id from t1 where id < 8 and (id = tt.id or id is null) having id is not null);
 id	select_type	table	type	possible_keys	key	key_len	ref	rows	filtered	Extra
 1	PRIMARY	tt	ALL	NULL	NULL	NULL	NULL	12	100.00	Using where
@@ -2830,7 +2830,7 @@ INSERT INTO t2 VALUES (1),(2),(3);
 EXPLAIN SELECT a, a IN (SELECT a FROM t1) FROM t2;
 id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
 1	PRIMARY	t2	ALL	NULL	NULL	NULL	NULL	3	
-2	DEPENDENT SUBQUERY	t1	index_subquery	a	a	5	func	2	Using index; Full scan on NULL key
+2	SUBQUERY	t1	index	NULL	a	5	NULL	5	Using index
 SELECT a, a IN (SELECT a FROM t1) FROM t2;
 a	a IN (SELECT a FROM t1)
 1	1
@@ -4181,14 +4181,14 @@ CREATE TABLE t1 (pk int PRIMARY KEY, int
 INSERT INTO t1 VALUES (10,1), (14,1);
 CREATE TABLE t2 (pk int PRIMARY KEY, int_key int);
 INSERT INTO t2 VALUES (3,3), (5,NULL), (7,3);
-# should have eq_ref for t1
+# should have eq_ref for t1, unless subquery materialization is used
 EXPLAIN
 SELECT * FROM t2 outr
 WHERE outr.int_key NOT IN (SELECT t1.pk FROM t1, t2)  
 ORDER BY outr.pk;
 id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
 x	x	outr	ALL	x	x	x	x	x	x
-x	x	t1	eq_ref	x	x	x	x	x	x
+x	x	t1	index	x	x	x	x	x	x
 x	x	t2	index	x	x	x	x	x	x
 # should not crash on debug binaries
 SELECT * FROM t2 outr
@@ -4265,9 +4265,9 @@ Z
 explain extended select a in (select max(ie) from t1 where oref=4 group by grp) from t3;
 id	select_type	table	type	possible_keys	key	key_len	ref	rows	filtered	Extra
 1	PRIMARY	t3	ALL	NULL	NULL	NULL	NULL	2	100.00	
-2	DEPENDENT SUBQUERY	t1	ALL	NULL	NULL	NULL	NULL	6	100.00	Using where; Using temporary; Using filesort
+2	SUBQUERY	t1	ALL	NULL	NULL	NULL	NULL	6	100.00	Using where; Using temporary; Using filesort
 Warnings:
-Note	1003	/* select#1 */ select <in_optimizer>(`test`.`t3`.`a`,<exists>(/* select#2 */ select max(`test`.`t1`.`ie`) from `test`.`t1` where (`test`.`t1`.`oref` = 4) group by `test`.`t1`.`grp` having trigcond_if(outer_field_is_not_null, (<cache>(`test`.`t3`.`a`) = <ref_null_helper>(max(`test`.`t1`.`ie`))), true))) AS `a in (select max(ie) from t1 where oref=4 group by grp)` from `test`.`t3`
+Note	1003	/* select#1 */ select <in_optimizer>(`test`.`t3`.`a`,`test`.`t3`.`a` in ( <materialize> (/* select#2 */ select max(`test`.`t1`.`ie`) from `test`.`t1` where (`test`.`t1`.`oref` = 4) group by `test`.`t1`.`grp` ), <primary_index_lookup>(`test`.`t3`.`a` in <temporary table> on distinct_key where ((`test`.`t3`.`a` = `materialized subselect`.`max(ie)`))))) AS `a in (select max(ie) from t1 where oref=4 group by grp)` from `test`.`t3`
 drop table t1, t2, t3;
 create table t1 (a int, oref int, key(a));
 insert into t1 values 
@@ -4901,9 +4901,9 @@ SELECT a FROM t1, t2 WHERE a=b AND (b NO
 id	select_type	table	type	possible_keys	key	key_len	ref	rows	filtered	Extra
 1	PRIMARY	t1	ALL	NULL	NULL	NULL	NULL	3	100.00	Using where
 1	PRIMARY	t2	eq_ref	PRIMARY	PRIMARY	4	test.t1.a	1	100.00	Using index
-2	DEPENDENT SUBQUERY	t1	ALL	NULL	NULL	NULL	NULL	3	100.00	Using where
+2	SUBQUERY	t1	ALL	NULL	NULL	NULL	NULL	3	100.00	
 Warnings:
-Note	1003	/* select#1 */ select `test`.`t1`.`a` AS `a` from `test`.`t1` join `test`.`t2` where ((`test`.`t2`.`b` = `test`.`t1`.`a`) and (not(<in_optimizer>(`test`.`t1`.`a`,<exists>(/* select#2 */ select 1 from `test`.`t1` where ((<cache>(`test`.`t2`.`b`) = `test`.`t1`.`a`) or isnull(`test`.`t1`.`a`)) having <is_not_null_test>(`test`.`t1`.`a`))))))
+Note	1003	/* select#1 */ select `test`.`t1`.`a` AS `a` from `test`.`t1` join `test`.`t2` where ((`test`.`t2`.`b` = `test`.`t1`.`a`) and (not(<in_optimizer>(`test`.`t1`.`a`,`test`.`t1`.`a` in ( <materialize> (/* select#2 */ select `test`.`t1`.`a` from `test`.`t1` ), <primary_index_lookup>(`test`.`t1`.`a` in <temporary table> on distinct_key where ((`test`.`t1`.`a` = `materialized subselect`.`a`))))))))
 SELECT a FROM t1, t2 WHERE a=b AND (b NOT IN (SELECT a FROM t1));
 a
 SELECT a FROM t1, t2 WHERE a=b AND (b NOT IN (SELECT a FROM t1 WHERE a > 4));
@@ -4923,8 +4923,8 @@ WHERE t1.id NOT IN (SELECT t2.id FROM t2
 WHERE t3.name='xxx' AND t2.id=t3.id);
 id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
 1	PRIMARY	t1	ALL	NULL	NULL	NULL	NULL	4	Using where
-2	DEPENDENT SUBQUERY	t2	eq_ref	PRIMARY	PRIMARY	4	func	1	Using where; Using index; Full scan on NULL key
-2	DEPENDENT SUBQUERY	t3	eq_ref	PRIMARY	PRIMARY	4	func	1	Using where; Full scan on NULL key
+2	SUBQUERY	t2	index	PRIMARY	PRIMARY	4	NULL	3	Using index
+2	SUBQUERY	t3	eq_ref	PRIMARY	PRIMARY	4	test.t2.id	1	Using where; Using join buffer (Batched Key Access)
 SELECT * FROM t1
 WHERE t1.id NOT IN (SELECT t2.id FROM t2,t3 
 WHERE t3.name='xxx' AND t2.id=t3.id);
@@ -4957,7 +4957,7 @@ a
 EXPLAIN SELECT a FROM t1 WHERE a NOT IN (SELECT a FROM t2);
 id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
 1	PRIMARY	t1	ALL	NULL	NULL	NULL	NULL	4	Using where
-2	DEPENDENT SUBQUERY	t2	unique_subquery	PRIMARY	PRIMARY	4	func	1	Using index; Using where
+2	SUBQUERY	t2	index	NULL	PRIMARY	4	NULL	2	Using index
 DROP TABLE t1, t2;
 CREATE TABLE t1 (a INT);
 INSERT INTO t1 VALUES(1);
@@ -6281,8 +6281,8 @@ INSERT INTO t2 VALUES (1), (2);
 EXPLAIN  
 SELECT i FROM t1 WHERE (1) NOT IN (SELECT i FROM t2);
 id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
-1	PRIMARY	NULL	NULL	NULL	NULL	NULL	NULL	NULL	Impossible WHERE noticed after reading const tables
-2	DEPENDENT SUBQUERY	t2	index_subquery	k	k	5	const	2	Using index
+1	PRIMARY	t1	system	NULL	NULL	NULL	NULL	1	
+2	SUBQUERY	t2	index	NULL	k	5	NULL	2	Using index
 DROP TABLE t2;
 DROP TABLE t1;
 #
@@ -7046,7 +7046,7 @@ WHERE (c_sq1_alias1.col_int_nokey != @va
 OR c_sq1_alias1.pk != @var3));
 id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
 1	PRIMARY	NULL	NULL	NULL	NULL	NULL	NULL	NULL	Impossible WHERE noticed after reading const tables
-2	DEPENDENT SUBQUERY	c_sq1_alias1	system	PRIMARY,col_varchar_key	NULL	NULL	NULL	1	
+2	UNCACHEABLE SUBQUERY	c_sq1_alias1	system	PRIMARY	NULL	NULL	NULL	1	
 SELECT sq4_alias1.*
 FROM t1 AS sq4_alias1
 WHERE (sq4_alias1.col_varchar_key , sq4_alias1.col_varchar_nokey)
@@ -7069,7 +7069,7 @@ OR c_sq1_alias1.pk != @var3)) ) AS alias
 id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
 1	PRIMARY	<derived2>	system	NULL	NULL	NULL	NULL	0	const row not found
 2	DERIVED	NULL	NULL	NULL	NULL	NULL	NULL	NULL	no matching row in const table
-3	DEPENDENT SUBQUERY	c_sq1_alias1	system	PRIMARY,col_varchar_key	NULL	NULL	NULL	1	
+3	UNCACHEABLE SUBQUERY	c_sq1_alias1	system	PRIMARY	NULL	NULL	NULL	1	
 SELECT * FROM ( SELECT sq4_alias1.*
 FROM t1 AS sq4_alias1
 WHERE (sq4_alias1.col_varchar_key , sq4_alias1.col_varchar_nokey)
@@ -7081,5 +7081,32 @@ WHERE (c_sq1_alias1.col_int_nokey != @va
 OR c_sq1_alias1.pk != @var3)) ) AS alias3;
 pk	col_int_nokey	col_int_key	col_time_key	col_varchar_key	col_varchar_nokey
 DROP TABLE t1,t2;
+#
+# Test that indexsubquery_engine only does one lookup if
+# the technique is unique_subquery: does not try to read the
+# next row if the first row failed the subquery's WHERE
+# condition (here: b=3).
+#
+create table t1(a int);
+insert into t1 values(1),(2);
+create table t2(a int primary key, b int);
+insert into t2 values(1,10),(2,10);
+explain select * from t1 where a in (select a from t2 where b=3);
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	PRIMARY	t1	ALL	NULL	NULL	NULL	NULL	2	Using where
+1	PRIMARY	t2	eq_ref	PRIMARY	PRIMARY	4	test.t1.a	1	Using where; Using join buffer (Batched Key Access)
+flush status;
+select * from t1 where a in (select a from t2 where b=3);
+a
+show status like "handler_read%";
+Variable_name	Value
+Handler_read_first	0
+Handler_read_key	2
+Handler_read_last	0
+Handler_read_next	2
+Handler_read_prev	0
+Handler_read_rnd	2
+Handler_read_rnd_next	3
+drop table t1,t2;
 set optimizer_switch=default;
 set optimizer_switch=default;

=== modified file 'mysql-test/r/subquery_mat.result'
--- a/mysql-test/r/subquery_mat.result	2012-01-31 11:19:25 +0000
+++ b/mysql-test/r/subquery_mat.result	2012-02-08 15:25:17 +0000
@@ -1294,5 +1294,485 @@ pk	pk
 1	NULL
 DROP TABLE t1, t2;
 # End of test for bug#13607423.
+
+Test of WL#6094 "Allow subquery materialization in NOT IN if all
+columns are not nullable"
+
+create table t1(a int not null);
+create table t2(a int not null);
+insert into t1 values(1),(2);
+insert into t2 values(1),(2);
+Test in SELECT list
+
+cols not nullable => subq materialization
+explain extended select a, (a,a) in (select a,a from t2) from t1;
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	filtered	Extra
+1	PRIMARY	t1	ALL	NULL	NULL	NULL	NULL	2	100.00	
+2	SUBQUERY	t2	ALL	NULL	NULL	NULL	NULL	2	100.00	
+Warnings:
+Note	1003	/* select#1 */ select `test`.`t1`.`a` AS `a`,<in_optimizer>((`test`.`t1`.`a`,`test`.`t1`.`a`),(`test`.`t1`.`a`,`test`.`t1`.`a`) in ( <materialize> (/* select#2 */ select `test`.`t2`.`a`,`test`.`t2`.`a` from `test`.`t2` ), <primary_index_lookup>(`test`.`t1`.`a` in <temporary table> on distinct_key where ((`test`.`t1`.`a` = `materialized subselect`.`a`) and (`test`.`t1`.`a` = `materialized subselect`.`a`))))) AS `(a,a) in (select a,a from t2)` from `test`.`t1`
+select a, (a,a) in (select a,a from t2) from t1;
+a	(a,a) in (select a,a from t2)
+1	1
+2	1
+
+cols not nullable => subq materialization
+explain extended select t1.a, t2.a, (t1.a,t1.a) in (select a,a from t2 as t3)
+from t1 join t2 on t1.a+t2.a=1000;
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	filtered	Extra
+1	PRIMARY	t1	ALL	NULL	NULL	NULL	NULL	2	100.00	
+1	PRIMARY	t2	ALL	NULL	NULL	NULL	NULL	2	100.00	Using where; Using join buffer (Block Nested Loop)
+2	SUBQUERY	t3	ALL	NULL	NULL	NULL	NULL	2	100.00	
+Warnings:
+Note	1003	/* select#1 */ select `test`.`t1`.`a` AS `a`,`test`.`t2`.`a` AS `a`,<in_optimizer>((`test`.`t1`.`a`,`test`.`t1`.`a`),(`test`.`t1`.`a`,`test`.`t1`.`a`) in ( <materialize> (/* select#2 */ select `test`.`t3`.`a`,`test`.`t3`.`a` from `test`.`t2` `t3` ), <primary_index_lookup>(`test`.`t1`.`a` in <temporary table> on distinct_key where ((`test`.`t1`.`a` = `materialized subselect`.`a`) and (`test`.`t1`.`a` = `materialized subselect`.`a`))))) AS `(t1.a,t1.a) in (select a,a from t2 as t3)` from `test`.`t1` join `test`.`t2` where ((`test`.`t1`.`a` + `test`.`t2`.`a`) = 1000)
+select t1.a, t2.a, (t1.a,t1.a) in (select a,a from t2 as t3)
+from t1 join t2 on t1.a+t2.a=1000;
+a	a	(t1.a,t1.a) in (select a,a from t2 as t3)
+
+t2.a is not nullable, but in the query it may appear as NULL
+as it's in an outer join. So, no materialization.
+explain extended select t1.a, t2.a, (t2.a,t2.a) in (select a,a from t2 as t3)
+from t1 left join t2 on t1.a+t2.a=1000;
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	filtered	Extra
+1	PRIMARY	t1	ALL	NULL	NULL	NULL	NULL	2	100.00	
+1	PRIMARY	t2	ALL	NULL	NULL	NULL	NULL	2	100.00	Using where; Using join buffer (Block Nested Loop)
+2	DEPENDENT SUBQUERY	t3	ALL	NULL	NULL	NULL	NULL	2	100.00	Using where
+Warnings:
+Note	1003	/* select#1 */ select `test`.`t1`.`a` AS `a`,`test`.`t2`.`a` AS `a`,<in_optimizer>((`test`.`t2`.`a`,`test`.`t2`.`a`),<exists>(/* select#2 */ select `test`.`t3`.`a`,`test`.`t3`.`a` from `test`.`t2` `t3` where (trigcond_if(outer_field_is_not_null, ((<cache>(`test`.`t2`.`a`) = `test`.`t3`.`a`) or <cache>(isnull(`test`.`t3`.`a`))), true) and trigcond_if(outer_field_is_not_null, ((<cache>(`test`.`t2`.`a`) = `test`.`t3`.`a`) or <cache>(isnull(`test`.`t3`.`a`))), true)) having (trigcond_if(outer_field_is_not_null, <is_not_null_test>(`test`.`t3`.`a`), true) and trigcond_if(outer_field_is_not_null, <is_not_null_test>(`test`.`t3`.`a`), true)))) AS `(t2.a,t2.a) in (select a,a from t2 as t3)` from `test`.`t1` left join `test`.`t2` on(((`test`.`t1`.`a` + `test`.`t2`.`a`) = 1000)) where 1
+select t1.a, t2.a, (t2.a,t2.a) in (select a,a from t2 as t3)
+from t1 left join t2 on t1.a+t2.a=1000;
+a	a	(t2.a,t2.a) in (select a,a from t2 as t3)
+1	NULL	NULL
+2	NULL	NULL
+
+alter table t2 modify a int;
+two nullable inner cols => no subq materialization
+explain extended select t1.a, t2.a, (t1.a,t1.a) in (select a,a from t2 as t3)
+from t1 join t2 on t1.a+t2.a=1000;
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	filtered	Extra
+1	PRIMARY	t1	ALL	NULL	NULL	NULL	NULL	2	100.00	
+1	PRIMARY	t2	ALL	NULL	NULL	NULL	NULL	2	100.00	Using where; Using join buffer (Block Nested Loop)
+2	DEPENDENT SUBQUERY	t3	ALL	NULL	NULL	NULL	NULL	2	100.00	Using where
+Warnings:
+Note	1003	/* select#1 */ select `test`.`t1`.`a` AS `a`,`test`.`t2`.`a` AS `a`,<in_optimizer>((`test`.`t1`.`a`,`test`.`t1`.`a`),<exists>(/* select#2 */ select `test`.`t3`.`a`,`test`.`t3`.`a` from `test`.`t2` `t3` where (((<cache>(`test`.`t1`.`a`) = `test`.`t3`.`a`) or isnull(`test`.`t3`.`a`)) and ((<cache>(`test`.`t1`.`a`) = `test`.`t3`.`a`) or isnull(`test`.`t3`.`a`))) having (<is_not_null_test>(`test`.`t3`.`a`) and <is_not_null_test>(`test`.`t3`.`a`)))) AS `(t1.a,t1.a) in (select a,a from t2 as t3)` from `test`.`t1` join `test`.`t2` where ((`test`.`t1`.`a` + `test`.`t2`.`a`) = 1000)
+select t1.a, t2.a, (t1.a,t1.a) in (select a,a from t2 as t3)
+from t1 join t2 on t1.a+t2.a=1000;
+a	a	(t1.a,t1.a) in (select a,a from t2 as t3)
+alter table t2 modify a int not null;
+
+Test in WHERE
+
+top-level => subq materialization. With one exception: if
+semijoin is enabled in @@optimizer_switch, semijoin is chosen,
+then rejected (due to outer join), and in that case, the
+fallback is IN->EXISTS, subq-materialization is not tried...
+explain extended select t1.a, t2.a
+from t1 join t2 on t1.a+t2.a=3
+where (t2.a,t2.a) in (select a,a from t2 as t3);
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	filtered	Extra
+1	PRIMARY	t1	ALL	NULL	NULL	NULL	NULL	2	100.00	
+1	PRIMARY	t2	ALL	NULL	NULL	NULL	NULL	2	100.00	Using where; Using join buffer (Block Nested Loop)
+2	SUBQUERY	t3	ALL	NULL	NULL	NULL	NULL	2	100.00	
+Warnings:
+Note	1003	/* select#1 */ select `test`.`t1`.`a` AS `a`,`test`.`t2`.`a` AS `a` from `test`.`t1` join `test`.`t2` where (<in_optimizer>((`test`.`t2`.`a`,`test`.`t2`.`a`),(`test`.`t2`.`a`,`test`.`t2`.`a`) in ( <materialize> (/* select#2 */ select `test`.`t3`.`a`,`test`.`t3`.`a` from `test`.`t2` `t3` ), <primary_index_lookup>(`test`.`t2`.`a` in <temporary table> on distinct_key where ((`test`.`t2`.`a` = `materialized subselect`.`a`) and (`test`.`t2`.`a` = `materialized subselect`.`a`))))) and ((`test`.`t1`.`a` + `test`.`t2`.`a`) = 3))
+select t1.a, t2.a
+from t1 join t2 on t1.a+t2.a=3
+where (t2.a,t2.a) in (select a,a from t2 as t3);
+a	a
+2	1
+1	2
+
+cols not nullable => subq materialization
+explain extended select t1.a, t2.a
+from t1 join t2 on t1.a+t2.a=3
+where (t2.a,t2.a) not in (select a,a from t2 as t3);
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	filtered	Extra
+1	PRIMARY	t1	ALL	NULL	NULL	NULL	NULL	2	100.00	
+1	PRIMARY	t2	ALL	NULL	NULL	NULL	NULL	2	100.00	Using where; Using join buffer (Block Nested Loop)
+2	SUBQUERY	t3	ALL	NULL	NULL	NULL	NULL	2	100.00	
+Warnings:
+Note	1003	/* select#1 */ select `test`.`t1`.`a` AS `a`,`test`.`t2`.`a` AS `a` from `test`.`t1` join `test`.`t2` where ((not(<in_optimizer>((`test`.`t2`.`a`,`test`.`t2`.`a`),(`test`.`t2`.`a`,`test`.`t2`.`a`) in ( <materialize> (/* select#2 */ select `test`.`t3`.`a`,`test`.`t3`.`a` from `test`.`t2` `t3` ), <primary_index_lookup>(`test`.`t2`.`a` in <temporary table> on distinct_key where ((`test`.`t2`.`a` = `materialized subselect`.`a`) and (`test`.`t2`.`a` = `materialized subselect`.`a`))))))) and ((`test`.`t1`.`a` + `test`.`t2`.`a`) = 3))
+select t1.a, t2.a
+from t1 join t2 on t1.a+t2.a=3
+where (t2.a,t2.a) not in (select a,a from t2 as t3);
+a	a
+drop table t1,t2;
+
+Test of WL6095 "Allow subquery materialization in NOT IN if
+single-column subquery"
+
+create table t1(a int null);
+create table t2(a int null);
+insert into t1 values(1),(2);
+insert into t2 values(1),(2);
+
+one col => subq materialization
+explain extended select a, a in (select a from t2) from t1;
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	filtered	Extra
+1	PRIMARY	t1	ALL	NULL	NULL	NULL	NULL	2	100.00	
+2	SUBQUERY	t2	ALL	NULL	NULL	NULL	NULL	2	100.00	
+Warnings:
+Note	1003	/* select#1 */ select `test`.`t1`.`a` AS `a`,<in_optimizer>(`test`.`t1`.`a`,`test`.`t1`.`a` in ( <materialize> (/* select#2 */ select `test`.`t2`.`a` from `test`.`t2` ), <primary_index_lookup>(`test`.`t1`.`a` in <temporary table> on distinct_key where ((`test`.`t1`.`a` = `materialized subselect`.`a`))))) AS `a in (select a from t2)` from `test`.`t1`
+select a, a in (select a from t2) from t1;
+a	a in (select a from t2)
+1	1
+2	1
+
+t2.a is not nullable, but in the query it may appear as NULL
+as it's in an outer join. But there is only one inner column so
+materialization is possible
+explain extended select t1.a, t2.a, t2.a in (select * from t2 as t3)
+from t1 left join t2 on t1.a+t2.a=1000;
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	filtered	Extra
+1	PRIMARY	t1	ALL	NULL	NULL	NULL	NULL	2	100.00	
+1	PRIMARY	t2	ALL	NULL	NULL	NULL	NULL	2	100.00	Using where; Using join buffer (Block Nested Loop)
+2	SUBQUERY	t3	ALL	NULL	NULL	NULL	NULL	2	100.00	
+Warnings:
+Note	1003	/* select#1 */ select `test`.`t1`.`a` AS `a`,`test`.`t2`.`a` AS `a`,<in_optimizer>(`test`.`t2`.`a`,`test`.`t2`.`a` in ( <materialize> (/* select#2 */ select `test`.`t3`.`a` from `test`.`t2` `t3` ), <primary_index_lookup>(`test`.`t2`.`a` in <temporary table> on distinct_key where ((`test`.`t2`.`a` = `materialized subselect`.`a`))))) AS `t2.a in (select * from t2 as t3)` from `test`.`t1` left join `test`.`t2` on(((`test`.`t1`.`a` + `test`.`t2`.`a`) = 1000)) where 1
+select t1.a, t2.a, t2.a in (select * from t2 as t3)
+from t1 left join t2 on t1.a+t2.a=1000;
+a	a	t2.a in (select * from t2 as t3)
+1	NULL	NULL
+2	NULL	NULL
+
+_two_ outer columns, nullable => no materialization
+explain extended select t1.a, t2.a, (t2.a,t2.a) in (select a,a from t2 as t3)
+from t1 left join t2 on t1.a+t2.a=1000;
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	filtered	Extra
+1	PRIMARY	t1	ALL	NULL	NULL	NULL	NULL	2	100.00	
+1	PRIMARY	t2	ALL	NULL	NULL	NULL	NULL	2	100.00	Using where; Using join buffer (Block Nested Loop)
+2	DEPENDENT SUBQUERY	t3	ALL	NULL	NULL	NULL	NULL	2	100.00	Using where
+Warnings:
+Note	1003	/* select#1 */ select `test`.`t1`.`a` AS `a`,`test`.`t2`.`a` AS `a`,<in_optimizer>((`test`.`t2`.`a`,`test`.`t2`.`a`),<exists>(/* select#2 */ select `test`.`t3`.`a`,`test`.`t3`.`a` from `test`.`t2` `t3` where (trigcond_if(outer_field_is_not_null, ((<cache>(`test`.`t2`.`a`) = `test`.`t3`.`a`) or isnull(`test`.`t3`.`a`)), true) and trigcond_if(outer_field_is_not_null, ((<cache>(`test`.`t2`.`a`) = `test`.`t3`.`a`) or isnull(`test`.`t3`.`a`)), true)) having (trigcond_if(outer_field_is_not_null, <is_not_null_test>(`test`.`t3`.`a`), true) and trigcond_if(outer_field_is_not_null, <is_not_null_test>(`test`.`t3`.`a`), true)))) AS `(t2.a,t2.a) in (select a,a from t2 as t3)` from `test`.`t1` left join `test`.`t2` on(((`test`.`t1`.`a` + `test`.`t2`.`a`) = 1000)) where 1
+select t1.a, t2.a, (t2.a,t2.a) in (select a,a from t2 as t3)
+from t1 left join t2 on t1.a+t2.a=1000;
+a	a	(t2.a,t2.a) in (select a,a from t2 as t3)
+1	NULL	NULL
+2	NULL	NULL
+drop table t1,t2;
+
+Test in HAVING
+create table t1(a int, b int);
+create table t2(a int);
+insert into t1 values(1,1),(1,2),(1,3),(2,1),(2,2),(2,3);
+insert into t2 values(10),(20);
+no NULLs.
+explain extended select t1.a as z, sum(t1.b) from t1 group by t1.a
+having (z in (select * from t2)) is null;
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	filtered	Extra
+1	PRIMARY	t1	ALL	NULL	NULL	NULL	NULL	6	100.00	Using temporary; Using filesort
+2	SUBQUERY	t2	ALL	NULL	NULL	NULL	NULL	2	100.00	
+Warnings:
+Note	1003	/* select#1 */ select `test`.`t1`.`a` AS `z`,sum(`test`.`t1`.`b`) AS `sum(t1.b)` from `test`.`t1` group by `test`.`t1`.`a` having isnull(<in_optimizer>(`z`,`z` in ( <materialize> (/* select#2 */ select `test`.`t2`.`a` from `test`.`t2` ), <primary_index_lookup>(`z` in <temporary table> on distinct_key where ((`z` = `materialized subselect`.`a`))))))
+select t1.a as z, sum(t1.b) from t1 group by t1.a
+having (z in (select * from t2)) is null;
+z	sum(t1.b)
+one outer NULL
+insert into t1 values(null,null);
+explain extended select t1.a as z, sum(t1.b) from t1 group by t1.a
+having (z in (select * from t2)) is null;
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	filtered	Extra
+1	PRIMARY	t1	ALL	NULL	NULL	NULL	NULL	7	100.00	Using temporary; Using filesort
+2	SUBQUERY	t2	ALL	NULL	NULL	NULL	NULL	2	100.00	
+Warnings:
+Note	1003	/* select#1 */ select `test`.`t1`.`a` AS `z`,sum(`test`.`t1`.`b`) AS `sum(t1.b)` from `test`.`t1` group by `test`.`t1`.`a` having isnull(<in_optimizer>(`z`,`z` in ( <materialize> (/* select#2 */ select `test`.`t2`.`a` from `test`.`t2` ), <primary_index_lookup>(`z` in <temporary table> on distinct_key where ((`z` = `materialized subselect`.`a`))))))
+select t1.a as z, sum(t1.b) from t1 group by t1.a
+having (z in (select * from t2)) is null;
+z	sum(t1.b)
+NULL	NULL
+one outer NULL and one inner NULL
+insert into t2 values(null);
+explain extended select t1.a as z, sum(t1.b) from t1 group by t1.a
+having (z in (select * from t2)) is null;
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	filtered	Extra
+1	PRIMARY	t1	ALL	NULL	NULL	NULL	NULL	7	100.00	Using temporary; Using filesort
+2	SUBQUERY	t2	ALL	NULL	NULL	NULL	NULL	3	100.00	
+Warnings:
+Note	1003	/* select#1 */ select `test`.`t1`.`a` AS `z`,sum(`test`.`t1`.`b`) AS `sum(t1.b)` from `test`.`t1` group by `test`.`t1`.`a` having isnull(<in_optimizer>(`z`,`z` in ( <materialize> (/* select#2 */ select `test`.`t2`.`a` from `test`.`t2` ), <primary_index_lookup>(`z` in <temporary table> on distinct_key where ((`z` = `materialized subselect`.`a`))))))
+select t1.a as z, sum(t1.b) from t1 group by t1.a
+having (z in (select * from t2)) is null;
+z	sum(t1.b)
+NULL	NULL
+1	6
+2	6
+one inner NULL
+delete from t1 where a is null;
+explain extended select t1.a as z, sum(t1.b) from t1 group by t1.a
+having (z in (select * from t2)) is null;
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	filtered	Extra
+1	PRIMARY	t1	ALL	NULL	NULL	NULL	NULL	6	100.00	Using temporary; Using filesort
+2	SUBQUERY	t2	ALL	NULL	NULL	NULL	NULL	3	100.00	
+Warnings:
+Note	1003	/* select#1 */ select `test`.`t1`.`a` AS `z`,sum(`test`.`t1`.`b`) AS `sum(t1.b)` from `test`.`t1` group by `test`.`t1`.`a` having isnull(<in_optimizer>(`z`,`z` in ( <materialize> (/* select#2 */ select `test`.`t2`.`a` from `test`.`t2` ), <primary_index_lookup>(`z` in <temporary table> on distinct_key where ((`z` = `materialized subselect`.`a`))))))
+select t1.a as z, sum(t1.b) from t1 group by t1.a
+having (z in (select * from t2)) is null;
+z	sum(t1.b)
+1	6
+2	6
+drop table t1,t2;
+
+Verify that an inner NULL is looked up only once (result is
+cached).
+create table t1(a int);
+create table t2(a int);
+insert into t1 values(1),(2),(3),(4),(5),(6);
+insert into t1 select * from t1;
+insert into t2 values(10),(20),(NULL);
+explain extended select a, (a in (select * from t2)) from t1;
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	filtered	Extra
+1	PRIMARY	t1	ALL	NULL	NULL	NULL	NULL	12	100.00	
+2	SUBQUERY	t2	ALL	NULL	NULL	NULL	NULL	3	100.00	
+Warnings:
+Note	1003	/* select#1 */ select `test`.`t1`.`a` AS `a`,<in_optimizer>(`test`.`t1`.`a`,`test`.`t1`.`a` in ( <materialize> (/* select#2 */ select `test`.`t2`.`a` from `test`.`t2` ), <primary_index_lookup>(`test`.`t1`.`a` in <temporary table> on distinct_key where ((`test`.`t1`.`a` = `materialized subselect`.`a`))))) AS `(a in (select * from t2))` from `test`.`t1`
+flush status;
+select a, (a in (select * from t2)) from t1;
+a	(a in (select * from t2))
+1	NULL
+2	NULL
+3	NULL
+4	NULL
+5	NULL
+6	NULL
+1	NULL
+2	NULL
+3	NULL
+4	NULL
+5	NULL
+6	NULL
+There will be one look-up in the temporary table for each row
+of t1 (12), plus one additional look-up to check whether table
+contains a NULL value.
+show status like "handler_read_key";
+Variable_name	Value
+Handler_read_key	13
+drop table t1,t2;
+#
+# Bug#13495157 - SUBQUERY MATERIALIZATION NOT USED FOR CERTAIN
+# STATEMENTS
+#
+CREATE TABLE t1(a INT);
+INSERT INTO t1 VALUES(1),(2),(3);
+CREATE TABLE t2(a INT);
+INSERT INTO t2 VALUES(1),(2),(4);
+# subquery materialization used for SELECT:
+EXPLAIN SELECT * FROM t1 WHERE a IN (SELECT * FROM t2 WHERE a <> 2);
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	PRIMARY	t1	ALL	NULL	NULL	NULL	NULL	3	Using where
+2	SUBQUERY	t2	ALL	NULL	NULL	NULL	NULL	3	Using where
+SELECT * FROM t1 WHERE a IN (SELECT * FROM t2 WHERE a <> 2);
+a
+1
+# Also used for INSERT SELECT:
+CREATE TABLE t3 SELECT * FROM t1;
+EXPLAIN INSERT INTO t3 SELECT * FROM t1 WHERE a IN (SELECT * FROM t2 WHERE a <> 2);
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	PRIMARY	t1	ALL	NULL	NULL	NULL	NULL	3	Using where
+2	SUBQUERY	t2	ALL	NULL	NULL	NULL	NULL	3	Using where
+INSERT INTO t3 SELECT * FROM t1 WHERE a IN (SELECT * FROM t2 WHERE a <> 2);
+SELECT * FROM t3;
+a
+1
+1
+2
+3
+EXPLAIN INSERT INTO t2 SELECT * FROM t1 WHERE a IN (SELECT * FROM t2 WHERE a <> 2);
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	PRIMARY	t1	ALL	NULL	NULL	NULL	NULL	3	Using where; Using temporary
+2	SUBQUERY	t2	ALL	NULL	NULL	NULL	NULL	3	Using where
+INSERT INTO t2 SELECT * FROM t1 WHERE a IN (SELECT * FROM t2 WHERE a <> 2);
+SELECT * FROM t2;
+a
+1
+1
+2
+4
+EXPLAIN INSERT INTO t2 SELECT * FROM t2 WHERE a IN (SELECT * FROM t2 WHERE a <> 2);
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	PRIMARY	t2	ALL	NULL	NULL	NULL	NULL	4	Using where; Using temporary
+2	SUBQUERY	t2	ALL	NULL	NULL	NULL	NULL	4	Using where
+INSERT INTO t2 SELECT * FROM t2 WHERE a IN (SELECT * FROM t2 WHERE a <> 2);
+SELECT * FROM t2;
+a
+1
+1
+1
+1
+2
+4
+4
+# Not used for single-table UPDATE, DELETE:
+EXPLAIN SELECT * FROM t2 WHERE a IN (SELECT * FROM t1);
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	PRIMARY	t2	ALL	NULL	NULL	NULL	NULL	7	Using where
+2	SUBQUERY	t1	ALL	NULL	NULL	NULL	NULL	3	
+EXPLAIN UPDATE t2 SET a=a-1 WHERE a IN (SELECT * FROM t1);
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	PRIMARY	t2	ALL	NULL	NULL	NULL	NULL	7	Using where
+2	DEPENDENT SUBQUERY	t1	ALL	NULL	NULL	NULL	NULL	3	Using where
+UPDATE t2 SET a=a-1 WHERE a IN (SELECT * FROM t1);
+SELECT * FROM t2;
+a
+0
+0
+0
+0
+1
+4
+4
+EXPLAIN DELETE FROM t2 WHERE a IN (SELECT * FROM t1);
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	PRIMARY	t2	ALL	NULL	NULL	NULL	NULL	7	Using where
+2	DEPENDENT SUBQUERY	t1	ALL	NULL	NULL	NULL	NULL	3	Using where
+DELETE FROM t2 WHERE a IN (SELECT * FROM t1);
+SELECT * FROM t2;
+a
+0
+0
+0
+0
+4
+4
+EXPLAIN UPDATE t2 SET a=a-1 WHERE a IN (SELECT * FROM t2);
+ERROR HY000: You can't specify target table 't2' for update in FROM clause
+EXPLAIN DELETE FROM t2 WHERE a IN (SELECT * FROM t2);
+ERROR HY000: You can't specify target table 't2' for update in FROM clause
+UPDATE t2 SET a=3 WHERE a=0;
+# Used for multi-table UPDATE, DELETE:
+EXPLAIN SELECT * FROM t2,t3 WHERE t2.a IN (SELECT * FROM t1 WHERE a <> 2);
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	PRIMARY	t3	ALL	NULL	NULL	NULL	NULL	4	
+1	PRIMARY	t2	ALL	NULL	NULL	NULL	NULL	6	Using where; Using join buffer (Block Nested Loop)
+2	SUBQUERY	t1	ALL	NULL	NULL	NULL	NULL	3	Using where
+EXPLAIN UPDATE t2,t3 SET t2.a=t2.a-2 WHERE t2.a IN (SELECT * FROM t1 WHERE a <> 2);
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	PRIMARY	t3	ALL	NULL	NULL	NULL	NULL	4	
+1	PRIMARY	t2	ALL	NULL	NULL	NULL	NULL	6	Using where; Using join buffer (Block Nested Loop)
+2	SUBQUERY	t1	ALL	NULL	NULL	NULL	NULL	3	Using where
+UPDATE t2,t3 SET t2.a=t2.a-2 WHERE t2.a IN (SELECT * FROM t1 WHERE a <> 2);
+SELECT * FROM t2;
+a
+1
+1
+1
+1
+4
+4
+EXPLAIN DELETE t2.* FROM t2,t3 WHERE t2.a IN (SELECT * FROM t1 WHERE a <> 2);
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	PRIMARY	t3	ALL	NULL	NULL	NULL	NULL	4	
+1	PRIMARY	t2	ALL	NULL	NULL	NULL	NULL	6	Using where; Using join buffer (Block Nested Loop)
+2	SUBQUERY	t1	ALL	NULL	NULL	NULL	NULL	3	Using where
+DELETE t2.* FROM t2,t3 WHERE t2.a IN (SELECT * FROM t1 WHERE a <> 2);
+SELECT * FROM t2;
+a
+4
+4
+EXPLAIN UPDATE t2,t3 SET t2.a=10 WHERE t2.a IN (SELECT * FROM t2 WHERE a <> 2);
+ERROR HY000: You can't specify target table 't2' for update in FROM clause
+EXPLAIN DELETE t2.* FROM t2,t3 WHERE t2.a IN (SELECT * FROM t2 WHERE a <> 2);
+ERROR HY000: You can't specify target table 't2' for update in FROM clause
+DROP TABLE t1,t2,t3;
+#
+# Test that subquery materialization only does one lookup: does
+# not try to read the next row if the first row failed the
+# subquery's WHERE. We use a case where index lookup is not
+# enough to satisfy IN(), because index has length two when the
+# outer value has length three, and thus the post-filtering
+# WHERE added by subselect_hash_sj_engine::setup() makes the
+# decision.
+#
+create table t1 (a varchar(3));
+create table t2 (a varchar(2));
+insert into t1 values('aaa'), ('aaa');
+insert into t2 values('aa'), ('aa');
+explain select * from t1 where a in (select a from t2);
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	PRIMARY	t1	ALL	NULL	NULL	NULL	NULL	2	Using where
+2	SUBQUERY	t2	ALL	NULL	NULL	NULL	NULL	2	
+flush status;
+select * from t1 where a in (select a from t2);
+a
+show status like "handler_read%";
+Variable_name	Value
+Handler_read_first	0
+Handler_read_key	1
+Handler_read_last	0
+Handler_read_next	0
+Handler_read_prev	0
+Handler_read_rnd	0
+Handler_read_rnd_next	6
+drop table t1,t2;
+#
+# Bug#13655791 DIFFERENT RESULT WITH WL6094 ON QUERY WITH XOR
+# IN WHERE CLAUSE + MYISAM
+#
+CREATE TABLE t1 (
+pk int NOT NULL,
+col_varchar_nokey varchar(1) DEFAULT NULL,
+PRIMARY KEY (pk)
+);
+INSERT INTO t1 VALUES (10,'x');
+CREATE TABLE t2 (
+pk int NOT NULL,
+col_varchar_nokey varchar(1) DEFAULT NULL,
+PRIMARY KEY (pk)
+);
+INSERT INTO t2 VALUES (1,'v'), (5,'x'), (11,'z'), (12,'c'), (15,'y');
+CREATE TABLE t3 (
+pk int NOT NULL,
+col_int_key int DEFAULT NULL,
+PRIMARY KEY (pk),
+KEY col_int_key (col_int_key)
+);
+INSERT INTO t3 VALUES (10,8);
+CREATE TABLE t4 (
+pk int NOT NULL,
+col_varchar_nokey varchar(1) DEFAULT NULL,
+PRIMARY KEY (pk)
+);
+INSERT INTO t4 VALUES (1,'x');
+EXPLAIN SELECT OUTR.pk, OUTR.col_varchar_nokey, OUTR2.col_varchar_nokey
+FROM t2 AS OUTR2
+JOIN t4 AS OUTR
+ON (OUTR2.col_varchar_nokey > OUTR.col_varchar_nokey)
+WHERE
+OUTR.col_varchar_nokey IN (
+SELECT INNR.col_varchar_nokey
+FROM t3 AS INNR2
+LEFT JOIN t1 AS INNR
+ON (INNR2.col_int_key >= INNR.pk)
+)
+XOR OUTR.pk < 6
+;
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	PRIMARY	OUTR	system	NULL	NULL	NULL	NULL	1	
+1	PRIMARY	OUTR2	ALL	NULL	NULL	NULL	NULL	5	Using where
+2	SUBQUERY	INNR2	system	NULL	NULL	NULL	NULL	1	
+2	SUBQUERY	INNR	system	PRIMARY	NULL	NULL	NULL	1	
+FLUSH STATUS;
+SELECT OUTR.pk, OUTR.col_varchar_nokey, OUTR2.col_varchar_nokey
+FROM t2 AS OUTR2
+JOIN t4 AS OUTR
+ON (OUTR2.col_varchar_nokey > OUTR.col_varchar_nokey)
+WHERE
+OUTR.col_varchar_nokey IN (
+SELECT INNR.col_varchar_nokey
+FROM t3 AS INNR2
+LEFT JOIN t1 AS INNR
+ON (INNR2.col_int_key >= INNR.pk)
+)
+XOR OUTR.pk < 6
+;
+pk	col_varchar_nokey	col_varchar_nokey
+SHOW STATUS LIKE "HANDLER_READ%";
+Variable_name	Value
+Handler_read_first	3
+Handler_read_key	2
+Handler_read_last	0
+Handler_read_next	0
+Handler_read_prev	0
+Handler_read_rnd	0
+Handler_read_rnd_next	9
+DROP TABLE t1,t2,t3,t4;
 # End of 5.6 tests
 set optimizer_switch=default;

=== modified file 'mysql-test/r/subquery_mat_all.result'
--- a/mysql-test/r/subquery_mat_all.result	2012-01-20 15:30:14 +0000
+++ b/mysql-test/r/subquery_mat_all.result	2012-02-08 15:25:17 +0000
@@ -1293,5 +1293,485 @@ pk	pk
 1	NULL
 DROP TABLE t1, t2;
 # End of test for bug#13607423.
+
+Test of WL#6094 "Allow subquery materialization in NOT IN if all
+columns are not nullable"
+
+create table t1(a int not null);
+create table t2(a int not null);
+insert into t1 values(1),(2);
+insert into t2 values(1),(2);
+Test in SELECT list
+
+cols not nullable => subq materialization
+explain extended select a, (a,a) in (select a,a from t2) from t1;
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	filtered	Extra
+1	PRIMARY	t1	ALL	NULL	NULL	NULL	NULL	2	100.00	
+2	SUBQUERY	t2	ALL	NULL	NULL	NULL	NULL	2	100.00	
+Warnings:
+Note	1003	/* select#1 */ select `test`.`t1`.`a` AS `a`,<in_optimizer>((`test`.`t1`.`a`,`test`.`t1`.`a`),(`test`.`t1`.`a`,`test`.`t1`.`a`) in ( <materialize> (/* select#2 */ select `test`.`t2`.`a`,`test`.`t2`.`a` from `test`.`t2` ), <primary_index_lookup>(`test`.`t1`.`a` in <temporary table> on distinct_key where ((`test`.`t1`.`a` = `materialized subselect`.`a`) and (`test`.`t1`.`a` = `materialized subselect`.`a`))))) AS `(a,a) in (select a,a from t2)` from `test`.`t1`
+select a, (a,a) in (select a,a from t2) from t1;
+a	(a,a) in (select a,a from t2)
+1	1
+2	1
+
+cols not nullable => subq materialization
+explain extended select t1.a, t2.a, (t1.a,t1.a) in (select a,a from t2 as t3)
+from t1 join t2 on t1.a+t2.a=1000;
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	filtered	Extra
+1	PRIMARY	t1	ALL	NULL	NULL	NULL	NULL	2	100.00	
+1	PRIMARY	t2	ALL	NULL	NULL	NULL	NULL	2	100.00	Using where; Using join buffer (Block Nested Loop)
+2	SUBQUERY	t3	ALL	NULL	NULL	NULL	NULL	2	100.00	
+Warnings:
+Note	1003	/* select#1 */ select `test`.`t1`.`a` AS `a`,`test`.`t2`.`a` AS `a`,<in_optimizer>((`test`.`t1`.`a`,`test`.`t1`.`a`),(`test`.`t1`.`a`,`test`.`t1`.`a`) in ( <materialize> (/* select#2 */ select `test`.`t3`.`a`,`test`.`t3`.`a` from `test`.`t2` `t3` ), <primary_index_lookup>(`test`.`t1`.`a` in <temporary table> on distinct_key where ((`test`.`t1`.`a` = `materialized subselect`.`a`) and (`test`.`t1`.`a` = `materialized subselect`.`a`))))) AS `(t1.a,t1.a) in (select a,a from t2 as t3)` from `test`.`t1` join `test`.`t2` where ((`test`.`t1`.`a` + `test`.`t2`.`a`) = 1000)
+select t1.a, t2.a, (t1.a,t1.a) in (select a,a from t2 as t3)
+from t1 join t2 on t1.a+t2.a=1000;
+a	a	(t1.a,t1.a) in (select a,a from t2 as t3)
+
+t2.a is not nullable, but in the query it may appear as NULL
+as it's in an outer join. So, no materialization.
+explain extended select t1.a, t2.a, (t2.a,t2.a) in (select a,a from t2 as t3)
+from t1 left join t2 on t1.a+t2.a=1000;
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	filtered	Extra
+1	PRIMARY	t1	ALL	NULL	NULL	NULL	NULL	2	100.00	
+1	PRIMARY	t2	ALL	NULL	NULL	NULL	NULL	2	100.00	Using where; Using join buffer (Block Nested Loop)
+2	DEPENDENT SUBQUERY	t3	ALL	NULL	NULL	NULL	NULL	2	100.00	Using where
+Warnings:
+Note	1003	/* select#1 */ select `test`.`t1`.`a` AS `a`,`test`.`t2`.`a` AS `a`,<in_optimizer>((`test`.`t2`.`a`,`test`.`t2`.`a`),<exists>(/* select#2 */ select `test`.`t3`.`a`,`test`.`t3`.`a` from `test`.`t2` `t3` where (trigcond_if(outer_field_is_not_null, ((<cache>(`test`.`t2`.`a`) = `test`.`t3`.`a`) or <cache>(isnull(`test`.`t3`.`a`))), true) and trigcond_if(outer_field_is_not_null, ((<cache>(`test`.`t2`.`a`) = `test`.`t3`.`a`) or <cache>(isnull(`test`.`t3`.`a`))), true)) having (trigcond_if(outer_field_is_not_null, <is_not_null_test>(`test`.`t3`.`a`), true) and trigcond_if(outer_field_is_not_null, <is_not_null_test>(`test`.`t3`.`a`), true)))) AS `(t2.a,t2.a) in (select a,a from t2 as t3)` from `test`.`t1` left join `test`.`t2` on(((`test`.`t1`.`a` + `test`.`t2`.`a`) = 1000)) where 1
+select t1.a, t2.a, (t2.a,t2.a) in (select a,a from t2 as t3)
+from t1 left join t2 on t1.a+t2.a=1000;
+a	a	(t2.a,t2.a) in (select a,a from t2 as t3)
+1	NULL	NULL
+2	NULL	NULL
+
+alter table t2 modify a int;
+two nullable inner cols => no subq materialization
+explain extended select t1.a, t2.a, (t1.a,t1.a) in (select a,a from t2 as t3)
+from t1 join t2 on t1.a+t2.a=1000;
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	filtered	Extra
+1	PRIMARY	t1	ALL	NULL	NULL	NULL	NULL	2	100.00	
+1	PRIMARY	t2	ALL	NULL	NULL	NULL	NULL	2	100.00	Using where; Using join buffer (Block Nested Loop)
+2	DEPENDENT SUBQUERY	t3	ALL	NULL	NULL	NULL	NULL	2	100.00	Using where
+Warnings:
+Note	1003	/* select#1 */ select `test`.`t1`.`a` AS `a`,`test`.`t2`.`a` AS `a`,<in_optimizer>((`test`.`t1`.`a`,`test`.`t1`.`a`),<exists>(/* select#2 */ select `test`.`t3`.`a`,`test`.`t3`.`a` from `test`.`t2` `t3` where (((<cache>(`test`.`t1`.`a`) = `test`.`t3`.`a`) or isnull(`test`.`t3`.`a`)) and ((<cache>(`test`.`t1`.`a`) = `test`.`t3`.`a`) or isnull(`test`.`t3`.`a`))) having (<is_not_null_test>(`test`.`t3`.`a`) and <is_not_null_test>(`test`.`t3`.`a`)))) AS `(t1.a,t1.a) in (select a,a from t2 as t3)` from `test`.`t1` join `test`.`t2` where ((`test`.`t1`.`a` + `test`.`t2`.`a`) = 1000)
+select t1.a, t2.a, (t1.a,t1.a) in (select a,a from t2 as t3)
+from t1 join t2 on t1.a+t2.a=1000;
+a	a	(t1.a,t1.a) in (select a,a from t2 as t3)
+alter table t2 modify a int not null;
+
+Test in WHERE
+
+top-level => subq materialization. With one exception: if
+semijoin is enabled in @@optimizer_switch, semijoin is chosen,
+then rejected (due to outer join), and in that case, the
+fallback is IN->EXISTS, subq-materialization is not tried...
+explain extended select t1.a, t2.a
+from t1 join t2 on t1.a+t2.a=3
+where (t2.a,t2.a) in (select a,a from t2 as t3);
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	filtered	Extra
+1	PRIMARY	t1	ALL	NULL	NULL	NULL	NULL	2	100.00	
+1	PRIMARY	t2	ALL	NULL	NULL	NULL	NULL	2	100.00	Using where; Using join buffer (Block Nested Loop)
+2	DEPENDENT SUBQUERY	t3	ALL	NULL	NULL	NULL	NULL	2	100.00	Using where
+Warnings:
+Note	1003	/* select#1 */ select `test`.`t1`.`a` AS `a`,`test`.`t2`.`a` AS `a` from `test`.`t1` join `test`.`t2` where (<in_optimizer>((`test`.`t2`.`a`,`test`.`t2`.`a`),<exists>(/* select#2 */ select `test`.`t3`.`a`,`test`.`t3`.`a` from `test`.`t2` `t3` where ((<cache>(`test`.`t2`.`a`) = `test`.`t3`.`a`) and (<cache>(`test`.`t2`.`a`) = `test`.`t3`.`a`)))) and ((`test`.`t1`.`a` + `test`.`t2`.`a`) = 3))
+select t1.a, t2.a
+from t1 join t2 on t1.a+t2.a=3
+where (t2.a,t2.a) in (select a,a from t2 as t3);
+a	a
+2	1
+1	2
+
+cols not nullable => subq materialization
+explain extended select t1.a, t2.a
+from t1 join t2 on t1.a+t2.a=3
+where (t2.a,t2.a) not in (select a,a from t2 as t3);
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	filtered	Extra
+1	PRIMARY	t1	ALL	NULL	NULL	NULL	NULL	2	100.00	
+1	PRIMARY	t2	ALL	NULL	NULL	NULL	NULL	2	100.00	Using where; Using join buffer (Block Nested Loop)
+2	SUBQUERY	t3	ALL	NULL	NULL	NULL	NULL	2	100.00	
+Warnings:
+Note	1003	/* select#1 */ select `test`.`t1`.`a` AS `a`,`test`.`t2`.`a` AS `a` from `test`.`t1` join `test`.`t2` where ((not(<in_optimizer>((`test`.`t2`.`a`,`test`.`t2`.`a`),(`test`.`t2`.`a`,`test`.`t2`.`a`) in ( <materialize> (/* select#2 */ select `test`.`t3`.`a`,`test`.`t3`.`a` from `test`.`t2` `t3` ), <primary_index_lookup>(`test`.`t2`.`a` in <temporary table> on distinct_key where ((`test`.`t2`.`a` = `materialized subselect`.`a`) and (`test`.`t2`.`a` = `materialized subselect`.`a`))))))) and ((`test`.`t1`.`a` + `test`.`t2`.`a`) = 3))
+select t1.a, t2.a
+from t1 join t2 on t1.a+t2.a=3
+where (t2.a,t2.a) not in (select a,a from t2 as t3);
+a	a
+drop table t1,t2;
+
+Test of WL6095 "Allow subquery materialization in NOT IN if
+single-column subquery"
+
+create table t1(a int null);
+create table t2(a int null);
+insert into t1 values(1),(2);
+insert into t2 values(1),(2);
+
+one col => subq materialization
+explain extended select a, a in (select a from t2) from t1;
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	filtered	Extra
+1	PRIMARY	t1	ALL	NULL	NULL	NULL	NULL	2	100.00	
+2	SUBQUERY	t2	ALL	NULL	NULL	NULL	NULL	2	100.00	
+Warnings:
+Note	1003	/* select#1 */ select `test`.`t1`.`a` AS `a`,<in_optimizer>(`test`.`t1`.`a`,`test`.`t1`.`a` in ( <materialize> (/* select#2 */ select `test`.`t2`.`a` from `test`.`t2` ), <primary_index_lookup>(`test`.`t1`.`a` in <temporary table> on distinct_key where ((`test`.`t1`.`a` = `materialized subselect`.`a`))))) AS `a in (select a from t2)` from `test`.`t1`
+select a, a in (select a from t2) from t1;
+a	a in (select a from t2)
+1	1
+2	1
+
+t2.a is not nullable, but in the query it may appear as NULL
+as it's in an outer join. But there is only one inner column so
+materialization is possible
+explain extended select t1.a, t2.a, t2.a in (select * from t2 as t3)
+from t1 left join t2 on t1.a+t2.a=1000;
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	filtered	Extra
+1	PRIMARY	t1	ALL	NULL	NULL	NULL	NULL	2	100.00	
+1	PRIMARY	t2	ALL	NULL	NULL	NULL	NULL	2	100.00	Using where; Using join buffer (Block Nested Loop)
+2	SUBQUERY	t3	ALL	NULL	NULL	NULL	NULL	2	100.00	
+Warnings:
+Note	1003	/* select#1 */ select `test`.`t1`.`a` AS `a`,`test`.`t2`.`a` AS `a`,<in_optimizer>(`test`.`t2`.`a`,`test`.`t2`.`a` in ( <materialize> (/* select#2 */ select `test`.`t3`.`a` from `test`.`t2` `t3` ), <primary_index_lookup>(`test`.`t2`.`a` in <temporary table> on distinct_key where ((`test`.`t2`.`a` = `materialized subselect`.`a`))))) AS `t2.a in (select * from t2 as t3)` from `test`.`t1` left join `test`.`t2` on(((`test`.`t1`.`a` + `test`.`t2`.`a`) = 1000)) where 1
+select t1.a, t2.a, t2.a in (select * from t2 as t3)
+from t1 left join t2 on t1.a+t2.a=1000;
+a	a	t2.a in (select * from t2 as t3)
+1	NULL	NULL
+2	NULL	NULL
+
+_two_ outer columns, nullable => no materialization
+explain extended select t1.a, t2.a, (t2.a,t2.a) in (select a,a from t2 as t3)
+from t1 left join t2 on t1.a+t2.a=1000;
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	filtered	Extra
+1	PRIMARY	t1	ALL	NULL	NULL	NULL	NULL	2	100.00	
+1	PRIMARY	t2	ALL	NULL	NULL	NULL	NULL	2	100.00	Using where; Using join buffer (Block Nested Loop)
+2	DEPENDENT SUBQUERY	t3	ALL	NULL	NULL	NULL	NULL	2	100.00	Using where
+Warnings:
+Note	1003	/* select#1 */ select `test`.`t1`.`a` AS `a`,`test`.`t2`.`a` AS `a`,<in_optimizer>((`test`.`t2`.`a`,`test`.`t2`.`a`),<exists>(/* select#2 */ select `test`.`t3`.`a`,`test`.`t3`.`a` from `test`.`t2` `t3` where (trigcond_if(outer_field_is_not_null, ((<cache>(`test`.`t2`.`a`) = `test`.`t3`.`a`) or isnull(`test`.`t3`.`a`)), true) and trigcond_if(outer_field_is_not_null, ((<cache>(`test`.`t2`.`a`) = `test`.`t3`.`a`) or isnull(`test`.`t3`.`a`)), true)) having (trigcond_if(outer_field_is_not_null, <is_not_null_test>(`test`.`t3`.`a`), true) and trigcond_if(outer_field_is_not_null, <is_not_null_test>(`test`.`t3`.`a`), true)))) AS `(t2.a,t2.a) in (select a,a from t2 as t3)` from `test`.`t1` left join `test`.`t2` on(((`test`.`t1`.`a` + `test`.`t2`.`a`) = 1000)) where 1
+select t1.a, t2.a, (t2.a,t2.a) in (select a,a from t2 as t3)
+from t1 left join t2 on t1.a+t2.a=1000;
+a	a	(t2.a,t2.a) in (select a,a from t2 as t3)
+1	NULL	NULL
+2	NULL	NULL
+drop table t1,t2;
+
+Test in HAVING
+create table t1(a int, b int);
+create table t2(a int);
+insert into t1 values(1,1),(1,2),(1,3),(2,1),(2,2),(2,3);
+insert into t2 values(10),(20);
+no NULLs.
+explain extended select t1.a as z, sum(t1.b) from t1 group by t1.a
+having (z in (select * from t2)) is null;
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	filtered	Extra
+1	PRIMARY	t1	ALL	NULL	NULL	NULL	NULL	6	100.00	Using temporary; Using filesort
+2	SUBQUERY	t2	ALL	NULL	NULL	NULL	NULL	2	100.00	
+Warnings:
+Note	1003	/* select#1 */ select `test`.`t1`.`a` AS `z`,sum(`test`.`t1`.`b`) AS `sum(t1.b)` from `test`.`t1` group by `test`.`t1`.`a` having isnull(<in_optimizer>(`z`,`z` in ( <materialize> (/* select#2 */ select `test`.`t2`.`a` from `test`.`t2` ), <primary_index_lookup>(`z` in <temporary table> on distinct_key where ((`z` = `materialized subselect`.`a`))))))
+select t1.a as z, sum(t1.b) from t1 group by t1.a
+having (z in (select * from t2)) is null;
+z	sum(t1.b)
+one outer NULL
+insert into t1 values(null,null);
+explain extended select t1.a as z, sum(t1.b) from t1 group by t1.a
+having (z in (select * from t2)) is null;
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	filtered	Extra
+1	PRIMARY	t1	ALL	NULL	NULL	NULL	NULL	7	100.00	Using temporary; Using filesort
+2	SUBQUERY	t2	ALL	NULL	NULL	NULL	NULL	2	100.00	
+Warnings:
+Note	1003	/* select#1 */ select `test`.`t1`.`a` AS `z`,sum(`test`.`t1`.`b`) AS `sum(t1.b)` from `test`.`t1` group by `test`.`t1`.`a` having isnull(<in_optimizer>(`z`,`z` in ( <materialize> (/* select#2 */ select `test`.`t2`.`a` from `test`.`t2` ), <primary_index_lookup>(`z` in <temporary table> on distinct_key where ((`z` = `materialized subselect`.`a`))))))
+select t1.a as z, sum(t1.b) from t1 group by t1.a
+having (z in (select * from t2)) is null;
+z	sum(t1.b)
+NULL	NULL
+one outer NULL and one inner NULL
+insert into t2 values(null);
+explain extended select t1.a as z, sum(t1.b) from t1 group by t1.a
+having (z in (select * from t2)) is null;
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	filtered	Extra
+1	PRIMARY	t1	ALL	NULL	NULL	NULL	NULL	7	100.00	Using temporary; Using filesort
+2	SUBQUERY	t2	ALL	NULL	NULL	NULL	NULL	3	100.00	
+Warnings:
+Note	1003	/* select#1 */ select `test`.`t1`.`a` AS `z`,sum(`test`.`t1`.`b`) AS `sum(t1.b)` from `test`.`t1` group by `test`.`t1`.`a` having isnull(<in_optimizer>(`z`,`z` in ( <materialize> (/* select#2 */ select `test`.`t2`.`a` from `test`.`t2` ), <primary_index_lookup>(`z` in <temporary table> on distinct_key where ((`z` = `materialized subselect`.`a`))))))
+select t1.a as z, sum(t1.b) from t1 group by t1.a
+having (z in (select * from t2)) is null;
+z	sum(t1.b)
+NULL	NULL
+1	6
+2	6
+one inner NULL
+delete from t1 where a is null;
+explain extended select t1.a as z, sum(t1.b) from t1 group by t1.a
+having (z in (select * from t2)) is null;
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	filtered	Extra
+1	PRIMARY	t1	ALL	NULL	NULL	NULL	NULL	6	100.00	Using temporary; Using filesort
+2	SUBQUERY	t2	ALL	NULL	NULL	NULL	NULL	3	100.00	
+Warnings:
+Note	1003	/* select#1 */ select `test`.`t1`.`a` AS `z`,sum(`test`.`t1`.`b`) AS `sum(t1.b)` from `test`.`t1` group by `test`.`t1`.`a` having isnull(<in_optimizer>(`z`,`z` in ( <materialize> (/* select#2 */ select `test`.`t2`.`a` from `test`.`t2` ), <primary_index_lookup>(`z` in <temporary table> on distinct_key where ((`z` = `materialized subselect`.`a`))))))
+select t1.a as z, sum(t1.b) from t1 group by t1.a
+having (z in (select * from t2)) is null;
+z	sum(t1.b)
+1	6
+2	6
+drop table t1,t2;
+
+Verify that an inner NULL is looked up only once (result is
+cached).
+create table t1(a int);
+create table t2(a int);
+insert into t1 values(1),(2),(3),(4),(5),(6);
+insert into t1 select * from t1;
+insert into t2 values(10),(20),(NULL);
+explain extended select a, (a in (select * from t2)) from t1;
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	filtered	Extra
+1	PRIMARY	t1	ALL	NULL	NULL	NULL	NULL	12	100.00	
+2	SUBQUERY	t2	ALL	NULL	NULL	NULL	NULL	3	100.00	
+Warnings:
+Note	1003	/* select#1 */ select `test`.`t1`.`a` AS `a`,<in_optimizer>(`test`.`t1`.`a`,`test`.`t1`.`a` in ( <materialize> (/* select#2 */ select `test`.`t2`.`a` from `test`.`t2` ), <primary_index_lookup>(`test`.`t1`.`a` in <temporary table> on distinct_key where ((`test`.`t1`.`a` = `materialized subselect`.`a`))))) AS `(a in (select * from t2))` from `test`.`t1`
+flush status;
+select a, (a in (select * from t2)) from t1;
+a	(a in (select * from t2))
+1	NULL
+2	NULL
+3	NULL
+4	NULL
+5	NULL
+6	NULL
+1	NULL
+2	NULL
+3	NULL
+4	NULL
+5	NULL
+6	NULL
+There will be one look-up in the temporary table for each row
+of t1 (12), plus one additional look-up to check whether table
+contains a NULL value.
+show status like "handler_read_key";
+Variable_name	Value
+Handler_read_key	13
+drop table t1,t2;
+#
+# Bug#13495157 - SUBQUERY MATERIALIZATION NOT USED FOR CERTAIN
+# STATEMENTS
+#
+CREATE TABLE t1(a INT);
+INSERT INTO t1 VALUES(1),(2),(3);
+CREATE TABLE t2(a INT);
+INSERT INTO t2 VALUES(1),(2),(4);
+# subquery materialization used for SELECT:
+EXPLAIN SELECT * FROM t1 WHERE a IN (SELECT * FROM t2 WHERE a <> 2);
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	PRIMARY	t1	ALL	NULL	NULL	NULL	NULL	3	Using where
+1	PRIMARY	t2	ALL	NULL	NULL	NULL	NULL	3	Using where; FirstMatch(t1); Using join buffer (Block Nested Loop)
+SELECT * FROM t1 WHERE a IN (SELECT * FROM t2 WHERE a <> 2);
+a
+1
+# Also used for INSERT SELECT:
+CREATE TABLE t3 SELECT * FROM t1;
+EXPLAIN INSERT INTO t3 SELECT * FROM t1 WHERE a IN (SELECT * FROM t2 WHERE a <> 2);
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	PRIMARY	t1	ALL	NULL	NULL	NULL	NULL	3	Using where
+1	PRIMARY	t2	ALL	NULL	NULL	NULL	NULL	3	Using where; FirstMatch(t1); Using join buffer (Block Nested Loop)
+INSERT INTO t3 SELECT * FROM t1 WHERE a IN (SELECT * FROM t2 WHERE a <> 2);
+SELECT * FROM t3;
+a
+1
+1
+2
+3
+EXPLAIN INSERT INTO t2 SELECT * FROM t1 WHERE a IN (SELECT * FROM t2 WHERE a <> 2);
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	PRIMARY	t1	ALL	NULL	NULL	NULL	NULL	3	Using where; Using temporary
+1	PRIMARY	t2	ALL	NULL	NULL	NULL	NULL	3	Using where; FirstMatch(t1); Using join buffer (Block Nested Loop)
+INSERT INTO t2 SELECT * FROM t1 WHERE a IN (SELECT * FROM t2 WHERE a <> 2);
+SELECT * FROM t2;
+a
+1
+1
+2
+4
+EXPLAIN INSERT INTO t2 SELECT * FROM t2 WHERE a IN (SELECT * FROM t2 WHERE a <> 2);
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	PRIMARY	t2	ALL	NULL	NULL	NULL	NULL	4	Using where; Using temporary
+1	PRIMARY	t2	ALL	NULL	NULL	NULL	NULL	4	Using where; FirstMatch(t2); Using join buffer (Block Nested Loop)
+INSERT INTO t2 SELECT * FROM t2 WHERE a IN (SELECT * FROM t2 WHERE a <> 2);
+SELECT * FROM t2;
+a
+1
+1
+1
+1
+2
+4
+4
+# Not used for single-table UPDATE, DELETE:
+EXPLAIN SELECT * FROM t2 WHERE a IN (SELECT * FROM t1);
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	PRIMARY	t1	ALL	NULL	NULL	NULL	NULL	3	Materialize; Scan
+1	PRIMARY	t2	ALL	NULL	NULL	NULL	NULL	7	Using where; Using join buffer (Block Nested Loop)
+EXPLAIN UPDATE t2 SET a=a-1 WHERE a IN (SELECT * FROM t1);
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	PRIMARY	t2	ALL	NULL	NULL	NULL	NULL	7	Using where
+2	DEPENDENT SUBQUERY	t1	ALL	NULL	NULL	NULL	NULL	3	Using where
+UPDATE t2 SET a=a-1 WHERE a IN (SELECT * FROM t1);
+SELECT * FROM t2;
+a
+0
+0
+0
+0
+1
+4
+4
+EXPLAIN DELETE FROM t2 WHERE a IN (SELECT * FROM t1);
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	PRIMARY	t2	ALL	NULL	NULL	NULL	NULL	7	Using where
+2	DEPENDENT SUBQUERY	t1	ALL	NULL	NULL	NULL	NULL	3	Using where
+DELETE FROM t2 WHERE a IN (SELECT * FROM t1);
+SELECT * FROM t2;
+a
+0
+0
+0
+0
+4
+4
+EXPLAIN UPDATE t2 SET a=a-1 WHERE a IN (SELECT * FROM t2);
+ERROR HY000: You can't specify target table 't2' for update in FROM clause
+EXPLAIN DELETE FROM t2 WHERE a IN (SELECT * FROM t2);
+ERROR HY000: You can't specify target table 't2' for update in FROM clause
+UPDATE t2 SET a=3 WHERE a=0;
+# Used for multi-table UPDATE, DELETE:
+EXPLAIN SELECT * FROM t2,t3 WHERE t2.a IN (SELECT * FROM t1 WHERE a <> 2);
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	PRIMARY	t1	ALL	NULL	NULL	NULL	NULL	3	Using where; Materialize; Scan
+1	PRIMARY	t2	ALL	NULL	NULL	NULL	NULL	6	Using where; Using join buffer (Block Nested Loop)
+1	PRIMARY	t3	ALL	NULL	NULL	NULL	NULL	4	Using join buffer (Block Nested Loop)
+EXPLAIN UPDATE t2,t3 SET t2.a=t2.a-2 WHERE t2.a IN (SELECT * FROM t1 WHERE a <> 2);
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	PRIMARY	t1	ALL	NULL	NULL	NULL	NULL	3	Using where; Materialize; Scan
+1	PRIMARY	t2	ALL	NULL	NULL	NULL	NULL	6	Using where; Using join buffer (Block Nested Loop)
+1	PRIMARY	t3	ALL	NULL	NULL	NULL	NULL	4	Using join buffer (Block Nested Loop)
+UPDATE t2,t3 SET t2.a=t2.a-2 WHERE t2.a IN (SELECT * FROM t1 WHERE a <> 2);
+SELECT * FROM t2;
+a
+1
+1
+1
+1
+4
+4
+EXPLAIN DELETE t2.* FROM t2,t3 WHERE t2.a IN (SELECT * FROM t1 WHERE a <> 2);
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	PRIMARY	t1	ALL	NULL	NULL	NULL	NULL	3	Using where; Materialize; Scan
+1	PRIMARY	t2	ALL	NULL	NULL	NULL	NULL	6	Using where; Using join buffer (Block Nested Loop)
+1	PRIMARY	t3	ALL	NULL	NULL	NULL	NULL	4	Using join buffer (Block Nested Loop)
+DELETE t2.* FROM t2,t3 WHERE t2.a IN (SELECT * FROM t1 WHERE a <> 2);
+SELECT * FROM t2;
+a
+4
+4
+EXPLAIN UPDATE t2,t3 SET t2.a=10 WHERE t2.a IN (SELECT * FROM t2 WHERE a <> 2);
+ERROR HY000: You can't specify target table 't2' for update in FROM clause
+EXPLAIN DELETE t2.* FROM t2,t3 WHERE t2.a IN (SELECT * FROM t2 WHERE a <> 2);
+ERROR HY000: You can't specify target table 't2' for update in FROM clause
+DROP TABLE t1,t2,t3;
+#
+# Test that subquery materialization only does one lookup: does
+# not try to read the next row if the first row failed the
+# subquery's WHERE. We use a case where index lookup is not
+# enough to satisfy IN(), because index has length two when the
+# outer value has length three, and thus the post-filtering
+# WHERE added by subselect_hash_sj_engine::setup() makes the
+# decision.
+#
+create table t1 (a varchar(3));
+create table t2 (a varchar(2));
+insert into t1 values('aaa'), ('aaa');
+insert into t2 values('aa'), ('aa');
+explain select * from t1 where a in (select a from t2);
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	PRIMARY	t1	ALL	NULL	NULL	NULL	NULL	2	
+1	PRIMARY	t2	ALL	NULL	NULL	NULL	NULL	2	Using where; FirstMatch(t1); Using join buffer (Block Nested Loop)
+flush status;
+select * from t1 where a in (select a from t2);
+a
+show status like "handler_read%";
+Variable_name	Value
+Handler_read_first	0
+Handler_read_key	0
+Handler_read_last	0
+Handler_read_next	0
+Handler_read_prev	0
+Handler_read_rnd	0
+Handler_read_rnd_next	6
+drop table t1,t2;
+#
+# Bug#13655791 DIFFERENT RESULT WITH WL6094 ON QUERY WITH XOR
+# IN WHERE CLAUSE + MYISAM
+#
+CREATE TABLE t1 (
+pk int NOT NULL,
+col_varchar_nokey varchar(1) DEFAULT NULL,
+PRIMARY KEY (pk)
+);
+INSERT INTO t1 VALUES (10,'x');
+CREATE TABLE t2 (
+pk int NOT NULL,
+col_varchar_nokey varchar(1) DEFAULT NULL,
+PRIMARY KEY (pk)
+);
+INSERT INTO t2 VALUES (1,'v'), (5,'x'), (11,'z'), (12,'c'), (15,'y');
+CREATE TABLE t3 (
+pk int NOT NULL,
+col_int_key int DEFAULT NULL,
+PRIMARY KEY (pk),
+KEY col_int_key (col_int_key)
+);
+INSERT INTO t3 VALUES (10,8);
+CREATE TABLE t4 (
+pk int NOT NULL,
+col_varchar_nokey varchar(1) DEFAULT NULL,
+PRIMARY KEY (pk)
+);
+INSERT INTO t4 VALUES (1,'x');
+EXPLAIN SELECT OUTR.pk, OUTR.col_varchar_nokey, OUTR2.col_varchar_nokey
+FROM t2 AS OUTR2
+JOIN t4 AS OUTR
+ON (OUTR2.col_varchar_nokey > OUTR.col_varchar_nokey)
+WHERE
+OUTR.col_varchar_nokey IN (
+SELECT INNR.col_varchar_nokey
+FROM t3 AS INNR2
+LEFT JOIN t1 AS INNR
+ON (INNR2.col_int_key >= INNR.pk)
+)
+XOR OUTR.pk < 6
+;
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	PRIMARY	OUTR	system	NULL	NULL	NULL	NULL	1	
+1	PRIMARY	OUTR2	ALL	NULL	NULL	NULL	NULL	5	Using where
+2	SUBQUERY	INNR2	system	NULL	NULL	NULL	NULL	1	
+2	SUBQUERY	INNR	system	PRIMARY	NULL	NULL	NULL	1	
+FLUSH STATUS;
+SELECT OUTR.pk, OUTR.col_varchar_nokey, OUTR2.col_varchar_nokey
+FROM t2 AS OUTR2
+JOIN t4 AS OUTR
+ON (OUTR2.col_varchar_nokey > OUTR.col_varchar_nokey)
+WHERE
+OUTR.col_varchar_nokey IN (
+SELECT INNR.col_varchar_nokey
+FROM t3 AS INNR2
+LEFT JOIN t1 AS INNR
+ON (INNR2.col_int_key >= INNR.pk)
+)
+XOR OUTR.pk < 6
+;
+pk	col_varchar_nokey	col_varchar_nokey
+SHOW STATUS LIKE "HANDLER_READ%";
+Variable_name	Value
+Handler_read_first	3
+Handler_read_key	2
+Handler_read_last	0
+Handler_read_next	0
+Handler_read_prev	0
+Handler_read_rnd	0
+Handler_read_rnd_next	9
+DROP TABLE t1,t2,t3,t4;
 # End of 5.6 tests
 set optimizer_switch=default;

=== modified file 'mysql-test/r/subquery_mat_none.result'
--- a/mysql-test/r/subquery_mat_none.result	2012-01-31 11:19:25 +0000
+++ b/mysql-test/r/subquery_mat_none.result	2012-02-08 15:25:17 +0000
@@ -1292,5 +1292,484 @@ pk	pk
 1	NULL
 DROP TABLE t1, t2;
 # End of test for bug#13607423.
+
+Test of WL#6094 "Allow subquery materialization in NOT IN if all
+columns are not nullable"
+
+create table t1(a int not null);
+create table t2(a int not null);
+insert into t1 values(1),(2);
+insert into t2 values(1),(2);
+Test in SELECT list
+
+cols not nullable => subq materialization
+explain extended select a, (a,a) in (select a,a from t2) from t1;
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	filtered	Extra
+1	PRIMARY	t1	ALL	NULL	NULL	NULL	NULL	2	100.00	
+2	DEPENDENT SUBQUERY	t2	ALL	NULL	NULL	NULL	NULL	2	100.00	Using where
+Warnings:
+Note	1003	/* select#1 */ select `test`.`t1`.`a` AS `a`,<in_optimizer>((`test`.`t1`.`a`,`test`.`t1`.`a`),<exists>(/* select#2 */ select `test`.`t2`.`a`,`test`.`t2`.`a` from `test`.`t2` where ((<cache>(`test`.`t1`.`a`) = `test`.`t2`.`a`) and (<cache>(`test`.`t1`.`a`) = `test`.`t2`.`a`)) having (<is_not_null_test>(`test`.`t2`.`a`) and <is_not_null_test>(`test`.`t2`.`a`)))) AS `(a,a) in (select a,a from t2)` from `test`.`t1`
+select a, (a,a) in (select a,a from t2) from t1;
+a	(a,a) in (select a,a from t2)
+1	1
+2	1
+
+cols not nullable => subq materialization
+explain extended select t1.a, t2.a, (t1.a,t1.a) in (select a,a from t2 as t3)
+from t1 join t2 on t1.a+t2.a=1000;
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	filtered	Extra
+1	PRIMARY	t1	ALL	NULL	NULL	NULL	NULL	2	100.00	
+1	PRIMARY	t2	ALL	NULL	NULL	NULL	NULL	2	100.00	Using where; Using join buffer (Block Nested Loop)
+2	DEPENDENT SUBQUERY	t3	ALL	NULL	NULL	NULL	NULL	2	100.00	Using where
+Warnings:
+Note	1003	/* select#1 */ select `test`.`t1`.`a` AS `a`,`test`.`t2`.`a` AS `a`,<in_optimizer>((`test`.`t1`.`a`,`test`.`t1`.`a`),<exists>(/* select#2 */ select `test`.`t3`.`a`,`test`.`t3`.`a` from `test`.`t2` `t3` where ((<cache>(`test`.`t1`.`a`) = `test`.`t3`.`a`) and (<cache>(`test`.`t1`.`a`) = `test`.`t3`.`a`)) having (<is_not_null_test>(`test`.`t3`.`a`) and <is_not_null_test>(`test`.`t3`.`a`)))) AS `(t1.a,t1.a) in (select a,a from t2 as t3)` from `test`.`t1` join `test`.`t2` where ((`test`.`t1`.`a` + `test`.`t2`.`a`) = 1000)
+select t1.a, t2.a, (t1.a,t1.a) in (select a,a from t2 as t3)
+from t1 join t2 on t1.a+t2.a=1000;
+a	a	(t1.a,t1.a) in (select a,a from t2 as t3)
+
+t2.a is not nullable, but in the query it may appear as NULL
+as it's in an outer join. So, no materialization.
+explain extended select t1.a, t2.a, (t2.a,t2.a) in (select a,a from t2 as t3)
+from t1 left join t2 on t1.a+t2.a=1000;
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	filtered	Extra
+1	PRIMARY	t1	ALL	NULL	NULL	NULL	NULL	2	100.00	
+1	PRIMARY	t2	ALL	NULL	NULL	NULL	NULL	2	100.00	Using where; Using join buffer (Block Nested Loop)
+2	DEPENDENT SUBQUERY	t3	ALL	NULL	NULL	NULL	NULL	2	100.00	Using where
+Warnings:
+Note	1003	/* select#1 */ select `test`.`t1`.`a` AS `a`,`test`.`t2`.`a` AS `a`,<in_optimizer>((`test`.`t2`.`a`,`test`.`t2`.`a`),<exists>(/* select#2 */ select `test`.`t3`.`a`,`test`.`t3`.`a` from `test`.`t2` `t3` where (trigcond_if(outer_field_is_not_null, ((<cache>(`test`.`t2`.`a`) = `test`.`t3`.`a`) or <cache>(isnull(`test`.`t3`.`a`))), true) and trigcond_if(outer_field_is_not_null, ((<cache>(`test`.`t2`.`a`) = `test`.`t3`.`a`) or <cache>(isnull(`test`.`t3`.`a`))), true)) having (trigcond_if(outer_field_is_not_null, <is_not_null_test>(`test`.`t3`.`a`), true) and trigcond_if(outer_field_is_not_null, <is_not_null_test>(`test`.`t3`.`a`), true)))) AS `(t2.a,t2.a) in (select a,a from t2 as t3)` from `test`.`t1` left join `test`.`t2` on(((`test`.`t1`.`a` + `test`.`t2`.`a`) = 1000)) where 1
+select t1.a, t2.a, (t2.a,t2.a) in (select a,a from t2 as t3)
+from t1 left join t2 on t1.a+t2.a=1000;
+a	a	(t2.a,t2.a) in (select a,a from t2 as t3)
+1	NULL	NULL
+2	NULL	NULL
+
+alter table t2 modify a int;
+two nullable inner cols => no subq materialization
+explain extended select t1.a, t2.a, (t1.a,t1.a) in (select a,a from t2 as t3)
+from t1 join t2 on t1.a+t2.a=1000;
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	filtered	Extra
+1	PRIMARY	t1	ALL	NULL	NULL	NULL	NULL	2	100.00	
+1	PRIMARY	t2	ALL	NULL	NULL	NULL	NULL	2	100.00	Using where; Using join buffer (Block Nested Loop)
+2	DEPENDENT SUBQUERY	t3	ALL	NULL	NULL	NULL	NULL	2	100.00	Using where
+Warnings:
+Note	1003	/* select#1 */ select `test`.`t1`.`a` AS `a`,`test`.`t2`.`a` AS `a`,<in_optimizer>((`test`.`t1`.`a`,`test`.`t1`.`a`),<exists>(/* select#2 */ select `test`.`t3`.`a`,`test`.`t3`.`a` from `test`.`t2` `t3` where (((<cache>(`test`.`t1`.`a`) = `test`.`t3`.`a`) or isnull(`test`.`t3`.`a`)) and ((<cache>(`test`.`t1`.`a`) = `test`.`t3`.`a`) or isnull(`test`.`t3`.`a`))) having (<is_not_null_test>(`test`.`t3`.`a`) and <is_not_null_test>(`test`.`t3`.`a`)))) AS `(t1.a,t1.a) in (select a,a from t2 as t3)` from `test`.`t1` join `test`.`t2` where ((`test`.`t1`.`a` + `test`.`t2`.`a`) = 1000)
+select t1.a, t2.a, (t1.a,t1.a) in (select a,a from t2 as t3)
+from t1 join t2 on t1.a+t2.a=1000;
+a	a	(t1.a,t1.a) in (select a,a from t2 as t3)
+alter table t2 modify a int not null;
+
+Test in WHERE
+
+top-level => subq materialization. With one exception: if
+semijoin is enabled in @@optimizer_switch, semijoin is chosen,
+then rejected (due to outer join), and in that case, the
+fallback is IN->EXISTS, subq-materialization is not tried...
+explain extended select t1.a, t2.a
+from t1 join t2 on t1.a+t2.a=3
+where (t2.a,t2.a) in (select a,a from t2 as t3);
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	filtered	Extra
+1	PRIMARY	t1	ALL	NULL	NULL	NULL	NULL	2	100.00	
+1	PRIMARY	t2	ALL	NULL	NULL	NULL	NULL	2	100.00	Using where; Using join buffer (Block Nested Loop)
+2	DEPENDENT SUBQUERY	t3	ALL	NULL	NULL	NULL	NULL	2	100.00	Using where
+Warnings:
+Note	1003	/* select#1 */ select `test`.`t1`.`a` AS `a`,`test`.`t2`.`a` AS `a` from `test`.`t1` join `test`.`t2` where (<in_optimizer>((`test`.`t2`.`a`,`test`.`t2`.`a`),<exists>(/* select#2 */ select `test`.`t3`.`a`,`test`.`t3`.`a` from `test`.`t2` `t3` where ((<cache>(`test`.`t2`.`a`) = `test`.`t3`.`a`) and (<cache>(`test`.`t2`.`a`) = `test`.`t3`.`a`)))) and ((`test`.`t1`.`a` + `test`.`t2`.`a`) = 3))
+select t1.a, t2.a
+from t1 join t2 on t1.a+t2.a=3
+where (t2.a,t2.a) in (select a,a from t2 as t3);
+a	a
+2	1
+1	2
+
+cols not nullable => subq materialization
+explain extended select t1.a, t2.a
+from t1 join t2 on t1.a+t2.a=3
+where (t2.a,t2.a) not in (select a,a from t2 as t3);
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	filtered	Extra
+1	PRIMARY	t1	ALL	NULL	NULL	NULL	NULL	2	100.00	
+1	PRIMARY	t2	ALL	NULL	NULL	NULL	NULL	2	100.00	Using where; Using join buffer (Block Nested Loop)
+2	DEPENDENT SUBQUERY	t3	ALL	NULL	NULL	NULL	NULL	2	100.00	Using where
+Warnings:
+Note	1003	/* select#1 */ select `test`.`t1`.`a` AS `a`,`test`.`t2`.`a` AS `a` from `test`.`t1` join `test`.`t2` where ((not(<in_optimizer>((`test`.`t2`.`a`,`test`.`t2`.`a`),<exists>(/* select#2 */ select `test`.`t3`.`a`,`test`.`t3`.`a` from `test`.`t2` `t3` where ((<cache>(`test`.`t2`.`a`) = `test`.`t3`.`a`) and (<cache>(`test`.`t2`.`a`) = `test`.`t3`.`a`)) having (<is_not_null_test>(`test`.`t3`.`a`) and <is_not_null_test>(`test`.`t3`.`a`)))))) and ((`test`.`t1`.`a` + `test`.`t2`.`a`) = 3))
+select t1.a, t2.a
+from t1 join t2 on t1.a+t2.a=3
+where (t2.a,t2.a) not in (select a,a from t2 as t3);
+a	a
+drop table t1,t2;
+
+Test of WL6095 "Allow subquery materialization in NOT IN if
+single-column subquery"
+
+create table t1(a int null);
+create table t2(a int null);
+insert into t1 values(1),(2);
+insert into t2 values(1),(2);
+
+one col => subq materialization
+explain extended select a, a in (select a from t2) from t1;
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	filtered	Extra
+1	PRIMARY	t1	ALL	NULL	NULL	NULL	NULL	2	100.00	
+2	DEPENDENT SUBQUERY	t2	ALL	NULL	NULL	NULL	NULL	2	100.00	Using where
+Warnings:
+Note	1003	/* select#1 */ select `test`.`t1`.`a` AS `a`,<in_optimizer>(`test`.`t1`.`a`,<exists>(/* select#2 */ select 1 from `test`.`t2` where trigcond_if(outer_field_is_not_null, ((<cache>(`test`.`t1`.`a`) = `test`.`t2`.`a`) or isnull(`test`.`t2`.`a`)), true) having trigcond_if(outer_field_is_not_null, <is_not_null_test>(`test`.`t2`.`a`), true))) AS `a in (select a from t2)` from `test`.`t1`
+select a, a in (select a from t2) from t1;
+a	a in (select a from t2)
+1	1
+2	1
+
+t2.a is not nullable, but in the query it may appear as NULL
+as it's in an outer join. But there is only one inner column so
+materialization is possible
+explain extended select t1.a, t2.a, t2.a in (select * from t2 as t3)
+from t1 left join t2 on t1.a+t2.a=1000;
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	filtered	Extra
+1	PRIMARY	t1	ALL	NULL	NULL	NULL	NULL	2	100.00	
+1	PRIMARY	t2	ALL	NULL	NULL	NULL	NULL	2	100.00	Using where; Using join buffer (Block Nested Loop)
+2	DEPENDENT SUBQUERY	t3	ALL	NULL	NULL	NULL	NULL	2	100.00	Using where
+Warnings:
+Note	1003	/* select#1 */ select `test`.`t1`.`a` AS `a`,`test`.`t2`.`a` AS `a`,<in_optimizer>(`test`.`t2`.`a`,<exists>(/* select#2 */ select 1 from `test`.`t2` `t3` where trigcond_if(outer_field_is_not_null, ((<cache>(`test`.`t2`.`a`) = `test`.`t3`.`a`) or isnull(`test`.`t3`.`a`)), true) having trigcond_if(outer_field_is_not_null, <is_not_null_test>(`test`.`t3`.`a`), true))) AS `t2.a in (select * from t2 as t3)` from `test`.`t1` left join `test`.`t2` on(((`test`.`t1`.`a` + `test`.`t2`.`a`) = 1000)) where 1
+select t1.a, t2.a, t2.a in (select * from t2 as t3)
+from t1 left join t2 on t1.a+t2.a=1000;
+a	a	t2.a in (select * from t2 as t3)
+1	NULL	NULL
+2	NULL	NULL
+
+_two_ outer columns, nullable => no materialization
+explain extended select t1.a, t2.a, (t2.a,t2.a) in (select a,a from t2 as t3)
+from t1 left join t2 on t1.a+t2.a=1000;
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	filtered	Extra
+1	PRIMARY	t1	ALL	NULL	NULL	NULL	NULL	2	100.00	
+1	PRIMARY	t2	ALL	NULL	NULL	NULL	NULL	2	100.00	Using where; Using join buffer (Block Nested Loop)
+2	DEPENDENT SUBQUERY	t3	ALL	NULL	NULL	NULL	NULL	2	100.00	Using where
+Warnings:
+Note	1003	/* select#1 */ select `test`.`t1`.`a` AS `a`,`test`.`t2`.`a` AS `a`,<in_optimizer>((`test`.`t2`.`a`,`test`.`t2`.`a`),<exists>(/* select#2 */ select `test`.`t3`.`a`,`test`.`t3`.`a` from `test`.`t2` `t3` where (trigcond_if(outer_field_is_not_null, ((<cache>(`test`.`t2`.`a`) = `test`.`t3`.`a`) or isnull(`test`.`t3`.`a`)), true) and trigcond_if(outer_field_is_not_null, ((<cache>(`test`.`t2`.`a`) = `test`.`t3`.`a`) or isnull(`test`.`t3`.`a`)), true)) having (trigcond_if(outer_field_is_not_null, <is_not_null_test>(`test`.`t3`.`a`), true) and trigcond_if(outer_field_is_not_null, <is_not_null_test>(`test`.`t3`.`a`), true)))) AS `(t2.a,t2.a) in (select a,a from t2 as t3)` from `test`.`t1` left join `test`.`t2` on(((`test`.`t1`.`a` + `test`.`t2`.`a`) = 1000)) where 1
+select t1.a, t2.a, (t2.a,t2.a) in (select a,a from t2 as t3)
+from t1 left join t2 on t1.a+t2.a=1000;
+a	a	(t2.a,t2.a) in (select a,a from t2 as t3)
+1	NULL	NULL
+2	NULL	NULL
+drop table t1,t2;
+
+Test in HAVING
+create table t1(a int, b int);
+create table t2(a int);
+insert into t1 values(1,1),(1,2),(1,3),(2,1),(2,2),(2,3);
+insert into t2 values(10),(20);
+no NULLs.
+explain extended select t1.a as z, sum(t1.b) from t1 group by t1.a
+having (z in (select * from t2)) is null;
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	filtered	Extra
+1	PRIMARY	t1	ALL	NULL	NULL	NULL	NULL	6	100.00	Using temporary; Using filesort
+2	DEPENDENT SUBQUERY	t2	ALL	NULL	NULL	NULL	NULL	2	100.00	Using where
+Warnings:
+Note	1003	/* select#1 */ select `test`.`t1`.`a` AS `z`,sum(`test`.`t1`.`b`) AS `sum(t1.b)` from `test`.`t1` group by `test`.`t1`.`a` having isnull(<in_optimizer>(`z`,<exists>(/* select#2 */ select 1 from `test`.`t2` where trigcond_if(outer_field_is_not_null, ((<cache>(`z`) = `test`.`t2`.`a`) or isnull(`test`.`t2`.`a`)), true) having trigcond_if(outer_field_is_not_null, <is_not_null_test>(`test`.`t2`.`a`), true))))
+select t1.a as z, sum(t1.b) from t1 group by t1.a
+having (z in (select * from t2)) is null;
+z	sum(t1.b)
+one outer NULL
+insert into t1 values(null,null);
+explain extended select t1.a as z, sum(t1.b) from t1 group by t1.a
+having (z in (select * from t2)) is null;
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	filtered	Extra
+1	PRIMARY	t1	ALL	NULL	NULL	NULL	NULL	7	100.00	Using temporary; Using filesort
+2	DEPENDENT SUBQUERY	t2	ALL	NULL	NULL	NULL	NULL	2	100.00	Using where
+Warnings:
+Note	1003	/* select#1 */ select `test`.`t1`.`a` AS `z`,sum(`test`.`t1`.`b`) AS `sum(t1.b)` from `test`.`t1` group by `test`.`t1`.`a` having isnull(<in_optimizer>(`z`,<exists>(/* select#2 */ select 1 from `test`.`t2` where trigcond_if(outer_field_is_not_null, ((<cache>(`z`) = `test`.`t2`.`a`) or isnull(`test`.`t2`.`a`)), true) having trigcond_if(outer_field_is_not_null, <is_not_null_test>(`test`.`t2`.`a`), true))))
+select t1.a as z, sum(t1.b) from t1 group by t1.a
+having (z in (select * from t2)) is null;
+z	sum(t1.b)
+NULL	NULL
+one outer NULL and one inner NULL
+insert into t2 values(null);
+explain extended select t1.a as z, sum(t1.b) from t1 group by t1.a
+having (z in (select * from t2)) is null;
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	filtered	Extra
+1	PRIMARY	t1	ALL	NULL	NULL	NULL	NULL	7	100.00	Using temporary; Using filesort
+2	DEPENDENT SUBQUERY	t2	ALL	NULL	NULL	NULL	NULL	3	100.00	Using where
+Warnings:
+Note	1003	/* select#1 */ select `test`.`t1`.`a` AS `z`,sum(`test`.`t1`.`b`) AS `sum(t1.b)` from `test`.`t1` group by `test`.`t1`.`a` having isnull(<in_optimizer>(`z`,<exists>(/* select#2 */ select 1 from `test`.`t2` where trigcond_if(outer_field_is_not_null, ((<cache>(`z`) = `test`.`t2`.`a`) or isnull(`test`.`t2`.`a`)), true) having trigcond_if(outer_field_is_not_null, <is_not_null_test>(`test`.`t2`.`a`), true))))
+select t1.a as z, sum(t1.b) from t1 group by t1.a
+having (z in (select * from t2)) is null;
+z	sum(t1.b)
+NULL	NULL
+1	6
+2	6
+one inner NULL
+delete from t1 where a is null;
+explain extended select t1.a as z, sum(t1.b) from t1 group by t1.a
+having (z in (select * from t2)) is null;
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	filtered	Extra
+1	PRIMARY	t1	ALL	NULL	NULL	NULL	NULL	6	100.00	Using temporary; Using filesort
+2	DEPENDENT SUBQUERY	t2	ALL	NULL	NULL	NULL	NULL	3	100.00	Using where
+Warnings:
+Note	1003	/* select#1 */ select `test`.`t1`.`a` AS `z`,sum(`test`.`t1`.`b`) AS `sum(t1.b)` from `test`.`t1` group by `test`.`t1`.`a` having isnull(<in_optimizer>(`z`,<exists>(/* select#2 */ select 1 from `test`.`t2` where trigcond_if(outer_field_is_not_null, ((<cache>(`z`) = `test`.`t2`.`a`) or isnull(`test`.`t2`.`a`)), true) having trigcond_if(outer_field_is_not_null, <is_not_null_test>(`test`.`t2`.`a`), true))))
+select t1.a as z, sum(t1.b) from t1 group by t1.a
+having (z in (select * from t2)) is null;
+z	sum(t1.b)
+1	6
+2	6
+drop table t1,t2;
+
+Verify that an inner NULL is looked up only once (result is
+cached).
+create table t1(a int);
+create table t2(a int);
+insert into t1 values(1),(2),(3),(4),(5),(6);
+insert into t1 select * from t1;
+insert into t2 values(10),(20),(NULL);
+explain extended select a, (a in (select * from t2)) from t1;
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	filtered	Extra
+1	PRIMARY	t1	ALL	NULL	NULL	NULL	NULL	12	100.00	
+2	DEPENDENT SUBQUERY	t2	ALL	NULL	NULL	NULL	NULL	3	100.00	Using where
+Warnings:
+Note	1003	/* select#1 */ select `test`.`t1`.`a` AS `a`,<in_optimizer>(`test`.`t1`.`a`,<exists>(/* select#2 */ select 1 from `test`.`t2` where trigcond_if(outer_field_is_not_null, ((<cache>(`test`.`t1`.`a`) = `test`.`t2`.`a`) or isnull(`test`.`t2`.`a`)), true) having trigcond_if(outer_field_is_not_null, <is_not_null_test>(`test`.`t2`.`a`), true))) AS `(a in (select * from t2))` from `test`.`t1`
+flush status;
+select a, (a in (select * from t2)) from t1;
+a	(a in (select * from t2))
+1	NULL
+2	NULL
+3	NULL
+4	NULL
+5	NULL
+6	NULL
+1	NULL
+2	NULL
+3	NULL
+4	NULL
+5	NULL
+6	NULL
+There will be one look-up in the temporary table for each row
+of t1 (12), plus one additional look-up to check whether table
+contains a NULL value.
+show status like "handler_read_key";
+Variable_name	Value
+Handler_read_key	0
+drop table t1,t2;
+#
+# Bug#13495157 - SUBQUERY MATERIALIZATION NOT USED FOR CERTAIN
+# STATEMENTS
+#
+CREATE TABLE t1(a INT);
+INSERT INTO t1 VALUES(1),(2),(3);
+CREATE TABLE t2(a INT);
+INSERT INTO t2 VALUES(1),(2),(4);
+# subquery materialization used for SELECT:
+EXPLAIN SELECT * FROM t1 WHERE a IN (SELECT * FROM t2 WHERE a <> 2);
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	PRIMARY	t1	ALL	NULL	NULL	NULL	NULL	3	Using where
+2	DEPENDENT SUBQUERY	t2	ALL	NULL	NULL	NULL	NULL	3	Using where
+SELECT * FROM t1 WHERE a IN (SELECT * FROM t2 WHERE a <> 2);
+a
+1
+# Also used for INSERT SELECT:
+CREATE TABLE t3 SELECT * FROM t1;
+EXPLAIN INSERT INTO t3 SELECT * FROM t1 WHERE a IN (SELECT * FROM t2 WHERE a <> 2);
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	PRIMARY	t1	ALL	NULL	NULL	NULL	NULL	3	Using where
+2	DEPENDENT SUBQUERY	t2	ALL	NULL	NULL	NULL	NULL	3	Using where
+INSERT INTO t3 SELECT * FROM t1 WHERE a IN (SELECT * FROM t2 WHERE a <> 2);
+SELECT * FROM t3;
+a
+1
+1
+2
+3
+EXPLAIN INSERT INTO t2 SELECT * FROM t1 WHERE a IN (SELECT * FROM t2 WHERE a <> 2);
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	PRIMARY	t1	ALL	NULL	NULL	NULL	NULL	3	Using where; Using temporary
+2	DEPENDENT SUBQUERY	t2	ALL	NULL	NULL	NULL	NULL	3	Using where
+INSERT INTO t2 SELECT * FROM t1 WHERE a IN (SELECT * FROM t2 WHERE a <> 2);
+SELECT * FROM t2;
+a
+1
+1
+2
+4
+EXPLAIN INSERT INTO t2 SELECT * FROM t2 WHERE a IN (SELECT * FROM t2 WHERE a <> 2);
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	PRIMARY	t2	ALL	NULL	NULL	NULL	NULL	4	Using where; Using temporary
+2	DEPENDENT SUBQUERY	t2	ALL	NULL	NULL	NULL	NULL	4	Using where
+INSERT INTO t2 SELECT * FROM t2 WHERE a IN (SELECT * FROM t2 WHERE a <> 2);
+SELECT * FROM t2;
+a
+1
+1
+1
+1
+2
+4
+4
+# Not used for single-table UPDATE, DELETE:
+EXPLAIN SELECT * FROM t2 WHERE a IN (SELECT * FROM t1);
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	PRIMARY	t2	ALL	NULL	NULL	NULL	NULL	7	Using where
+2	DEPENDENT SUBQUERY	t1	ALL	NULL	NULL	NULL	NULL	3	Using where
+EXPLAIN UPDATE t2 SET a=a-1 WHERE a IN (SELECT * FROM t1);
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	PRIMARY	t2	ALL	NULL	NULL	NULL	NULL	7	Using where
+2	DEPENDENT SUBQUERY	t1	ALL	NULL	NULL	NULL	NULL	3	Using where
+UPDATE t2 SET a=a-1 WHERE a IN (SELECT * FROM t1);
+SELECT * FROM t2;
+a
+0
+0
+0
+0
+1
+4
+4
+EXPLAIN DELETE FROM t2 WHERE a IN (SELECT * FROM t1);
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	PRIMARY	t2	ALL	NULL	NULL	NULL	NULL	7	Using where
+2	DEPENDENT SUBQUERY	t1	ALL	NULL	NULL	NULL	NULL	3	Using where
+DELETE FROM t2 WHERE a IN (SELECT * FROM t1);
+SELECT * FROM t2;
+a
+0
+0
+0
+0
+4
+4
+EXPLAIN UPDATE t2 SET a=a-1 WHERE a IN (SELECT * FROM t2);
+ERROR HY000: You can't specify target table 't2' for update in FROM clause
+EXPLAIN DELETE FROM t2 WHERE a IN (SELECT * FROM t2);
+ERROR HY000: You can't specify target table 't2' for update in FROM clause
+UPDATE t2 SET a=3 WHERE a=0;
+# Used for multi-table UPDATE, DELETE:
+EXPLAIN SELECT * FROM t2,t3 WHERE t2.a IN (SELECT * FROM t1 WHERE a <> 2);
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	PRIMARY	t3	ALL	NULL	NULL	NULL	NULL	4	
+1	PRIMARY	t2	ALL	NULL	NULL	NULL	NULL	6	Using where; Using join buffer (Block Nested Loop)
+2	DEPENDENT SUBQUERY	t1	ALL	NULL	NULL	NULL	NULL	3	Using where
+EXPLAIN UPDATE t2,t3 SET t2.a=t2.a-2 WHERE t2.a IN (SELECT * FROM t1 WHERE a <> 2);
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	PRIMARY	t3	ALL	NULL	NULL	NULL	NULL	4	
+1	PRIMARY	t2	ALL	NULL	NULL	NULL	NULL	6	Using where; Using join buffer (Block Nested Loop)
+2	DEPENDENT SUBQUERY	t1	ALL	NULL	NULL	NULL	NULL	3	Using where
+UPDATE t2,t3 SET t2.a=t2.a-2 WHERE t2.a IN (SELECT * FROM t1 WHERE a <> 2);
+SELECT * FROM t2;
+a
+1
+1
+1
+1
+4
+4
+EXPLAIN DELETE t2.* FROM t2,t3 WHERE t2.a IN (SELECT * FROM t1 WHERE a <> 2);
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	PRIMARY	t3	ALL	NULL	NULL	NULL	NULL	4	
+1	PRIMARY	t2	ALL	NULL	NULL	NULL	NULL	6	Using where; Using join buffer (Block Nested Loop)
+2	DEPENDENT SUBQUERY	t1	ALL	NULL	NULL	NULL	NULL	3	Using where
+DELETE t2.* FROM t2,t3 WHERE t2.a IN (SELECT * FROM t1 WHERE a <> 2);
+SELECT * FROM t2;
+a
+4
+4
+EXPLAIN UPDATE t2,t3 SET t2.a=10 WHERE t2.a IN (SELECT * FROM t2 WHERE a <> 2);
+ERROR HY000: You can't specify target table 't2' for update in FROM clause
+EXPLAIN DELETE t2.* FROM t2,t3 WHERE t2.a IN (SELECT * FROM t2 WHERE a <> 2);
+ERROR HY000: You can't specify target table 't2' for update in FROM clause
+DROP TABLE t1,t2,t3;
+#
+# Test that subquery materialization only does one lookup: does
+# not try to read the next row if the first row failed the
+# subquery's WHERE. We use a case where index lookup is not
+# enough to satisfy IN(), because index has length two when the
+# outer value has length three, and thus the post-filtering
+# WHERE added by subselect_hash_sj_engine::setup() makes the
+# decision.
+#
+create table t1 (a varchar(3));
+create table t2 (a varchar(2));
+insert into t1 values('aaa'), ('aaa');
+insert into t2 values('aa'), ('aa');
+explain select * from t1 where a in (select a from t2);
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	PRIMARY	t1	ALL	NULL	NULL	NULL	NULL	2	Using where
+2	DEPENDENT SUBQUERY	t2	ALL	NULL	NULL	NULL	NULL	2	Using where
+flush status;
+select * from t1 where a in (select a from t2);
+a
+show status like "handler_read%";
+Variable_name	Value
+Handler_read_first	0
+Handler_read_key	0
+Handler_read_last	0
+Handler_read_next	0
+Handler_read_prev	0
+Handler_read_rnd	0
+Handler_read_rnd_next	9
+drop table t1,t2;
+#
+# Bug#13655791 DIFFERENT RESULT WITH WL6094 ON QUERY WITH XOR
+# IN WHERE CLAUSE + MYISAM
+#
+CREATE TABLE t1 (
+pk int NOT NULL,
+col_varchar_nokey varchar(1) DEFAULT NULL,
+PRIMARY KEY (pk)
+);
+INSERT INTO t1 VALUES (10,'x');
+CREATE TABLE t2 (
+pk int NOT NULL,
+col_varchar_nokey varchar(1) DEFAULT NULL,
+PRIMARY KEY (pk)
+);
+INSERT INTO t2 VALUES (1,'v'), (5,'x'), (11,'z'), (12,'c'), (15,'y');
+CREATE TABLE t3 (
+pk int NOT NULL,
+col_int_key int DEFAULT NULL,
+PRIMARY KEY (pk),
+KEY col_int_key (col_int_key)
+);
+INSERT INTO t3 VALUES (10,8);
+CREATE TABLE t4 (
+pk int NOT NULL,
+col_varchar_nokey varchar(1) DEFAULT NULL,
+PRIMARY KEY (pk)
+);
+INSERT INTO t4 VALUES (1,'x');
+EXPLAIN SELECT OUTR.pk, OUTR.col_varchar_nokey, OUTR2.col_varchar_nokey
+FROM t2 AS OUTR2
+JOIN t4 AS OUTR
+ON (OUTR2.col_varchar_nokey > OUTR.col_varchar_nokey)
+WHERE
+OUTR.col_varchar_nokey IN (
+SELECT INNR.col_varchar_nokey
+FROM t3 AS INNR2
+LEFT JOIN t1 AS INNR
+ON (INNR2.col_int_key >= INNR.pk)
+)
+XOR OUTR.pk < 6
+;
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	PRIMARY	NULL	NULL	NULL	NULL	NULL	NULL	NULL	Impossible WHERE noticed after reading const tables
+2	DEPENDENT SUBQUERY	INNR2	system	NULL	NULL	NULL	NULL	1	
+2	DEPENDENT SUBQUERY	INNR	system	PRIMARY	NULL	NULL	NULL	1	
+FLUSH STATUS;
+SELECT OUTR.pk, OUTR.col_varchar_nokey, OUTR2.col_varchar_nokey
+FROM t2 AS OUTR2
+JOIN t4 AS OUTR
+ON (OUTR2.col_varchar_nokey > OUTR.col_varchar_nokey)
+WHERE
+OUTR.col_varchar_nokey IN (
+SELECT INNR.col_varchar_nokey
+FROM t3 AS INNR2
+LEFT JOIN t1 AS INNR
+ON (INNR2.col_int_key >= INNR.pk)
+)
+XOR OUTR.pk < 6
+;
+pk	col_varchar_nokey	col_varchar_nokey
+SHOW STATUS LIKE "HANDLER_READ%";
+Variable_name	Value
+Handler_read_first	3
+Handler_read_key	0
+Handler_read_last	0
+Handler_read_next	0
+Handler_read_prev	0
+Handler_read_rnd	0
+Handler_read_rnd_next	3
+DROP TABLE t1,t2,t3,t4;
 # End of 5.6 tests
 set optimizer_switch=default;

=== modified file 'mysql-test/r/subquery_nomat_nosj.result'
--- a/mysql-test/r/subquery_nomat_nosj.result	2012-02-07 14:50:31 +0000
+++ b/mysql-test/r/subquery_nomat_nosj.result	2012-02-08 15:25:17 +0000
@@ -4180,7 +4180,7 @@ CREATE TABLE t1 (pk int PRIMARY KEY, int
 INSERT INTO t1 VALUES (10,1), (14,1);
 CREATE TABLE t2 (pk int PRIMARY KEY, int_key int);
 INSERT INTO t2 VALUES (3,3), (5,NULL), (7,3);
-# should have eq_ref for t1
+# should have eq_ref for t1, unless subquery materialization is used
 EXPLAIN
 SELECT * FROM t2 outr
 WHERE outr.int_key NOT IN (SELECT t1.pk FROM t1, t2)  
@@ -7080,4 +7080,31 @@ WHERE (c_sq1_alias1.col_int_nokey != @va
 OR c_sq1_alias1.pk != @var3)) ) AS alias3;
 pk	col_int_nokey	col_int_key	col_time_key	col_varchar_key	col_varchar_nokey
 DROP TABLE t1,t2;
+#
+# Test that indexsubquery_engine only does one lookup if
+# the technique is unique_subquery: does not try to read the
+# next row if the first row failed the subquery's WHERE
+# condition (here: b=3).
+#
+create table t1(a int);
+insert into t1 values(1),(2);
+create table t2(a int primary key, b int);
+insert into t2 values(1,10),(2,10);
+explain select * from t1 where a in (select a from t2 where b=3);
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	PRIMARY	t1	ALL	NULL	NULL	NULL	NULL	2	Using where
+2	DEPENDENT SUBQUERY	t2	unique_subquery	PRIMARY	PRIMARY	4	func	1	Using where
+flush status;
+select * from t1 where a in (select a from t2 where b=3);
+a
+show status like "handler_read%";
+Variable_name	Value
+Handler_read_first	0
+Handler_read_key	2
+Handler_read_last	0
+Handler_read_next	0
+Handler_read_prev	0
+Handler_read_rnd	0
+Handler_read_rnd_next	3
+drop table t1,t2;
 set optimizer_switch=default;

=== modified file 'mysql-test/r/subquery_nomat_nosj_bka.result'
--- a/mysql-test/r/subquery_nomat_nosj_bka.result	2012-02-07 14:50:31 +0000
+++ b/mysql-test/r/subquery_nomat_nosj_bka.result	2012-02-08 15:25:17 +0000
@@ -4181,7 +4181,7 @@ CREATE TABLE t1 (pk int PRIMARY KEY, int
 INSERT INTO t1 VALUES (10,1), (14,1);
 CREATE TABLE t2 (pk int PRIMARY KEY, int_key int);
 INSERT INTO t2 VALUES (3,3), (5,NULL), (7,3);
-# should have eq_ref for t1
+# should have eq_ref for t1, unless subquery materialization is used
 EXPLAIN
 SELECT * FROM t2 outr
 WHERE outr.int_key NOT IN (SELECT t1.pk FROM t1, t2)  
@@ -7081,5 +7081,32 @@ WHERE (c_sq1_alias1.col_int_nokey != @va
 OR c_sq1_alias1.pk != @var3)) ) AS alias3;
 pk	col_int_nokey	col_int_key	col_time_key	col_varchar_key	col_varchar_nokey
 DROP TABLE t1,t2;
+#
+# Test that indexsubquery_engine only does one lookup if
+# the technique is unique_subquery: does not try to read the
+# next row if the first row failed the subquery's WHERE
+# condition (here: b=3).
+#
+create table t1(a int);
+insert into t1 values(1),(2);
+create table t2(a int primary key, b int);
+insert into t2 values(1,10),(2,10);
+explain select * from t1 where a in (select a from t2 where b=3);
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	PRIMARY	t1	ALL	NULL	NULL	NULL	NULL	2	Using where
+2	DEPENDENT SUBQUERY	t2	unique_subquery	PRIMARY	PRIMARY	4	func	1	Using where
+flush status;
+select * from t1 where a in (select a from t2 where b=3);
+a
+show status like "handler_read%";
+Variable_name	Value
+Handler_read_first	0
+Handler_read_key	2
+Handler_read_last	0
+Handler_read_next	0
+Handler_read_prev	0
+Handler_read_rnd	0
+Handler_read_rnd_next	3
+drop table t1,t2;
 set optimizer_switch=default;
 set optimizer_switch=default;

=== modified file 'mysql-test/r/subquery_nomat_nosj_bka_nixbnl.result'
--- a/mysql-test/r/subquery_nomat_nosj_bka_nixbnl.result	2012-02-07 14:50:31 +0000
+++ b/mysql-test/r/subquery_nomat_nosj_bka_nixbnl.result	2012-02-08 15:25:17 +0000
@@ -4181,7 +4181,7 @@ CREATE TABLE t1 (pk int PRIMARY KEY, int
 INSERT INTO t1 VALUES (10,1), (14,1);
 CREATE TABLE t2 (pk int PRIMARY KEY, int_key int);
 INSERT INTO t2 VALUES (3,3), (5,NULL), (7,3);
-# should have eq_ref for t1
+# should have eq_ref for t1, unless subquery materialization is used
 EXPLAIN
 SELECT * FROM t2 outr
 WHERE outr.int_key NOT IN (SELECT t1.pk FROM t1, t2)  
@@ -7081,5 +7081,32 @@ WHERE (c_sq1_alias1.col_int_nokey != @va
 OR c_sq1_alias1.pk != @var3)) ) AS alias3;
 pk	col_int_nokey	col_int_key	col_time_key	col_varchar_key	col_varchar_nokey
 DROP TABLE t1,t2;
+#
+# Test that indexsubquery_engine only does one lookup if
+# the technique is unique_subquery: does not try to read the
+# next row if the first row failed the subquery's WHERE
+# condition (here: b=3).
+#
+create table t1(a int);
+insert into t1 values(1),(2);
+create table t2(a int primary key, b int);
+insert into t2 values(1,10),(2,10);
+explain select * from t1 where a in (select a from t2 where b=3);
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	PRIMARY	t1	ALL	NULL	NULL	NULL	NULL	2	Using where
+2	DEPENDENT SUBQUERY	t2	unique_subquery	PRIMARY	PRIMARY	4	func	1	Using where
+flush status;
+select * from t1 where a in (select a from t2 where b=3);
+a
+show status like "handler_read%";
+Variable_name	Value
+Handler_read_first	0
+Handler_read_key	2
+Handler_read_last	0
+Handler_read_next	0
+Handler_read_prev	0
+Handler_read_rnd	0
+Handler_read_rnd_next	3
+drop table t1,t2;
 set optimizer_switch=default;
 set optimizer_switch=default;

=== modified file 'mysql-test/r/subquery_none.result'
--- a/mysql-test/r/subquery_none.result	2012-02-07 14:50:31 +0000
+++ b/mysql-test/r/subquery_none.result	2012-02-08 15:25:17 +0000
@@ -4179,7 +4179,7 @@ CREATE TABLE t1 (pk int PRIMARY KEY, int
 INSERT INTO t1 VALUES (10,1), (14,1);
 CREATE TABLE t2 (pk int PRIMARY KEY, int_key int);
 INSERT INTO t2 VALUES (3,3), (5,NULL), (7,3);
-# should have eq_ref for t1
+# should have eq_ref for t1, unless subquery materialization is used
 EXPLAIN
 SELECT * FROM t2 outr
 WHERE outr.int_key NOT IN (SELECT t1.pk FROM t1, t2)  
@@ -7079,4 +7079,31 @@ WHERE (c_sq1_alias1.col_int_nokey != @va
 OR c_sq1_alias1.pk != @var3)) ) AS alias3;
 pk	col_int_nokey	col_int_key	col_time_key	col_varchar_key	col_varchar_nokey
 DROP TABLE t1,t2;
+#
+# Test that indexsubquery_engine only does one lookup if
+# the technique is unique_subquery: does not try to read the
+# next row if the first row failed the subquery's WHERE
+# condition (here: b=3).
+#
+create table t1(a int);
+insert into t1 values(1),(2);
+create table t2(a int primary key, b int);
+insert into t2 values(1,10),(2,10);
+explain select * from t1 where a in (select a from t2 where b=3);
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	PRIMARY	t1	ALL	NULL	NULL	NULL	NULL	2	Using where
+2	DEPENDENT SUBQUERY	t2	unique_subquery	PRIMARY	PRIMARY	4	func	1	Using where
+flush status;
+select * from t1 where a in (select a from t2 where b=3);
+a
+show status like "handler_read%";
+Variable_name	Value
+Handler_read_first	0
+Handler_read_key	2
+Handler_read_last	0
+Handler_read_next	0
+Handler_read_prev	0
+Handler_read_rnd	0
+Handler_read_rnd_next	3
+drop table t1,t2;
 set optimizer_switch=default;

=== modified file 'mysql-test/r/subquery_none_bka.result'
--- a/mysql-test/r/subquery_none_bka.result	2012-02-07 14:50:31 +0000
+++ b/mysql-test/r/subquery_none_bka.result	2012-02-08 15:25:17 +0000
@@ -4180,7 +4180,7 @@ CREATE TABLE t1 (pk int PRIMARY KEY, int
 INSERT INTO t1 VALUES (10,1), (14,1);
 CREATE TABLE t2 (pk int PRIMARY KEY, int_key int);
 INSERT INTO t2 VALUES (3,3), (5,NULL), (7,3);
-# should have eq_ref for t1
+# should have eq_ref for t1, unless subquery materialization is used
 EXPLAIN
 SELECT * FROM t2 outr
 WHERE outr.int_key NOT IN (SELECT t1.pk FROM t1, t2)  
@@ -7080,5 +7080,32 @@ WHERE (c_sq1_alias1.col_int_nokey != @va
 OR c_sq1_alias1.pk != @var3)) ) AS alias3;
 pk	col_int_nokey	col_int_key	col_time_key	col_varchar_key	col_varchar_nokey
 DROP TABLE t1,t2;
+#
+# Test that indexsubquery_engine only does one lookup if
+# the technique is unique_subquery: does not try to read the
+# next row if the first row failed the subquery's WHERE
+# condition (here: b=3).
+#
+create table t1(a int);
+insert into t1 values(1),(2);
+create table t2(a int primary key, b int);
+insert into t2 values(1,10),(2,10);
+explain select * from t1 where a in (select a from t2 where b=3);
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	PRIMARY	t1	ALL	NULL	NULL	NULL	NULL	2	Using where
+2	DEPENDENT SUBQUERY	t2	unique_subquery	PRIMARY	PRIMARY	4	func	1	Using where
+flush status;
+select * from t1 where a in (select a from t2 where b=3);
+a
+show status like "handler_read%";
+Variable_name	Value
+Handler_read_first	0
+Handler_read_key	2
+Handler_read_last	0
+Handler_read_next	0
+Handler_read_prev	0
+Handler_read_rnd	0
+Handler_read_rnd_next	3
+drop table t1,t2;
 set optimizer_switch=default;
 set optimizer_switch=default;

=== modified file 'mysql-test/r/subquery_none_bka_nixbnl.result'
--- a/mysql-test/r/subquery_none_bka_nixbnl.result	2012-02-07 14:50:31 +0000
+++ b/mysql-test/r/subquery_none_bka_nixbnl.result	2012-02-08 15:25:17 +0000
@@ -4180,7 +4180,7 @@ CREATE TABLE t1 (pk int PRIMARY KEY, int
 INSERT INTO t1 VALUES (10,1), (14,1);
 CREATE TABLE t2 (pk int PRIMARY KEY, int_key int);
 INSERT INTO t2 VALUES (3,3), (5,NULL), (7,3);
-# should have eq_ref for t1
+# should have eq_ref for t1, unless subquery materialization is used
 EXPLAIN
 SELECT * FROM t2 outr
 WHERE outr.int_key NOT IN (SELECT t1.pk FROM t1, t2)  
@@ -7080,5 +7080,32 @@ WHERE (c_sq1_alias1.col_int_nokey != @va
 OR c_sq1_alias1.pk != @var3)) ) AS alias3;
 pk	col_int_nokey	col_int_key	col_time_key	col_varchar_key	col_varchar_nokey
 DROP TABLE t1,t2;
+#
+# Test that indexsubquery_engine only does one lookup if
+# the technique is unique_subquery: does not try to read the
+# next row if the first row failed the subquery's WHERE
+# condition (here: b=3).
+#
+create table t1(a int);
+insert into t1 values(1),(2);
+create table t2(a int primary key, b int);
+insert into t2 values(1,10),(2,10);
+explain select * from t1 where a in (select a from t2 where b=3);
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	PRIMARY	t1	ALL	NULL	NULL	NULL	NULL	2	Using where
+2	DEPENDENT SUBQUERY	t2	unique_subquery	PRIMARY	PRIMARY	4	func	1	Using where
+flush status;
+select * from t1 where a in (select a from t2 where b=3);
+a
+show status like "handler_read%";
+Variable_name	Value
+Handler_read_first	0
+Handler_read_key	2
+Handler_read_last	0
+Handler_read_next	0
+Handler_read_prev	0
+Handler_read_rnd	0
+Handler_read_rnd_next	3
+drop table t1,t2;
 set optimizer_switch=default;
 set optimizer_switch=default;

=== modified file 'mysql-test/r/subquery_sj_all.result'
--- a/mysql-test/r/subquery_sj_all.result	2012-01-31 11:19:25 +0000
+++ b/mysql-test/r/subquery_sj_all.result	2012-02-08 15:25:17 +0000
@@ -6706,9 +6706,9 @@ AND grandparent1.col_varchar_key IS NOT 
 );
 id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
 1	PRIMARY	t1	ALL	NULL	NULL	NULL	NULL	2	Using where
-2	DEPENDENT SUBQUERY	parent1	ALL	NULL	NULL	NULL	NULL	20	Start temporary
-2	DEPENDENT SUBQUERY	parent2	eq_ref	PRIMARY	PRIMARY	4	test.parent1.pk	1	Using index
-2	DEPENDENT SUBQUERY	grandparent1	ref	col_varchar_key	col_varchar_key	3	test.parent1.col_varchar_nokey	1	Using index condition; Using where; End temporary
+2	SUBQUERY	parent1	ALL	NULL	NULL	NULL	NULL	20	Start temporary
+2	SUBQUERY	parent2	eq_ref	PRIMARY	PRIMARY	4	test.parent1.pk	1	Using index
+2	SUBQUERY	grandparent1	ref	col_varchar_key	col_varchar_key	3	test.parent1.col_varchar_nokey	1	Using index condition; End temporary
 SELECT *
 FROM t1
 WHERE g1 NOT IN
@@ -7091,10 +7091,10 @@ ON parent1.col_int_nokey = parent2.col_i
 AND grandparent1.col_int_key <> 3
 );
 id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
-1	PRIMARY	NULL	NULL	NULL	NULL	NULL	NULL	NULL	Impossible WHERE noticed after reading const tables
-2	DEPENDENT SUBQUERY	parent1	ref	col_int_key	col_int_key	4	func	2	Using index condition; Start temporary
-2	DEPENDENT SUBQUERY	parent2	index	col_int_key	col_int_key	4	NULL	1	Using where; Using index; Using join buffer (Block Nested Loop)
-2	DEPENDENT SUBQUERY	grandparent1	ref	col_int_key	col_int_key	4	func	2	Using index condition; Using where; End temporary
+1	PRIMARY	t3	system	NULL	NULL	NULL	NULL	1	
+2	SUBQUERY	grandparent1	ALL	col_int_key	NULL	NULL	NULL	11	Using where
+2	SUBQUERY	parent1	ALL	col_int_key	NULL	NULL	NULL	11	Start materialize
+2	SUBQUERY	parent2	index	col_int_key	col_int_key	4	NULL	1	Using where; Using index; End materialize; Using join buffer (Block Nested Loop)
 SELECT * FROM t3
 WHERE g1 NOT IN
 (SELECT grandparent1.col_int_nokey AS g1

=== modified file 'mysql-test/r/subquery_sj_all_bka.result'
--- a/mysql-test/r/subquery_sj_all_bka.result	2012-01-31 11:19:25 +0000
+++ b/mysql-test/r/subquery_sj_all_bka.result	2012-02-08 15:25:17 +0000
@@ -6707,9 +6707,9 @@ AND grandparent1.col_varchar_key IS NOT 
 );
 id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
 1	PRIMARY	t1	ALL	NULL	NULL	NULL	NULL	2	Using where
-2	DEPENDENT SUBQUERY	parent1	ALL	NULL	NULL	NULL	NULL	20	Start temporary
-2	DEPENDENT SUBQUERY	parent2	eq_ref	PRIMARY	PRIMARY	4	test.parent1.pk	1	Using index
-2	DEPENDENT SUBQUERY	grandparent1	ref	col_varchar_key	col_varchar_key	3	test.parent1.col_varchar_nokey	1	Using index condition; Using where; End temporary; Using join buffer (Batched Key Access)
+2	SUBQUERY	parent1	ALL	NULL	NULL	NULL	NULL	20	Start temporary
+2	SUBQUERY	parent2	eq_ref	PRIMARY	PRIMARY	4	test.parent1.pk	1	Using index
+2	SUBQUERY	grandparent1	ref	col_varchar_key	col_varchar_key	3	test.parent1.col_varchar_nokey	1	Using index condition; End temporary; Using join buffer (Batched Key Access)
 SELECT *
 FROM t1
 WHERE g1 NOT IN
@@ -7092,10 +7092,10 @@ ON parent1.col_int_nokey = parent2.col_i
 AND grandparent1.col_int_key <> 3
 );
 id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
-1	PRIMARY	NULL	NULL	NULL	NULL	NULL	NULL	NULL	Impossible WHERE noticed after reading const tables
-2	DEPENDENT SUBQUERY	parent1	ref	col_int_key	col_int_key	4	func	2	Using index condition; Start temporary
-2	DEPENDENT SUBQUERY	parent2	index	col_int_key	col_int_key	4	NULL	1	Using where; Using index; Using join buffer (Block Nested Loop)
-2	DEPENDENT SUBQUERY	grandparent1	ref	col_int_key	col_int_key	4	func	2	Using index condition; Using where; End temporary; Using join buffer (Batched Key Access)
+1	PRIMARY	t3	system	NULL	NULL	NULL	NULL	1	
+2	SUBQUERY	grandparent1	ALL	col_int_key	NULL	NULL	NULL	11	Using where
+2	SUBQUERY	parent1	ALL	col_int_key	NULL	NULL	NULL	11	Start materialize
+2	SUBQUERY	parent2	index	col_int_key	col_int_key	4	NULL	1	Using where; Using index; End materialize; Using join buffer (Block Nested Loop)
 SELECT * FROM t3
 WHERE g1 NOT IN
 (SELECT grandparent1.col_int_nokey AS g1

=== modified file 'mysql-test/r/subquery_sj_all_bka_nixbnl.result'
--- a/mysql-test/r/subquery_sj_all_bka_nixbnl.result	2012-01-31 11:19:25 +0000
+++ b/mysql-test/r/subquery_sj_all_bka_nixbnl.result	2012-02-08 15:25:17 +0000
@@ -6707,9 +6707,9 @@ AND grandparent1.col_varchar_key IS NOT 
 );
 id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
 1	PRIMARY	t1	ALL	NULL	NULL	NULL	NULL	2	Using where
-2	DEPENDENT SUBQUERY	parent1	ALL	NULL	NULL	NULL	NULL	20	Start temporary
-2	DEPENDENT SUBQUERY	parent2	eq_ref	PRIMARY	PRIMARY	4	test.parent1.pk	1	Using index
-2	DEPENDENT SUBQUERY	grandparent1	ref	col_varchar_key	col_varchar_key	3	test.parent1.col_varchar_nokey	1	Using index condition; Using where; End temporary; Using join buffer (Batched Key Access)
+2	SUBQUERY	parent1	ALL	NULL	NULL	NULL	NULL	20	Start temporary
+2	SUBQUERY	parent2	eq_ref	PRIMARY	PRIMARY	4	test.parent1.pk	1	Using index
+2	SUBQUERY	grandparent1	ref	col_varchar_key	col_varchar_key	3	test.parent1.col_varchar_nokey	1	Using index condition; End temporary; Using join buffer (Batched Key Access)
 SELECT *
 FROM t1
 WHERE g1 NOT IN
@@ -7092,10 +7092,10 @@ ON parent1.col_int_nokey = parent2.col_i
 AND grandparent1.col_int_key <> 3
 );
 id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
-1	PRIMARY	NULL	NULL	NULL	NULL	NULL	NULL	NULL	Impossible WHERE noticed after reading const tables
-2	DEPENDENT SUBQUERY	grandparent1	ref	col_int_key	col_int_key	4	func	2	Using index condition; Using where
-2	DEPENDENT SUBQUERY	parent1	ref	col_int_key	col_int_key	4	func	2	Using index condition
-2	DEPENDENT SUBQUERY	parent2	ref	col_int_key	col_int_key	4	test.parent1.col_int_nokey	2	Using index; FirstMatch(grandparent1)
+1	PRIMARY	t3	system	NULL	NULL	NULL	NULL	1	
+2	SUBQUERY	grandparent1	ALL	col_int_key	NULL	NULL	NULL	11	Using where
+2	SUBQUERY	parent1	ALL	col_int_key	NULL	NULL	NULL	11	Start materialize
+2	SUBQUERY	parent2	ref	col_int_key	col_int_key	4	test.parent1.col_int_nokey	2	Using index; End materialize
 SELECT * FROM t3
 WHERE g1 NOT IN
 (SELECT grandparent1.col_int_nokey AS g1

=== modified file 'mysql-test/r/subquery_sj_all_bkaunique.result'
--- a/mysql-test/r/subquery_sj_all_bkaunique.result	2012-01-31 11:19:25 +0000
+++ b/mysql-test/r/subquery_sj_all_bkaunique.result	2012-02-08 15:25:17 +0000
@@ -6708,9 +6708,9 @@ AND grandparent1.col_varchar_key IS NOT 
 );
 id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
 1	PRIMARY	t1	ALL	NULL	NULL	NULL	NULL	2	Using where
-2	DEPENDENT SUBQUERY	parent1	ALL	NULL	NULL	NULL	NULL	20	Start temporary
-2	DEPENDENT SUBQUERY	parent2	eq_ref	PRIMARY	PRIMARY	4	test.parent1.pk	1	Using index
-2	DEPENDENT SUBQUERY	grandparent1	ref	col_varchar_key	col_varchar_key	3	test.parent1.col_varchar_nokey	1	Using index condition; Using where; End temporary; Using join buffer (Batched Key Access (unique))
+2	SUBQUERY	parent1	ALL	NULL	NULL	NULL	NULL	20	Start temporary
+2	SUBQUERY	parent2	eq_ref	PRIMARY	PRIMARY	4	test.parent1.pk	1	Using index
+2	SUBQUERY	grandparent1	ref	col_varchar_key	col_varchar_key	3	test.parent1.col_varchar_nokey	1	Using index condition; End temporary; Using join buffer (Batched Key Access (unique))
 SELECT *
 FROM t1
 WHERE g1 NOT IN
@@ -7093,10 +7093,10 @@ ON parent1.col_int_nokey = parent2.col_i
 AND grandparent1.col_int_key <> 3
 );
 id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
-1	PRIMARY	NULL	NULL	NULL	NULL	NULL	NULL	NULL	Impossible WHERE noticed after reading const tables
-2	DEPENDENT SUBQUERY	parent1	ref	col_int_key	col_int_key	4	func	2	Using index condition; Start temporary
-2	DEPENDENT SUBQUERY	parent2	index	col_int_key	col_int_key	4	NULL	1	Using where; Using index; Using join buffer (Block Nested Loop)
-2	DEPENDENT SUBQUERY	grandparent1	ref	col_int_key	col_int_key	4	func	2	Using index condition; Using where; End temporary; Using join buffer (Batched Key Access (unique))
+1	PRIMARY	t3	system	NULL	NULL	NULL	NULL	1	
+2	SUBQUERY	grandparent1	ALL	col_int_key	NULL	NULL	NULL	11	Using where
+2	SUBQUERY	parent1	ALL	col_int_key	NULL	NULL	NULL	11	Start materialize
+2	SUBQUERY	parent2	index	col_int_key	col_int_key	4	NULL	1	Using where; Using index; End materialize; Using join buffer (Block Nested Loop)
 SELECT * FROM t3
 WHERE g1 NOT IN
 (SELECT grandparent1.col_int_nokey AS g1

=== modified file 'mysql-test/r/subquery_sj_mat.result'
--- a/mysql-test/r/subquery_sj_mat.result	2012-01-31 11:19:25 +0000
+++ b/mysql-test/r/subquery_sj_mat.result	2012-02-08 15:25:17 +0000
@@ -6706,9 +6706,9 @@ AND grandparent1.col_varchar_key IS NOT 
 );
 id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
 1	PRIMARY	t1	ALL	NULL	NULL	NULL	NULL	2	Using where
-2	DEPENDENT SUBQUERY	parent1	ALL	NULL	NULL	NULL	NULL	20	Start temporary
-2	DEPENDENT SUBQUERY	parent2	eq_ref	PRIMARY	PRIMARY	4	test.parent1.pk	1	Using index
-2	DEPENDENT SUBQUERY	grandparent1	ref	col_varchar_key	col_varchar_key	3	test.parent1.col_varchar_nokey	1	Using where; End temporary
+2	SUBQUERY	parent1	ALL	NULL	NULL	NULL	NULL	20	Start temporary
+2	SUBQUERY	parent2	eq_ref	PRIMARY	PRIMARY	4	test.parent1.pk	1	Using index
+2	SUBQUERY	grandparent1	ref	col_varchar_key	col_varchar_key	3	test.parent1.col_varchar_nokey	1	Using where; End temporary
 SELECT *
 FROM t1
 WHERE g1 NOT IN
@@ -7091,10 +7091,10 @@ ON parent1.col_int_nokey = parent2.col_i
 AND grandparent1.col_int_key <> 3
 );
 id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
-1	PRIMARY	NULL	NULL	NULL	NULL	NULL	NULL	NULL	Impossible WHERE noticed after reading const tables
-2	DEPENDENT SUBQUERY	parent1	ref	col_int_key	col_int_key	4	func	2	Using where; Start temporary
-2	DEPENDENT SUBQUERY	parent2	index	col_int_key	col_int_key	4	NULL	1	Using where; Using index; Using join buffer (Block Nested Loop)
-2	DEPENDENT SUBQUERY	grandparent1	ref	col_int_key	col_int_key	4	func	2	Using where; End temporary
+1	PRIMARY	t3	system	NULL	NULL	NULL	NULL	1	
+2	SUBQUERY	grandparent1	ALL	col_int_key	NULL	NULL	NULL	11	Using where
+2	SUBQUERY	parent1	ALL	col_int_key	NULL	NULL	NULL	11	Start materialize
+2	SUBQUERY	parent2	index	col_int_key	col_int_key	4	NULL	1	Using where; Using index; End materialize; Using join buffer (Block Nested Loop)
 SELECT * FROM t3
 WHERE g1 NOT IN
 (SELECT grandparent1.col_int_nokey AS g1

=== modified file 'mysql-test/r/subquery_sj_mat_bka.result'
--- a/mysql-test/r/subquery_sj_mat_bka.result	2012-01-31 11:19:25 +0000
+++ b/mysql-test/r/subquery_sj_mat_bka.result	2012-02-08 15:25:17 +0000
@@ -6707,9 +6707,9 @@ AND grandparent1.col_varchar_key IS NOT 
 );
 id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
 1	PRIMARY	t1	ALL	NULL	NULL	NULL	NULL	2	Using where
-2	DEPENDENT SUBQUERY	parent1	ALL	NULL	NULL	NULL	NULL	20	Start temporary
-2	DEPENDENT SUBQUERY	parent2	eq_ref	PRIMARY	PRIMARY	4	test.parent1.pk	1	Using index
-2	DEPENDENT SUBQUERY	grandparent1	ref	col_varchar_key	col_varchar_key	3	test.parent1.col_varchar_nokey	1	Using where; End temporary
+2	SUBQUERY	parent1	ALL	NULL	NULL	NULL	NULL	20	Start temporary
+2	SUBQUERY	parent2	eq_ref	PRIMARY	PRIMARY	4	test.parent1.pk	1	Using index
+2	SUBQUERY	grandparent1	ref	col_varchar_key	col_varchar_key	3	test.parent1.col_varchar_nokey	1	Using where; End temporary
 SELECT *
 FROM t1
 WHERE g1 NOT IN
@@ -7092,10 +7092,10 @@ ON parent1.col_int_nokey = parent2.col_i
 AND grandparent1.col_int_key <> 3
 );
 id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
-1	PRIMARY	NULL	NULL	NULL	NULL	NULL	NULL	NULL	Impossible WHERE noticed after reading const tables
-2	DEPENDENT SUBQUERY	parent1	ref	col_int_key	col_int_key	4	func	2	Using where; Start temporary
-2	DEPENDENT SUBQUERY	parent2	index	col_int_key	col_int_key	4	NULL	1	Using where; Using index; Using join buffer (Block Nested Loop)
-2	DEPENDENT SUBQUERY	grandparent1	ref	col_int_key	col_int_key	4	func	2	Using where; End temporary
+1	PRIMARY	t3	system	NULL	NULL	NULL	NULL	1	
+2	SUBQUERY	grandparent1	ALL	col_int_key	NULL	NULL	NULL	11	Using where
+2	SUBQUERY	parent1	ALL	col_int_key	NULL	NULL	NULL	11	Start materialize
+2	SUBQUERY	parent2	index	col_int_key	col_int_key	4	NULL	1	Using where; Using index; End materialize; Using join buffer (Block Nested Loop)
 SELECT * FROM t3
 WHERE g1 NOT IN
 (SELECT grandparent1.col_int_nokey AS g1

=== modified file 'mysql-test/r/subquery_sj_mat_bka_nixbnl.result'
--- a/mysql-test/r/subquery_sj_mat_bka_nixbnl.result	2012-01-31 11:19:25 +0000
+++ b/mysql-test/r/subquery_sj_mat_bka_nixbnl.result	2012-02-08 15:25:17 +0000
@@ -6707,9 +6707,9 @@ AND grandparent1.col_varchar_key IS NOT 
 );
 id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
 1	PRIMARY	t1	ALL	NULL	NULL	NULL	NULL	2	Using where
-2	DEPENDENT SUBQUERY	parent1	ALL	NULL	NULL	NULL	NULL	20	Start temporary
-2	DEPENDENT SUBQUERY	parent2	eq_ref	PRIMARY	PRIMARY	4	test.parent1.pk	1	Using index
-2	DEPENDENT SUBQUERY	grandparent1	ref	col_varchar_key	col_varchar_key	3	test.parent1.col_varchar_nokey	1	Using where; End temporary
+2	SUBQUERY	parent1	ALL	NULL	NULL	NULL	NULL	20	Start temporary
+2	SUBQUERY	parent2	eq_ref	PRIMARY	PRIMARY	4	test.parent1.pk	1	Using index
+2	SUBQUERY	grandparent1	ref	col_varchar_key	col_varchar_key	3	test.parent1.col_varchar_nokey	1	Using where; End temporary
 SELECT *
 FROM t1
 WHERE g1 NOT IN
@@ -7092,10 +7092,10 @@ ON parent1.col_int_nokey = parent2.col_i
 AND grandparent1.col_int_key <> 3
 );
 id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
-1	PRIMARY	NULL	NULL	NULL	NULL	NULL	NULL	NULL	Impossible WHERE noticed after reading const tables
-2	DEPENDENT SUBQUERY	grandparent1	ref	col_int_key	col_int_key	4	func	2	Using where; Start temporary
-2	DEPENDENT SUBQUERY	parent1	ref	col_int_key	col_int_key	4	func	2	Using where
-2	DEPENDENT SUBQUERY	parent2	ref	col_int_key	col_int_key	4	test.parent1.col_int_nokey	2	Using index; End temporary
+1	PRIMARY	t3	system	NULL	NULL	NULL	NULL	1	
+2	SUBQUERY	grandparent1	ALL	col_int_key	NULL	NULL	NULL	11	Using where
+2	SUBQUERY	parent1	ALL	col_int_key	NULL	NULL	NULL	11	Start materialize
+2	SUBQUERY	parent2	ref	col_int_key	col_int_key	4	test.parent1.col_int_nokey	2	Using index; End materialize
 SELECT * FROM t3
 WHERE g1 NOT IN
 (SELECT grandparent1.col_int_nokey AS g1

=== modified file 'mysql-test/r/subquery_sj_mat_bkaunique.result'
--- a/mysql-test/r/subquery_sj_mat_bkaunique.result	2012-01-31 11:19:25 +0000
+++ b/mysql-test/r/subquery_sj_mat_bkaunique.result	2012-02-08 15:25:17 +0000
@@ -6708,9 +6708,9 @@ AND grandparent1.col_varchar_key IS NOT 
 );
 id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
 1	PRIMARY	t1	ALL	NULL	NULL	NULL	NULL	2	Using where
-2	DEPENDENT SUBQUERY	parent1	ALL	NULL	NULL	NULL	NULL	20	Start temporary
-2	DEPENDENT SUBQUERY	parent2	eq_ref	PRIMARY	PRIMARY	4	test.parent1.pk	1	Using index
-2	DEPENDENT SUBQUERY	grandparent1	ref	col_varchar_key	col_varchar_key	3	test.parent1.col_varchar_nokey	1	Using where; End temporary
+2	SUBQUERY	parent1	ALL	NULL	NULL	NULL	NULL	20	Start temporary
+2	SUBQUERY	parent2	eq_ref	PRIMARY	PRIMARY	4	test.parent1.pk	1	Using index
+2	SUBQUERY	grandparent1	ref	col_varchar_key	col_varchar_key	3	test.parent1.col_varchar_nokey	1	Using where; End temporary
 SELECT *
 FROM t1
 WHERE g1 NOT IN
@@ -7093,10 +7093,10 @@ ON parent1.col_int_nokey = parent2.col_i
 AND grandparent1.col_int_key <> 3
 );
 id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
-1	PRIMARY	NULL	NULL	NULL	NULL	NULL	NULL	NULL	Impossible WHERE noticed after reading const tables
-2	DEPENDENT SUBQUERY	parent1	ref	col_int_key	col_int_key	4	func	2	Using where; Start temporary
-2	DEPENDENT SUBQUERY	parent2	index	col_int_key	col_int_key	4	NULL	1	Using where; Using index; Using join buffer (Block Nested Loop)
-2	DEPENDENT SUBQUERY	grandparent1	ref	col_int_key	col_int_key	4	func	2	Using where; End temporary
+1	PRIMARY	t3	system	NULL	NULL	NULL	NULL	1	
+2	SUBQUERY	grandparent1	ALL	col_int_key	NULL	NULL	NULL	11	Using where
+2	SUBQUERY	parent1	ALL	col_int_key	NULL	NULL	NULL	11	Start materialize
+2	SUBQUERY	parent2	index	col_int_key	col_int_key	4	NULL	1	Using where; Using index; End materialize; Using join buffer (Block Nested Loop)
 SELECT * FROM t3
 WHERE g1 NOT IN
 (SELECT grandparent1.col_int_nokey AS g1

=== modified file 'mysql-test/r/subquery_sj_mat_nosj.result'
--- a/mysql-test/r/subquery_sj_mat_nosj.result	2012-01-31 11:19:25 +0000
+++ b/mysql-test/r/subquery_sj_mat_nosj.result	2012-02-08 15:25:17 +0000
@@ -6782,7 +6782,7 @@ AND grandparent1.col_varchar_key IS NOT 
 );
 id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
 1	PRIMARY	t1	ALL	NULL	NULL	NULL	NULL	2	Using where
-2	DEPENDENT SUBQUERY	grandparent1	ALL	col_varchar_key	NULL	NULL	NULL	20	Using where
+2	SUBQUERY	grandparent1	ALL	col_varchar_key	NULL	NULL	NULL	20	Using where
 3	SUBQUERY	parent1	ALL	NULL	NULL	NULL	NULL	20	
 3	SUBQUERY	parent2	eq_ref	PRIMARY	PRIMARY	4	test.parent1.pk	1	Using index
 SELECT *
@@ -7167,8 +7167,8 @@ ON parent1.col_int_nokey = parent2.col_i
 AND grandparent1.col_int_key <> 3
 );
 id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
-1	PRIMARY	NULL	NULL	NULL	NULL	NULL	NULL	NULL	Impossible WHERE noticed after reading const tables
-2	DEPENDENT SUBQUERY	grandparent1	ALL	col_int_key	NULL	NULL	NULL	11	Using where
+1	PRIMARY	t3	system	NULL	NULL	NULL	NULL	1	
+2	SUBQUERY	grandparent1	ALL	col_int_key	NULL	NULL	NULL	11	Using where
 3	SUBQUERY	parent1	ALL	NULL	NULL	NULL	NULL	11	
 3	SUBQUERY	parent2	index	col_int_key	col_int_key	4	NULL	1	Using where; Using index; Using join buffer (Block Nested Loop)
 SELECT * FROM t3

=== modified file 'mysql-test/suite/innodb/r/innodb_mysql.result'
--- a/mysql-test/suite/innodb/r/innodb_mysql.result	2012-01-30 13:13:15 +0000
+++ b/mysql-test/suite/innodb/r/innodb_mysql.result	2012-02-08 15:25:17 +0000
@@ -1436,7 +1436,8 @@ explain
 select b from t1 where a not in (select b from t1,t2 group by a) group by a;
 id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
 1	PRIMARY	NULL	NULL	NULL	NULL	NULL	NULL	NULL	Impossible WHERE noticed after reading const tables
-2	DEPENDENT SUBQUERY	NULL	NULL	NULL	NULL	NULL	NULL	NULL	Impossible WHERE noticed after reading const tables
+2	SUBQUERY	t1	system	NULL	NULL	NULL	NULL	0	const row not found
+2	SUBQUERY	t2	ALL	NULL	NULL	NULL	NULL	1	
 DROP TABLE t1,t2;
 End of 5.0 tests
 CREATE TABLE `t2` (

=== modified file 'mysql-test/suite/opt_trace/include/general.inc'
--- a/mysql-test/suite/opt_trace/include/general.inc	2011-11-18 14:51:40 +0000
+++ b/mysql-test/suite/opt_trace/include/general.inc	2012-02-08 15:25:17 +0000
@@ -82,12 +82,12 @@ select * from information_schema.OPTIMIZ
 select (@query:=QUERY)+NULL, (@trace:=TRACE)+NULL from information_schema.OPTIMIZER_TRACE;
 select length(@trace);
 # The concatenation of query and trace above has length:
-# - >13900 in normal mode
-# - <13900 in ps-protocol mode (because IN->EXISTS is done at PREPARE
+# - >14100 in normal mode
+# - <14100 in ps-protocol mode (because IN->EXISTS is done at PREPARE
 # and we trace only EXECUTE)
 # - So in normal mode, the lines below verify truncation,
 # whereas in ps-protocol mode they verify non-truncation.
-set optimizer_trace_max_mem_size=13900;
+set optimizer_trace_max_mem_size=14100;
 select length(@query)+length(@trace) > @@optimizer_trace_max_mem_size;
 SELECT * FROM t5 WHERE 5 IN (SELECT 1 FROM t6 WHERE d = ifnull(c,null) UNION SELECT 2 FROM t6 WHERE d = ifnull(c,null));
 select (@missing_bytes:=missing_bytes_beyond_max_mem_size) from information_schema.OPTIMIZER_TRACE;

=== modified file 'mysql-test/suite/opt_trace/r/bugs_no_prot_all.result'
--- a/mysql-test/suite/opt_trace/r/bugs_no_prot_all.result	2012-01-30 13:13:15 +0000
+++ b/mysql-test/suite/opt_trace/r/bugs_no_prot_all.result	2012-02-08 15:25:17 +0000
@@ -190,7 +190,10 @@ WHERE sq2_alias1.col_varchar_nokey <= al
                     "select#": 2,
                     "from": "IN (SELECT)",
                     "to": "materialization",
-                    "chosen": false
+                    "has_nullable_expressions": true,
+                    "treat_UNKNOWN_as_FALSE": false,
+                    "chosen": false,
+                    "cause": "cannot_handle_partial_matches"
                   } /* transformation */
                 },
                 {

=== modified file 'mysql-test/suite/opt_trace/r/bugs_no_prot_none.result'
--- a/mysql-test/suite/opt_trace/r/bugs_no_prot_none.result	2012-01-26 13:09:59 +0000
+++ b/mysql-test/suite/opt_trace/r/bugs_no_prot_none.result	2012-02-08 15:25:17 +0000
@@ -174,14 +174,6 @@ WHERE sq2_alias1.col_varchar_nokey <= al
                         "transformation": {
                           "select#": 4,
                           "from": "IN (SELECT)",
-                          "to": "materialization",
-                          "chosen": false
-                        } /* transformation */
-                      },
-                      {
-                        "transformation": {
-                          "select#": 4,
-                          "from": "IN (SELECT)",
                           "to": "EXISTS (CORRELATED SELECT)",
                           "chosen": true,
                           "evaluating_constant_where_conditions": [
@@ -206,14 +198,6 @@ WHERE sq2_alias1.col_varchar_nokey <= al
                   "transformation": {
                     "select#": 2,
                     "from": "IN (SELECT)",
-                    "to": "materialization",
-                    "chosen": false
-                  } /* transformation */
-                },
-                {
-                  "transformation": {
-                    "select#": 2,
-                    "from": "IN (SELECT)",
                     "to": "EXISTS (CORRELATED SELECT)",
                     "chosen": true,
                     "evaluating_constant_where_conditions": [
@@ -383,14 +367,6 @@ FROM t1
                   "transformation": {
                     "select#": 2,
                     "from": "IN (SELECT)",
-                    "to": "materialization",
-                    "chosen": false
-                  } /* transformation */
-                },
-                {
-                  "transformation": {
-                    "select#": 2,
-                    "from": "IN (SELECT)",
                     "to": "EXISTS (CORRELATED SELECT)",
                     "chosen": true,
                     "evaluating_constant_where_conditions": [
@@ -823,14 +799,6 @@ ON table2 .col_int_key = table1 .col_int
                   "transformation": {
                     "select#": 3,
                     "from": "IN (SELECT)",
-                    "to": "materialization",
-                    "chosen": false
-                  } /* transformation */
-                },
-                {
-                  "transformation": {
-                    "select#": 3,
-                    "from": "IN (SELECT)",
                     "to": "EXISTS (CORRELATED SELECT)",
                     "chosen": true,
                     "evaluating_constant_where_conditions": [

=== modified file 'mysql-test/suite/opt_trace/r/bugs_ps_prot_all.result'
--- a/mysql-test/suite/opt_trace/r/bugs_ps_prot_all.result	2012-01-30 13:13:15 +0000
+++ b/mysql-test/suite/opt_trace/r/bugs_ps_prot_all.result	2012-02-08 15:25:17 +0000
@@ -190,7 +190,10 @@ WHERE sq2_alias1.col_varchar_nokey <= al
                     "select#": 2,
                     "from": "IN (SELECT)",
                     "to": "materialization",
-                    "chosen": false
+                    "has_nullable_expressions": true,
+                    "treat_UNKNOWN_as_FALSE": false,
+                    "chosen": false,
+                    "cause": "cannot_handle_partial_matches"
                   } /* transformation */
                 },
                 {

=== modified file 'mysql-test/suite/opt_trace/r/bugs_ps_prot_none.result'
--- a/mysql-test/suite/opt_trace/r/bugs_ps_prot_none.result	2012-01-30 08:57:24 +0000
+++ b/mysql-test/suite/opt_trace/r/bugs_ps_prot_none.result	2012-02-08 15:25:17 +0000
@@ -174,14 +174,6 @@ WHERE sq2_alias1.col_varchar_nokey <= al
                         "transformation": {
                           "select#": 4,
                           "from": "IN (SELECT)",
-                          "to": "materialization",
-                          "chosen": false
-                        } /* transformation */
-                      },
-                      {
-                        "transformation": {
-                          "select#": 4,
-                          "from": "IN (SELECT)",
                           "to": "EXISTS (CORRELATED SELECT)",
                           "chosen": true,
                           "evaluating_constant_where_conditions": [
@@ -206,14 +198,6 @@ WHERE sq2_alias1.col_varchar_nokey <= al
                   "transformation": {
                     "select#": 2,
                     "from": "IN (SELECT)",
-                    "to": "materialization",
-                    "chosen": false
-                  } /* transformation */
-                },
-                {
-                  "transformation": {
-                    "select#": 2,
-                    "from": "IN (SELECT)",
                     "to": "EXISTS (CORRELATED SELECT)",
                     "chosen": true,
                     "evaluating_constant_where_conditions": [
@@ -378,14 +362,6 @@ FROM t1
                     "to": "semijoin",
                     "chosen": false
                   } /* transformation */
-                },
-                {
-                  "transformation": {
-                    "select#": 2,
-                    "from": "IN (SELECT)",
-                    "to": "materialization",
-                    "chosen": false
-                  } /* transformation */
                 }
               ] /* steps */
             } /* join_preparation */
@@ -808,14 +784,6 @@ ON table2 .col_int_key = table1 .col_int
                     "to": "semijoin",
                     "chosen": false
                   } /* transformation */
-                },
-                {
-                  "transformation": {
-                    "select#": 3,
-                    "from": "IN (SELECT)",
-                    "to": "materialization",
-                    "chosen": false
-                  } /* transformation */
                 }
               ] /* steps */
             } /* join_preparation */

=== modified file 'mysql-test/suite/opt_trace/r/general2_no_prot.result'
--- a/mysql-test/suite/opt_trace/r/general2_no_prot.result	2012-01-26 13:09:59 +0000
+++ b/mysql-test/suite/opt_trace/r/general2_no_prot.result	2012-02-08 15:25:17 +0000
@@ -704,7 +704,10 @@ TRACE
                     "select#": 2,
                     "from": "IN (SELECT)",
                     "to": "materialization",
-                    "chosen": false
+                    "has_nullable_expressions": true,
+                    "treat_UNKNOWN_as_FALSE": false,
+                    "chosen": false,
+                    "cause": "cannot_handle_partial_matches"
                   } /* transformation */
                 },
                 {
@@ -2653,14 +2656,6 @@ from t0 where a in
                   "transformation": {
                     "select#": 2,
                     "from": "IN (SELECT)",
-                    "to": "materialization",
-                    "chosen": false
-                  } /* transformation */
-                },
-                {
-                  "transformation": {
-                    "select#": 2,
-                    "from": "IN (SELECT)",
                     "to": "EXISTS (CORRELATED SELECT)",
                     "chosen": true,
                     "evaluating_constant_where_conditions": [

=== modified file 'mysql-test/suite/opt_trace/r/general2_ps_prot.result'
--- a/mysql-test/suite/opt_trace/r/general2_ps_prot.result	2012-01-30 08:57:24 +0000
+++ b/mysql-test/suite/opt_trace/r/general2_ps_prot.result	2012-02-08 15:25:17 +0000
@@ -732,7 +732,8 @@ TRACE
                     "select#": 2,
                     "from": "IN (SELECT)",
                     "to": "materialization",
-                    "chosen": false
+                    "chosen": false,
+                    "cause": "already have strategy"
                   } /* transformation */
                 }
               ] /* steps */
@@ -2706,14 +2707,6 @@ from t0 where a in
                   "transformation": {
                     "select#": 2,
                     "from": "IN (SELECT)",
-                    "to": "materialization",
-                    "chosen": false
-                  } /* transformation */
-                },
-                {
-                  "transformation": {
-                    "select#": 2,
-                    "from": "IN (SELECT)",
                     "to": "EXISTS (CORRELATED SELECT)",
                     "chosen": true,
                     "evaluating_constant_where_conditions": [

=== modified file 'mysql-test/suite/opt_trace/r/general_no_prot_all.result'
--- a/mysql-test/suite/opt_trace/r/general_no_prot_all.result	2012-01-26 13:09:59 +0000
+++ b/mysql-test/suite/opt_trace/r/general_no_prot_all.result	2012-02-08 15:25:17 +0000
@@ -1005,7 +1005,8 @@ SELECT * FROM t5 WHERE 5 IN (SELECT 1 FR
                     "select#": 2,
                     "from": "IN (SELECT)",
                     "to": "materialization",
-                    "chosen": false
+                    "chosen": false,
+                    "cause": "in UNION"
                   } /* transformation */
                 },
                 {
@@ -1041,7 +1042,8 @@ SELECT * FROM t5 WHERE 5 IN (SELECT 1 FR
                     "select#": 3,
                     "from": "IN (SELECT)",
                     "to": "materialization",
-                    "chosen": false
+                    "chosen": false,
+                    "cause": "in UNION"
                   } /* transformation */
                 },
                 {
@@ -1230,7 +1232,8 @@ SELECT * FROM t5 WHERE 5 IN (SELECT 1 FR
                           "select#": "fake",
                           "from": "IN (SELECT)",
                           "to": "materialization",
-                          "chosen": false
+                          "chosen": false,
+                          "cause": "in UNION"
                         } /* transformation */
                       }
                     ] /* steps */
@@ -1341,8 +1344,8 @@ select (@query:=QUERY)+NULL, (@trace:=TR
 NULL	NULL
 select length(@trace);
 length(@trace)
-14543
-set optimizer_trace_max_mem_size=13900;
+14672
+set optimizer_trace_max_mem_size=14100;
 select length(@query)+length(@trace) > @@optimizer_trace_max_mem_size;
 length(@query)+length(@trace) > @@optimizer_trace_max_mem_size
 1
@@ -1350,7 +1353,7 @@ SELECT * FROM t5 WHERE 5 IN (SELECT 1 FR
 c
 select (@missing_bytes:=missing_bytes_beyond_max_mem_size) from information_schema.OPTIMIZER_TRACE;
 (@missing_bytes:=missing_bytes_beyond_max_mem_size)
-761
+681
 select (@query2:=QUERY)+NULL,(@trace2:=TRACE)+NULL from information_schema.OPTIMIZER_TRACE;
 (@query2:=QUERY)+NULL	(@trace2:=TRACE)+NULL
 NULL	NULL
@@ -1358,7 +1361,7 @@ select length(@trace2),
 (length(@trace2) + @missing_bytes) = length(@trace),
 @query2 = @query;
 length(@trace2)	(length(@trace2) + @missing_bytes) = length(@trace)	@query2 = @query
-13782	1	1
+13991	1	1
 select length(@query2) + length(@trace2)
 between (@@optimizer_trace_max_mem_size-200) and (@@optimizer_trace_max_mem_size+200);
 length(@query2) + length(@trace2)
@@ -1721,6 +1724,8 @@ explain SELECT c FROM t5 where c+1 in (s
                     "select#": 2,
                     "from": "IN (SELECT)",
                     "to": "materialization",
+                    "has_nullable_expressions": true,
+                    "treat_UNKNOWN_as_FALSE": true,
                     "chosen": true
                   } /* transformation */
                 }
@@ -2504,14 +2509,6 @@ explain extended select * from t1 where 
                   "transformation": {
                     "select#": 2,
                     "from": "IN (SELECT)",
-                    "to": "materialization",
-                    "chosen": false
-                  } /* transformation */
-                },
-                {
-                  "transformation": {
-                    "select#": 2,
-                    "from": "IN (SELECT)",
                     "to": "EXISTS (CORRELATED SELECT)",
                     "chosen": true,
                     "evaluating_constant_where_conditions": [
@@ -2780,14 +2777,6 @@ explain extended select * from t1 where 
                   "transformation": {
                     "select#": 2,
                     "from": "IN (SELECT)",
-                    "to": "materialization",
-                    "chosen": false
-                  } /* transformation */
-                },
-                {
-                  "transformation": {
-                    "select#": 2,
-                    "from": "IN (SELECT)",
                     "to": "EXISTS (CORRELATED SELECT)",
                     "chosen": true,
                     "evaluating_constant_where_conditions": [
@@ -3395,7 +3384,7 @@ where a1 in (select b1 from t2_16 where 
                     "from": "IN (SELECT)",
                     "to": "materialization",
                     "chosen": false,
-                    "cause": "field_types"
+                    "cause": "inner blob"
                   } /* transformation */
                 },
                 {
@@ -4420,6 +4409,8 @@ concat(c1,'y') IN
                     "select#": 2,
                     "from": "IN (SELECT)",
                     "to": "materialization",
+                    "has_nullable_expressions": true,
+                    "treat_UNKNOWN_as_FALSE": true,
                     "chosen": true
                   } /* transformation */
                 }
@@ -4446,6 +4437,8 @@ concat(c1,'y') IN
                     "select#": 3,
                     "from": "IN (SELECT)",
                     "to": "materialization",
+                    "has_nullable_expressions": true,
+                    "treat_UNKNOWN_as_FALSE": true,
                     "chosen": true
                   } /* transformation */
                 }
@@ -6101,7 +6094,10 @@ select * from t1 where (t1.a,t1.b) not i
                     "select#": 2,
                     "from": "IN (SELECT)",
                     "to": "materialization",
-                    "chosen": false
+                    "has_nullable_expressions": true,
+                    "treat_UNKNOWN_as_FALSE": false,
+                    "chosen": false,
+                    "cause": "cannot_handle_partial_matches"
                   } /* transformation */
                 },
                 {

=== modified file 'mysql-test/suite/opt_trace/r/general_no_prot_none.result'
--- a/mysql-test/suite/opt_trace/r/general_no_prot_none.result	2012-01-26 13:09:59 +0000
+++ b/mysql-test/suite/opt_trace/r/general_no_prot_none.result	2012-02-08 15:25:17 +0000
@@ -1003,14 +1003,6 @@ SELECT * FROM t5 WHERE 5 IN (SELECT 1 FR
                   "transformation": {
                     "select#": 2,
                     "from": "IN (SELECT)",
-                    "to": "materialization",
-                    "chosen": false
-                  } /* transformation */
-                },
-                {
-                  "transformation": {
-                    "select#": 2,
-                    "from": "IN (SELECT)",
                     "to": "EXISTS (CORRELATED SELECT)",
                     "chosen": true,
                     "evaluating_constant_where_conditions": [
@@ -1039,14 +1031,6 @@ SELECT * FROM t5 WHERE 5 IN (SELECT 1 FR
                   "transformation": {
                     "select#": 3,
                     "from": "IN (SELECT)",
-                    "to": "materialization",
-                    "chosen": false
-                  } /* transformation */
-                },
-                {
-                  "transformation": {
-                    "select#": 3,
-                    "from": "IN (SELECT)",
                     "to": "EXISTS (CORRELATED SELECT)",
                     "chosen": true,
                     "evaluating_constant_where_conditions": [
@@ -1223,14 +1207,6 @@ SELECT * FROM t5 WHERE 5 IN (SELECT 1 FR
                           "to": "semijoin",
                           "chosen": false
                         } /* transformation */
-                      },
-                      {
-                        "transformation": {
-                          "select#": "fake",
-                          "from": "IN (SELECT)",
-                          "to": "materialization",
-                          "chosen": false
-                        } /* transformation */
                       }
                     ] /* steps */
                   } /* join_preparation */
@@ -1340,16 +1316,16 @@ select (@query:=QUERY)+NULL, (@trace:=TR
 NULL	NULL
 select length(@trace);
 length(@trace)
-14543
-set optimizer_trace_max_mem_size=13900;
+13668
+set optimizer_trace_max_mem_size=14100;
 select length(@query)+length(@trace) > @@optimizer_trace_max_mem_size;
 length(@query)+length(@trace) > @@optimizer_trace_max_mem_size
-1
+0
 SELECT * FROM t5 WHERE 5 IN (SELECT 1 FROM t6 WHERE d = ifnull(c,null) UNION SELECT 2 FROM t6 WHERE d = ifnull(c,null));
 c
 select (@missing_bytes:=missing_bytes_beyond_max_mem_size) from information_schema.OPTIMIZER_TRACE;
 (@missing_bytes:=missing_bytes_beyond_max_mem_size)
-761
+0
 select (@query2:=QUERY)+NULL,(@trace2:=TRACE)+NULL from information_schema.OPTIMIZER_TRACE;
 (@query2:=QUERY)+NULL	(@trace2:=TRACE)+NULL
 NULL	NULL
@@ -1357,12 +1333,12 @@ select length(@trace2),
 (length(@trace2) + @missing_bytes) = length(@trace),
 @query2 = @query;
 length(@trace2)	(length(@trace2) + @missing_bytes) = length(@trace)	@query2 = @query
-13782	1	1
+13668	1	1
 select length(@query2) + length(@trace2)
 between (@@optimizer_trace_max_mem_size-200) and (@@optimizer_trace_max_mem_size+200);
 length(@query2) + length(@trace2)
 between (@@optimizer_trace_max_mem_size-200) and (@@optimizer_trace_max_mem_size+200)
-1
+0
 select instr(@trace, @trace2) = 1;
 instr(@trace, @trace2) = 1
 1
@@ -1412,14 +1388,6 @@ explain SELECT c FROM t5 where c+1 in (s
                   "transformation": {
                     "select#": 2,
                     "from": "IN (SELECT)",
-                    "to": "materialization",
-                    "chosen": false
-                  } /* transformation */
-                },
-                {
-                  "transformation": {
-                    "select#": 2,
-                    "from": "IN (SELECT)",
                     "to": "EXISTS (CORRELATED SELECT)",
                     "chosen": true,
                     "evaluating_constant_where_conditions": [
@@ -1702,14 +1670,6 @@ explain SELECT c FROM t5 where c+1 in (s
                   "transformation": {
                     "select#": 2,
                     "from": "IN (SELECT)",
-                    "to": "materialization",
-                    "chosen": false
-                  } /* transformation */
-                },
-                {
-                  "transformation": {
-                    "select#": 2,
-                    "from": "IN (SELECT)",
                     "to": "EXISTS (CORRELATED SELECT)",
                     "chosen": true,
                     "evaluating_constant_where_conditions": [
@@ -2465,14 +2425,6 @@ explain extended select * from t1 where 
                   "transformation": {
                     "select#": 2,
                     "from": "IN (SELECT)",
-                    "to": "materialization",
-                    "chosen": false
-                  } /* transformation */
-                },
-                {
-                  "transformation": {
-                    "select#": 2,
-                    "from": "IN (SELECT)",
                     "to": "EXISTS (CORRELATED SELECT)",
                     "chosen": true,
                     "evaluating_constant_where_conditions": [
@@ -2741,14 +2693,6 @@ explain extended select * from t1 where 
                   "transformation": {
                     "select#": 2,
                     "from": "IN (SELECT)",
-                    "to": "materialization",
-                    "chosen": false
-                  } /* transformation */
-                },
-                {
-                  "transformation": {
-                    "select#": 2,
-                    "from": "IN (SELECT)",
                     "to": "EXISTS (CORRELATED SELECT)",
                     "chosen": true,
                     "evaluating_constant_where_conditions": [
@@ -3354,14 +3298,6 @@ where a1 in (select b1 from t2_16 where 
                   "transformation": {
                     "select#": 2,
                     "from": "IN (SELECT)",
-                    "to": "materialization",
-                    "chosen": false
-                  } /* transformation */
-                },
-                {
-                  "transformation": {
-                    "select#": 2,
-                    "from": "IN (SELECT)",
                     "to": "EXISTS (CORRELATED SELECT)",
                     "chosen": true,
                     "evaluating_constant_where_conditions": [
@@ -3639,14 +3575,6 @@ WHERE c2 IN ( SELECT c2 FROM t2 WHERE c2
                   "transformation": {
                     "select#": 2,
                     "from": "IN (SELECT)",
-                    "to": "materialization",
-                    "chosen": false
-                  } /* transformation */
-                },
-                {
-                  "transformation": {
-                    "select#": 2,
-                    "from": "IN (SELECT)",
                     "to": "EXISTS (CORRELATED SELECT)",
                     "chosen": true,
                     "evaluating_constant_where_conditions": [
@@ -4378,14 +4306,6 @@ concat(c1,'y') IN
                   "transformation": {
                     "select#": 2,
                     "from": "IN (SELECT)",
-                    "to": "materialization",
-                    "chosen": false
-                  } /* transformation */
-                },
-                {
-                  "transformation": {
-                    "select#": 2,
-                    "from": "IN (SELECT)",
                     "to": "EXISTS (CORRELATED SELECT)",
                     "chosen": true,
                     "evaluating_constant_where_conditions": [
@@ -4414,14 +4334,6 @@ concat(c1,'y') IN
                   "transformation": {
                     "select#": 3,
                     "from": "IN (SELECT)",
-                    "to": "materialization",
-                    "chosen": false
-                  } /* transformation */
-                },
-                {
-                  "transformation": {
-                    "select#": 3,
-                    "from": "IN (SELECT)",
                     "to": "EXISTS (CORRELATED SELECT)",
                     "chosen": true,
                     "evaluating_constant_where_conditions": [
@@ -4884,14 +4796,6 @@ trace
                   "transformation": {
                     "select#": 2,
                     "from": "IN (SELECT)",
-                    "to": "materialization",
-                    "chosen": false
-                  } /* transformation */
-                },
-                {
-                  "transformation": {
-                    "select#": 2,
-                    "from": "IN (SELECT)",
                     "to": "EXISTS (CORRELATED SELECT)",
                     "chosen": true,
                     "evaluating_constant_where_conditions": [
@@ -4933,14 +4837,6 @@ trace
                     "to": "semijoin",
                     "chosen": false
                   } /* transformation */
-                },
-                {
-                  "transformation": {
-                    "select#": 2,
-                    "from": "IN (SELECT)",
-                    "to": "materialization",
-                    "chosen": false
-                  } /* transformation */
                 }
               ] /* steps */
             } /* join_preparation */
@@ -5047,14 +4943,6 @@ trace
                     "to": "semijoin",
                     "chosen": false
                   } /* transformation */
-                },
-                {
-                  "transformation": {
-                    "select#": 2,
-                    "from": "IN (SELECT)",
-                    "to": "materialization",
-                    "chosen": false
-                  } /* transformation */
                 }
               ] /* steps */
             } /* join_preparation */
@@ -5356,14 +5244,6 @@ select * from t1 where (t1.a,t1.b) not i
                   "transformation": {
                     "select#": 2,
                     "from": "IN (SELECT)",
-                    "to": "materialization",
-                    "chosen": false
-                  } /* transformation */
-                },
-                {
-                  "transformation": {
-                    "select#": 2,
-                    "from": "IN (SELECT)",
                     "to": "EXISTS (CORRELATED SELECT)",
                     "chosen": true,
                     "evaluating_constant_where_conditions": [
@@ -7420,14 +7300,6 @@ select d into res from t6 where d in (se
                   "transformation": {
                     "select#": 2,
                     "from": "IN (SELECT)",
-                    "to": "materialization",
-                    "chosen": false
-                  } /* transformation */
-                },
-                {
-                  "transformation": {
-                    "select#": 2,
-                    "from": "IN (SELECT)",
                     "to": "EXISTS (CORRELATED SELECT)",
                     "chosen": true,
                     "evaluating_constant_where_conditions": [
@@ -9184,14 +9056,6 @@ select d into res from t6 where d in (se
                   "transformation": {
                     "select#": 2,
                     "from": "IN (SELECT)",
-                    "to": "materialization",
-                    "chosen": false
-                  } /* transformation */
-                },
-                {
-                  "transformation": {
-                    "select#": 2,
-                    "from": "IN (SELECT)",
                     "to": "EXISTS (CORRELATED SELECT)",
                     "chosen": true,
                     "evaluating_constant_where_conditions": [

=== modified file 'mysql-test/suite/opt_trace/r/general_ps_prot_all.result'
--- a/mysql-test/suite/opt_trace/r/general_ps_prot_all.result	2012-01-30 08:57:24 +0000
+++ b/mysql-test/suite/opt_trace/r/general_ps_prot_all.result	2012-02-08 15:25:17 +0000
@@ -1005,7 +1005,8 @@ SELECT * FROM t5 WHERE 5 IN (SELECT 1 FR
                     "select#": 2,
                     "from": "IN (SELECT)",
                     "to": "materialization",
-                    "chosen": false
+                    "chosen": false,
+                    "cause": "in UNION"
                   } /* transformation */
                 }
               ] /* steps */
@@ -1031,7 +1032,8 @@ SELECT * FROM t5 WHERE 5 IN (SELECT 1 FR
                     "select#": 3,
                     "from": "IN (SELECT)",
                     "to": "materialization",
-                    "chosen": false
+                    "chosen": false,
+                    "cause": "in UNION"
                   } /* transformation */
                 }
               ] /* steps */
@@ -1210,7 +1212,8 @@ SELECT * FROM t5 WHERE 5 IN (SELECT 1 FR
                           "select#": "fake",
                           "from": "IN (SELECT)",
                           "to": "materialization",
-                          "chosen": false
+                          "chosen": false,
+                          "cause": "in UNION"
                         } /* transformation */
                       }
                     ] /* steps */
@@ -1321,8 +1324,8 @@ select (@query:=QUERY)+NULL, (@trace:=TR
 NULL	NULL
 select length(@trace);
 length(@trace)
-13765
-set optimizer_trace_max_mem_size=13900;
+13894
+set optimizer_trace_max_mem_size=14100;
 select length(@query)+length(@trace) > @@optimizer_trace_max_mem_size;
 length(@query)+length(@trace) > @@optimizer_trace_max_mem_size
 0
@@ -1338,7 +1341,7 @@ select length(@trace2),
 (length(@trace2) + @missing_bytes) = length(@trace),
 @query2 = @query;
 length(@trace2)	(length(@trace2) + @missing_bytes) = length(@trace)	@query2 = @query
-13765	1	1
+13894	1	1
 select length(@query2) + length(@trace2)
 between (@@optimizer_trace_max_mem_size-200) and (@@optimizer_trace_max_mem_size+200);
 length(@query2) + length(@trace2)
@@ -1701,6 +1704,8 @@ explain SELECT c FROM t5 where c+1 in (s
                     "select#": 2,
                     "from": "IN (SELECT)",
                     "to": "materialization",
+                    "has_nullable_expressions": true,
+                    "treat_UNKNOWN_as_FALSE": true,
                     "chosen": true
                   } /* transformation */
                 }
@@ -2484,14 +2489,6 @@ explain extended select * from t1 where 
                   "transformation": {
                     "select#": 2,
                     "from": "IN (SELECT)",
-                    "to": "materialization",
-                    "chosen": false
-                  } /* transformation */
-                },
-                {
-                  "transformation": {
-                    "select#": 2,
-                    "from": "IN (SELECT)",
                     "to": "EXISTS (CORRELATED SELECT)",
                     "chosen": true,
                     "evaluating_constant_where_conditions": [
@@ -2760,14 +2757,6 @@ explain extended select * from t1 where 
                   "transformation": {
                     "select#": 2,
                     "from": "IN (SELECT)",
-                    "to": "materialization",
-                    "chosen": false
-                  } /* transformation */
-                },
-                {
-                  "transformation": {
-                    "select#": 2,
-                    "from": "IN (SELECT)",
                     "to": "EXISTS (CORRELATED SELECT)",
                     "chosen": true,
                     "evaluating_constant_where_conditions": [
@@ -3375,7 +3364,7 @@ where a1 in (select b1 from t2_16 where 
                     "from": "IN (SELECT)",
                     "to": "materialization",
                     "chosen": false,
-                    "cause": "field_types"
+                    "cause": "inner blob"
                   } /* transformation */
                 },
                 {
@@ -4400,6 +4389,8 @@ concat(c1,'y') IN
                     "select#": 2,
                     "from": "IN (SELECT)",
                     "to": "materialization",
+                    "has_nullable_expressions": true,
+                    "treat_UNKNOWN_as_FALSE": true,
                     "chosen": true
                   } /* transformation */
                 }
@@ -4426,6 +4417,8 @@ concat(c1,'y') IN
                     "select#": 3,
                     "from": "IN (SELECT)",
                     "to": "materialization",
+                    "has_nullable_expressions": true,
+                    "treat_UNKNOWN_as_FALSE": true,
                     "chosen": true
                   } /* transformation */
                 }
@@ -6081,7 +6074,8 @@ select * from t1 where (t1.a,t1.b) not i
                     "select#": 2,
                     "from": "IN (SELECT)",
                     "to": "materialization",
-                    "chosen": false
+                    "chosen": false,
+                    "cause": "already have strategy"
                   } /* transformation */
                 }
               ] /* steps */

=== modified file 'mysql-test/suite/opt_trace/r/general_ps_prot_none.result'
--- a/mysql-test/suite/opt_trace/r/general_ps_prot_none.result	2012-01-30 08:57:24 +0000
+++ b/mysql-test/suite/opt_trace/r/general_ps_prot_none.result	2012-02-08 15:25:17 +0000
@@ -998,14 +998,6 @@ SELECT * FROM t5 WHERE 5 IN (SELECT 1 FR
                     "to": "semijoin",
                     "chosen": false
                   } /* transformation */
-                },
-                {
-                  "transformation": {
-                    "select#": 2,
-                    "from": "IN (SELECT)",
-                    "to": "materialization",
-                    "chosen": false
-                  } /* transformation */
                 }
               ] /* steps */
             } /* join_preparation */
@@ -1024,14 +1016,6 @@ SELECT * FROM t5 WHERE 5 IN (SELECT 1 FR
                     "to": "semijoin",
                     "chosen": false
                   } /* transformation */
-                },
-                {
-                  "transformation": {
-                    "select#": 3,
-                    "from": "IN (SELECT)",
-                    "to": "materialization",
-                    "chosen": false
-                  } /* transformation */
                 }
               ] /* steps */
             } /* join_preparation */
@@ -1203,14 +1187,6 @@ SELECT * FROM t5 WHERE 5 IN (SELECT 1 FR
                           "to": "semijoin",
                           "chosen": false
                         } /* transformation */
-                      },
-                      {
-                        "transformation": {
-                          "select#": "fake",
-                          "from": "IN (SELECT)",
-                          "to": "materialization",
-                          "chosen": false
-                        } /* transformation */
                       }
                     ] /* steps */
                   } /* join_preparation */
@@ -1320,8 +1296,8 @@ select (@query:=QUERY)+NULL, (@trace:=TR
 NULL	NULL
 select length(@trace);
 length(@trace)
-13765
-set optimizer_trace_max_mem_size=13900;
+12890
+set optimizer_trace_max_mem_size=14100;
 select length(@query)+length(@trace) > @@optimizer_trace_max_mem_size;
 length(@query)+length(@trace) > @@optimizer_trace_max_mem_size
 0
@@ -1337,12 +1313,12 @@ select length(@trace2),
 (length(@trace2) + @missing_bytes) = length(@trace),
 @query2 = @query;
 length(@trace2)	(length(@trace2) + @missing_bytes) = length(@trace)	@query2 = @query
-13765	1	1
+12890	1	1
 select length(@query2) + length(@trace2)
 between (@@optimizer_trace_max_mem_size-200) and (@@optimizer_trace_max_mem_size+200);
 length(@query2) + length(@trace2)
 between (@@optimizer_trace_max_mem_size-200) and (@@optimizer_trace_max_mem_size+200)
-1
+0
 select instr(@trace, @trace2) = 1;
 instr(@trace, @trace2) = 1
 1
@@ -1392,14 +1368,6 @@ explain SELECT c FROM t5 where c+1 in (s
                   "transformation": {
                     "select#": 2,
                     "from": "IN (SELECT)",
-                    "to": "materialization",
-                    "chosen": false
-                  } /* transformation */
-                },
-                {
-                  "transformation": {
-                    "select#": 2,
-                    "from": "IN (SELECT)",
                     "to": "EXISTS (CORRELATED SELECT)",
                     "chosen": true,
                     "evaluating_constant_where_conditions": [
@@ -1682,14 +1650,6 @@ explain SELECT c FROM t5 where c+1 in (s
                   "transformation": {
                     "select#": 2,
                     "from": "IN (SELECT)",
-                    "to": "materialization",
-                    "chosen": false
-                  } /* transformation */
-                },
-                {
-                  "transformation": {
-                    "select#": 2,
-                    "from": "IN (SELECT)",
                     "to": "EXISTS (CORRELATED SELECT)",
                     "chosen": true,
                     "evaluating_constant_where_conditions": [
@@ -2445,14 +2405,6 @@ explain extended select * from t1 where 
                   "transformation": {
                     "select#": 2,
                     "from": "IN (SELECT)",
-                    "to": "materialization",
-                    "chosen": false
-                  } /* transformation */
-                },
-                {
-                  "transformation": {
-                    "select#": 2,
-                    "from": "IN (SELECT)",
                     "to": "EXISTS (CORRELATED SELECT)",
                     "chosen": true,
                     "evaluating_constant_where_conditions": [
@@ -2721,14 +2673,6 @@ explain extended select * from t1 where 
                   "transformation": {
                     "select#": 2,
                     "from": "IN (SELECT)",
-                    "to": "materialization",
-                    "chosen": false
-                  } /* transformation */
-                },
-                {
-                  "transformation": {
-                    "select#": 2,
-                    "from": "IN (SELECT)",
                     "to": "EXISTS (CORRELATED SELECT)",
                     "chosen": true,
                     "evaluating_constant_where_conditions": [
@@ -3334,14 +3278,6 @@ where a1 in (select b1 from t2_16 where 
                   "transformation": {
                     "select#": 2,
                     "from": "IN (SELECT)",
-                    "to": "materialization",
-                    "chosen": false
-                  } /* transformation */
-                },
-                {
-                  "transformation": {
-                    "select#": 2,
-                    "from": "IN (SELECT)",
                     "to": "EXISTS (CORRELATED SELECT)",
                     "chosen": true,
                     "evaluating_constant_where_conditions": [
@@ -3614,14 +3550,6 @@ WHERE c2 IN ( SELECT c2 FROM t2 WHERE c2
                     "to": "semijoin",
                     "chosen": false
                   } /* transformation */
-                },
-                {
-                  "transformation": {
-                    "select#": 2,
-                    "from": "IN (SELECT)",
-                    "to": "materialization",
-                    "chosen": false
-                  } /* transformation */
                 }
               ] /* steps */
             } /* join_preparation */
@@ -4348,14 +4276,6 @@ concat(c1,'y') IN
                   "transformation": {
                     "select#": 2,
                     "from": "IN (SELECT)",
-                    "to": "materialization",
-                    "chosen": false
-                  } /* transformation */
-                },
-                {
-                  "transformation": {
-                    "select#": 2,
-                    "from": "IN (SELECT)",
                     "to": "EXISTS (CORRELATED SELECT)",
                     "chosen": true,
                     "evaluating_constant_where_conditions": [
@@ -4384,14 +4304,6 @@ concat(c1,'y') IN
                   "transformation": {
                     "select#": 3,
                     "from": "IN (SELECT)",
-                    "to": "materialization",
-                    "chosen": false
-                  } /* transformation */
-                },
-                {
-                  "transformation": {
-                    "select#": 3,
-                    "from": "IN (SELECT)",
                     "to": "EXISTS (CORRELATED SELECT)",
                     "chosen": true,
                     "evaluating_constant_where_conditions": [
@@ -4854,14 +4766,6 @@ trace
                   "transformation": {
                     "select#": 2,
                     "from": "IN (SELECT)",
-                    "to": "materialization",
-                    "chosen": false
-                  } /* transformation */
-                },
-                {
-                  "transformation": {
-                    "select#": 2,
-                    "from": "IN (SELECT)",
                     "to": "EXISTS (CORRELATED SELECT)",
                     "chosen": true,
                     "evaluating_constant_where_conditions": [
@@ -4903,14 +4807,6 @@ trace
                     "to": "semijoin",
                     "chosen": false
                   } /* transformation */
-                },
-                {
-                  "transformation": {
-                    "select#": 2,
-                    "from": "IN (SELECT)",
-                    "to": "materialization",
-                    "chosen": false
-                  } /* transformation */
                 }
               ] /* steps */
             } /* join_preparation */
@@ -5017,14 +4913,6 @@ trace
                     "to": "semijoin",
                     "chosen": false
                   } /* transformation */
-                },
-                {
-                  "transformation": {
-                    "select#": 2,
-                    "from": "IN (SELECT)",
-                    "to": "materialization",
-                    "chosen": false
-                  } /* transformation */
                 }
               ] /* steps */
             } /* join_preparation */
@@ -5321,14 +5209,6 @@ select * from t1 where (t1.a,t1.b) not i
                     "to": "semijoin",
                     "chosen": false
                   } /* transformation */
-                },
-                {
-                  "transformation": {
-                    "select#": 2,
-                    "from": "IN (SELECT)",
-                    "to": "materialization",
-                    "chosen": false
-                  } /* transformation */
                 }
               ] /* steps */
             } /* join_preparation */
@@ -7340,14 +7220,6 @@ select d into res from t6 where d in (se
                   "transformation": {
                     "select#": 2,
                     "from": "IN (SELECT)",
-                    "to": "materialization",
-                    "chosen": false
-                  } /* transformation */
-                },
-                {
-                  "transformation": {
-                    "select#": 2,
-                    "from": "IN (SELECT)",
                     "to": "EXISTS (CORRELATED SELECT)",
                     "chosen": true,
                     "evaluating_constant_where_conditions": [
@@ -9118,14 +8990,6 @@ select d into res from t6 where d in (se
                   "transformation": {
                     "select#": 2,
                     "from": "IN (SELECT)",
-                    "to": "materialization",
-                    "chosen": false
-                  } /* transformation */
-                },
-                {
-                  "transformation": {
-                    "select#": 2,
-                    "from": "IN (SELECT)",
                     "to": "EXISTS (CORRELATED SELECT)",
                     "chosen": true,
                     "evaluating_constant_where_conditions": [

=== modified file 'mysql-test/suite/opt_trace/r/subquery_no_prot.result'
--- a/mysql-test/suite/opt_trace/r/subquery_no_prot.result	2012-01-26 13:09:59 +0000
+++ b/mysql-test/suite/opt_trace/r/subquery_no_prot.result	2012-02-08 15:25:17 +0000
@@ -996,7 +996,8 @@ field4,field5,field6	{
                     "select#": 6,
                     "from": "IN (SELECT)",
                     "to": "materialization",
-                    "chosen": false
+                    "chosen": false,
+                    "cause": "correlated"
                   } /* transformation */
                 },
                 {

=== modified file 'mysql-test/suite/opt_trace/r/subquery_ps_prot.result'
--- a/mysql-test/suite/opt_trace/r/subquery_ps_prot.result	2012-01-30 08:57:24 +0000
+++ b/mysql-test/suite/opt_trace/r/subquery_ps_prot.result	2012-02-08 15:25:17 +0000
@@ -984,7 +984,8 @@ field4,field5,field6	{
                     "select#": 6,
                     "from": "IN (SELECT)",
                     "to": "materialization",
-                    "chosen": false
+                    "chosen": false,
+                    "cause": "correlated"
                   } /* transformation */
                 }
               ] /* steps */

=== modified file 'sql/item_cmpfunc.cc'
--- a/sql/item_cmpfunc.cc	2012-02-08 10:33:37 +0000
+++ b/sql/item_cmpfunc.cc	2012-02-08 15:25:17 +0000
@@ -1943,8 +1943,11 @@ void Item_in_optimizer::fix_after_pullou
 
 /**
    The implementation of optimized \<outer expression\> [NOT] IN \<subquery\>
-   predicates. The implementation works as follows.
+   predicates. It applies to predicates which have gone through the IN->EXISTS
+   transformation in in_to_exists_transformer functions; not to subquery
+   materialization (which has no triggered conditions).
 
+   The implementation works as follows.
    For the current value of the outer expression
    
    - If it contains only NULL values, the original (before rewrite by the
@@ -2021,12 +2024,14 @@ longlong Item_in_optimizer::val_int()
   
   if (cache->null_value)
   {
+    Item_in_subselect * const item_subs=
+      static_cast<Item_in_subselect *>(args[1]);
     /*
       We're evaluating 
       "<outer_value_list> [NOT] IN (SELECT <inner_value_list>...)" 
       where one or more of the outer values is NULL. 
     */
-    if (((Item_in_subselect*)args[1])->is_top_level_item())
+    if (item_subs->is_top_level_item())
     {
       /*
         We're evaluating a top level item, e.g. 
@@ -2049,7 +2054,6 @@ longlong Item_in_optimizer::val_int()
         SELECT evaluated over the non-NULL values produces at least
         one row, FALSE otherwise
       */
-      Item_in_subselect *item_subs=(Item_in_subselect*)args[1]; 
       bool all_left_cols_null= true;
       const uint ncols= cache->cols();
 
@@ -2080,7 +2084,7 @@ longlong Item_in_optimizer::val_int()
       {
         /* The subquery has to be evaluated */
         (void) item_subs->val_bool_result();
-        if (item_subs->engine->no_rows())
+        if (!item_subs->value)
           null_value= item_subs->null_value;
         else
           null_value= TRUE;

=== modified file 'sql/item_cmpfunc.h'
--- a/sql/item_cmpfunc.h	2012-02-08 10:33:37 +0000
+++ b/sql/item_cmpfunc.h	2012-02-08 15:25:17 +0000
@@ -255,7 +255,7 @@ class Item_cache;
 
 class Item_in_optimizer: public Item_bool_func
 {
-protected:
+private:
   Item_cache *cache;
   bool save_cache;
   /* 
@@ -1481,7 +1481,8 @@ class Item_in_subselect;
 
 /* 
   This is like IS NOT NULL but it also remembers if it ever has
-  encountered a NULL.
+  encountered a NULL; it remembers this in the "was_null" property of the
+  "owner" item.
 */
 class Item_is_not_null_test :public Item_func_isnull
 {

=== modified file 'sql/item_subselect.cc'
--- a/sql/item_subselect.cc	2012-01-17 09:45:08 +0000
+++ b/sql/item_subselect.cc	2012-02-08 15:25:17 +0000
@@ -151,7 +151,7 @@ void Item_in_subselect::cleanup()
     delete left_expr_cache;
     left_expr_cache= NULL;
   }
-  first_execution= TRUE;
+  left_expr_cache_filled= false;
   need_expr_cache= TRUE;
   Item_subselect::cleanup();
   DBUG_VOID_RETURN;
@@ -452,20 +452,22 @@ bool Item_in_subselect::exec()
       init_left_expr_cache())
     DBUG_RETURN(TRUE);
 
-  /* If the new left operand is already in the cache, reuse the old result. */
-  if (left_expr_cache && test_if_item_cache_changed(*left_expr_cache) < 0)
+  if (left_expr_cache != NULL)
   {
-    /* Always compute IN for the first row as the cache is not valid for it. */
-    if (!first_execution)
-      DBUG_RETURN(FALSE);
-    first_execution= FALSE;
+    const int result= test_if_item_cache_changed(*left_expr_cache);
+    if (left_expr_cache_filled &&               // cache was previously filled
+        result < 0)         // new value is identical to previous cached value
+    {
+      /*
+        We needn't do a full execution, can just reuse "value", "was_null",
+        "null_value" of the previous execution.
+      */
+      DBUG_RETURN(false);
+    }
+    left_expr_cache_filled= true;
   }
 
-  /*
-    The exec() method below updates item::value, and item::null_value, thus if
-    we don't call it, the next call to item::val_int() will return whatever
-    result was computed by its previous call.
-  */
+  null_value= was_null= false;
   const bool retval= Item_subselect::exec();
   DBUG_RETURN(retval);
 }
@@ -888,7 +890,7 @@ bool Item_in_subselect::test_limit(st_se
 Item_in_subselect::Item_in_subselect(Item * left_exp,
 				     st_select_lex *select_lex):
   Item_exists_subselect(), left_expr(left_exp), left_expr_cache(NULL),
-  first_execution(TRUE), need_expr_cache(TRUE), expr(NULL),
+  left_expr_cache_filled(false), need_expr_cache(TRUE), expr(NULL),
   optimizer(NULL), was_null(FALSE), abort_on_null(FALSE),
   pushed_cond_guards(NULL), upper_item(NULL)
 {
@@ -1019,7 +1021,6 @@ double Item_in_subselect::val_real()
   */
   DBUG_ASSERT(0);
   DBUG_ASSERT(fixed == 1);
-  null_value= was_null= FALSE;
   if (exec())
   {
     reset();
@@ -1039,7 +1040,6 @@ longlong Item_in_subselect::val_int()
   */
   DBUG_ASSERT(0);
   DBUG_ASSERT(fixed == 1);
-  null_value= was_null= FALSE;
   if (exec())
   {
     reset();
@@ -1059,7 +1059,6 @@ String *Item_in_subselect::val_str(Strin
   */
   DBUG_ASSERT(0);
   DBUG_ASSERT(fixed == 1);
-  null_value= was_null= FALSE;
   if (exec())
   {
     reset();
@@ -1078,7 +1077,6 @@ String *Item_in_subselect::val_str(Strin
 bool Item_in_subselect::val_bool()
 {
   DBUG_ASSERT(fixed == 1);
-  null_value= was_null= FALSE;
   if (exec())
   {
     reset();
@@ -1096,7 +1094,6 @@ my_decimal *Item_in_subselect::val_decim
     method should not be used
   */
   DBUG_ASSERT(0);
-  null_value= was_null= FALSE;
   DBUG_ASSERT(fixed == 1);
   if (exec())
   {
@@ -2230,28 +2227,6 @@ bool subselect_union_engine::is_executed
 }
 
 
-/*
-  Check if last execution of the subquery engine produced any rows
-
-  SYNOPSIS
-    subselect_union_engine::no_rows()
-
-  DESCRIPTION
-    Check if last execution of the subquery engine produced any rows. The
-    return value is undefined if last execution ended in an error.
-
-  RETURN
-    TRUE  - Last subselect execution has produced no rows
-    FALSE - Otherwise
-*/
-
-bool subselect_union_engine::no_rows() const
-{
-  /* Check if we got any rows when reading UNION result from temp. table: */
-  return test(!unit->fake_select_lex->join->send_records);
-}
-
-
 subselect_union_engine::subselect_union_engine(st_select_lex_unit *u,
 					       select_result_interceptor *result_arg,
 					       Item_subselect *item_arg)
@@ -2325,7 +2300,7 @@ bool subselect_union_engine::prepare()
 }
 
 
-bool subselect_uniquesubquery_engine::prepare()
+bool subselect_indexsubquery_engine::prepare()
 {
   /* Should never be called. */
   DBUG_ASSERT(FALSE);
@@ -2333,27 +2308,6 @@ bool subselect_uniquesubquery_engine::pr
 }
 
 
-/*
-  Check if last execution of the subquery engine produced any rows
-
-  SYNOPSIS
-    subselect_single_select_engine::no_rows()
-
-  DESCRIPTION
-    Check if last execution of the subquery engine produced any rows. The
-    return value is undefined if last execution ended in an error.
-
-  RETURN
-    TRUE  - Last subselect execution has produced no rows
-    FALSE - Otherwise
-*/
-
-bool subselect_single_select_engine::no_rows() const
-{ 
-  return !item->assigned();
-}
-
-
 /* 
  makes storage for the output values for the subquery and calcuates 
  their data and column types and their nullability.
@@ -2407,7 +2361,7 @@ void subselect_union_engine::fix_length_
   }
 }
 
-void subselect_uniquesubquery_engine::fix_length_and_dec(Item_cache **row)
+void subselect_indexsubquery_engine::fix_length_and_dec(Item_cache **row)
 {
   //this never should be called
   DBUG_ASSERT(0);
@@ -2548,29 +2502,23 @@ bool subselect_union_engine::exec()
 }
 
 
-/*
-  Search for at least one row satisfying select condition
- 
-  SYNOPSIS
-    subselect_uniquesubquery_engine::scan_table()
+/**
+  Search, using a table scan, for at least one row satisfying select
+  condition.
 
-  DESCRIPTION
-    Scan the table using sequential access until we find at least one row
-    satisfying select condition.
-    
-    The caller must set this->empty_result_set=FALSE before calling this
-    function. This function will set it to TRUE if it finds a matching row.
+  The caller must set item's 'value' to 'false' before calling this
+  function. This function will set it to 'true' if it finds a matching row.
 
-  RETURN
-    FALSE - OK
-    TRUE  - Error
+  @returns false if ok, true if read error.
 */
-
-bool subselect_uniquesubquery_engine::scan_table()
+bool subselect_indexsubquery_engine::scan_table()
 {
   int error;
   TABLE *table= tab->table;
-  DBUG_ENTER("subselect_uniquesubquery_engine::scan_table");
+  DBUG_ENTER("subselect_indexsubquery_engine::scan_table");
+
+  // We never need to do a table scan of the materialized table.
+  DBUG_ASSERT(engine_type() != HASH_SJ_ENGINE);
 
   if (table->file->inited)
     table->file->ha_index_end();
@@ -2594,7 +2542,6 @@ bool subselect_uniquesubquery_engine::sc
     if (!cond || cond->val_int())
     {
       static_cast<Item_in_subselect*>(item)->value= true;
-      empty_result_set= FALSE;
       break;
     }
   }
@@ -2640,7 +2587,7 @@ bool subselect_uniquesubquery_engine::sc
      value of NULL, not rows that match if the "inner_col IS NULL"
      condition is disabled. Index lookup can be used for this.
 
-  @see subselect_uniquesubquery_engine::exec()
+  @see subselect_indexsubquery_engine::exec()
   @see Item_in_optimizer::val_int()
 
   @param[out] require_scan   true if a NULL value is found that falls 
@@ -2651,10 +2598,10 @@ bool subselect_uniquesubquery_engine::sc
                              otherwise.
   
 */
-void subselect_uniquesubquery_engine::copy_ref_key(bool *require_scan, 
-                                                   bool *convert_error)
+void subselect_indexsubquery_engine::copy_ref_key(bool *require_scan,
+                                                  bool *convert_error)
 {
-  DBUG_ENTER("subselect_uniquesubquery_engine::copy_ref_key");
+  DBUG_ENTER("subselect_indexsubquery_engine::copy_ref_key");
 
   *require_scan= false;
   *convert_error= false;
@@ -2669,6 +2616,15 @@ void subselect_uniquesubquery_engine::co
 
     if (s_key->null_key)
     {
+      /*
+        If we have materialized the subquery:
+        - this NULL ref item cannot be local to the subquery (any such
+        conditions was handled during materialization)
+        - neither can it be outer, because this case is
+        separately managed in subselect_hash_sj_engine::exec().
+      */
+      DBUG_ASSERT(engine_type() != HASH_SJ_ENGINE);
+
       const bool *cond_guard= tab->ref.cond_guards[part_no];
 
       /*
@@ -2711,100 +2667,6 @@ void subselect_uniquesubquery_engine::co
 
 
 /*
-  Execute subselect
-
-  SYNOPSIS
-    subselect_uniquesubquery_engine::exec()
-
-  DESCRIPTION
-    Find rows corresponding to the ref key using index access.
-    If some part of the lookup key is NULL, then we're evaluating
-      NULL IN (SELECT ... )
-    This is a special case, we don't need to search for NULL in the table,
-    instead, the result value is 
-      - NULL  if select produces empty row set
-      - FALSE otherwise.
-
-    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
-    return FALSE. 
-    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
-    
-  RETURN
-    FALSE - ok
-    TRUE  - an error occured while scanning
-*/
-
-bool subselect_uniquesubquery_engine::exec()
-{
-  DBUG_ENTER("subselect_uniquesubquery_engine::exec");
-  int error;
-  TABLE *table= tab->table;
-  TABLE_LIST *tl= table->pos_in_table_list;
-  empty_result_set= TRUE;
-  table->status= 0;
- 
-  if (engine_type() == UNIQUESUBQUERY_ENGINE &&
-      tl->uses_materialization() && !tl->materialized)
-  {
-    bool err= mysql_handle_single_derived(table->in_use->lex, tl,
-                                          mysql_derived_create) ||
-              mysql_handle_single_derived(table->in_use->lex, tl,
-                                          mysql_derived_materialize);
-    if (!tab->table->in_use->lex->describe)
-      mysql_handle_single_derived(table->in_use->lex, tl,
-                                  mysql_derived_cleanup);
-    if (err)
-      DBUG_RETURN(1);
-  }
-
-  /* Copy the ref key and check for nulls... */
-  bool require_scan, convert_error;
-  copy_ref_key(&require_scan, &convert_error);
-  if (convert_error)
-  {
-    ((Item_in_subselect *) item)->value= 0;
-    DBUG_RETURN(0);
-  }
-
-  if (require_scan)
-  {
-    const bool scan_result= scan_table();
-    DBUG_RETURN(scan_result);
-  }
-  if (!table->file->inited)
-    table->file->ha_index_init(tab->ref.key, 0);
-  error= table->file->ha_index_read_map(table->record[0],
-                                        tab->ref.key_buff,
-                                        make_prev_keypart_map(tab->ref.key_parts),
-                                        HA_READ_KEY_EXACT);
-  DBUG_PRINT("info", ("lookup result: %i", error));
-  if (error &&
-      error != HA_ERR_KEY_NOT_FOUND && error != HA_ERR_END_OF_FILE)
-    error= report_error(table, error);
-  else
-  {
-    error= 0;
-    table->null_row= 0;
-    if (!table->status && (!cond || cond->val_int()))
-    {
-      ((Item_in_subselect *) item)->value= 1;
-      empty_result_set= FALSE;
-    }
-    else
-      ((Item_in_subselect *) item)->value= 0;
-  }
-
-  DBUG_RETURN(error != 0);
-}
-
-
-/*
   Index-lookup subselect 'engine' - run the subquery
 
   SYNOPSIS
@@ -2825,6 +2687,8 @@ bool subselect_uniquesubquery_engine::ex
     3. If check_null==TRUE, make another lookup via key=NULL, search for a 
        row that satisfies subq_where. If found, return NULL, otherwise
        return FALSE.
+    4. If unique==true, there can be only one row with key=oe and only one row
+       with key=NULL, we use that fact to shorten the search process.
 
   TODO
     The step #1 can be optimized further when the index has several key
@@ -2862,13 +2726,13 @@ bool subselect_indexsubquery_engine::exe
   int error;
   bool null_finding= 0;
   TABLE *table= tab->table;
+  // 'tl' is NULL if this is a tmp table created by subselect_hash_sj_engine.
   TABLE_LIST *tl= table->pos_in_table_list;
-
-  ((Item_in_subselect *) item)->value= 0;
-  empty_result_set= TRUE;
+  Item_in_subselect *const item_in= static_cast<Item_in_subselect*>(item);
+  item_in->value= false;
   table->status= 0;
 
-  if (tl->uses_materialization() && !tl->materialized)
+  if (tl && tl->uses_materialization() && !tl->materialized)
   {
     bool err= mysql_handle_single_derived(table->in_use->lex, tl,
                                           mysql_derived_create) ||
@@ -2885,17 +2749,14 @@ bool subselect_indexsubquery_engine::exe
   {
     /* We need to check for NULL if there wasn't a matching value */
     *tab->ref.null_ref_key= 0;			// Search first for not null
-    ((Item_in_subselect *) item)->was_null= 0;
+    item_in->was_null= false;
   }
 
   /* Copy the ref key and check for nulls... */
   bool require_scan, convert_error;
   copy_ref_key(&require_scan, &convert_error);
   if (convert_error)
-  {
-    ((Item_in_subselect *) item)->value= 0;
     DBUG_RETURN(0);
-  }
 
   if (require_scan)
   {
@@ -2904,7 +2765,7 @@ bool subselect_indexsubquery_engine::exe
   }
 
   if (!table->file->inited)
-    table->file->ha_index_init(tab->ref.key, 1);
+    table->file->ha_index_init(tab->ref.key, !unique /* sorted */);
   error= table->file->ha_index_read_map(table->record[0],
                                         tab->ref.key_buff,
                                         make_prev_keypart_map(tab->ref.key_parts),
@@ -2922,13 +2783,21 @@ bool subselect_indexsubquery_engine::exe
       {
         if ((!cond || cond->val_int()) && (!having || having->val_int()))
         {
-          empty_result_set= FALSE;
+          item_in->value= true;
           if (null_finding)
-            ((Item_in_subselect *) item)->was_null= 1;
-          else
-            ((Item_in_subselect *) item)->value= 1;
+          {
+            /*
+              This is dead code; subqueries with check_null==true are always
+              transformed with IN-to-EXISTS and thus their artificial HAVING
+              rejects NULL values...
+            */
+            DBUG_ASSERT(false);
+            item_in->was_null= true;
+          }
           break;
         }
+        if (unique)
+          break;
         error= table->file->ha_index_next_same(table->record[0],
                                               tab->ref.key_buff,
                                               tab->ref.key_length);
@@ -2942,9 +2811,14 @@ bool subselect_indexsubquery_engine::exe
       {
         if (!check_null || null_finding)
           break;			/* We don't need to check nulls */
+        /*
+          Check if there exists a row with a null value in the index. We come
+          here only if ref_or_null, and ref_or_null is always on a single
+          column (first keypart of the index). So we have only one NULL bit to
+          turn on:
+        */
         *tab->ref.null_ref_key= 1;
         null_finding= 1;
-        /* Check if there exists a row with a null value in the index */
         if ((error= (safe_index_read(tab) == 1)))
           break;
       }
@@ -2990,7 +2864,7 @@ void subselect_union_engine::exclude()
 }
 
 
-void subselect_uniquesubquery_engine::exclude()
+void subselect_indexsubquery_engine::exclude()
 {
   //this never should be called
   DBUG_ASSERT(0);
@@ -3085,41 +2959,12 @@ void subselect_union_engine::print(Strin
 }
 
 
-void subselect_uniquesubquery_engine::print(String *str,
-                                            enum_query_type query_type)
-{
-  char *table_name= tab->table->s->table_name.str;
-  str->append(STRING_WITH_LEN("<primary_index_lookup>("));
-  tab->ref.items[0]->print(str, query_type);
-  str->append(STRING_WITH_LEN(" in "));
-  if (tab->table->s->table_category == TABLE_CATEGORY_TEMPORARY)
-  {
-    /*
-      Temporary tables' names change across runs, so they can't be used for
-      EXPLAIN EXTENDED.
-    */
-    str->append(STRING_WITH_LEN("<temporary table>"));
-  }
-  else
-    str->append(table_name, tab->table->s->table_name.length);
-  KEY *key_info= tab->table->key_info+ tab->ref.key;
-  str->append(STRING_WITH_LEN(" on "));
-  str->append(key_info->name);
-  if (cond)
-  {
-    str->append(STRING_WITH_LEN(" where "));
-    cond->print(str, query_type);
-  }
-  str->append(')');
-}
-
-
 /*
 TODO:
-The above ::print method should be changed as below. Do it after
+The ::print method below should be changed as follows. Do it after
 all other tests pass.
 
-void subselect_uniquesubquery_engine::print(String *str)
+void subselect_indexsubquery_engine::print(String *str)
 {
   KEY *key_info= tab->table->key_info + tab->ref.key;
   str->append(STRING_WITH_LEN("<primary_index_lookup>("));
@@ -3141,17 +2986,27 @@ void subselect_uniquesubquery_engine::pr
 void subselect_indexsubquery_engine::print(String *str,
                                            enum_query_type query_type)
 {
-  str->append(STRING_WITH_LEN("<index_lookup>("));
+  if (unique)
+    str->append(STRING_WITH_LEN("<primary_index_lookup>("));
+  else
+    str->append(STRING_WITH_LEN("<index_lookup>("));
   tab->ref.items[0]->print(str, query_type);
   str->append(STRING_WITH_LEN(" in "));
-  /*
-    For materialized derived tables/views use table/view alias instead of
-    temporary table name, as it changes on each run and not acceptable for
-    EXPLAIN EXTENDED.
-  */
   if (tab->table->pos_in_table_list &&
-      tab->table->pos_in_table_list->uses_materialization())
+           tab->table->pos_in_table_list->uses_materialization())
+  {
+    /*
+      For materialized derived tables/views use table/view alias instead of
+      temporary table name, as it changes on each run and not acceptable for
+      EXPLAIN EXTENDED.
+    */
     str->append(tab->table->alias, strlen(tab->table->alias));
+  }
+  else if (tab->table->s->table_category == TABLE_CATEGORY_TEMPORARY)
+  {
+    // Could be from subselect_hash_sj_engine.
+    str->append(STRING_WITH_LEN("<temporary table>"));
+  }
   else
     str->append(tab->table->s->table_name.str, tab->table->s->table_name.length);
   KEY *key_info= tab->table->key_info+ tab->ref.key;
@@ -3227,8 +3082,8 @@ bool subselect_union_engine::change_resu
     TRUE  error
 */
 
-bool subselect_uniquesubquery_engine::change_result(Item_subselect *si,
-                                                    select_result_interceptor *res)
+bool subselect_indexsubquery_engine::change_result(Item_subselect *si,
+                                                   select_result_interceptor *res)
 {
   DBUG_ASSERT(0);
   return TRUE;
@@ -3293,7 +3148,7 @@ bool subselect_union_engine::no_tables()
     FALSE there are some tables in subquery
 */
 
-bool subselect_uniquesubquery_engine::no_tables() const
+bool subselect_indexsubquery_engine::no_tables() const
 {
   /* returning value is correct, but this method should never be called */
   return 0;
@@ -3310,7 +3165,8 @@ bool subselect_uniquesubquery_engine::no
 
   @detail
   - Create a temporary table to store the result of the IN subquery. The
-    temporary table has one hash index on all its columns.
+    temporary table has one hash index on all its columns. If single-column,
+    the index allows at most one NULL row.
   - Create a new result sink that sends the result stream of the subquery to
     the temporary table,
   - Create and initialize a new JOIN_TAB, and TABLE_REF objects to perform
@@ -3393,7 +3249,7 @@ bool subselect_hash_sj_engine::setup(Lis
     plan operator into the materialized subquery result. Notice that:
     - this JOIN_TAB has no corresponding JOIN (and doesn't need one), and
     - here we initialize only those members that are used by
-      subselect_uniquesubquery_engine, so these objects are incomplete.
+      subselect_indexsubquery_engine, so these objects are incomplete.
   */ 
   JOIN_TAB * const tmp_tab= new (thd->mem_root) JOIN_TAB;
   if (tmp_tab == NULL)
@@ -3414,11 +3270,14 @@ bool subselect_hash_sj_engine::setup(Lis
   /*
     Create an artificial condition to post-filter those rows matched by index
     lookups that cannot be distinguished by the index lookup procedure, e.g.
-    because of truncation. Prepared statements execution requires that
-    fix_fields is called for every execution. In order to call fix_fields we
-    need to create a Name_resolution_context and a corresponding TABLE_LIST
-    for the temporary table for the subquery, so that all column references
-    to the materialized subquery table can be resolved correctly.
+    because of truncation (if the outer column type's length is bigger than
+    the inner column type's, index lookup will use a truncated outer
+    value as search key, yielding false positives).
+    Prepared statements execution requires that fix_fields is called
+    for every execution. In order to call fix_fields we need to create a
+    Name_resolution_context and a corresponding TABLE_LIST for the temporary
+    table for the subquery, so that all column references to the materialized
+    subquery table can be resolved correctly.
   */
   DBUG_ASSERT(cond == NULL);
   if (!(cond= new Item_cond_and))
@@ -3448,7 +3307,7 @@ bool subselect_hash_sj_engine::setup(Lis
     Item_func_eq *eq_cond; 
     /* Item for the corresponding field from the materialized temp table. */
     Item_field *right_col_item;
-    int null_count= test(key_parts[part_no].field->real_maybe_null());
+    const bool nullable= key_parts[part_no].field->real_maybe_null();
     tmp_tab->ref.items[part_no]= item_in->left_expr->element_index(part_no);
 
     if (!(right_col_item= new Item_field(thd, context, 
@@ -3470,10 +3329,26 @@ bool subselect_hash_sj_engine::setup(Lis
                             cur_ref_buff + test(maybe_null), we could
                             use that information instead.
                          */
-                         cur_ref_buff + null_count,
-                         null_count ? cur_ref_buff : 0,
+                         cur_ref_buff + (nullable ? 1 : 0),
+                         nullable ? cur_ref_buff : 0,
                          key_parts[part_no].length,
                          tmp_tab->ref.items[part_no]);
+    if (nullable &&          // nullable column in tmp table,
+        // and UNKNOWN should not be interpreted as FALSE
+        !item_in->is_top_level_item())
+    {
+      // It must be the single column, or we wouldn't be here
+      DBUG_ASSERT(tmp_key_parts == 1);
+      // Be ready to search for NULL into inner column:
+      tmp_tab->ref.null_ref_key= cur_ref_buff;
+      mat_table_has_nulls= NEX_UNKNOWN;
+    }
+    else
+    {
+      tmp_tab->ref.null_ref_key= NULL;
+      mat_table_has_nulls= NEX_IRRELEVANT_OR_FALSE;
+    }
+
     cur_ref_buff+= key_parts[part_no].store_length;
   }
   tmp_tab->ref.key_err= 1;
@@ -3543,7 +3418,7 @@ void subselect_hash_sj_engine::cleanup()
 bool subselect_hash_sj_engine::exec()
 {
   Item_in_subselect *item_in= (Item_in_subselect *) item;
-
+  TABLE *const table= tab->table;
   DBUG_ENTER("subselect_hash_sj_engine::exec");
 
   /*
@@ -3552,7 +3427,7 @@ bool subselect_hash_sj_engine::exec()
   */
   if (!is_materialized)
   {
-    bool res= false;
+    bool res;
     THD * const thd= item->unit->thd;
     SELECT_LEX *save_select= thd->lex->current_select;
     thd->lex->current_select= materialize_engine->select_lex;
@@ -3576,21 +3451,10 @@ bool subselect_hash_sj_engine::exec()
         unlocking).
      */
     is_materialized= TRUE;
-    /*
-      If the subquery returned no rows, the temporary table is empty, so we know
-      directly that the result of IN is FALSE. We first update the table
-      statistics, then we test if the temporary table for the query result is
-      empty.
-    */
-    tab->table->file->info(HA_STATUS_VARIABLE);
-    if (!tab->table->file->stats.records)
-    {
-      empty_result_set= TRUE;
-      item_in->value= FALSE;
-      /* TODO: check we need this: item_in->null_value= FALSE; */
-      thd->lex->current_select= save_select;
-      DBUG_RETURN(FALSE);
-    }
+
+    // Calculate row count:
+    table->file->info(HA_STATUS_VARIABLE);
+
     /* Set tmp_param only if its usable, i.e. tmp_param->copy_field != NULL. */
     tmp_param= &(item_in->unit->outer_select()->join->tmp_table_param);
     if (tmp_param && !tmp_param->copy_field)
@@ -3600,12 +3464,71 @@ err:
     thd->lex->current_select= save_select;
     if (res)
       DBUG_RETURN(res);
-  }
+  } // if (!is_materialized)
 
+  if (table->file->stats.records == 0)
+  {
+    // The correct answer is FALSE.
+    item_in->value= false;
+    DBUG_RETURN(false);
+  }
   /*
-    Lookup the left IN operand in the hash index of the materialized subquery.
+    Here we could be brutal and set item_in->null_value. But we prefer to be
+    well-behaved and rather set the properties which
+    Item_in_subselect::val_bool() and Item_in_optimizer::val_int() expect,
+    and then those functions will set null_value based on those properties.
   */
-  DBUG_RETURN(subselect_uniquesubquery_engine::exec());
+  if (item_in->left_expr->element_index(0)->null_value)
+  {
+    /*
+      The first outer expression oe1 is NULL. It is the single outer
+      expression because if there would be more ((oe1,oe2,...)IN(...)) then
+      either they would be non-nullable (so we wouldn't be here) or the
+      predicate would be top-level (so we wouldn't be here,
+      Item_in_optimizer::val_int() would have short-cut). The correct answer
+      is UNKNOWN. Do as if searching with all triggered conditions disabled:
+      this would surely find a row. The caller will translate this to UNKNOWN.
+    */
+    DBUG_ASSERT(item_in->left_expr->cols() == 1);
+    item_in->value= true;
+    DBUG_RETURN(false);
+  }
+
+  if (subselect_indexsubquery_engine::exec())  // Search with index
+    DBUG_RETURN(true);
+
+  if (!item_in->value && // no exact match
+      mat_table_has_nulls != NEX_IRRELEVANT_OR_FALSE)
+  {
+    /*
+      There is only one outer expression. It's not NULL. exec() above has set
+      the answer to FALSE, but if there exists an inner NULL in the temporary
+      table, then the correct answer is UNKNOWN, so let's find out.
+    */
+    if (mat_table_has_nulls == NEX_UNKNOWN)   // We do not know yet
+    {
+      // Search for NULL inside tmp table, and remember the outcome.
+      DBUG_ASSERT(table->file->inited);
+      *tab->ref.null_ref_key= 1;
+      if (safe_index_read(tab) == 1)
+        DBUG_RETURN(true);
+      *tab->ref.null_ref_key= 0; // prepare for next searches of non-NULL
+      mat_table_has_nulls=
+        (table->status == 0) ? NEX_TRUE : NEX_IRRELEVANT_OR_FALSE;
+    }
+    if (mat_table_has_nulls == NEX_TRUE)
+    {
+      /*
+        There exists an inner NULL. The correct answer is UNKNOWN.
+        Do as if searching with all triggered conditions enabled; that
+        would not find any match, but Item_is_not_null_test would notice a
+        NULL:
+      */
+      item_in->value= false;
+      item_in->was_null= true;
+    }
+  }
+  DBUG_RETURN(false);
 }
 
 
@@ -3619,7 +3542,7 @@ void subselect_hash_sj_engine::print(Str
   materialize_engine->print(str, query_type);
   str->append(STRING_WITH_LEN(" ), "));
   if (tab)
-    subselect_uniquesubquery_engine::print(str, query_type);
+    subselect_indexsubquery_engine::print(str, query_type);
   else
     str->append(STRING_WITH_LEN(
            "<the access method for lookups is not yet created>"

=== modified file 'sql/item_subselect.h'
--- a/sql/item_subselect.h	2011-11-10 14:58:23 +0000
+++ b/sql/item_subselect.h	2012-02-08 15:25:17 +0000
@@ -320,7 +320,6 @@ public:
   virtual void print(String *str, enum_query_type query_type);
 
   friend class select_exists_subselect;
-  friend class subselect_uniquesubquery_engine;
   friend class subselect_indexsubquery_engine;
 };
 
@@ -350,7 +349,7 @@ protected:
     runtime memory root, for each execution, thus need not be freed.
   */
   List<Cached_item> *left_expr_cache;
-  bool first_execution;
+  bool left_expr_cache_filled; ///< Whether left_expr_cache holds a value
   /** The need for expr cache may be optimized away, @sa init_left_expr_cache. */
   bool need_expr_cache;
 
@@ -389,7 +388,7 @@ public:
   Item_in_subselect(Item * left_expr, st_select_lex *select_lex);
   Item_in_subselect()
     :Item_exists_subselect(), left_expr(NULL), left_expr_cache(NULL),
-    first_execution(TRUE), need_expr_cache(TRUE), expr(NULL),
+    left_expr_cache_filled(false), need_expr_cache(TRUE), expr(NULL),
     optimizer(NULL), was_null(FALSE), abort_on_null(FALSE),
     pushed_cond_guards(NULL), upper_item(NULL)
   {}
@@ -492,10 +491,6 @@ public:
       either captured by previously set up select_result-based 'sink' or
       stored somewhere by the exec() method itself.
 
-      A required side effect: If at least one pushed-down predicate is
-      disabled, subselect_engine->no_rows() must return correct result after 
-      the exec() call.
-
     RETURN
       0 - OK
       1 - Either an execution error, or the engine was "changed", and the
@@ -515,8 +510,6 @@ public:
                              select_result_interceptor *result)= 0;
   virtual bool no_tables() const = 0;
   virtual bool is_executed() const { return FALSE; }
-  /* Check if subquery produced any rows during last query execution */
-  virtual bool no_rows() const = 0;
   virtual enum_engine_type engine_type() const { return ABSTRACT_ENGINE; }
 #ifndef DBUG_OFF
   /**
@@ -557,7 +550,6 @@ public:
   virtual bool no_tables() const;
   virtual bool may_be_null() const;
   virtual bool is_executed() const { return executed; }
-  virtual bool no_rows() const;
   virtual enum_engine_type engine_type() const { return SINGLE_SELECT_ENGINE; }
   bool save_join_if_explain(); 
 
@@ -585,7 +577,6 @@ public:
                              select_result_interceptor *result);
   virtual bool no_tables() const;
   virtual bool is_executed() const;
-  virtual bool no_rows() const;
   virtual enum_engine_type engine_type() const { return UNION_ENGINE; }
 
 private:
@@ -596,65 +587,32 @@ private:
 struct st_join_table;
 
 
-/*
-  A subquery execution engine that evaluates the subquery by doing one index
-  lookup in a unique index.
+/**
+  A subquery execution engine that evaluates the subquery by doing index
+  lookups in a single table's 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) 
-  
+
+    outer_expr IN (SELECT tbl.key FROM tbl WHERE subq_where)
+
+  or, row-based:
+
+    (oe1, .. oeN) IN (SELECT key_part1, ... 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_indexsubquery_engine : public subselect_engine
 {
 protected:
   st_join_table *tab;
   Item *cond; /* The WHERE condition of subselect */
-  /* 
-    TRUE<=> last execution produced empty set. Valid only when left
-    expression is NULL.
-  */
-  bool empty_result_set;
-public:
-
-  // constructor can assign THD because it will be called after JOIN::prepare
-  subselect_uniquesubquery_engine(THD *thd_arg, st_join_table *tab_arg,
-				  Item_subselect *subs, Item *where)
-    :subselect_engine(subs, 0), tab(tab_arg), cond(where) {}
-  virtual void cleanup() {}
-  virtual bool prepare();
-  virtual void fix_length_and_dec(Item_cache** row);
-  virtual bool exec();
-  virtual uint cols() const { return 1; }
-  virtual uint8 uncacheable() const { return UNCACHEABLE_DEPENDENT; }
-  virtual void exclude();
-  virtual table_map upper_select_const_tables() const { return 0; }
-  virtual void print (String *str, enum_query_type query_type);
-  virtual bool change_result(Item_subselect *si,
-                             select_result_interceptor *result);
-  virtual bool no_tables() const;
-  bool scan_table();
-  void copy_ref_key(bool *require_scan, bool *convert_error);
-  virtual bool no_rows() const { return empty_result_set; }
-  virtual enum_engine_type engine_type() const { return UNIQUESUBQUERY_ENGINE; }
-};
-
-
-class subselect_indexsubquery_engine: public subselect_uniquesubquery_engine
-{
 private:
   /* FALSE for 'ref', TRUE for 'ref-or-null'. */
   bool check_null;
   /* 
-    The "having" clause. This clause (further reffered to as "artificial
+    The "having" clause. This clause (further referred 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
@@ -662,40 +620,39 @@ private:
       (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;
+  /**
+     Whether a lookup for key value (x0,y0) (x0 and/or y0 being NULL or not
+     NULL) will find at most one row.
+  */
+  bool unique;
 public:
 
   // constructor can assign THD because it will be called after JOIN::prepare
   subselect_indexsubquery_engine(THD *thd_arg, st_join_table *tab_arg,
 				 Item_subselect *subs, Item *where,
-                                 Item *having_arg, bool chk_null)
-    :subselect_uniquesubquery_engine(thd_arg, tab_arg, subs, where),
-     check_null(chk_null),
-     having(having_arg)
-  {}
+                                 Item *having_arg, bool chk_null,
+                                 bool unique_arg)
+    :subselect_engine(subs, 0), tab(tab_arg), cond(where),
+    check_null(chk_null), having(having_arg), unique(unique_arg)
+  {};
   virtual bool exec();
   virtual void print (String *str, enum_query_type query_type);
-  virtual enum_engine_type engine_type() const { return INDEXSUBQUERY_ENGINE; }
+  virtual enum_engine_type engine_type() const
+  { return unique ? UNIQUESUBQUERY_ENGINE : INDEXSUBQUERY_ENGINE; }
+  virtual void cleanup() {}
+  virtual bool prepare();
+  virtual void fix_length_and_dec(Item_cache** row);
+  virtual uint cols() const { return 1; }
+  virtual uint8 uncacheable() const { return UNCACHEABLE_DEPENDENT; }
+  virtual void exclude();
+  virtual table_map upper_select_const_tables() const { return 0; }
+  virtual bool change_result(Item_subselect *si,
+                             select_result_interceptor *result);
+  virtual bool no_tables() const;
+  bool scan_table();
+  void copy_ref_key(bool *require_scan, bool *convert_error);
 };
 
 /*
@@ -723,14 +680,29 @@ inline bool Item_subselect::is_uncacheab
 /**
   Compute an IN predicate via a hash semi-join. The subquery is materialized
   during the first evaluation of the IN predicate. The IN predicate is executed
-  via the functionality inherited from subselect_uniquesubquery_engine.
+  via the functionality inherited from subselect_indexsubquery_engine.
 */
 
-class subselect_hash_sj_engine: public subselect_uniquesubquery_engine
+class subselect_hash_sj_engine: public subselect_indexsubquery_engine
 {
 private:
   /* TRUE if the subquery was materialized into a temp table. */
   bool is_materialized;
+  /**
+     Existence of inner NULLs in materialized table:
+     By design, other values than IRRELEVANT_OR_FALSE are possible only if the
+     subquery has only one inner expression.
+  */
+  enum nulls_exist
+  {
+    /// none, or they don't matter
+    NEX_IRRELEVANT_OR_FALSE= 0,
+    /// they matter, and we don't know yet if they exists
+    NEX_UNKNOWN= 1,
+    /// they matter, and we know there exists at least one.
+    NEX_TRUE= 2
+  };
+  enum nulls_exist mat_table_has_nulls;
   /*
     The old engine already chosen at parse time and stored in permanent memory.
     Through this member we can re-create and re-prepare the join object
@@ -744,10 +716,10 @@ private:
 
 public:
   subselect_hash_sj_engine(THD *thd, Item_subselect *in_predicate,
-                               subselect_single_select_engine *old_engine)
-    :subselect_uniquesubquery_engine(thd, NULL, in_predicate, NULL),
-    is_materialized(FALSE), materialize_engine(old_engine),
-    tmp_param(NULL)
+                           subselect_single_select_engine *old_engine)
+    :subselect_indexsubquery_engine(thd, NULL, in_predicate, NULL,
+                                    NULL, false, true),
+    is_materialized(false), materialize_engine(old_engine), tmp_param(NULL)
   {}
   ~subselect_hash_sj_engine();
 

=== modified file 'sql/sql_class.cc'
--- a/sql/sql_class.cc	2012-01-31 15:16:16 +0000
+++ b/sql/sql_class.cc	2012-02-08 15:25:17 +0000
@@ -2978,6 +2978,12 @@ bool select_exists_subselect::send_data(
     unit->offset_limit_cnt--;
     DBUG_RETURN(0);
   }
+  /*
+    A subquery may be evaluated 1) by executing the JOIN 2) by optimized
+    functions (index_subquery, subquery materialization).
+    It's only in (1) that we get here when we find a row. In (2) "value" is
+    set elsewhere.
+  */
   it->value= 1;
   it->assigned(1);
   DBUG_RETURN(0);

=== modified file 'sql/sql_optimizer.cc'
--- a/sql/sql_optimizer.cc	2012-02-07 20:32:47 +0000
+++ b/sql/sql_optimizer.cc	2012-02-08 15:25:17 +0000
@@ -768,8 +768,10 @@ JOIN::optimize()
         join_tab[0].type= JT_UNIQUE_SUBQUERY;
         error= 0;
         changed= TRUE;
-        engine= new subselect_uniquesubquery_engine(thd, join_tab, unit->item,
-                                                    where);
+        engine= new subselect_indexsubquery_engine(thd, join_tab, unit->item,
+                                                   where, NULL /* having */,
+                                                   false /* check_null */,
+                                                   true /* unique */);
       }
       else if (join_tab[0].type == JT_REF &&
 	       join_tab[0].ref.items[0]->name == in_left_expr_name)
@@ -780,7 +782,7 @@ JOIN::optimize()
         error= 0;
         changed= TRUE;
         engine= new subselect_indexsubquery_engine(thd, join_tab, unit->item,
-                                                   where, NULL, 0);
+                                                   where, NULL, false, false);
       }
     } else if (join_tab[0].type == JT_REF_OR_NULL &&
 	       join_tab[0].ref.items[0]->name == in_left_expr_name &&
@@ -792,7 +794,13 @@ JOIN::optimize()
       conds= remove_additional_cond(conds);
       save_index_subquery_explain_info(join_tab, conds);
       engine= new subselect_indexsubquery_engine(thd, join_tab, unit->item,
-                                                 conds, having, 1);
+                                                 conds, having, true, false);
+      /**
+         @todo Above we passed unique=false. But for this query:
+          (oe1, oe2) IN (SELECT primary_key, non_key_maybe_null_field FROM tbl)
+         we could use "unique=true" for the first index component and let
+         Item_is_not_null_test(non_key_maybe_null_field) handle the second.
+      */
     }
     if (changed)
       DBUG_RETURN(unit->item->change_engine(engine));
@@ -8591,8 +8599,7 @@ static Item *remove_additional_cond(Item
       where     Subquery's WHERE clause
 
   DESCRIPTION
-    For index lookup-based subquery (i.e. one executed with
-    subselect_uniquesubquery_engine or subselect_indexsubquery_engine),
+    For index lookup-based subquery (subselect_indexsubquery_engine),
     check its EXPLAIN output row should contain 
       "Using index" (TAB_INFO_FULL_SCAN_ON_NULL) 
       "Using Where" (TAB_INFO_USING_WHERE)

=== modified file 'sql/sql_resolver.cc'
--- a/sql/sql_resolver.cc	2012-01-10 18:58:10 +0000
+++ b/sql/sql_resolver.cc	2012-02-08 15:25:17 +0000
@@ -377,42 +377,136 @@ err:
 
 
 /**
-  @brief Check if subquery predicate's compared types allow materialization.
+  Check if the subquery predicate can be executed via materialization.
 
-  @param predicate subquery predicate
+  @param predicate IN subquery predicate
+  @param thd       THD
+  @param select_lex SELECT_LEX of the subquery
+  @param outer      Parent SELECT_LEX (outer to subquery)
 
-  @return TRUE if subquery types allow materialization, FALSE otherwise.
-
-  @details
-    This is a temporary fix for BUG#36752.
-    See bug report for description of restrictions we need to put on the
-    compared expressions.
+  @return TRUE if subquery allows materialization, FALSE otherwise.
 */
 
-static 
-bool subquery_types_allow_materialization(Item_in_subselect *predicate)
+static
+bool subquery_allows_materialization(Item_in_subselect *predicate,
+                                     THD *thd,
+                                     SELECT_LEX *select_lex,
+                                     const SELECT_LEX *outer)
 {
-  DBUG_ENTER("subquery_types_allow_materialization");
+  bool has_nullables= false;
+  const uint elements= predicate->unit->first_select()->item_list.elements;
+  DBUG_ENTER("subquery_allows_materialization");
+  DBUG_ASSERT(elements >= 1);
+  DBUG_ASSERT(predicate->left_expr->cols() == elements);
+
+  OPT_TRACE_TRANSFORM(&thd->opt_trace, trace_wrapper, trace_mat,
+                      select_lex->select_number,
+                      "IN (SELECT)", "materialization");
 
-  DBUG_ASSERT(predicate->left_expr->fixed);
-  DBUG_ASSERT(predicate->left_expr->cols() ==
-              predicate->unit->first_select()->item_list.elements);
-
-  List_iterator<Item> it(predicate->unit->first_select()->item_list);
-  uint elements= predicate->unit->first_select()->item_list.elements;
-  
-  for (uint i= 0; i < elements; i++)
-  {
-    Item *inner= it++;
-    if (!types_allow_materialization(predicate->left_expr->element_index(i),
-                                     inner))
-      DBUG_RETURN(FALSE);
-    if (inner->is_blob_field())
-      DBUG_RETURN(FALSE);
+  const char *cause= NULL;
+  if (select_lex->is_part_of_union())
+  {
+    // Subquery must be a single query specification clause (not a UNION)
+    cause= "in UNION";
+  }
+  else if (!select_lex->master_unit()->first_select()->leaf_tables)
+  {
+    // Subquery has no tables, hence no point in materializing.
+    cause= "no inner tables";
+  }
+  else if (!outer->join)
+  {
+    /*
+      Maybe this is a subquery of a single table UPDATE/DELETE (TODO:
+      handle this by switching to multi-table UPDATE/DELETE).
+    */
+    cause= "parent query has no JOIN";
   }
+  else if (!outer->leaf_tables)
+  {
+    /*
+      The upper query is SELECT ... FROM DUAL. We can't do materialization for
+      SELECT .. FROM DUAL because it does not call
+      setup_subquery_materialization(). We could fix it but it's not worth it.
+    */
+    cause= "no tables in outer query";
+  }
+  else if (predicate->is_correlated)
+  {
+    /*
+      Subquery should not be correlated.
+      TODO:
+      This is an overly restrictive condition. It can be extended to:
+         (Subquery is non-correlated ||
+          Subquery is correlated to any query outer to IN predicate ||
+          (Subquery is correlated to the immediate outer query &&
+           Subquery !contains {GROUP BY, ORDER BY [LIMIT],
+           aggregate functions}) && subquery predicate is not under "NOT IN"))
+    */
+    cause= "correlated";
+  }
+  else if (predicate->exec_method !=
+           Item_exists_subselect::EXEC_UNSPECIFIED)
+  {
+    // An execution method was already chosen (by a prepared statement).
+    cause= "already have strategy";
+  }
+  else
+  {
+    /*
+      Check that involved expression types allow materialization.
+      This is a temporary fix for BUG#36752; see bug report for
+      description of restrictions we need to put on the compared expressions.
+    */
+    DBUG_ASSERT(predicate->left_expr->fixed);
+    List_iterator<Item> it(predicate->unit->first_select()->item_list);
 
-  DBUG_PRINT("info",("subquery_types_allow_materialization: ok, allowed"));
-  DBUG_RETURN(TRUE);
+    for (uint i= 0; i < elements; i++)
+    {
+      Item * const inner= it++;
+      Item * const outer= predicate->left_expr->element_index(i);
+      if (!types_allow_materialization(outer, inner))
+      {
+        cause= "type mismatch";
+        break;
+      }
+      if (inner->is_blob_field())                 // 6
+      {
+        cause= "inner blob";
+        break;
+      }
+      has_nullables|= outer->maybe_null | inner->maybe_null;
+    }
+
+    if (!cause)
+    {
+      trace_mat.add("has_nullable_expressions", has_nullables);
+      /*
+        Subquery materialization cannot handle NULLs partial matching
+        properly, yet. If the outer or inner values are NULL, the
+        subselect_hash_sj_engine may reply FALSE when it should reply UNKNOWN.
+        So, we must limit it to those three cases:
+        - when FALSE and UNKNOWN are equivalent answers. I.e. this is a a
+        top-level predicate (this implies it is not negated).
+        - when outer and inner values cannot be NULL.
+        - when there is a single inner column (because for this we have a
+        limited implementation of NULLs partial matching).
+      */
+      const bool is_top_level= predicate->is_top_level_item();
+      trace_mat.add("treat_UNKNOWN_as_FALSE", is_top_level);
+
+      if (!is_top_level && has_nullables && (elements > 1))
+        cause= "cannot_handle_partial_matches";
+      else
+      {
+        trace_mat.add("chosen", true);
+        DBUG_RETURN(TRUE);
+      }
+    }
+  }
+  DBUG_ASSERT(cause != NULL);
+  trace_mat.add("chosen", false).add_alnum("cause", cause);
+  DBUG_RETURN(false);
 }
 
 
@@ -437,13 +531,11 @@ bool subquery_types_allow_materializatio
 
 */
 
-static
-bool resolve_subquery(THD *thd, JOIN *join)
+static bool resolve_subquery(THD *thd, JOIN *join)
 {
   DBUG_ENTER("resolve_subquery");
 
-  enum {SQ_NONE, SQ_SEMIJOIN, SQ_MATERIALIZATION} sq_choice= SQ_NONE;
-  bool types_problem= false; // if types prevented subq materialization
+  bool chose_semijoin= false;
   SELECT_LEX *const select_lex= join->select_lex;
   SELECT_LEX *const outer= select_lex->outer_select();
 
@@ -548,93 +640,37 @@ bool resolve_subquery(THD *thd, JOIN *jo
 
     /* Register the subquery for further processing in flatten_subqueries() */
     outer->join->sj_subselects.push_back(in_exists_predicate);
-    sq_choice= SQ_SEMIJOIN;
+    chose_semijoin= true;
   }
-  else
+
+  if (subq_predicate_substype == Item_subselect::IN_SUBS)
+  {
+    Opt_trace_context * const trace= &join->thd->opt_trace;
+    OPT_TRACE_TRANSFORM(trace, oto0, oto1,
+                        select_lex->select_number, "IN (SELECT)", "semijoin");
+    oto1.add("chosen", chose_semijoin);
+  }
+
+  if (!chose_semijoin &&
+      thd->optimizer_switch_flag(OPTIMIZER_SWITCH_MATERIALIZATION) &&
+      (subq_predicate_substype == Item_subselect::IN_SUBS))
   {
-    DBUG_PRINT("info", ("Subquery can't be converted to semi-join"));
     /*
       Check if the subquery predicate can be executed via materialization.
-      The required conditions are:
-      1. Subquery predicate is an IN/=ANY subquery predicate
-      2. Subquery is a single SELECT (not a UNION)
-      3. Subquery is not a table-less query. In this case there is no
-         point in materializing.
-        3A The upper query is not a confluent SELECT ... FROM DUAL. We
-           can't do materialization for SELECT .. FROM DUAL because it
-           does not call setup_subquery_materialization(). We could make 
-           SELECT ... FROM DUAL call that function but that doesn't seem
-           to be the case that is worth handling.
-      4. Subquery predicate is a top-level predicate
-         (this implies it is not negated)
-         TODO: this is a limitation that should be lifted once we
-         implement correct NULL semantics (WL#3830)
-      5. Subquery is non-correlated
-         TODO:
-         This is an overly restrictive condition. It can be extended to:
-         (Subquery is non-correlated ||
-          Subquery is correlated to any query outer to IN predicate ||
-          (Subquery is correlated to the immediate outer query &&
-           Subquery !contains {GROUP BY, ORDER BY [LIMIT],
-           aggregate functions}) && subquery predicate is not under "NOT IN"))
-      6. No execution method was already chosen (by a prepared statement).
-      7. Involved expression types allow materialization (temporary only)
-
-      (*) The subquery must be part of a SELECT or CREATE SELECT statement. We
-           should relax this and allow it in multi/single-table UPDATE/DELETE,
-           INSERT SELECT (after studying potential problems when the
-           inserts/updates/deletes are done to a table which is itself part of
-           the subquery).
 
       We have to determine whether we will perform subquery materialization
       before calling the IN=>EXISTS transformation, so that we know whether to
       perform the whole transformation or only that part of it which wraps
       Item_in_subselect in an Item_in_optimizer.
     */
-    Item_in_subselect *in_predicate= (Item_in_subselect *)subq_predicate;
-
-    if (thd->optimizer_switch_flag(OPTIMIZER_SWITCH_MATERIALIZATION)  && 
-        subq_predicate_substype == Item_subselect::IN_SUBS &&        // 1
-        !select_lex->is_part_of_union() &&                              // 2
-        select_lex->master_unit()->first_select()->leaf_tables &&       // 3
-        (thd->lex->sql_command == SQLCOM_SELECT ||
-         thd->lex->sql_command == SQLCOM_CREATE_TABLE) &&               // *
-        outer->leaf_tables &&                                           // 3A
-        in_predicate->is_top_level_item() &&                            // 4
-        !in_predicate->is_correlated &&                                 // 5
-        in_predicate->exec_method ==
-        Item_exists_subselect::EXEC_UNSPECIFIED)        // 6
-    {
-      if (subquery_types_allow_materialization(in_predicate))             // 7
-      {
-        in_predicate->exec_method=
-          Item_exists_subselect::EXEC_MATERIALIZATION;
-        sq_choice= SQ_MATERIALIZATION;
-      }
-      else
-        types_problem= true;
-    }
-  }
-
-  Opt_trace_context * const trace= &join->thd->opt_trace;
-  if (subq_predicate_substype == Item_subselect::IN_SUBS)
-  {
-    {
-      OPT_TRACE_TRANSFORM(trace, oto, oto1, select_lex->select_number,
-                          "IN (SELECT)", "semijoin");
-      oto1.add("chosen", sq_choice == SQ_SEMIJOIN);
-    }
-    if (sq_choice != SQ_SEMIJOIN)
-    {
-      OPT_TRACE_TRANSFORM(trace, oto0, oto1, select_lex->select_number,
-                          "IN (SELECT)", "materialization");
-      oto1.add("chosen", sq_choice == SQ_MATERIALIZATION);
-      if (types_problem)
-        oto1.add_alnum("cause", "field_types");
-    }
+    Item_in_subselect *in_predicate= static_cast<Item_in_subselect *>
+      (subq_predicate);
+    if (subquery_allows_materialization(in_predicate, thd, select_lex, outer))
+      in_predicate->exec_method=
+        Item_exists_subselect::EXEC_MATERIALIZATION;
   }
 
-  if (sq_choice != SQ_SEMIJOIN &&
+  if (!chose_semijoin &&
       subq_predicate->select_transformer(join) == Item_subselect::RES_ERROR)
     DBUG_RETURN(TRUE);
 

=== modified file 'sql/sql_select.cc'
--- a/sql/sql_select.cc	2012-02-01 09:59:13 +0000
+++ b/sql/sql_select.cc	2012-02-08 15:25:17 +0000
@@ -1510,7 +1510,10 @@ bool create_ref_for_key(JOIN *join, JOIN
 	instead of JT_REF_OR_NULL in case if field can't be null
       */
       if ((keyuse->optimize & KEY_OPTIMIZE_REF_OR_NULL) && maybe_null)
-	null_ref_key= key_buff;
+      {
+        DBUG_ASSERT(null_ref_key == NULL); // or we would overwrite it below
+        null_ref_key= key_buff;
+      }
       key_buff+=keyinfo->key_part[part_no].store_length;
     }
   } /* not ftkey */

No bundle (reason: useless for push emails).
Thread
bzr push into mysql-trunk branch (guilhem.bichot:3870) Guilhem Bichot10 Feb