List:Commits« Previous MessageNext Message »
From:Roy Lyseng Date:February 9 2012 2:51pm
Subject:bzr push into mysql-trunk branch (roy.lyseng:3864 to 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
 3869 Guilhem Bichot	2012-02-09 [merge]
      merge with trunk

    modified:
      mysql-test/include/icp_tests.inc
      mysql-test/include/join_cache.inc
      mysql-test/include/subquery.inc
      mysql-test/include/subquery_mat.inc
      mysql-test/r/innodb_icp.result
      mysql-test/r/innodb_icp_all.result
      mysql-test/r/innodb_icp_none.result
      mysql-test/r/join_cache_bka.result
      mysql-test/r/join_cache_bka_nixbnl.result
      mysql-test/r/join_cache_bkaunique.result
      mysql-test/r/join_cache_bnl.result
      mysql-test/r/join_cache_nojb.result
      mysql-test/r/myisam_icp.result
      mysql-test/r/myisam_icp_all.result
      mysql-test/r/myisam_icp_none.result
      mysql-test/r/subquery_all.result
      mysql-test/r/subquery_all_bka.result
      mysql-test/r/subquery_all_bka_nixbnl.result
      mysql-test/r/subquery_mat.result
      mysql-test/r/subquery_mat_all.result
      mysql-test/r/subquery_mat_none.result
      mysql-test/r/subquery_nomat_nosj.result
      mysql-test/r/subquery_nomat_nosj_bka.result
      mysql-test/r/subquery_nomat_nosj_bka_nixbnl.result
      mysql-test/r/subquery_none.result
      mysql-test/r/subquery_none_bka.result
      mysql-test/r/subquery_none_bka_nixbnl.result
      mysql-test/r/subquery_sj_all.result
      mysql-test/r/subquery_sj_all_bka.result
      mysql-test/r/subquery_sj_all_bka_nixbnl.result
      mysql-test/r/subquery_sj_all_bkaunique.result
      mysql-test/r/subquery_sj_mat.result
      mysql-test/r/subquery_sj_mat_bka.result
      mysql-test/r/subquery_sj_mat_bka_nixbnl.result
      mysql-test/r/subquery_sj_mat_bkaunique.result
      mysql-test/r/subquery_sj_mat_nosj.result
      mysql-test/suite/innodb/r/innodb_mysql.result
      mysql-test/suite/opt_trace/include/general.inc
      mysql-test/suite/opt_trace/r/bugs_no_prot_all.result
      mysql-test/suite/opt_trace/r/bugs_no_prot_none.result
      mysql-test/suite/opt_trace/r/bugs_ps_prot_all.result
      mysql-test/suite/opt_trace/r/bugs_ps_prot_none.result
      mysql-test/suite/opt_trace/r/general2_no_prot.result
      mysql-test/suite/opt_trace/r/general2_ps_prot.result
      mysql-test/suite/opt_trace/r/general_no_prot_all.result
      mysql-test/suite/opt_trace/r/general_no_prot_none.result
      mysql-test/suite/opt_trace/r/general_ps_prot_all.result
      mysql-test/suite/opt_trace/r/general_ps_prot_none.result
      mysql-test/suite/opt_trace/r/subquery_no_prot.result
      mysql-test/suite/opt_trace/r/subquery_ps_prot.result
      sql/item_cmpfunc.cc
      sql/item_cmpfunc.h
      sql/item_subselect.cc
      sql/item_subselect.h
      sql/sql_class.cc
      sql/sql_optimizer.cc
      sql/sql_resolver.cc
      sql/sql_select.cc
 3868 Venkata Sidagam	2012-02-09
      Bug #11755870-47704: HASH INDEX ON VARCHAR PREFIX NOT WORKING CORRECTLY.
      
      Brief description: Insert some rows to MEMORY table with HASH key on varchar field.
      A select on specific existing tuple is showing empty set.
      
      Problem Analysis/solution: In hp_key_cmp() when the seg->type == HA_KEYTYPE_VARTEXT1
      the char_length_rec is the length of pos string and it is compared with key string for
      char_length_key bytes. Here both char_length_rec and char_length_key will be of different
      values when we have hash index on column prefix. So the comparition is failing and 
      the result set is empty.
      
      So the fix is, if char_length_rec has more value than the char_length_key make 
      char_length_rec as same as char_length_key
     @ storage/heap/hp_hash.c
        Modified hp_key_cmp() function

    modified:
      mysql-test/r/heap.result
      mysql-test/t/heap.test
      storage/heap/hp_hash.c
 3867 Dmitry Shulga	2012-02-09
      Patch for bug#11764747 (formerly known as 57612): SET GLOBAL READ_ONLY=1 cannot
      progress when a table is locked with LOCK TABLES.
      
      The reason for the bug was that mysql server makes a flush of all open tables
      during handling of statement 'SET GLOBAL READ_ONLY=1'. Therefore if some of
      these tables were locked by "LOCK TABLE ... READ" from a different connection,
      then execution of statement 'SET GLOBAL READ_ONLY=1' would be waiting for
      the lock for such table even if the table was locked in a compatible read mode.
      
      Flushing of all open tables before setting of read_only system variable
      is inherited from 5.1 implementation since this was the only possible approach
      to ensure that there isn't any pending write operations on open tables.
      
      Start from version 5.5 and above such behaviour is guaranteed by the fact
      that we acquire global_read_lock before setting read_only flag. Since
      acquiring of global_read_lock is successful only when there isn't any 
      active write operation then we can remove flushing of open tables from
      processing of SET GLOBAL READ_ONLY=1.
      
      This modification changes the server behavior so that read locks held
      by other connections (LOCK TABLE ... READ) no longer will block attempts
      to enable read_only.
     @ mysql-test/t/read_only.test
        Modified testcase in order to takes into account the changed behaviour
        in processing of 'set read_only' statement.
     @ sql/sys_vars.cc
        Removed flushing of open tables from processing of
        SET GLOBAL READ_ONLY=1.

    modified:
      mysql-test/r/read_only.result
      mysql-test/t/read_only.test
      sql/sys_vars.cc
 3866 Jorgen Loland	2012-02-09
      Bug#13354910 - MAKE_JOIN_STATISTICS() INVOKES 
                     RECORDS_IN_RANGE(MIN_KEY=0X0, MAX_KEY=0X0)
      
      Some range predicates that by them selves make up partial 
      ranges are merged into a full range by the range optimizer. 
      An example is the query for this bug:
      
        "WHERE col IN (1,2,6) OR col NOT IN (1)"
      
      This predicate is always true and is correctly merged into
      the range [-inf <= col <= +inf]. However, although the range
      optimizer did make a full range, it did not realize that it's
      meaningless to use this as a range predicate since that will 
      be the same as doing an index scan.
      
      For the query in the bug the problem was that 
      SEL_ARG::copy_max() had a bug: When checking if the range was
      a full range, it was checked if max_flag was set to 
      (NO_MAX_RANGE | NO_MIN_RANGE). But the NO_MIN_FLAG is stored
      in min_flag, so the test is changed to 
      (max_flag & NO_MAX_FLAG) and (min_flag & NO_MIN_FLAG).
      
      Enabling the DBUG_ASSERT(max_key || min_key) in 
      ha_innobase::records_in_range() then showed another related 
      bug: innodb_icp started failing because SEL_ARG::copy_min()
      had the very same bug as copy_max(). This was fixed as well.
      
      In an attempt at unittesting SEL_ARG, copy_min() and copy_max()
      have now been added to the opt_range-t gunit test.
      
      To see the assert, add "DBUG_ASSERT(min_key || max_key);"
      to the beginning of ha_innobase::records_in_range().
     @ mysql-test/include/index_merge1.inc
        Added a new test to test the intended behavior of a
        query that changed due to the bugfix.
     @ mysql-test/r/index_merge_myisam.result
        Updated result file because an invalid execution plan changed.
        Also added a new test to test the intended behavior of the
        query that changed.
     @ sql/opt_range.cc
        SEL_ARG::copy_min() and copy_max() now checks if the NO_MIN_RANGE
        flag is set in min_flag and the NO_MAX_FLAG in max_flag.
     @ unittest/gunit/opt_range-t.cc
        Add tests for SEL_ARG::copy_min() and SEL_ARG::copy_max().

    modified:
      mysql-test/include/index_merge1.inc
      mysql-test/r/index_merge_myisam.result
      sql/opt_range.cc
      unittest/gunit/opt_range-t.cc
 3865 Guilhem Bichot	2012-02-08
      Fix for
      Bug #13688248 	CRASH IN DIAGNOSTICS_AREA::SET_OK_STATUS WHEN USING DEBUG_SYNC.
      And, to test the above, I had to fix
      Bug #13688015 	THE OPTION --LOOSE-DEBUG-SYNC-TIMEOUT IS PLACED WRONG
     @ mysql-test/mysql-test-run.pl
        Options specified in .opt files should be added last so they can
        override defaults of mtr (fixes Bug#13688015)
     @ mysql-test/t/debug_sync2.test
        test for bug, used to assert
     @ sql/debug_sync.cc
        In the scenario of debug_sync2.test, debug sync point timed out;
        thus a warning was raised, but because sql_mode=traditional,
        push_warning() changed it to an error. But code in mysql_insert()
        (container of the debug sync point) didn't handle this error,
        finally an OK was sent, which triggered an assertion (you can't
        have an OK *and* an error at the same time).
        Fix: warnings of debug_sync should never be errors (sql_mode=traditional
        is, per the doc, for *bad data* errors; a debug sync timeout
        will not cause bad data; and debug_sync is only meant for our QA and devs,
        and we only ship it in debug builds).
        So: disable abort_on_warning.

    added:
      mysql-test/r/debug_sync2.result
      mysql-test/t/debug_sync2-master.opt
      mysql-test/t/debug_sync2.test
    modified:
      mysql-test/mysql-test-run.pl
      sql/debug_sync.cc
 3864 Jorgen Loland	2012-02-09
      BUG#13354910 - MAKE_JOIN_STATISTICS() INVOKES 
                     RECORDS_IN_RANGE(MIN_KEY=0X0, MAX_KEY=0X0)
      
      Preparation patch: improve readability of key_or() by using more
      descriptive variable names.
     @ sql/opt_range.cc
        Improve readability of key_or() by using more descriptive
        variable names.

    modified:
      sql/opt_range.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/index_merge1.inc'
--- a/mysql-test/include/index_merge1.inc	2011-11-03 07:01:49 +0000
+++ b/mysql-test/include/index_merge1.inc	2012-02-09 11:22:17 +0000
@@ -167,8 +167,14 @@ explain select * from t0 where
 explain select * from t0 where
     ((key3 <5 or key5 < 4) and (key1 < 4 or key2 < 4))
   or
-    ((key3 >=5 or key5 < 2) and (key5 < 5 or key6 < 6));
+    ((key3 >5 or key5 < 2) and (key5 < 5 or key6 < 6));
 
+explain select * from t0 force index(i1, i2, i3, i4, i5, i6 ) where
+    ((key3 <5 or key5 < 4) and (key1 < 4 or key2 < 4))
+  or
+    ((key3 >5 or key5 < 2) and (key5 < 5 or key6 < 6));
+
+# Can't merge any indexes here (predicate on key3 is always true)
 explain select * from t0 force index(i1, i2, i3, i4, i5, i6 ) where
     ((key3 <5 or key5 < 4) and (key1 < 4 or key2 < 4))
   or

=== 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
@@ -1021,5 +1020,327 @@ eval $query;
 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/mysql-test-run.pl'
--- a/mysql-test/mysql-test-run.pl	2012-01-31 16:18:20 +0000
+++ b/mysql-test/mysql-test-run.pl	2012-02-08 20:52:22 +0000
@@ -4865,6 +4865,14 @@ sub mysqld_arguments ($$$) {
     mtr_add_arg($args, "--gdb");
   }
 
+  # Enable the debug sync facility, set default wait timeout.
+  # Facility stays disabled if timeout value is zero.
+  mtr_add_arg($args, "--loose-debug-sync-timeout=%s",
+              $opt_debug_sync_timeout) unless $opt_user_args;
+
+  # Options specified in .opt files should be added last so they can
+  # override defaults above.
+
   my $found_skip_core= 0;
   foreach my $arg ( @$extra_opts )
   {
@@ -4900,11 +4908,6 @@ sub mysqld_arguments ($$$) {
   {
     mtr_add_arg($args, "%s", "--core-file");
   }
-
-  # Enable the debug sync facility, set default wait timeout.
-  # Facility stays disabled if timeout value is zero.
-  mtr_add_arg($args, "--loose-debug-sync-timeout=%s",
-              $opt_debug_sync_timeout) unless $opt_user_args;
 
   return $args;
 }

=== added file 'mysql-test/r/debug_sync2.result'
--- a/mysql-test/r/debug_sync2.result	1970-01-01 00:00:00 +0000
+++ b/mysql-test/r/debug_sync2.result	2012-02-08 20:52:22 +0000
@@ -0,0 +1,13 @@
+#
+# Bug#13688248 CRASH IN DIAGNOSTICS_AREA::SET_OK_STATUS WHEN USING DEBUG_SYNC
+#
+SET SESSION DEBUG_SYNC= 'RESET';
+CREATE TABLE t1 (pk INT, PRIMARY KEY(pk));
+SET SESSION sql_mode=TRADITIONAL;
+SET SESSION autocommit=1;
+INSERT INTO t1 VALUES(1);
+SET SESSION debug_sync='write_row_replace SIGNAL go_ahead1 WAIT_FOR comes_never ';
+REPLACE INTO t1 ( pk ) VALUES ( 1 );
+Warnings:
+Warning	1639	debug sync point wait timed out
+DROP TABLE t1;

=== modified file 'mysql-test/r/events_restart.result'
--- a/mysql-test/r/events_restart.result	2011-05-04 12:59:24 +0000
+++ b/mysql-test/r/events_restart.result	2012-02-09 14:56:44 +0000
@@ -65,3 +65,26 @@ select @@event_scheduler;
 ON
 drop table execution_log;
 drop database events_test;
+#
+# Test for bug#11748899 -- EVENT SET TO DISABLED AND ON COMPLETION
+#                          NOT PRESERVE IS DELETED AT SERVER
+#
+SELECT @@event_scheduler;
+@@event_scheduler
+ON
+USE test;
+DROP EVENT IF EXISTS e1;
+CREATE EVENT e1 ON SCHEDULE EVERY 1 SECOND DISABLE DO SELECT 1;
+SHOW EVENTS;
+Db	Name	Definer	Time zone	Type	Execute at	Interval value	Interval field	Starts	Ends	Status	Originator	character_set_client	collation_connection	Database Collation
+test	e1	root@localhost	SYSTEM	RECURRING	#	1	SECOND	#	#	DISABLED	1	latin1	latin1_swedish_ci	latin1_swedish_ci
+"Now we restart the server"
+USE test;
+SELECT @@event_scheduler;
+@@event_scheduler
+ON
+SHOW EVENTS;
+Db	Name	Definer	Time zone	Type	Execute at	Interval value	Interval field	Starts	Ends	Status	Originator	character_set_client	collation_connection	Database Collation
+test	e1	root@localhost	SYSTEM	RECURRING	#	1	SECOND	#	#	DISABLED	1	latin1	latin1_swedish_ci	latin1_swedish_ci
+DROP EVENT e1;
+# end test for bug#11748899

=== modified file 'mysql-test/r/heap.result'
--- a/mysql-test/r/heap.result	2011-09-27 12:17:43 +0000
+++ b/mysql-test/r/heap.result	2012-02-09 12:17:37 +0000
@@ -775,3 +775,17 @@ id	select_type	table	type	possible_keys	
 3	DERIVED	t1	range	PRIMARY	PRIMARY	4	NULL	1	Using index condition; Using where
 DROP TABLE t1,t2,h1;
 DROP VIEW v1;
+CREATE TABLE t1 (
+c1 VARCHAR(10) NOT NULL,
+KEY i1 (c1(3))
+) ENGINE=MEMORY DEFAULT CHARSET=latin1;
+INSERT INTO t1 VALUES ('foo1'), ('bar2'), ('baz3');
+SELECT * FROM t1 WHERE c1='bar2';
+c1
+bar2
+#should show one tuple!
+SELECT * FROM t1 IGNORE INDEX (i1) WHERE c1='bar2';
+c1
+bar2
+#should show one tuple!
+DROP TABLE t1;

=== modified file 'mysql-test/r/index_merge_myisam.result'
--- a/mysql-test/r/index_merge_myisam.result	2012-01-30 13:13:15 +0000
+++ b/mysql-test/r/index_merge_myisam.result	2012-02-09 11:22:17 +0000
@@ -170,15 +170,21 @@ id	select_type	table	type	possible_keys	
 explain select * from t0 where
 ((key3 <5 or key5 < 4) and (key1 < 4 or key2 < 4))
 or
-((key3 >=5 or key5 < 2) and (key5 < 5 or key6 < 6));
+((key3 >5 or key5 < 2) and (key5 < 5 or key6 < 6));
 id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
 1	SIMPLE	t0	ALL	i1,i2,i3,i5,i6	NULL	NULL	NULL	1024	Using where
 explain select * from t0 force index(i1, i2, i3, i4, i5, i6 ) where
 ((key3 <5 or key5 < 4) and (key1 < 4 or key2 < 4))
 or
+((key3 >5 or key5 < 2) and (key5 < 5 or key6 < 6));
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	SIMPLE	t0	index_merge	i1,i2,i3,i5,i6	i3,i5	4,4	NULL	1024	Using sort_union(i3,i5); Using where
+explain select * from t0 force index(i1, i2, i3, i4, i5, i6 ) where
+((key3 <5 or key5 < 4) and (key1 < 4 or key2 < 4))
+or
 ((key3 >=5 or key5 < 2) and (key5 < 5 or key6 < 6));
 id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
-1	SIMPLE	t0	index_merge	i1,i2,i3,i5,i6	i3,i5	0,4	NULL	1024	Using sort_union(i3,i5); Using where
+1	SIMPLE	t0	ALL	i1,i2,i3,i5,i6	NULL	NULL	NULL	1024	Using where
 select * from t0 where key1 < 5 or key8 < 4 order by key1;
 key1	key2	key3	key4	key5	key6	key7	key8
 1	1	1	1	1	1	1	1023

=== 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/read_only.result'
--- a/mysql-test/r/read_only.result	2010-08-30 06:38:09 +0000
+++ b/mysql-test/r/read_only.result	2012-02-09 12:57:42 +0000
@@ -75,18 +75,16 @@ connection default;
 set global read_only=1;
 ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction
 unlock tables ;
-send set global read_only=1;
 set global read_only=1;
-connection con1;
 select @@global.read_only;
 @@global.read_only
-0
-unlock tables ;
+1
+connection con1;
 select @@global.read_only;
 @@global.read_only
 1
+unlock tables ;
 connection default;
-reap;
 connection default;
 set global read_only=0;
 BEGIN;

=== 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": [
@@ -816,14 +792,6 @@ ON table2 .col_int_key = table1 .col_int
                     "select#": 3,
                     "from": "IN (SELECT)",
                     "to": "semijoin",
-                    "chosen": false
-                  } /* transformation */
-                },
-                {
-                  "transformation": {
-                    "select#": 3,
-                    "from": "IN (SELECT)",
-                    "to": "materialization",
                     "chosen": false
                   } /* transformation */
                 },

=== 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 */
@@ -806,14 +782,6 @@ ON table2 .col_int_key = table1 .col_int
                     "select#": 3,
                     "from": "IN (SELECT)",
                     "to": "semijoin",
-                    "chosen": false
-                  } /* transformation */
-                },
-                {
-                  "transformation": {
-                    "select#": 3,
-                    "from": "IN (SELECT)",
-                    "to": "materialization",
                     "chosen": false
                   } /* transformation */
                 }

=== 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 */
                 },
                 {
@@ -2646,14 +2649,6 @@ from t0 where a in
                     "select#": 2,
                     "from": "IN (SELECT)",
                     "to": "semijoin",
-                    "chosen": false
-                  } /* transformation */
-                },
-                {
-                  "transformation": {
-                    "select#": 2,
-                    "from": "IN (SELECT)",
-                    "to": "materialization",
                     "chosen": false
                   } /* transformation */
                 },

=== 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 */
@@ -2699,14 +2700,6 @@ from t0 where a in
                     "select#": 2,
                     "from": "IN (SELECT)",
                     "to": "semijoin",
-                    "chosen": false
-                  } /* transformation */
-                },
-                {
-                  "transformation": {
-                    "select#": 2,
-                    "from": "IN (SELECT)",
-                    "to": "materialization",
                     "chosen": false
                   } /* transformation */
                 },

=== 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": [
@@ -9177,14 +9049,6 @@ select d into res from t6 where d in (se
                     "select#": 2,
                     "from": "IN (SELECT)",
                     "to": "semijoin",
-                    "chosen": false
-                  } /* transformation */
-                },
-                {
-                  "transformation": {
-                    "select#": 2,
-                    "from": "IN (SELECT)",
-                    "to": "materialization",
                     "chosen": false
                   } /* transformation */
                 },

=== 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": [
@@ -9111,14 +8983,6 @@ select d into res from t6 where d in (se
                     "select#": 2,
                     "from": "IN (SELECT)",
                     "to": "semijoin",
-                    "chosen": false
-                  } /* transformation */
-                },
-                {
-                  "transformation": {
-                    "select#": 2,
-                    "from": "IN (SELECT)",
-                    "to": "materialization",
                     "chosen": false
                   } /* transformation */
                 },

=== 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 */

=== added file 'mysql-test/t/debug_sync2-master.opt'
--- a/mysql-test/t/debug_sync2-master.opt	1970-01-01 00:00:00 +0000
+++ b/mysql-test/t/debug_sync2-master.opt	2012-02-08 20:52:22 +0000
@@ -0,0 +1 @@
+--loose-debug-sync-timeout=1

=== added file 'mysql-test/t/debug_sync2.test'
--- a/mysql-test/t/debug_sync2.test	1970-01-01 00:00:00 +0000
+++ b/mysql-test/t/debug_sync2.test	2012-02-08 20:52:22 +0000
@@ -0,0 +1,25 @@
+--source include/have_innodb.inc
+--source include/have_debug_sync.inc
+
+--echo #
+--echo # Bug#13688248 CRASH IN DIAGNOSTICS_AREA::SET_OK_STATUS WHEN USING DEBUG_SYNC
+--echo #
+
+SET SESSION DEBUG_SYNC= 'RESET';
+
+CREATE TABLE t1 (pk INT, PRIMARY KEY(pk));
+
+connect (con1,localhost,root,,);
+connection con1;
+SET SESSION sql_mode=TRADITIONAL;
+SET SESSION autocommit=1;
+
+INSERT INTO t1 VALUES(1);
+
+connection con1;
+SET SESSION debug_sync='write_row_replace SIGNAL go_ahead1 WAIT_FOR comes_never ';
+--send
+REPLACE INTO t1 ( pk ) VALUES ( 1 );
+--reap;
+
+DROP TABLE t1;

=== modified file 'mysql-test/t/events_restart.test'
--- a/mysql-test/t/events_restart.test	2011-05-04 12:59:24 +0000
+++ b/mysql-test/t/events_restart.test	2012-02-09 14:56:44 +0000
@@ -106,3 +106,26 @@ let $wait_condition=
   select count(*) = 0 from information_schema.processlist
   where db='events_test' and command = 'Connect' and user=current_user();
 --source include/wait_condition.inc
+
+--echo #
+--echo # Test for bug#11748899 -- EVENT SET TO DISABLED AND ON COMPLETION
+--echo #                          NOT PRESERVE IS DELETED AT SERVER
+--echo #
+SELECT @@event_scheduler;
+USE test;
+--disable_warnings
+DROP EVENT IF EXISTS e1;
+--enable_warnings
+CREATE EVENT e1 ON SCHEDULE EVERY 1 SECOND DISABLE DO SELECT 1; 
+--replace_column 6 # 9 # 10 #
+SHOW EVENTS;
+
+--echo "Now we restart the server"
+--source include/restart_mysqld.inc
+USE test;
+SELECT @@event_scheduler;
+--replace_column 6 # 9 # 10 #
+SHOW EVENTS;
+DROP EVENT e1;
+
+--echo # end test for bug#11748899

=== modified file 'mysql-test/t/heap.test'
--- a/mysql-test/t/heap.test	2011-09-27 12:17:43 +0000
+++ b/mysql-test/t/heap.test	2012-02-09 12:17:37 +0000
@@ -516,3 +516,19 @@ WHERE ('h', 0) NOT IN ( SELECT * FROM v1
 
 DROP TABLE t1,t2,h1;
 DROP VIEW v1;
+
+#
+# BUG 11755870 - 47704: HASH INDEX ON VARCHAR PREFIX NOT WORKING CORRECTLY.
+#
+CREATE TABLE t1 (
+    c1 VARCHAR(10) NOT NULL,
+    KEY i1 (c1(3))
+) ENGINE=MEMORY DEFAULT CHARSET=latin1;
+INSERT INTO t1 VALUES ('foo1'), ('bar2'), ('baz3');
+
+SELECT * FROM t1 WHERE c1='bar2';
+--echo #should show one tuple!
+
+SELECT * FROM t1 IGNORE INDEX (i1) WHERE c1='bar2';
+--echo #should show one tuple!
+DROP TABLE t1;

=== modified file 'mysql-test/t/read_only.test'
--- a/mysql-test/t/read_only.test	2009-03-06 14:56:17 +0000
+++ b/mysql-test/t/read_only.test	2012-02-09 12:57:42 +0000
@@ -169,23 +169,19 @@ connection default;
 --error ER_LOCK_OR_ACTIVE_TRANSACTION
 set global read_only=1;
 unlock tables ;
-# The following call blocks until con1 releases the read lock.
-# Blocking is a limitation, and could be improved.
---echo send set global read_only=1;
-send set global read_only=1;
+
+# after unlock tables in current connection
+# the next command must be executed successfully
+set global read_only=1;
+select @@global.read_only;
 
 --echo connection con1;
 connection con1;
 select @@global.read_only;
 unlock tables ;
-let $wait_condition= SELECT @@global.read_only= 1;
---source include/wait_condition.inc
-select @@global.read_only;
 
 --echo connection default;
 connection default;
---echo reap;
-reap;
 
 # pending transaction / READ_ONLY
 # - is an error in the same connection

=== modified file 'sql/debug_sync.cc'
--- a/sql/debug_sync.cc	2012-01-24 11:24:54 +0000
+++ b/sql/debug_sync.cc	2012-02-08 20:52:22 +0000
@@ -1788,8 +1788,12 @@ static void debug_sync_execute(THD *thd,
                         sig_wait, sig_glob, error));});
         if (error == ETIMEDOUT || error == ETIME)
         {
+          // We should not make the statement fail, even if in strict mode.
+          const bool save_abort_on_warning= thd->abort_on_warning;
+          thd->abort_on_warning= false;
           push_warning(thd, Sql_condition::WARN_LEVEL_WARN,
                        ER_DEBUG_SYNC_TIMEOUT, ER(ER_DEBUG_SYNC_TIMEOUT));
+          thd->abort_on_warning= save_abort_on_warning;
           break;
         }
         error= 0;

=== modified file 'sql/events.cc'
--- a/sql/events.cc	2011-10-27 08:43:56 +0000
+++ b/sql/events.cc	2012-02-09 14:56:44 +0000
@@ -825,7 +825,16 @@ Events::init(my_bool opt_noacl_or_bootst
   */
   thd->thread_stack= (char*) &thd;
   thd->store_globals();
-
+  /*
+    Set current time for the thread that handles events.
+    Current time is stored in data member start_time of THD class.
+    Subsequently, this value is used to check whether event was expired
+    when make loading events from storage. Check for event expiration time
+    is done at Event_queue_element::compute_next_execution_time() where
+    event's status set to Event_parse_data::DISABLED and dropped flag set
+    to true if event was expired.
+  */
+  thd->set_time();
   /*
     We will need Event_db_repository anyway, even if the scheduler is
     disabled - to perform events DDL.
@@ -1109,7 +1118,6 @@ Events::load_events_from_db(THD *thd)
   {
     Event_queue_element *et;
     bool created;
-    bool drop_on_completion;
 
     if (!(et= new Event_queue_element))
       goto end;
@@ -1124,9 +1132,6 @@ Events::load_events_from_db(THD *thd)
       delete et;
       goto end;
     }
-    drop_on_completion= (et->on_completion ==
-                         Event_parse_data::ON_COMPLETION_DROP);
-
 
     if (event_queue->create_event(thd, et, &created))
     {
@@ -1136,7 +1141,7 @@ Events::load_events_from_db(THD *thd)
     }
     if (created)
       count++;
-    else if (drop_on_completion)
+    else if (et->dropped)
     {
       /*
         If not created, a stale event - drop if immediately if

=== 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/opt_range.cc'
--- a/sql/opt_range.cc	2012-02-09 09:09:58 +0000
+++ b/sql/opt_range.cc	2012-02-09 11:22:17 +0000
@@ -483,8 +483,7 @@ public:
     if (cmp_min_to_min(arg) > 0)
     {
       min_value=arg->min_value; min_flag=arg->min_flag;
-      if ((max_flag & (NO_MAX_RANGE | NO_MIN_RANGE)) ==
-	  (NO_MAX_RANGE | NO_MIN_RANGE))
+      if ((max_flag & NO_MAX_RANGE) && (min_flag & NO_MIN_RANGE))
 	return 1;				// Full range
     }
     maybe_flag|=arg->maybe_flag;
@@ -495,8 +494,7 @@ public:
     if (cmp_max_to_max(arg) <= 0)
     {
       max_value=arg->max_value; max_flag=arg->max_flag;
-      if ((max_flag & (NO_MAX_RANGE | NO_MIN_RANGE)) ==
-	  (NO_MAX_RANGE | NO_MIN_RANGE))
+      if ((max_flag & NO_MAX_RANGE) && (min_flag & NO_MIN_RANGE))
 	return 1;				// Full range
     }
     maybe_flag|=arg->maybe_flag;
@@ -7475,7 +7473,16 @@ key_or(RANGE_OPT_PARAM *param, SEL_ARG *
           cur_key2->next= next_key2;                 // New copy of cur_key2
         }
 
-        cur_key2->copy_min(cur_key1);
+        if (cur_key2->copy_min(cur_key1))
+        {
+          // cur_key2 is full range: [-inf <= cur_key2 <= +inf]
+          key1->free_tree();
+          key2->free_tree();
+          if (key1->maybe_flag)
+            return new SEL_ARG(SEL_ARG::MAYBE_KEY);
+          return 0;
+        }
+
         if (!(key1= key1->tree_delete(cur_key1)))
         {
           /*

=== 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 */

=== modified file 'sql/sys_vars.cc'
--- a/sql/sys_vars.cc	2012-02-02 16:56:36 +0000
+++ b/sql/sys_vars.cc	2012-02-09 12:57:42 +0000
@@ -2033,14 +2033,15 @@ static bool fix_read_only(sys_var *self,
   }
 
   /*
-    Perform a 'FLUSH TABLES WITH READ LOCK'.
-    This is a 3 step process:
-    - [1] lock_global_read_lock()
-    - [2] close_cached_tables()
-    - [3] make_global_read_lock_block_commit()
-    [1] prevents new connections from obtaining tables locked for write.
-    [2] waits until all existing connections close their tables.
-    [3] prevents transactions from being committed.
+    READ_ONLY=1 prevents write locks from being taken on tables and
+    blocks transactions from committing. We therefore should make sure
+    that no such events occur while setting the read_only variable.
+    This is a 2 step process:
+    [1] lock_global_read_lock()
+      Prevents connections from obtaining new write locks on
+      tables. Note that we can still have active rw transactions.
+    [2] make_global_read_lock_block_commit()
+      Prevents transactions from committing.
   */
 
   read_only= opt_readonly;
@@ -2048,19 +2049,6 @@ static bool fix_read_only(sys_var *self,
 
   if (thd->global_read_lock.lock_global_read_lock(thd))
     goto end_with_mutex_unlock;
-
-  /*
-    This call will be blocked by any connection holding a READ or WRITE lock.
-    Ideally, we want to wait only for pending WRITE locks, but since:
-    con 1> LOCK TABLE T FOR READ;
-    con 2> LOCK TABLE T FOR WRITE; (blocked by con 1)
-    con 3> SET GLOBAL READ ONLY=1; (blocked by con 2)
-    can cause to wait on a read lock, it's required for the client application
-    to unlock everything, and acceptable for the server to wait on all locks.
-  */
-  if ((result= close_cached_tables(thd, NULL, TRUE,
-                                   thd->variables.lock_wait_timeout)))
-    goto end_with_read_lock;
 
   if ((result= thd->global_read_lock.make_global_read_lock_block_commit(thd)))
     goto end_with_read_lock;

=== modified file 'storage/heap/hp_hash.c'
--- a/storage/heap/hp_hash.c	2011-07-21 12:54:54 +0000
+++ b/storage/heap/hp_hash.c	2012-02-09 12:17:37 +0000
@@ -652,6 +652,10 @@ int hp_key_cmp(HP_KEYDEF *keydef, const 
         char_length2= my_charpos(cs, pos, pos + char_length_rec, char_length2);
         set_if_smaller(char_length_rec, char_length2);
       }
+      else
+      {
+        set_if_smaller(char_length_rec, seg->length);
+      }
 
       if (cs->coll->strnncollsp(seg->charset,
                                 (uchar*) pos, char_length_rec,

=== modified file 'unittest/gunit/opt_range-t.cc'
--- a/unittest/gunit/opt_range-t.cc	2011-12-20 09:51:05 +0000
+++ b/unittest/gunit/opt_range-t.cc	2012-02-09 11:22:17 +0000
@@ -48,6 +48,7 @@ protected:
     initializer.SetUp();
     m_opt_param.thd= thd();
     m_opt_param.mem_root= &m_alloc;
+    m_opt_param.current_table= 1<<0;
     init_sql_alloc(&m_alloc, thd()->variables.range_alloc_block_size, 0);
   }
 
@@ -102,7 +103,7 @@ const SEL_TREE *null_tree= NULL;
 class Mock_field_long : public Field_long
 {
 public:
-  Mock_field_long()
+  Mock_field_long(THD *thd, Item *item)
     : Field_long(0,                             // ptr_arg
                  8,                             // len_arg
                  NULL,                          // null_ptr_arg
@@ -118,30 +119,313 @@ public:
     m_share.db.str= const_cast<char*>(foo);
     m_share.db.length= strlen(m_share.db.str);
 
+    bitmap_init(&share_allset, 0, sizeof(my_bitmap_map), 0);
+    bitmap_set_above(&share_allset, 0, 1); //all bits 1
+    m_share.all_set= share_allset;
+
     memset(&m_table, 0, sizeof(m_table));
     m_table.s= &m_share;
+
+    bitmap_init(&tbl_readset, 0, sizeof(my_bitmap_map), 0);
+    m_table.read_set= &tbl_readset;
+
+    bitmap_init(&tbl_writeset, 0, sizeof(my_bitmap_map), 0);
+    m_table.write_set= &tbl_writeset;
+
+    m_table.map= 1<<0;
+    m_table.in_use= thd;
     this->table_name= &m_table_name;
     this->table= &m_table;
+    this->ptr= (uchar*) alloc_root((thd->mem_root), KEY_LENGTH);
+    if (item)
+      item->save_in_field_no_warnings(this, true);      
   }
+  
+  // #bytes to store the value - see Field_long::key_lenght()
+  static const int KEY_LENGTH= 4;
   const char *m_table_name;
   TABLE_SHARE m_share;
   TABLE       m_table;
+  MY_BITMAP   share_allset;
+  MY_BITMAP   tbl_readset;
+  MY_BITMAP   tbl_writeset;
 };
 
 
+class Debug_sel_arg : public SEL_ARG
+{
+public:
+  Debug_sel_arg(Field *f, const uchar *min_val, const uchar *max_val, 
+               const KEY_PART_INFO *kpi_)
+    : SEL_ARG(f, min_val, max_val), kpi(kpi_)
+  {}
+
+  void print(String *s)
+  {
+    append_range(s, kpi, min_value, max_value, min_flag | max_flag);
+  }
+private:
+  const KEY_PART_INFO * const kpi;
+};
+
 TEST_F(SelArgTest, SimpleCond)
 {
   EXPECT_NE(null_tree, get_mm_tree(&m_opt_param, new Item_int(42)));
 }
 
 
+/*
+  TODO: Here we try to build a range, but a lot of mocking remains
+  before it works as intended. Currently get_mm_tree() returns NULL
+  because m_opt_param.key_parts and m_opt_param.key_parts_end have not
+  been setup. 
+*/
 TEST_F(SelArgTest, EqualCond)
 {
-  Mock_field_long field_long;
-  EXPECT_EQ(null_tree,
-            get_mm_tree(&m_opt_param,
-                        new Item_equal(new Item_int(42),
-                                       new Item_field(&field_long))));
+  Mock_field_long field_long(thd(), NULL);
+  m_opt_param.table= &field_long.m_table;
+  SEL_TREE *tree= get_mm_tree(&m_opt_param,
+                              new Item_equal(new Item_int(42),
+                                             new Item_field(&field_long)));
+  EXPECT_EQ(null_tree, tree);
+}
+
+
+TEST_F(SelArgTest, SelArgOnevalue)
+{
+  Mock_field_long field_long7(thd(), new Item_int(7));
+
+  KEY_PART_INFO kpi;
+  kpi.init_from_field(&field_long7);
+
+  uchar range_val7[field_long7.KEY_LENGTH];
+  field_long7.get_key_image(range_val7, kpi.length, Field::itRAW);
+
+  Debug_sel_arg sel_arg7(&field_long7, range_val7, range_val7, &kpi);
+  String range_string;
+  sel_arg7.print(&range_string);
+  const char expected[]= "7 <= field_name <= 7";
+  EXPECT_STREQ(expected, range_string.c_ptr());
+
+  sel_arg7.min_flag|= NO_MIN_RANGE;
+  range_string.length(0);
+  sel_arg7.print(&range_string);
+  const char expected2[]= "field_name <= 7";
+  EXPECT_STREQ(expected2, range_string.c_ptr());
+
+  sel_arg7.max_flag= NEAR_MAX;
+  range_string.length(0);
+  sel_arg7.print(&range_string);
+  const char expected3[]= "field_name < 7";
+  EXPECT_STREQ(expected3, range_string.c_ptr());
+
+  sel_arg7.min_flag= NEAR_MIN;
+  sel_arg7.max_flag= NO_MAX_RANGE;
+  range_string.length(0);
+  sel_arg7.print(&range_string);
+  const char expected4[]= "7 < field_name";
+  EXPECT_STREQ(expected4, range_string.c_ptr());
+
+  sel_arg7.min_flag= 0;
+  range_string.length(0);
+  sel_arg7.print(&range_string);
+  const char expected5[]= "7 <= field_name";
+  EXPECT_STREQ(expected5, range_string.c_ptr());
+}
+
+
+TEST_F(SelArgTest, SelArgBetween)
+{
+  Mock_field_long field_long3(thd(), new Item_int(3));
+  Mock_field_long field_long5(thd(), new Item_int(5));
+
+  KEY_PART_INFO kpi;
+  kpi.init_from_field(&field_long3);
+
+  uchar range_val3[field_long3.KEY_LENGTH];
+  field_long3.get_key_image(range_val3, kpi.length, Field::itRAW);
+
+  uchar range_val5[field_long5.KEY_LENGTH];
+  field_long5.get_key_image(range_val5, kpi.length, Field::itRAW);
+
+  Debug_sel_arg sel_arg35(&field_long3, range_val3, range_val5, &kpi);
+
+  String range_string;
+  sel_arg35.print(&range_string);
+  const char expected[]= "3 <= field_name <= 5";
+  EXPECT_STREQ(expected, range_string.c_ptr());
+
+  range_string.length(0);
+  sel_arg35.min_flag= NEAR_MIN;
+  sel_arg35.print(&range_string);
+  const char expected2[]= "3 < field_name <= 5";
+  EXPECT_STREQ(expected2, range_string.c_ptr());
+
+  range_string.length(0);
+  sel_arg35.max_flag= NEAR_MAX;
+  sel_arg35.print(&range_string);
+  const char expected3[]= "3 < field_name < 5";
+  EXPECT_STREQ(expected3, range_string.c_ptr());
+
+  range_string.length(0);
+  sel_arg35.min_flag= 0;
+  sel_arg35.print(&range_string);
+  const char expected4[]= "3 <= field_name < 5";
+  EXPECT_STREQ(expected4, range_string.c_ptr());
+
+  range_string.length(0);
+  sel_arg35.min_flag= NO_MIN_RANGE;
+  sel_arg35.max_flag= 0;
+  sel_arg35.print(&range_string);
+  const char expected5[]= "field_name <= 5";
+  EXPECT_STREQ(expected5, range_string.c_ptr());
+
+  range_string.length(0);
+  sel_arg35.min_flag= 0;
+  sel_arg35.max_flag= NO_MAX_RANGE;
+  sel_arg35.print(&range_string);
+  const char expected6[]= "3 <= field_name";
+  EXPECT_STREQ(expected6, range_string.c_ptr());
+}
+
+TEST_F(SelArgTest, CopyMax)
+{
+  Mock_field_long field_long3(thd(), new Item_int(3));
+  Mock_field_long field_long5(thd(), new Item_int(5));
+
+  KEY_PART_INFO kpi;
+  kpi.init_from_field(&field_long3);
+
+  uchar range_val3[field_long3.KEY_LENGTH];
+  field_long3.get_key_image(range_val3, kpi.length, Field::itRAW);
+
+  uchar range_val5[field_long5.KEY_LENGTH];
+  field_long5.get_key_image(range_val5, kpi.length, Field::itRAW);
+
+  Debug_sel_arg sel_arg3(&field_long3, range_val3, range_val3, &kpi);
+  sel_arg3.min_flag= NO_MIN_RANGE;
+  Debug_sel_arg sel_arg5(&field_long5, range_val5, range_val5, &kpi);
+  sel_arg5.min_flag= NO_MIN_RANGE;
+
+  String range_string;
+  sel_arg3.print(&range_string);
+  const char expected[]= "field_name <= 3";
+  EXPECT_STREQ(expected, range_string.c_ptr());
+
+  range_string.length(0);
+  sel_arg5.print(&range_string);
+  const char expected2[]= "field_name <= 5";
+  EXPECT_STREQ(expected2, range_string.c_ptr());
+
+  /*
+    Ranges now:
+                       -inf ----------------3-5----------- +inf
+    sel_arg3:          [-------------------->
+    sel_arg5:          [---------------------->
+    Below: merge these two ranges into sel_arg3 using copy_max()
+  */
+  bool full_range= sel_arg3.copy_max(&sel_arg5);
+  // The merged range does not cover all possible values
+  EXPECT_FALSE(full_range);
+
+  range_string.length(0);
+  sel_arg3.print(&range_string);
+  const char expected3[]= "field_name <= 5";
+  EXPECT_STREQ(expected3, range_string.c_ptr());
+
+  range_string.length(0);
+  sel_arg5.min_flag= 0;
+  sel_arg5.max_flag= NO_MAX_RANGE;
+  sel_arg5.print(&range_string);
+  const char expected4[]= "5 <= field_name";
+  EXPECT_STREQ(expected4, range_string.c_ptr());
+
+  /*
+    Ranges now:
+                       -inf ----------------3-5----------- +inf
+    sel_arg3:          [---------------------->
+    sel_arg5:                                 <---------------]
+    Below: merge these two ranges into sel_arg3 using copy_max()
+  */
+
+  full_range= sel_arg3.copy_max(&sel_arg5);
+  // The new range covers all possible values
+  EXPECT_TRUE(full_range);
+
+  range_string.length(0);
+  sel_arg3.print(&range_string);
+  const char expected5[]= "field_name";
+  EXPECT_STREQ(expected5, range_string.c_ptr());
+}
+
+TEST_F(SelArgTest, CopyMin)
+{
+  Mock_field_long field_long3(thd(), new Item_int(3));
+  Mock_field_long field_long5(thd(), new Item_int(5));
+
+  KEY_PART_INFO kpi;
+  kpi.init_from_field(&field_long3);
+
+  uchar range_val3[field_long3.KEY_LENGTH];
+  field_long3.get_key_image(range_val3, kpi.length, Field::itRAW);
+
+  uchar range_val5[field_long5.KEY_LENGTH];
+  field_long5.get_key_image(range_val5, kpi.length, Field::itRAW);
+
+  Debug_sel_arg sel_arg3(&field_long3, range_val3, range_val3, &kpi);
+  sel_arg3.max_flag= NO_MAX_RANGE;
+  Debug_sel_arg sel_arg5(&field_long5, range_val5, range_val5, &kpi);
+  sel_arg5.max_flag= NO_MAX_RANGE;
+
+  String range_string;
+  sel_arg3.print(&range_string);
+  const char expected[]= "3 <= field_name";
+  EXPECT_STREQ(expected, range_string.c_ptr());
+
+  range_string.length(0);
+  sel_arg5.print(&range_string);
+  const char expected2[]= "5 <= field_name";
+  EXPECT_STREQ(expected2, range_string.c_ptr());
+
+  /*
+    Ranges now:
+                       -inf ----------------3-5----------- +inf
+    sel_arg3:                               <-----------------]
+    sel_arg5:                                 <---------------]
+    Below: merge these two ranges into sel_arg3 using copy_max()
+  */
+  bool full_range= sel_arg5.copy_min(&sel_arg3);
+  // The merged range does not cover all possible values
+  EXPECT_FALSE(full_range);
+
+  range_string.length(0);
+  sel_arg5.print(&range_string);
+  const char expected3[]= "3 <= field_name";
+  EXPECT_STREQ(expected3, range_string.c_ptr());
+
+  range_string.length(0);
+  sel_arg3.max_flag= 0;
+  sel_arg3.min_flag= NO_MIN_RANGE;
+  sel_arg3.print(&range_string);
+  const char expected4[]= "field_name <= 3";
+  EXPECT_STREQ(expected4, range_string.c_ptr());
+
+  /*
+    Ranges now:
+                       -inf ----------------3-5----------- +inf
+    sel_arg3:          [-------------------->                
+    sel_arg5:                               <-----------------]
+    Below: merge these two ranges into sel_arg5 using copy_min()
+  */
+
+  full_range= sel_arg5.copy_min(&sel_arg3);
+  // The new range covers all possible values
+  EXPECT_TRUE(full_range);
+
+  range_string.length(0);
+  sel_arg5.print(&range_string);
+  const char expected5[]= "field_name";
+  EXPECT_STREQ(expected5, range_string.c_ptr());
 }
 
 }

No bundle (reason: useless for push emails).
Thread
bzr push into mysql-trunk branch (roy.lyseng:3864 to 3870) Roy Lyseng10 Feb