[Commits] Rev 2852: MWL#89: Cost-based choice between Materialization and IN->EXISTS transformation in file:///home/tsk/mprog/src/5.3-mwl89/

timour at askmonty.org timour at askmonty.org
Tue Nov 16 08:52:02 EET 2010


At file:///home/tsk/mprog/src/5.3-mwl89/

------------------------------------------------------------
revno: 2852 [merge]
revision-id: timour at askmonty.org-20101116065138-lsj9eppk8epogasz
parent: psergey at askmonty.org-20101111123526-tngd0657sv21i8zq
parent: timour at askmonty.org-20101112070925-6exyk6ftvsluwt8b
committer: timour at askmonty.org
branch nick: 5.3-mwl89
timestamp: Tue 2010-11-16 08:51:38 +0200
message:
  MWL#89: Cost-based choice between Materialization and IN->EXISTS transformation
    
    Merge 5.3-mwl89 into 5.3 main.
added:
  mysql-test/include/subselect_mat_cost.inc subselect_mat_cost.i-20101020124245-pk792yug61li1if5-1
  mysql-test/r/subselect_mat_cost.result subselect_mat_cost.r-20101009142759-690v5v03b2dzubpf-2
  mysql-test/t/subselect_mat_cost.test subselect_mat_cost.t-20101009142759-690v5v03b2dzubpf-1
modified:
  mysql-test/include/check_shared_row_lock.inc check_shared_row_loc-20100427110333-0syx5b4s77k50o52-1
  mysql-test/r/subselect3.result sp1f-subselect3.result-20061031174245-v7hvtc7uwevifiq4lziwv5gdcxpeak7t
  mysql-test/r/subselect3_jcl6.result subselect3_jcl6.resu-20100117143923-cf6j4mu5zzng00u7-1
  mysql-test/r/subselect4.result subselect4.result-20090903150316-1sul3u8k29ooxm3r-2
  mysql-test/r/subselect_cache.result subquery_cache.resul-20100710101008-oqtsrd0hav47cxxm-4
  mysql-test/r/subselect_mat.result subselect_mat.result-20100117143924-r0jv32dj80dg3b5h-1
  mysql-test/t/subselect3.test   sp1f-subselect3.test-20061031174245-pcxt5ljylerxhx2jkfhrbqfv5vqcazlz
  mysql-test/t/subselect4.test   subselect4.test-20090903150316-1sul3u8k29ooxm3r-1
  mysql-test/t/subselect_mat.test subselect_mat.test-20100117143929-iif102ysgna1tyj0-1
  sql/item.h                     sp1f-item.h-19700101030959-rrkb43htudd62batmoteashkebcwykpa
  sql/item_cmpfunc.cc            sp1f-item_cmpfunc.cc-19700101030959-hrk7pi2n6qpwxauufnkizirsoucdcx2e
  sql/item_cmpfunc.h             sp1f-item_cmpfunc.h-19700101030959-pcvbjplo4e4ng7ibynfhcd6pjyem57gr
  sql/item_subselect.cc          sp1f-item_subselect.cc-20020512204640-qep43aqhsfrwkqmrobni6czc3fqj36oo
  sql/item_subselect.h           sp1f-item_subselect.h-20020512204640-qdg77wil56cxyhtc2bjjdrppxq3wqgh3
  sql/mysql_priv.h               sp1f-mysql_priv.h-19700101030959-4fl65tqpop5zfgxaxkqotu2fa2ree5ci
  sql/mysqld.cc                  sp1f-mysqld.cc-19700101030959-zpswdvekpvixxzxf7gdtofzel7nywtfj
  sql/opt_subselect.cc           opt_subselect.cc-20100215190428-nekkl8wisp0k6nlk-1
  sql/share/errmsg.txt           sp1f-errmsg.txt-20041213212820-do5w642w224ja7ctyqhyl6iihdmpkzv5
  sql/sql_class.cc               sp1f-sql_class.cc-19700101030959-rpotnweaff2pikkozh3butrf7mv3oero
  sql/sql_class.h                sp1f-sql_class.h-19700101030959-jnqnbrjyqsvgncsibnumsmg3lyi7pa5s
  sql/sql_delete.cc              sp1f-sql_delete.cc-19700101030959-ch2a6r6ushvc2vfwxt7ehcjuplelwthr
  sql/sql_lex.cc                 sp1f-sql_lex.cc-19700101030959-4pizwlu5rqkti27gcwsvxkawq6bc2kph
  sql/sql_lex.h                  sp1f-sql_lex.h-19700101030959-sgldb2sooc7twtw5q7pgjx7qzqiaa3sn
  sql/sql_select.cc              sp1f-sql_select.cc-19700101030959-egb7whpkh76zzvikycs5nsnuviu4fdlb
  sql/sql_select.h               sp1f-sql_select.h-19700101030959-oqegfxr76xlgmrzd6qlevonoibfnwzoz
  sql/sql_union.cc               sp1f-sql_unions.cc-20010613103653-ljpdcuczpligpiljxsifua5riwhxyomz
  sql/sql_update.cc              sp1f-sql_update.cc-19700101030959-edlgskfuer2ylczbw2znrr5gzfefiyw7
-------------- next part --------------
=== modified file 'mysql-test/include/check_shared_row_lock.inc'
--- a/mysql-test/include/check_shared_row_lock.inc	2010-05-27 20:07:40 +0000
+++ b/mysql-test/include/check_shared_row_lock.inc	2010-11-12 07:09:25 +0000
@@ -33,7 +33,8 @@ connection default;
 # least it acquires S-locks on some of rows.
 let $wait_condition=
   select count(*) = 1 from information_schema.processlist
-  where state in ("Sending data","statistics", "preparing") and
+  where state in ("Sending data","statistics", "preparing", "updating",
+                  "executing", "Searching rows for update") and
         info = "$wait_statement";
 --source include/wait_condition.inc
 

=== added file 'mysql-test/include/subselect_mat_cost.inc'
--- a/mysql-test/include/subselect_mat_cost.inc	1970-01-01 00:00:00 +0000
+++ b/mysql-test/include/subselect_mat_cost.inc	2010-10-23 18:28:58 +0000
@@ -0,0 +1,152 @@
+-- echo
+-- echo /* A. Subqueries in the SELECT clause. */
+explain
+select a1, a1 in (select b1 from t2 where b1 > '0') from t1;
+select a1, a1 in (select b1 from t2 where b1 > '0') from t1;
+-- echo
+explain
+select a1, a2, (a1, a2) in (select b1, b2 from t2 where b1 > '0') from t1;
+select a1, a2, (a1, a2) in (select b1, b2 from t2 where b1 > '0') from t1;
+-- echo
+explain
+select a1, a2, (a1, a2) in (select b1, b2 from t2 where b1 > '0' and b1 < '9') from t1;
+select a1, a2, (a1, a2) in (select b1, b2 from t2 where b1 > '0' and b1 < '9') from t1;
+
+-- echo
+-- echo /*
+-- echo   B. "Natural" examples of subqueries without grouping that
+-- echo   cannot be flattened into semijoin.
+-- echo */
+
+explain
+select a1 from t1 where a1 in (select b2 from t2) or a2 < '9';
+select a1 from t1 where a1 in (select b2 from t2) or a2 < '9';
+-- echo
+explain
+select a1, a2 from t1 where (a1, a2) in (select b1, b2 from t2 where b1 > '0') or a2 < '9';
+select a1, a2 from t1 where (a1, a2) in (select b1, b2 from t2 where b1 > '0') or a2 < '9';
+-- echo UNION subqueries are currently limited to only use IN-TO-EXISTS.
+explain
+select a2 from t1 where a2 in (select b2 from t2 UNION select b3 from t2 as t3);
+select a2 from t1 where a2 in (select b2 from t2 UNION select b3 from t2 as t3);
+-- echo
+explain
+select a1 from t1 where a1 = '1 - 02' and a1 in (select max(b1) from t2 where b2 = '2 - 02');
+select a1 from t1 where a1 = '1 - 02' and a1 in (select max(b1) from t2 where b2 = '2 - 02');
+-- echo
+explain
+select a1, a2 from t1 where (a1, a2) in (select b1, b2 from t2 order by b3);
+select a1, a2 from t1 where (a1, a2) in (select b1, b2 from t2 order by b3);
+
+-- echo
+-- echo /* C. Subqueries in the WHERE clause with GROUP BY. */
+explain
+select * from t1 where a1 in (select b1 from t2 where b1 > '0' group by b1);
+select * from t1 where a1 in (select b1 from t2 where b1 > '0' group by b1);
+-- echo
+explain
+select * from t1 where (a1, a2) in (select b1, b2 from t2 where b1 > '0' group by b1, b2);
+select * from t1 where (a1, a2) in (select b1, b2 from t2 where b1 > '0' group by b1, b2);
+-- echo
+explain
+select * from t1 where (a1, a2) in (select b1, b2 from t2 where b1 > '0' group by b1, b2 having b2 <  '2 - 04');
+select * from t1 where (a1, a2) in (select b1, b2 from t2 where b1 > '0' group by b1, b2 having b2 <  '2 - 04');
+-- echo
+explain
+select * from t1 where (a1, a2, a3) in (select b1, b2, b3 from t2 group by b1, b2, b3);
+select * from t1 where (a1, a2, a3) in (select b1, b2, b3 from t2 group by b1, b2, b3);
+-- echo
+explain
+select * from t1 where (a1, a2, a3) in (select b1, b2, b3 from t2 where b3 = '3 - 02' group by b1, b2);
+select * from t1 where (a1, a2, a3) in (select b1, b2, b3 from t2 where b3 = '3 - 02' group by b1, b2);
+-- echo
+explain
+select * from t1 where (a1,a2,a3) in (select b1,b2,b3 from t2 where b1 = '1 - 01' group by b1,b2,b3);
+select * from t1 where (a1,a2,a3) in (select b1,b2,b3 from t2 where b1 = '1 - 01' group by b1,b2,b3);
+
+-- echo
+-- echo /*
+-- echo   D. Subqueries for which materialization is not possible, and the
+-- echo   optimizer reverts to in-to-exists.
+-- echo */
+# The first two cases are rejected during the prepare phase by the procedure
+# subquery_types_allow_materialization().
+explain
+select left(a1,7), left(a2,7) from t1_1024 where a1 in (select b1 from t2_1024 where b1 > '0') or a2 < '9';
+select left(a1,7), left(a2,7) from t1_1024 where a1 in (select b1 from t2_1024 where b1 > '0') or a2 < '9';
+explain
+select left(a1,7), left(a2,7) from t1_1024 where (a1,a2) in (select b1, b2 from t2_1024 where b1 > '0') or a2 < '9';
+select left(a1,7), left(a2,7) from t1_1024 where (a1,a2) in (select b1, b2 from t2_1024 where b1 > '0') or a2 < '9';
+-- echo
+# The following two subqueries return the result of a string function with a
+# blob argument, where the return type may be != blob. These are rejected during
+# cost-based optimization when attempting to create a temporary table.
+explain
+select left(a1,7), left(a2,7) from t1_1024 where a1 in (select substring(b1,1,1024) from t2_1024 where b1 > '0') or a2 < '9';
+select left(a1,7), left(a2,7) from t1_1024 where a1 in (select substring(b1,1,1024) from t2_1024 where b1 > '0') or a2 < '9';
+explain
+select left(a1,7), left(a2,7) from t1_1024 where (a1,a2) in (select substring(b1,1,1024), substring(b2,1,1024) from t2_1024 where b1 > '0') or a2 < '9';
+select left(a1,7), left(a2,7) from t1_1024 where (a1,a2) in (select substring(b1,1,1024), substring(b2,1,1024) from t2_1024 where b1 > '0') or a2 < '9';
+-- echo
+
+
+-- echo
+-- echo /* E. Edge cases. */
+-- echo
+
+-- echo /* E.1 Both materialization and in_to_exists cannot be off. */
+set @save_optimizer_switch=@@optimizer_switch;
+set @@optimizer_switch = 'materialization=off,in_to_exists=off';
+--error ER_ILLEGAL_SUBQUERY_OPTIMIZER_SWITCHES
+select * from t1 where a1 in (select b1 from t2 where b1 > '0' group by b1);
+set @@optimizer_switch = @save_optimizer_switch;
+
+-- echo /* E.2 Outer query without tables, always uses IN-TO-EXISTS. */
+explain
+select '1 - 03' in (select b1 from t2 where b1 > '0');
+select '1 - 03' in (select b1 from t2 where b1 > '0');
+
+-- echo /* E.3 Subqueries without tables. */
+explain
+select a1 from t1 where a1 in (select '1 - 03') or a2 < '9';
+select a1 from t1 where a1 in (select '1 - 03') or a2 < '9';
+-- echo UNION subqueries are currently limited to only use IN-TO-EXISTS.
+explain
+select a1 from t1 where a1 in (select '1 - 03' UNION select '1 - 02');
+select a1 from t1 where a1 in (select '1 - 03' UNION select '1 - 02');
+
+-- echo /* E.4 optimize_cond detects FALSE where/having clause. */
+explain
+select a1 from t1 where a1 in (select b1 from t2 where b1 = b2 and b2 = '1 - 03' and b1 = '1 - 02' ) or a2 < '9';
+select a1 from t1 where a1 in (select b1 from t2 where b1 = b2 and b2 = '1 - 03' and b1 = '1 - 02' ) or a2 < '9';
+
+-- echo /* E.5 opt_sum_query detects no matching min/max row or substitutes MIN/MAX with a const. */
+-- echo TODO this test produces wrong result due to missing logic to handle the case
+-- echo when JOIN::optimize detects an empty subquery result.
+explain
+select a1 from t1 where a1 in (select max(b1) from t2);
+select a1 from t1 where a1 in (select max(b1) from t2);
+-- echo
+explain
+select a1 from t1 where a1 in (select max(b1) from t2 where b1 = '7 - 02');
+select a1 from t1 where a1 in (select max(b1) from t2 where b1 = '7 - 02');
+
+-- echo /* E.6 make_join_select detects impossible WHERE. *
+
+-- echo TODO
+
+-- echo /* E.7 constant optimization detects "no matching row in const table". */
+
+-- echo TODO
+
+-- echo /* E.8 Impossible WHERE noticed after reading const tables. */
+explain
+select '1 - 03' in (select b1 from t2 where b1 > '0' and b1 < '0');
+select '1 - 03' in (select b1 from t2 where b1 > '0' and b1 < '0');
+
+-- echo
+-- echo /* F. UPDATE/DELETE with subqueries. */
+-- echo
+
+-- echo TODO
+-- echo

=== modified file 'mysql-test/r/subselect3.result'
--- a/mysql-test/r/subselect3.result	2010-09-06 12:34:24 +0000
+++ b/mysql-test/r/subselect3.result	2010-11-05 12:42:58 +0000
@@ -840,11 +840,16 @@ x	ROW(11, 12) = (SELECT MAX(x), 22)	ROW(
 1       0       0
 2       0       0
 11      0       0
-# 2nd and 3rd columns should be same for x == 11 only
+# 2nd and 3rd columns should be same
+EXPLAIN SELECT a AS x, ROW(11, 12) = (SELECT MAX(x), 12), ROW(11, 12) IN (SELECT MAX(x), 12) FROM t1;
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1      ALL     NULL    NULL    NULL    NULL    3       
+3       DEPENDENT SUBQUERY      NULL    NULL    NULL    NULL    NULL    NULL    NULL    No tables used
+2       DEPENDENT SUBQUERY      NULL    NULL    NULL    NULL    NULL    NULL    NULL    No tables used
 SELECT a AS x, ROW(11, 12) = (SELECT MAX(x), 12), ROW(11, 12) IN (SELECT MAX(x), 12) FROM t1;
 x       ROW(11, 12) = (SELECT MAX(x), 12)       ROW(11, 12) IN (SELECT MAX(x), 12)
-1       0       1
-2       0       1
+1       0       0
+2       0       0
 11      1       1
 DROP TABLE t1;
 # both columns should be same

=== modified file 'mysql-test/r/subselect3_jcl6.result'
--- a/mysql-test/r/subselect3_jcl6.result	2010-09-06 12:34:24 +0000
+++ b/mysql-test/r/subselect3_jcl6.result	2010-11-05 12:42:58 +0000
@@ -844,11 +844,16 @@ x	ROW(11, 12) = (SELECT MAX(x), 22)	ROW(
 1       0       0
 2       0       0
 11      0       0
-# 2nd and 3rd columns should be same for x == 11 only
+# 2nd and 3rd columns should be same
+EXPLAIN SELECT a AS x, ROW(11, 12) = (SELECT MAX(x), 12), ROW(11, 12) IN (SELECT MAX(x), 12) FROM t1;
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1      ALL     NULL    NULL    NULL    NULL    3       
+3       DEPENDENT SUBQUERY      NULL    NULL    NULL    NULL    NULL    NULL    NULL    No tables used
+2       DEPENDENT SUBQUERY      NULL    NULL    NULL    NULL    NULL    NULL    NULL    No tables used
 SELECT a AS x, ROW(11, 12) = (SELECT MAX(x), 12), ROW(11, 12) IN (SELECT MAX(x), 12) FROM t1;
 x       ROW(11, 12) = (SELECT MAX(x), 12)       ROW(11, 12) IN (SELECT MAX(x), 12)
-1       0       1
-2       0       1
+1       0       0
+2       0       0
 11      1       1
 DROP TABLE t1;
 # both columns should be same

=== modified file 'mysql-test/r/subselect4.result'
--- a/mysql-test/r/subselect4.result	2010-10-28 17:04:23 +0000
+++ b/mysql-test/r/subselect4.result	2010-11-11 16:42:55 +0000
@@ -397,3 +397,42 @@ pk
 # Restore old value for Index condition pushdown
 SET SESSION engine_condition_pushdown=@old_icp;
 DROP TABLE t1,t2;
+#
+# LP BUG#641203 Query returns rows where no result is expected (impossible WHERE)
+#
+CREATE TABLE t1 (c1 varchar(1) DEFAULT NULL);
+CREATE TABLE t2 (c1 varchar(1) DEFAULT NULL);
+INSERT INTO t2 VALUES ('k'), ('d');
+CREATE TABLE t3 (c1 varchar(1) DEFAULT NULL);
+INSERT INTO t3 VALUES ('a'), ('b'), ('c');
+CREATE TABLE t4 (c1 varchar(1) primary key);
+INSERT INTO t4 VALUES ('k'), ('d');
+EXPLAIN
+SELECT * FROM t1 RIGHT JOIN t2 ON t1.c1 WHERE 's' IN (SELECT c1 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      ALL     NULL    NULL    NULL    NULL    2       Using where
+SELECT * FROM t1 RIGHT JOIN t2 ON t1.c1 WHERE 's' IN (SELECT c1 FROM t2);
+c1      c1
+EXPLAIN
+SELECT * FROM t2 LEFT JOIN t1 ON t1.c1 WHERE 's' IN (SELECT c1 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      ALL     NULL    NULL    NULL    NULL    2       Using where
+SELECT * FROM t2 LEFT JOIN t1 ON t1.c1 WHERE 's' IN (SELECT c1 FROM t2);
+c1      c1
+EXPLAIN
+SELECT * FROM (t2 LEFT JOIN t1 ON t1.c1) LEFT JOIN t3 on t3.c1 WHERE 's' IN (SELECT c1 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      ALL     NULL    NULL    NULL    NULL    2       Using where
+SELECT * FROM (t2 LEFT JOIN t1 ON t1.c1) LEFT JOIN t3 on t3.c1 WHERE 's' IN (SELECT c1 FROM t2);
+c1      c1      c1
+EXPLAIN
+SELECT * FROM t4 LEFT JOIN t2 ON t4.c1 WHERE 's' IN (SELECT c1 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      ALL     NULL    NULL    NULL    NULL    2       Using where
+SELECT * FROM t4 LEFT JOIN t2 ON t4.c1 WHERE 's' IN (SELECT c1 FROM t2);
+c1      c1
+drop table t1, t2, t3, t4;

=== modified file 'mysql-test/r/subselect_cache.result'
--- a/mysql-test/r/subselect_cache.result	2010-11-09 11:13:56 +0000
+++ b/mysql-test/r/subselect_cache.result	2010-11-16 06:51:38 +0000
@@ -897,7 +897,7 @@ a	b	SUBS
 5       6       1
 4       5       1
 7       8       NULL
-9       NULL    NULL
+9       NULL    1
 show status like "subquery_cache%";
 Variable_name   Value
 Subquery_cache_hit      0
@@ -916,7 +916,7 @@ a	b	SUBS
 5       6       1
 4       5       1
 7       8       NULL
-9       NULL    NULL
+9       NULL    1
 show status like "subquery_cache%";
 Variable_name   Value
 Subquery_cache_hit      6
@@ -977,7 +977,7 @@ a	b	SUBS
 5       6       0
 4       5       0
 7       8       NULL
-9       NULL    NULL
+9       NULL    0
 show status like "subquery_cache%";
 Variable_name   Value
 Subquery_cache_hit      0
@@ -996,7 +996,7 @@ a	b	SUBS
 5       6       0
 4       5       0
 7       8       NULL
-9       NULL    NULL
+9       NULL    0
 show status like "subquery_cache%";
 Variable_name   Value
 Subquery_cache_hit      6

=== modified file 'mysql-test/r/subselect_mat.result'
--- a/mysql-test/r/subselect_mat.result	2010-10-25 20:48:43 +0000
+++ b/mysql-test/r/subselect_mat.result	2010-11-05 12:42:58 +0000
@@ -30,7 +30,7 @@ create index it3i3 on t3i (c1, c2);
 insert into t1i select * from t1;
 insert into t2i select * from t2;
 insert into t3i select * from t3;
-set @@optimizer_switch='semijoin=off';
+set @@optimizer_switch='materialization=on,in_to_exists=off,semijoin=off';
 /******************************************************************************
 * Simple tests.
 ******************************************************************************/
@@ -176,33 +176,33 @@ a1	a2
 1 - 02  2 - 02
 select * from t1 where (a1, a2) in (select b1, min(b2) from t2i limit 1,1);
 ERROR 42000: This version of MySQL doesn't yet support 'LIMIT & IN/ALL/ANY/SOME subquery'
-set @@optimizer_switch='default,semijoin=off';
+set @@optimizer_switch='materialization=on,in_to_exists=off,semijoin=on';
 prepare st1 from
 "select * from t1 where (a1, a2) in (select b1, min(b2) from t2 where b1 > '0' group by b1)";
-set @@optimizer_switch='default,materialization=off';
+set @@optimizer_switch='materialization=off,in_to_exists=on,semijoin=on';
 execute st1;
 a1      a2
 1 - 01  2 - 01
 1 - 02  2 - 02
-set @@optimizer_switch='default,semijoin=off';
+set @@optimizer_switch='materialization=on,in_to_exists=off,semijoin=on';
 execute st1;
 a1      a2
 1 - 01  2 - 01
 1 - 02  2 - 02
-set @@optimizer_switch='default,materialization=off';
+set @@optimizer_switch='materialization=off,in_to_exists=on,semijoin=on';
 prepare st1 from
 "select * from t1 where (a1, a2) in (select b1, min(b2) from t2 where b1 > '0' group by b1)";
-set @@optimizer_switch='default,semijoin=off';
+set @@optimizer_switch='materialization=on,in_to_exists=off,semijoin=off';
 execute st1;
 a1      a2
 1 - 01  2 - 01
 1 - 02  2 - 02
-set @@optimizer_switch='default,materialization=off';
+set @@optimizer_switch='materialization=off,in_to_exists=on,semijoin=on';
 execute st1;
 a1      a2
 1 - 01  2 - 01
 1 - 02  2 - 02
-set @@optimizer_switch='default,semijoin=off';
+set @@optimizer_switch='materialization=on,in_to_exists=off,semijoin=off';
 explain extended
 select * from t1 where (a1, a2) in (select b1, b2 from t2 order by b1, b2);
 id      select_type     table   type    possible_keys   key     key_len ref     rows    filtered        Extra
@@ -549,7 +549,7 @@ a1	a2
 Test that BLOBs are not materialized (except when arguments of some functions).
 */
 # force materialization to be always considered
-set @@optimizer_switch='semijoin=off';
+set @@optimizer_switch='materialization=on,in_to_exists=off,semijoin=off';
 set @prefix_len = 6;
 set @blob_len = 16;
 set @suffix_len = @blob_len - @prefix_len;
@@ -951,7 +951,7 @@ insert into t1bit values (b'010', b'110'
 insert into t2bit values (b'001', b'101');
 insert into t2bit values (b'010', b'110');
 insert into t2bit values (b'110', b'111');
-set @@optimizer_switch='semijoin=off';
+set @@optimizer_switch='materialization=on,in_to_exists=off,semijoin=off';
 explain extended select bin(a1), bin(a2)
 from t1bit
 where (a1, a2) in (select b1, b2 from t2bit);
@@ -994,7 +994,7 @@ drop table t1, t2, t3, t1i, t2i, t3i, co
 /******************************************************************************
 * Test the cache of the left operand of IN.
 ******************************************************************************/
-set @@optimizer_switch='semijoin=off';
+set @@optimizer_switch='materialization=on,in_to_exists=off,semijoin=off';
 create table t1 (s1 int);
 create table t2 (s2 int);
 insert into t1 values (5),(1),(0);
@@ -1136,27 +1136,40 @@ drop table t2;
 create table t1 (a1 int key);
 create table t2 (b1 int);
 insert into t1 values (5);
+Only the last query returns correct result. Filed as BUG#40037.
+set @@optimizer_switch='materialization=on,in_to_exists=off,semijoin=off';
 explain select min(a1) from t1 where 7 in (select b1 from t2 group by b1);
 id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
 1       PRIMARY NULL    NULL    NULL    NULL    NULL    NULL    NULL    Select tables optimized away
 2       SUBQUERY        t2      system  NULL    NULL    NULL    NULL    0       const row not found
 select min(a1) from t1 where 7 in (select b1 from t2 group by b1);
 min(a1)
-set @@optimizer_switch='default,materialization=off';
+NULL
+set @@optimizer_switch='materialization=off,in_to_exists=on,semijoin=off';
 explain select min(a1) from t1 where 7 in (select b1 from t2 group by b1);
 id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
 1       PRIMARY NULL    NULL    NULL    NULL    NULL    NULL    NULL    Select tables optimized away
 2       DEPENDENT SUBQUERY      t2      system  NULL    NULL    NULL    NULL    0       const row not found
 select min(a1) from t1 where 7 in (select b1 from t2 group by b1);
 min(a1)
-set @@optimizer_switch='default,semijoin=off';
+NULL
+set @@optimizer_switch='materialization=on,in_to_exists=off,semijoin=off';
 explain select min(a1) from t1 where 7 in (select b1 from t2);
 id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
 1       PRIMARY NULL    NULL    NULL    NULL    NULL    NULL    NULL    Select tables optimized away
 2       SUBQUERY        t2      system  NULL    NULL    NULL    NULL    0       const row not found
 select min(a1) from t1 where 7 in (select b1 from t2);
 min(a1)
-set @@optimizer_switch='default,materialization=off';
+NULL
+set @@optimizer_switch='materialization=off,in_to_exists=on,semijoin=off';
+explain select min(a1) from t1 where 7 in (select b1 from t2);
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY NULL    NULL    NULL    NULL    NULL    NULL    NULL    Select tables optimized away
+2       DEPENDENT SUBQUERY      NULL    NULL    NULL    NULL    NULL    NULL    NULL    Impossible WHERE noticed after reading const tables
+select min(a1) from t1 where 7 in (select b1 from t2);
+min(a1)
+NULL
+set @@optimizer_switch='materialization=off,in_to_exists=off,semijoin=on';
 explain select min(a1) from t1 where 7 in (select b1 from t2);
 id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
 1       PRIMARY NULL    NULL    NULL    NULL    NULL    NULL    NULL    Impossible WHERE noticed after reading const tables
@@ -1167,7 +1180,7 @@ drop table t1,t2;
 create table t1 (a char(2), b varchar(10));
 insert into t1 values ('a',  'aaa');
 insert into t1 values ('aa', 'aaaa');
-set @@optimizer_switch='default,semijoin=off';
+set @@optimizer_switch='materialization=on,in_to_exists=off,semijoin=off';
 explain select a,b from t1 where b in (select a from t1);
 id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
 1       PRIMARY t1      ALL     NULL    NULL    NULL    NULL    2       Using where
@@ -1187,7 +1200,7 @@ INSERT INTO t1 (f1, f2) VALUES (10, 1.66
 CREATE TABLE t2 LIKE t1;
 INSERT INTO t2 VALUES (1, 1.789);
 INSERT INTO t2 VALUES (13, 1.454);
-SET @@optimizer_switch='default,semijoin=on,materialization=on';
+SET @@optimizer_switch='materialization=on,in_to_exists=off,semijoin=on';
 EXPLAIN SELECT COUNT(*) FROM t1 WHERE (f1,f2) IN (SELECT f1,f2 FROM t2);
 id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
 1       PRIMARY subselect2      ALL     unique_key      NULL    NULL    NULL    2       
@@ -1208,7 +1221,7 @@ PRIMARY KEY (pk)
 INSERT INTO t1 VALUES (1,'o','ffff','ffff','ffoo'),(2,'f','ffff','ffff','ffff');
 CREATE TABLE t2 LIKE t1;
 INSERT INTO t2 VALUES (1,'i','iiii','iiii','iiii'),(2,'f','ffff','ffff','ffff');
-SET @@optimizer_switch='default,semijoin=on,materialization=on';
+SET @@optimizer_switch='materialization=on,in_to_exists=off,semijoin=on';
 EXPLAIN SELECT pk FROM t1 WHERE (a) IN (SELECT a FROM t2 WHERE pk > 0);
 id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
 1       PRIMARY t1      ALL     NULL    NULL    NULL    NULL    2       
@@ -1237,7 +1250,7 @@ i
 3
 4
 set @save_optimizer_switch=@@optimizer_switch;
-set session optimizer_switch='materialization=off';
+set session optimizer_switch='materialization=off,in_to_exists=on';
 select * from t1 where t1.i in (select t2.i from t2 join t3 where t2.i + t3.i = 5);
 i
 1

=== added file 'mysql-test/r/subselect_mat_cost.result'
--- a/mysql-test/r/subselect_mat_cost.result	1970-01-01 00:00:00 +0000
+++ b/mysql-test/r/subselect_mat_cost.result	2010-11-05 15:10:48 +0000
@@ -0,0 +1,3877 @@
+drop table if exists t1, t2, t1_1024, t2_1024;
+drop procedure if exists make_t1_indexes;
+drop procedure if exists make_t2_indexes;
+drop procedure if exists remove_t1_indexes;
+drop procedure if exists remove_t2_indexes;
+drop procedure if exists add_materialization_data;
+drop procedure if exists delete_materialization_data;
+drop procedure if exists set_all_columns_not_null;
+drop procedure if exists set_all_columns_nullable;
+create table t1 (a1 char(8), a2 char(8), a3 char(8), a4 int);
+insert into t1 values ('1 - 00', '2 - 00', '3 - 00', 0);
+insert into t1 values ('1 - 01', '2 - 01', '3 - 01', 1);
+insert into t1 values ('1 - 02', '2 - 02', '3 - 02', 2);
+create table t2 (b1 char(8), b2 char(8), b3 char(8), b4 int);
+insert into t2 values ('1 - 01', '2 - 01', '3 - 01', 1);
+insert into t2 values ('1 - 01', '2 - 01', '3 - 02', 2);
+insert into t2 values ('1 - 02', '2 - 02', '3 - 03', 3);
+insert into t2 values ('1 - 02', '2 - 02', '3 - 04', 4);
+insert into t2 values ('1 - 03', '2 - 03', '3 - 05', 5);
+create table t1_1024 (a1 blob(1024), a2 blob(1024));
+insert into t1_1024 values (concat('1 - 00', repeat('x', 1018)), concat('2 - 00', repeat('x', 1018)));
+insert into t1_1024 values (concat('1 - 01', repeat('x', 1018)), concat('2 - 01', repeat('x', 1018)));
+create table t2_1024 (b1 blob(1024), b2 blob(1024));
+insert into t2_1024 values (concat('1 - 01', repeat('x', 1018)), concat('2 - 01', repeat('x', 1018)));
+insert into t2_1024 values (concat('1 - 02', repeat('x', 1018)), concat('2 - 02', repeat('x', 1018)));
+insert into t2_1024 values (concat('1 - 03', repeat('x', 1018)), concat('2 - 03', repeat('x', 1018)));
+insert into t2_1024 values (concat('1 - 04', repeat('x', 1018)), concat('2 - 04', repeat('x', 1018)));
+create procedure make_t1_indexes()
+begin
+create index it1i1 on t1 (a1);
+create index it1i2 on t1 (a2);
+create index it1i3 on t1 (a1, a2);
+create index it1_1024i1 on t1_1024 (a1(6));
+create index it1_1024i2 on t1_1024 (a2(6));
+create index it1_1024i3 on t1_1024 (a1(6), a2(6));
+end|
+create procedure make_t2_indexes()
+begin
+create index it2i1 on t2 (b1);
+create index it2i2 on t2 (b2);
+create index it2i3 on t2 (b1, b2);
+create unique index it2i4 on t2 (b1, b2, b3);
+create index it2_1024i1 on t2_1024 (b1(6));
+create index it2_1024i2 on t2_1024 (b2(6));
+create index it2_1024i3 on t2_1024 (b1(6), b2(6));
+end|
+create procedure remove_t1_indexes()
+begin
+drop index it1i1 on t1;
+drop index it1i2 on t1;
+drop index it1i3 on t1;
+drop index it1_1024i1 on t1_1024;
+drop index it1_1024i2 on t1_1024;
+drop index it1_1024i3 on t1_1024;
+end|
+create procedure remove_t2_indexes()
+begin
+drop index it2i1 on t2;
+drop index it2i2 on t2;
+drop index it2i3 on t2;
+drop index it2i4 on t2;
+drop index it2_1024i1 on t2_1024;
+drop index it2_1024i2 on t2_1024;
+drop index it2_1024i3 on t2_1024;
+end|
+create procedure add_materialization_data()
+begin
+insert into t1 values ('1 - 03', '2 - 03', '3 - 03', 3);
+insert into t1 values ('1 - 04', '2 - 04', '3 - 04', 4);
+insert into t1 values ('1 - 05', '2 - 05', '3 - 05', 5);
+insert into t1 values ('1 - 06', '2 - 06', '3 - 06', 6);
+insert into t1 values ('1 - 07', '2 - 07', '3 - 07', 7);
+insert into t1_1024 values (concat('1 - 03', repeat('x', 1018)), concat('2 - 03', repeat('x', 1018)));
+end|
+create procedure delete_materialization_data()
+begin
+delete from t1 where a1 >= '1 - 03';
+delete from t1_1024 where a1 >= '1 - 03';
+end|
+create procedure set_all_columns_not_null()
+begin
+alter table t1 modify a1 char(8) not null, modify a2 char(8) not null, modify a3 char(8) not null;
+alter table t2 modify b1 char(8) not null, modify b2 char(8) not null, modify b3 char(8) not null;
+end|
+create procedure set_all_columns_nullable()
+begin
+alter table t1 modify a1 char(8) null, modify a2 char(8) null, modify a3 char(8) null;
+alter table t2 modify b1 char(8) null, modify b2 char(8) null, modify b3 char(8) null;
+end|
+
+/******************************************************************************
+1. Both materialization and in-to-exists are ON, make a cost-based choice.
+******************************************************************************/
+set @@optimizer_switch='materialization=on,in_to_exists=on';
+
+/* 1.1 In-to-exists is cheaper */
+call make_t1_indexes();
+/* 1.1.1 non-indexed table access */
+
+/* A. Subqueries in the SELECT clause. */
+explain
+select a1, a1 in (select b1 from t2 where b1 > '0') from t1;
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1      index   NULL    it1i1   9       NULL    3       Using index
+2       DEPENDENT SUBQUERY      t2      ALL     NULL    NULL    NULL    NULL    5       Using where
+select a1, a1 in (select b1 from t2 where b1 > '0') from t1;
+a1      a1 in (select b1 from t2 where b1 > '0')
+1 - 00  0
+1 - 01  1
+1 - 02  1
+
+explain
+select a1, a2, (a1, a2) in (select b1, b2 from t2 where b1 > '0') from t1;
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1      index   NULL    it1i3   18      NULL    3       Using index
+2       DEPENDENT SUBQUERY      t2      ALL     NULL    NULL    NULL    NULL    5       Using where
+select a1, a2, (a1, a2) in (select b1, b2 from t2 where b1 > '0') from t1;
+a1      a2      (a1, a2) in (select b1, b2 from t2 where b1 > '0')
+1 - 00  2 - 00  0
+1 - 01  2 - 01  1
+1 - 02  2 - 02  1
+
+explain
+select a1, a2, (a1, a2) in (select b1, b2 from t2 where b1 > '0' and b1 < '9') from t1;
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1      index   NULL    it1i3   18      NULL    3       Using index
+2       DEPENDENT SUBQUERY      t2      ALL     NULL    NULL    NULL    NULL    5       Using where
+select a1, a2, (a1, a2) in (select b1, b2 from t2 where b1 > '0' and b1 < '9') from t1;
+a1      a2      (a1, a2) in (select b1, b2 from t2 where b1 > '0' and b1 < '9')
+1 - 00  2 - 00  0
+1 - 01  2 - 01  1
+1 - 02  2 - 02  1
+
+/*
+B. "Natural" examples of subqueries without grouping that
+cannot be flattened into semijoin.
+*/
+explain
+select a1 from t1 where a1 in (select b2 from t2) or a2 < '9';
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1      index   it1i2   it1i3   18      NULL    3       Using where; Using index
+2       DEPENDENT SUBQUERY      t2      ALL     NULL    NULL    NULL    NULL    5       Using where
+select a1 from t1 where a1 in (select b2 from t2) or a2 < '9';
+a1
+1 - 00
+1 - 01
+1 - 02
+
+explain
+select a1, a2 from t1 where (a1, a2) in (select b1, b2 from t2 where b1 > '0') or a2 < '9';
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1      index   it1i2   it1i3   18      NULL    3       Using where; Using index
+2       DEPENDENT SUBQUERY      t2      ALL     NULL    NULL    NULL    NULL    5       Using where
+select a1, a2 from t1 where (a1, a2) in (select b1, b2 from t2 where b1 > '0') or a2 < '9';
+a1      a2
+1 - 00  2 - 00
+1 - 01  2 - 01
+1 - 02  2 - 02
+UNION subqueries are currently limited to only use IN-TO-EXISTS.
+explain
+select a2 from t1 where a2 in (select b2 from t2 UNION select b3 from t2 as t3);
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1      index   NULL    it1i2   9       NULL    3       Using where; Using index
+2       DEPENDENT SUBQUERY      t2      ALL     NULL    NULL    NULL    NULL    5       Using where
+3       DEPENDENT UNION t3      ALL     NULL    NULL    NULL    NULL    5       Using where
+NULL    UNION RESULT    <union2,3>      ALL     NULL    NULL    NULL    NULL    NULL    
+select a2 from t1 where a2 in (select b2 from t2 UNION select b3 from t2 as t3);
+a2
+2 - 01
+2 - 02
+
+explain
+select a1 from t1 where a1 = '1 - 02' and a1 in (select max(b1) from t2 where b2 = '2 - 02');
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1      ref     it1i1,it1i3     it1i1   9       const   1       Using where; Using index
+2       DEPENDENT SUBQUERY      t2      ALL     NULL    NULL    NULL    NULL    5       Using where
+select a1 from t1 where a1 = '1 - 02' and a1 in (select max(b1) from t2 where b2 = '2 - 02');
+a1
+1 - 02
+
+explain
+select a1, a2 from t1 where (a1, a2) in (select b1, b2 from t2 order by b3);
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1      index   NULL    it1i3   18      NULL    3       Using where; Using index
+2       DEPENDENT SUBQUERY      t2      ALL     NULL    NULL    NULL    NULL    5       Using where
+select a1, a2 from t1 where (a1, a2) in (select b1, b2 from t2 order by b3);
+a1      a2
+1 - 01  2 - 01
+1 - 02  2 - 02
+
+/* C. Subqueries in the WHERE clause with GROUP BY. */
+explain
+select * from t1 where a1 in (select b1 from t2 where b1 > '0' group by b1);
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1      ALL     NULL    NULL    NULL    NULL    3       Using where
+2       DEPENDENT SUBQUERY      t2      ALL     NULL    NULL    NULL    NULL    5       Using where; Using temporary; Using filesort
+select * from t1 where a1 in (select b1 from t2 where b1 > '0' group by b1);
+a1      a2      a3      a4
+1 - 01  2 - 01  3 - 01  1
+1 - 02  2 - 02  3 - 02  2
+
+explain
+select * from t1 where (a1, a2) in (select b1, b2 from t2 where b1 > '0' group by b1, b2);
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1      ALL     NULL    NULL    NULL    NULL    3       Using where
+2       DEPENDENT SUBQUERY      t2      ALL     NULL    NULL    NULL    NULL    5       Using where; Using temporary; Using filesort
+select * from t1 where (a1, a2) in (select b1, b2 from t2 where b1 > '0' group by b1, b2);
+a1      a2      a3      a4
+1 - 01  2 - 01  3 - 01  1
+1 - 02  2 - 02  3 - 02  2
+
+explain
+select * from t1 where (a1, a2) in (select b1, b2 from t2 where b1 > '0' group by b1, b2 having b2 <  '2 - 04');
+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    5       Using where; Using temporary; Using filesort
+select * from t1 where (a1, a2) in (select b1, b2 from t2 where b1 > '0' group by b1, b2 having b2 <  '2 - 04');
+a1      a2      a3      a4
+1 - 01  2 - 01  3 - 01  1
+1 - 02  2 - 02  3 - 02  2
+
+explain
+select * from t1 where (a1, a2, a3) in (select b1, b2, b3 from t2 group by b1, b2, b3);
+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    5       Using temporary; Using filesort
+select * from t1 where (a1, a2, a3) in (select b1, b2, b3 from t2 group by b1, b2, b3);
+a1      a2      a3      a4
+1 - 01  2 - 01  3 - 01  1
+
+explain
+select * from t1 where (a1, a2, a3) in (select b1, b2, b3 from t2 where b3 = '3 - 02' group by b1, b2);
+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    5       Using where; Using temporary; Using filesort
+select * from t1 where (a1, a2, a3) in (select b1, b2, b3 from t2 where b3 = '3 - 02' group by b1, b2);
+a1      a2      a3      a4
+
+explain
+select * from t1 where (a1,a2,a3) in (select b1,b2,b3 from t2 where b1 = '1 - 01' group by b1,b2,b3);
+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    5       Using where; Using temporary; Using filesort
+select * from t1 where (a1,a2,a3) in (select b1,b2,b3 from t2 where b1 = '1 - 01' group by b1,b2,b3);
+a1      a2      a3      a4
+1 - 01  2 - 01  3 - 01  1
+
+/*
+D. Subqueries for which materialization is not possible, and the
+optimizer reverts to in-to-exists.
+*/
+explain
+select left(a1,7), left(a2,7) from t1_1024 where a1 in (select b1 from t2_1024 where b1 > '0') or a2 < '9';
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1_1024 ALL     it1_1024i2      NULL    NULL    NULL    2       Using where
+2       DEPENDENT SUBQUERY      t2_1024 ALL     NULL    NULL    NULL    NULL    4       Using where
+select left(a1,7), left(a2,7) from t1_1024 where a1 in (select b1 from t2_1024 where b1 > '0') or a2 < '9';
+left(a1,7)      left(a2,7)
+1 - 00x 2 - 00x
+1 - 01x 2 - 01x
+explain
+select left(a1,7), left(a2,7) from t1_1024 where (a1,a2) in (select b1, b2 from t2_1024 where b1 > '0') or a2 < '9';
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1_1024 ALL     it1_1024i2      NULL    NULL    NULL    2       Using where
+2       DEPENDENT SUBQUERY      t2_1024 ALL     NULL    NULL    NULL    NULL    4       Using where
+select left(a1,7), left(a2,7) from t1_1024 where (a1,a2) in (select b1, b2 from t2_1024 where b1 > '0') or a2 < '9';
+left(a1,7)      left(a2,7)
+1 - 00x 2 - 00x
+1 - 01x 2 - 01x
+
+explain
+select left(a1,7), left(a2,7) from t1_1024 where a1 in (select substring(b1,1,1024) from t2_1024 where b1 > '0') or a2 < '9';
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1_1024 ALL     it1_1024i2      NULL    NULL    NULL    2       Using where
+2       DEPENDENT SUBQUERY      t2_1024 ALL     NULL    NULL    NULL    NULL    4       Using where
+select left(a1,7), left(a2,7) from t1_1024 where a1 in (select substring(b1,1,1024) from t2_1024 where b1 > '0') or a2 < '9';
+left(a1,7)      left(a2,7)
+1 - 00x 2 - 00x
+1 - 01x 2 - 01x
+explain
+select left(a1,7), left(a2,7) from t1_1024 where (a1,a2) in (select substring(b1,1,1024), substring(b2,1,1024) from t2_1024 where b1 > '0') or a2 < '9';
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1_1024 ALL     it1_1024i2      NULL    NULL    NULL    2       Using where
+2       DEPENDENT SUBQUERY      t2_1024 ALL     NULL    NULL    NULL    NULL    4       Using where
+select left(a1,7), left(a2,7) from t1_1024 where (a1,a2) in (select substring(b1,1,1024), substring(b2,1,1024) from t2_1024 where b1 > '0') or a2 < '9';
+left(a1,7)      left(a2,7)
+1 - 00x 2 - 00x
+1 - 01x 2 - 01x
+
+
+/* E. Edge cases. */
+
+/* E.1 Both materialization and in_to_exists cannot be off. */
+set @save_optimizer_switch=@@optimizer_switch;
+set @@optimizer_switch = 'materialization=off,in_to_exists=off';
+select * from t1 where a1 in (select b1 from t2 where b1 > '0' group by b1);
+ERROR HY000: At least one of the 'in_to_exists' or 'materialization' optimizer_switch flags must be 'on'.
+set @@optimizer_switch = @save_optimizer_switch;
+/* E.2 Outer query without tables, always uses IN-TO-EXISTS. */
+explain
+select '1 - 03' in (select b1 from t2 where b1 > '0');
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY NULL    NULL    NULL    NULL    NULL    NULL    NULL    No tables used
+2       DEPENDENT SUBQUERY      t2      ALL     NULL    NULL    NULL    NULL    5       Using where
+select '1 - 03' in (select b1 from t2 where b1 > '0');
+'1 - 03' in (select b1 from t2 where b1 > '0')
+1
+/* E.3 Subqueries without tables. */
+explain
+select a1 from t1 where a1 in (select '1 - 03') or a2 < '9';
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1      index   it1i1,it1i2,it1i3       it1i3   18      NULL    3       Using where; Using index
+Warnings:
+Note    1249    Select 2 was reduced during optimization
+select a1 from t1 where a1 in (select '1 - 03') or a2 < '9';
+a1
+1 - 00
+1 - 01
+1 - 02
+UNION subqueries are currently limited to only use IN-TO-EXISTS.
+explain
+select a1 from t1 where a1 in (select '1 - 03' UNION select '1 - 02');
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1      index   NULL    it1i1   9       NULL    3       Using where; Using index
+2       DEPENDENT SUBQUERY      NULL    NULL    NULL    NULL    NULL    NULL    NULL    No tables used
+3       DEPENDENT UNION NULL    NULL    NULL    NULL    NULL    NULL    NULL    No tables used
+NULL    UNION RESULT    <union2,3>      ALL     NULL    NULL    NULL    NULL    NULL    
+select a1 from t1 where a1 in (select '1 - 03' UNION select '1 - 02');
+a1
+1 - 02
+/* E.4 optimize_cond detects FALSE where/having clause. */
+explain
+select a1 from t1 where a1 in (select b1 from t2 where b1 = b2 and b2 = '1 - 03' and b1 = '1 - 02' ) or a2 < '9';
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1      index   it1i2   it1i3   18      NULL    3       Using where; Using index
+2       SUBQUERY        NULL    NULL    NULL    NULL    NULL    NULL    NULL    Impossible WHERE
+select a1 from t1 where a1 in (select b1 from t2 where b1 = b2 and b2 = '1 - 03' and b1 = '1 - 02' ) or a2 < '9';
+a1
+1 - 00
+1 - 01
+1 - 02
+/* E.5 opt_sum_query detects no matching min/max row or substitutes MIN/MAX with a const. */
+TODO this test produces wrong result due to missing logic to handle the case
+when JOIN::optimize detects an empty subquery result.
+explain
+select a1 from t1 where a1 in (select max(b1) from t2);
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1      index   NULL    it1i1   9       NULL    3       Using where; Using index
+2       DEPENDENT SUBQUERY      t2      ALL     NULL    NULL    NULL    NULL    5       
+select a1 from t1 where a1 in (select max(b1) from t2);
+a1
+
+explain
+select a1 from t1 where a1 in (select max(b1) from t2 where b1 = '7 - 02');
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1      index   NULL    it1i1   9       NULL    3       Using where; Using index
+2       DEPENDENT SUBQUERY      t2      ALL     NULL    NULL    NULL    NULL    5       Using where
+select a1 from t1 where a1 in (select max(b1) from t2 where b1 = '7 - 02');
+a1
+/* E.6 make_join_select detects impossible WHERE. *
+TODO
+/* E.7 constant optimization detects "no matching row in const table". */
+TODO
+/* E.8 Impossible WHERE noticed after reading const tables. */
+explain
+select '1 - 03' in (select b1 from t2 where b1 > '0' and b1 < '0');
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY NULL    NULL    NULL    NULL    NULL    NULL    NULL    No tables used
+2       DEPENDENT SUBQUERY      t2      ALL     NULL    NULL    NULL    NULL    5       Using where
+select '1 - 03' in (select b1 from t2 where b1 > '0' and b1 < '0');
+'1 - 03' in (select b1 from t2 where b1 > '0' and b1 < '0')
+0
+
+/* F. UPDATE/DELETE with subqueries. */
+
+TODO
+
+/* 1.1.2 indexed table access, nullabale columns. */
+call make_t2_indexes();
+
+/* A. Subqueries in the SELECT clause. */
+explain
+select a1, a1 in (select b1 from t2 where b1 > '0') from t1;
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1      index   NULL    it1i1   9       NULL    3       Using index
+2       DEPENDENT SUBQUERY      t2      index_subquery  it2i4,it2i1,it2i3       it2i1   9       func    2       Using index; Using where; Full scan on NULL key
+select a1, a1 in (select b1 from t2 where b1 > '0') from t1;
+a1      a1 in (select b1 from t2 where b1 > '0')
+1 - 00  0
+1 - 01  1
+1 - 02  1
+
+explain
+select a1, a2, (a1, a2) in (select b1, b2 from t2 where b1 > '0') from t1;
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1      index   NULL    it1i3   18      NULL    3       Using index
+2       DEPENDENT SUBQUERY      t2      index_subquery  it2i4,it2i1,it2i2,it2i3 it2i3   9       func    2       Using index; Using where; Full scan on NULL key
+select a1, a2, (a1, a2) in (select b1, b2 from t2 where b1 > '0') from t1;
+a1      a2      (a1, a2) in (select b1, b2 from t2 where b1 > '0')
+1 - 00  2 - 00  0
+1 - 01  2 - 01  1
+1 - 02  2 - 02  1
+
+explain
+select a1, a2, (a1, a2) in (select b1, b2 from t2 where b1 > '0' and b1 < '9') from t1;
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1      index   NULL    it1i3   18      NULL    3       Using index
+2       DEPENDENT SUBQUERY      t2      index_subquery  it2i4,it2i1,it2i2,it2i3 it2i3   9       func    2       Using index; Using where; Full scan on NULL key
+select a1, a2, (a1, a2) in (select b1, b2 from t2 where b1 > '0' and b1 < '9') from t1;
+a1      a2      (a1, a2) in (select b1, b2 from t2 where b1 > '0' and b1 < '9')
+1 - 00  2 - 00  0
+1 - 01  2 - 01  1
+1 - 02  2 - 02  1
+
+/*
+B. "Natural" examples of subqueries without grouping that
+cannot be flattened into semijoin.
+*/
+explain
+select a1 from t1 where a1 in (select b2 from t2) or a2 < '9';
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1      index   it1i2   it1i3   18      NULL    3       Using where; Using index
+2       DEPENDENT SUBQUERY      t2      index_subquery  it2i2   it2i2   9       func    2       Using index
+select a1 from t1 where a1 in (select b2 from t2) or a2 < '9';
+a1
+1 - 00
+1 - 01
+1 - 02
+
+explain
+select a1, a2 from t1 where (a1, a2) in (select b1, b2 from t2 where b1 > '0') or a2 < '9';
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1      index   it1i2   it1i3   18      NULL    3       Using where; Using index
+2       DEPENDENT SUBQUERY      t2      index_subquery  it2i4,it2i1,it2i2,it2i3 it2i4   18      func,func       1       Using index; Using where
+select a1, a2 from t1 where (a1, a2) in (select b1, b2 from t2 where b1 > '0') or a2 < '9';
+a1      a2
+1 - 00  2 - 00
+1 - 01  2 - 01
+1 - 02  2 - 02
+UNION subqueries are currently limited to only use IN-TO-EXISTS.
+explain
+select a2 from t1 where a2 in (select b2 from t2 UNION select b3 from t2 as t3);
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1      index   NULL    it1i2   9       NULL    3       Using where; Using index
+2       DEPENDENT SUBQUERY      t2      ref     it2i2   it2i2   9       func    2       Using index
+3       DEPENDENT UNION t3      index   NULL    it2i4   27      NULL    5       Using where; Using index
+NULL    UNION RESULT    <union2,3>      ALL     NULL    NULL    NULL    NULL    NULL    
+select a2 from t1 where a2 in (select b2 from t2 UNION select b3 from t2 as t3);
+a2
+2 - 01
+2 - 02
+
+explain
+select a1 from t1 where a1 = '1 - 02' and a1 in (select max(b1) from t2 where b2 = '2 - 02');
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1      ref     it1i1,it1i3     it1i1   9       const   1       Using where; Using index
+2       DEPENDENT SUBQUERY      t2      ref     it2i2   it2i2   9       const   1       Using index condition
+select a1 from t1 where a1 = '1 - 02' and a1 in (select max(b1) from t2 where b2 = '2 - 02');
+a1
+1 - 02
+
+explain
+select a1, a2 from t1 where (a1, a2) in (select b1, b2 from t2 order by b3);
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1      index   NULL    it1i3   18      NULL    3       Using where; Using index
+2       DEPENDENT SUBQUERY      t2      index_subquery  it2i4,it2i1,it2i2,it2i3 it2i4   18      func,func       1       Using index; Using where
+select a1, a2 from t1 where (a1, a2) in (select b1, b2 from t2 order by b3);
+a1      a2
+1 - 01  2 - 01
+1 - 02  2 - 02
+
+/* C. Subqueries in the WHERE clause with GROUP BY. */
+explain
+select * from t1 where a1 in (select b1 from t2 where b1 > '0' group by b1);
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1      ALL     NULL    NULL    NULL    NULL    3       Using where
+2       DEPENDENT SUBQUERY      t2      index   it2i4,it2i1,it2i3       it2i1   9       NULL    1       Using where; Using index
+select * from t1 where a1 in (select b1 from t2 where b1 > '0' group by b1);
+a1      a2      a3      a4
+1 - 01  2 - 01  3 - 01  1
+1 - 02  2 - 02  3 - 02  2
+
+explain
+select * from t1 where (a1, a2) in (select b1, b2 from t2 where b1 > '0' group by b1, b2);
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1      ALL     NULL    NULL    NULL    NULL    3       Using where
+2       DEPENDENT SUBQUERY      t2      index   it2i4,it2i1,it2i3       it2i3   18      NULL    1       Using where; Using index
+select * from t1 where (a1, a2) in (select b1, b2 from t2 where b1 > '0' group by b1, b2);
+a1      a2      a3      a4
+1 - 01  2 - 01  3 - 01  1
+1 - 02  2 - 02  3 - 02  2
+
+explain
+select * from t1 where (a1, a2) in (select b1, b2 from t2 where b1 > '0' group by b1, b2 having b2 <  '2 - 04');
+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      index   it2i4,it2i1,it2i3       it2i3   18      NULL    1       Using where; Using index
+select * from t1 where (a1, a2) in (select b1, b2 from t2 where b1 > '0' group by b1, b2 having b2 <  '2 - 04');
+a1      a2      a3      a4
+1 - 01  2 - 01  3 - 01  1
+1 - 02  2 - 02  3 - 02  2
+
+explain
+select * from t1 where (a1, a2, a3) in (select b1, b2, b3 from t2 group by b1, b2, b3);
+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      index   NULL    it2i4   27      NULL    1       Using index
+select * from t1 where (a1, a2, a3) in (select b1, b2, b3 from t2 group by b1, b2, b3);
+a1      a2      a3      a4
+1 - 01  2 - 01  3 - 01  1
+
+explain
+select * from t1 where (a1, a2, a3) in (select b1, b2, b3 from t2 where b3 = '3 - 02' group by b1, b2);
+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      index   NULL    it2i4   27      NULL    1       Using where; Using index
+select * from t1 where (a1, a2, a3) in (select b1, b2, b3 from t2 where b3 = '3 - 02' group by b1, b2);
+a1      a2      a3      a4
+
+explain
+select * from t1 where (a1,a2,a3) in (select b1,b2,b3 from t2 where b1 = '1 - 01' group by b1,b2,b3);
+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      range   it2i4,it2i1,it2i3       it2i4   27      NULL    2       Using where; Using index for group-by
+select * from t1 where (a1,a2,a3) in (select b1,b2,b3 from t2 where b1 = '1 - 01' group by b1,b2,b3);
+a1      a2      a3      a4
+1 - 01  2 - 01  3 - 01  1
+
+/*
+D. Subqueries for which materialization is not possible, and the
+optimizer reverts to in-to-exists.
+*/
+explain
+select left(a1,7), left(a2,7) from t1_1024 where a1 in (select b1 from t2_1024 where b1 > '0') or a2 < '9';
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1_1024 ALL     it1_1024i2      NULL    NULL    NULL    2       Using where
+2       DEPENDENT SUBQUERY      t2_1024 index_subquery  it2_1024i1,it2_1024i3   it2_1024i3      9       func    1       Using where
+select left(a1,7), left(a2,7) from t1_1024 where a1 in (select b1 from t2_1024 where b1 > '0') or a2 < '9';
+left(a1,7)      left(a2,7)
+1 - 00x 2 - 00x
+1 - 01x 2 - 01x
+explain
+select left(a1,7), left(a2,7) from t1_1024 where (a1,a2) in (select b1, b2 from t2_1024 where b1 > '0') or a2 < '9';
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1_1024 ALL     it1_1024i2      NULL    NULL    NULL    2       Using where
+2       DEPENDENT SUBQUERY      t2_1024 index_subquery  it2_1024i1,it2_1024i2,it2_1024i3        it2_1024i1      9       func    2       Using where
+select left(a1,7), left(a2,7) from t1_1024 where (a1,a2) in (select b1, b2 from t2_1024 where b1 > '0') or a2 < '9';
+left(a1,7)      left(a2,7)
+1 - 00x 2 - 00x
+1 - 01x 2 - 01x
+
+explain
+select left(a1,7), left(a2,7) from t1_1024 where a1 in (select substring(b1,1,1024) from t2_1024 where b1 > '0') or a2 < '9';
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1_1024 ALL     it1_1024i2      NULL    NULL    NULL    2       Using where
+2       DEPENDENT SUBQUERY      t2_1024 range   it2_1024i1,it2_1024i3   it2_1024i1      9       NULL    4       Using where
+select left(a1,7), left(a2,7) from t1_1024 where a1 in (select substring(b1,1,1024) from t2_1024 where b1 > '0') or a2 < '9';
+left(a1,7)      left(a2,7)
+1 - 00x 2 - 00x
+1 - 01x 2 - 01x
+explain
+select left(a1,7), left(a2,7) from t1_1024 where (a1,a2) in (select substring(b1,1,1024), substring(b2,1,1024) from t2_1024 where b1 > '0') or a2 < '9';
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1_1024 ALL     it1_1024i2      NULL    NULL    NULL    2       Using where
+2       DEPENDENT SUBQUERY      t2_1024 range   it2_1024i1,it2_1024i3   it2_1024i1      9       NULL    4       Using where
+select left(a1,7), left(a2,7) from t1_1024 where (a1,a2) in (select substring(b1,1,1024), substring(b2,1,1024) from t2_1024 where b1 > '0') or a2 < '9';
+left(a1,7)      left(a2,7)
+1 - 00x 2 - 00x
+1 - 01x 2 - 01x
+
+
+/* E. Edge cases. */
+
+/* E.1 Both materialization and in_to_exists cannot be off. */
+set @save_optimizer_switch=@@optimizer_switch;
+set @@optimizer_switch = 'materialization=off,in_to_exists=off';
+select * from t1 where a1 in (select b1 from t2 where b1 > '0' group by b1);
+ERROR HY000: At least one of the 'in_to_exists' or 'materialization' optimizer_switch flags must be 'on'.
+set @@optimizer_switch = @save_optimizer_switch;
+/* E.2 Outer query without tables, always uses IN-TO-EXISTS. */
+explain
+select '1 - 03' in (select b1 from t2 where b1 > '0');
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY NULL    NULL    NULL    NULL    NULL    NULL    NULL    No tables used
+2       DEPENDENT SUBQUERY      t2      index_subquery  it2i4,it2i1,it2i3       it2i1   9       const   2       Using index; Using where
+select '1 - 03' in (select b1 from t2 where b1 > '0');
+'1 - 03' in (select b1 from t2 where b1 > '0')
+1
+/* E.3 Subqueries without tables. */
+explain
+select a1 from t1 where a1 in (select '1 - 03') or a2 < '9';
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1      index   it1i1,it1i2,it1i3       it1i3   18      NULL    3       Using where; Using index
+Warnings:
+Note    1249    Select 2 was reduced during optimization
+select a1 from t1 where a1 in (select '1 - 03') or a2 < '9';
+a1
+1 - 00
+1 - 01
+1 - 02
+UNION subqueries are currently limited to only use IN-TO-EXISTS.
+explain
+select a1 from t1 where a1 in (select '1 - 03' UNION select '1 - 02');
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1      index   NULL    it1i1   9       NULL    3       Using where; Using index
+2       DEPENDENT SUBQUERY      NULL    NULL    NULL    NULL    NULL    NULL    NULL    No tables used
+3       DEPENDENT UNION NULL    NULL    NULL    NULL    NULL    NULL    NULL    No tables used
+NULL    UNION RESULT    <union2,3>      ALL     NULL    NULL    NULL    NULL    NULL    
+select a1 from t1 where a1 in (select '1 - 03' UNION select '1 - 02');
+a1
+1 - 02
+/* E.4 optimize_cond detects FALSE where/having clause. */
+explain
+select a1 from t1 where a1 in (select b1 from t2 where b1 = b2 and b2 = '1 - 03' and b1 = '1 - 02' ) or a2 < '9';
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1      index   it1i2   it1i3   18      NULL    3       Using where; Using index
+2       SUBQUERY        NULL    NULL    NULL    NULL    NULL    NULL    NULL    Impossible WHERE
+select a1 from t1 where a1 in (select b1 from t2 where b1 = b2 and b2 = '1 - 03' and b1 = '1 - 02' ) or a2 < '9';
+a1
+1 - 00
+1 - 01
+1 - 02
+/* E.5 opt_sum_query detects no matching min/max row or substitutes MIN/MAX with a const. */
+TODO this test produces wrong result due to missing logic to handle the case
+when JOIN::optimize detects an empty subquery result.
+explain
+select a1 from t1 where a1 in (select max(b1) from t2);
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1      index   NULL    it1i1   9       NULL    3       Using where; Using index
+2       DEPENDENT SUBQUERY      NULL    NULL    NULL    NULL    NULL    NULL    NULL    Select tables optimized away
+select a1 from t1 where a1 in (select max(b1) from t2);
+a1
+
+explain
+select a1 from t1 where a1 in (select max(b1) from t2 where b1 = '7 - 02');
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1      index   NULL    it1i1   9       NULL    3       Using where; Using index
+2       DEPENDENT SUBQUERY      NULL    NULL    NULL    NULL    NULL    NULL    NULL    No matching min/max row
+select a1 from t1 where a1 in (select max(b1) from t2 where b1 = '7 - 02');
+a1
+/* E.6 make_join_select detects impossible WHERE. *
+TODO
+/* E.7 constant optimization detects "no matching row in const table". */
+TODO
+/* E.8 Impossible WHERE noticed after reading const tables. */
+explain
+select '1 - 03' in (select b1 from t2 where b1 > '0' and b1 < '0');
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY NULL    NULL    NULL    NULL    NULL    NULL    NULL    No tables used
+2       DEPENDENT SUBQUERY      NULL    NULL    NULL    NULL    NULL    NULL    NULL    Impossible WHERE noticed after reading const tables
+select '1 - 03' in (select b1 from t2 where b1 > '0' and b1 < '0');
+'1 - 03' in (select b1 from t2 where b1 > '0' and b1 < '0')
+0
+
+/* F. UPDATE/DELETE with subqueries. */
+
+TODO
+
+/* 1.1.3 indexed table access, non-nullabale columns. */
+call set_all_columns_not_null();
+
+/* A. Subqueries in the SELECT clause. */
+explain
+select a1, a1 in (select b1 from t2 where b1 > '0') from t1;
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1      index   NULL    it1i1   8       NULL    3       Using index
+2       DEPENDENT SUBQUERY      t2      index_subquery  it2i4,it2i1,it2i3       it2i4   8       func    1       Using index; Using where
+select a1, a1 in (select b1 from t2 where b1 > '0') from t1;
+a1      a1 in (select b1 from t2 where b1 > '0')
+1 - 00  0
+1 - 01  1
+1 - 02  1
+
+explain
+select a1, a2, (a1, a2) in (select b1, b2 from t2 where b1 > '0') from t1;
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1      index   NULL    it1i3   16      NULL    3       Using index
+2       DEPENDENT SUBQUERY      t2      ref     it2i4,it2i1,it2i2,it2i3 it2i4   8       func    2       Using where; Using index
+select a1, a2, (a1, a2) in (select b1, b2 from t2 where b1 > '0') from t1;
+a1      a2      (a1, a2) in (select b1, b2 from t2 where b1 > '0')
+1 - 00  2 - 00  0
+1 - 01  2 - 01  1
+1 - 02  2 - 02  1
+
+explain
+select a1, a2, (a1, a2) in (select b1, b2 from t2 where b1 > '0' and b1 < '9') from t1;
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1      index   NULL    it1i3   16      NULL    3       Using index
+2       DEPENDENT SUBQUERY      t2      ref     it2i4,it2i1,it2i2,it2i3 it2i4   8       func    2       Using where; Using index
+select a1, a2, (a1, a2) in (select b1, b2 from t2 where b1 > '0' and b1 < '9') from t1;
+a1      a2      (a1, a2) in (select b1, b2 from t2 where b1 > '0' and b1 < '9')
+1 - 00  2 - 00  0
+1 - 01  2 - 01  1
+1 - 02  2 - 02  1
+
+/*
+B. "Natural" examples of subqueries without grouping that
+cannot be flattened into semijoin.
+*/
+explain
+select a1 from t1 where a1 in (select b2 from t2) or a2 < '9';
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1      index   it1i2   it1i3   16      NULL    3       Using where; Using index
+2       DEPENDENT SUBQUERY      t2      index_subquery  it2i2   it2i2   8       func    2       Using index
+select a1 from t1 where a1 in (select b2 from t2) or a2 < '9';
+a1
+1 - 00
+1 - 01
+1 - 02
+
+explain
+select a1, a2 from t1 where (a1, a2) in (select b1, b2 from t2 where b1 > '0') or a2 < '9';
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1      index   it1i2   it1i3   16      NULL    3       Using where; Using index
+2       DEPENDENT SUBQUERY      t2      index_subquery  it2i4,it2i1,it2i2,it2i3 it2i4   16      func,func       1       Using index; Using where
+select a1, a2 from t1 where (a1, a2) in (select b1, b2 from t2 where b1 > '0') or a2 < '9';
+a1      a2
+1 - 00  2 - 00
+1 - 01  2 - 01
+1 - 02  2 - 02
+UNION subqueries are currently limited to only use IN-TO-EXISTS.
+explain
+select a2 from t1 where a2 in (select b2 from t2 UNION select b3 from t2 as t3);
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1      index   NULL    it1i2   8       NULL    3       Using where; Using index
+2       DEPENDENT SUBQUERY      t2      ref     it2i2   it2i2   8       func    2       Using index
+3       DEPENDENT UNION t3      index   NULL    it2i4   24      NULL    5       Using where; Using index
+NULL    UNION RESULT    <union2,3>      ALL     NULL    NULL    NULL    NULL    NULL    
+select a2 from t1 where a2 in (select b2 from t2 UNION select b3 from t2 as t3);
+a2
+2 - 01
+2 - 02
+
+explain
+select a1 from t1 where a1 = '1 - 02' and a1 in (select max(b1) from t2 where b2 = '2 - 02');
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1      ref     it1i1,it1i3     it1i1   8       const   1       Using where; Using index
+2       DEPENDENT SUBQUERY      t2      ref     it2i2   it2i2   8       const   1       Using index condition
+select a1 from t1 where a1 = '1 - 02' and a1 in (select max(b1) from t2 where b2 = '2 - 02');
+a1
+1 - 02
+
+explain
+select a1, a2 from t1 where (a1, a2) in (select b1, b2 from t2 order by b3);
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1      index   NULL    it1i3   16      NULL    3       Using where; Using index
+2       DEPENDENT SUBQUERY      t2      index_subquery  it2i4,it2i1,it2i2,it2i3 it2i4   16      func,func       1       Using index; Using where
+select a1, a2 from t1 where (a1, a2) in (select b1, b2 from t2 order by b3);
+a1      a2
+1 - 01  2 - 01
+1 - 02  2 - 02
+
+/* C. Subqueries in the WHERE clause with GROUP BY. */
+explain
+select * from t1 where a1 in (select b1 from t2 where b1 > '0' group by b1);
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1      ALL     NULL    NULL    NULL    NULL    3       Using where
+2       DEPENDENT SUBQUERY      t2      index   it2i4,it2i1,it2i3       it2i1   8       NULL    1       Using where; Using index
+select * from t1 where a1 in (select b1 from t2 where b1 > '0' group by b1);
+a1      a2      a3      a4
+1 - 01  2 - 01  3 - 01  1
+1 - 02  2 - 02  3 - 02  2
+
+explain
+select * from t1 where (a1, a2) in (select b1, b2 from t2 where b1 > '0' group by b1, b2);
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1      ALL     NULL    NULL    NULL    NULL    3       Using where
+2       DEPENDENT SUBQUERY      t2      index   it2i4,it2i1,it2i3       it2i3   16      NULL    1       Using where; Using index
+select * from t1 where (a1, a2) in (select b1, b2 from t2 where b1 > '0' group by b1, b2);
+a1      a2      a3      a4
+1 - 01  2 - 01  3 - 01  1
+1 - 02  2 - 02  3 - 02  2
+
+explain
+select * from t1 where (a1, a2) in (select b1, b2 from t2 where b1 > '0' group by b1, b2 having b2 <  '2 - 04');
+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      index   it2i4,it2i1,it2i3       it2i3   16      NULL    1       Using where; Using index
+select * from t1 where (a1, a2) in (select b1, b2 from t2 where b1 > '0' group by b1, b2 having b2 <  '2 - 04');
+a1      a2      a3      a4
+1 - 01  2 - 01  3 - 01  1
+1 - 02  2 - 02  3 - 02  2
+
+explain
+select * from t1 where (a1, a2, a3) in (select b1, b2, b3 from t2 group by b1, b2, b3);
+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      index   NULL    it2i4   24      NULL    1       Using index
+select * from t1 where (a1, a2, a3) in (select b1, b2, b3 from t2 group by b1, b2, b3);
+a1      a2      a3      a4
+1 - 01  2 - 01  3 - 01  1
+
+explain
+select * from t1 where (a1, a2, a3) in (select b1, b2, b3 from t2 where b3 = '3 - 02' group by b1, b2);
+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      index   NULL    it2i4   24      NULL    1       Using where; Using index
+select * from t1 where (a1, a2, a3) in (select b1, b2, b3 from t2 where b3 = '3 - 02' group by b1, b2);
+a1      a2      a3      a4
+
+explain
+select * from t1 where (a1,a2,a3) in (select b1,b2,b3 from t2 where b1 = '1 - 01' group by b1,b2,b3);
+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      range   it2i4,it2i1,it2i3       it2i4   24      NULL    2       Using where; Using index for group-by
+select * from t1 where (a1,a2,a3) in (select b1,b2,b3 from t2 where b1 = '1 - 01' group by b1,b2,b3);
+a1      a2      a3      a4
+1 - 01  2 - 01  3 - 01  1
+
+/*
+D. Subqueries for which materialization is not possible, and the
+optimizer reverts to in-to-exists.
+*/
+explain
+select left(a1,7), left(a2,7) from t1_1024 where a1 in (select b1 from t2_1024 where b1 > '0') or a2 < '9';
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1_1024 ALL     it1_1024i2      NULL    NULL    NULL    2       Using where
+2       DEPENDENT SUBQUERY      t2_1024 index_subquery  it2_1024i1,it2_1024i3   it2_1024i3      9       func    1       Using where
+select left(a1,7), left(a2,7) from t1_1024 where a1 in (select b1 from t2_1024 where b1 > '0') or a2 < '9';
+left(a1,7)      left(a2,7)
+1 - 00x 2 - 00x
+1 - 01x 2 - 01x
+explain
+select left(a1,7), left(a2,7) from t1_1024 where (a1,a2) in (select b1, b2 from t2_1024 where b1 > '0') or a2 < '9';
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1_1024 ALL     it1_1024i2      NULL    NULL    NULL    2       Using where
+2       DEPENDENT SUBQUERY      t2_1024 index_subquery  it2_1024i1,it2_1024i2,it2_1024i3        it2_1024i1      9       func    2       Using where
+select left(a1,7), left(a2,7) from t1_1024 where (a1,a2) in (select b1, b2 from t2_1024 where b1 > '0') or a2 < '9';
+left(a1,7)      left(a2,7)
+1 - 00x 2 - 00x
+1 - 01x 2 - 01x
+
+explain
+select left(a1,7), left(a2,7) from t1_1024 where a1 in (select substring(b1,1,1024) from t2_1024 where b1 > '0') or a2 < '9';
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1_1024 ALL     it1_1024i2      NULL    NULL    NULL    2       Using where
+2       DEPENDENT SUBQUERY      t2_1024 range   it2_1024i1,it2_1024i3   it2_1024i1      9       NULL    4       Using where
+select left(a1,7), left(a2,7) from t1_1024 where a1 in (select substring(b1,1,1024) from t2_1024 where b1 > '0') or a2 < '9';
+left(a1,7)      left(a2,7)
+1 - 00x 2 - 00x
+1 - 01x 2 - 01x
+explain
+select left(a1,7), left(a2,7) from t1_1024 where (a1,a2) in (select substring(b1,1,1024), substring(b2,1,1024) from t2_1024 where b1 > '0') or a2 < '9';
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1_1024 ALL     it1_1024i2      NULL    NULL    NULL    2       Using where
+2       DEPENDENT SUBQUERY      t2_1024 range   it2_1024i1,it2_1024i3   it2_1024i1      9       NULL    4       Using where
+select left(a1,7), left(a2,7) from t1_1024 where (a1,a2) in (select substring(b1,1,1024), substring(b2,1,1024) from t2_1024 where b1 > '0') or a2 < '9';
+left(a1,7)      left(a2,7)
+1 - 00x 2 - 00x
+1 - 01x 2 - 01x
+
+
+/* E. Edge cases. */
+
+/* E.1 Both materialization and in_to_exists cannot be off. */
+set @save_optimizer_switch=@@optimizer_switch;
+set @@optimizer_switch = 'materialization=off,in_to_exists=off';
+select * from t1 where a1 in (select b1 from t2 where b1 > '0' group by b1);
+ERROR HY000: At least one of the 'in_to_exists' or 'materialization' optimizer_switch flags must be 'on'.
+set @@optimizer_switch = @save_optimizer_switch;
+/* E.2 Outer query without tables, always uses IN-TO-EXISTS. */
+explain
+select '1 - 03' in (select b1 from t2 where b1 > '0');
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY NULL    NULL    NULL    NULL    NULL    NULL    NULL    No tables used
+2       DEPENDENT SUBQUERY      t2      index_subquery  it2i4,it2i1,it2i3       it2i1   8       const   5       Using index; Using where
+select '1 - 03' in (select b1 from t2 where b1 > '0');
+'1 - 03' in (select b1 from t2 where b1 > '0')
+1
+/* E.3 Subqueries without tables. */
+explain
+select a1 from t1 where a1 in (select '1 - 03') or a2 < '9';
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1      index   it1i1,it1i2,it1i3       it1i3   16      NULL    3       Using where; Using index
+Warnings:
+Note    1249    Select 2 was reduced during optimization
+select a1 from t1 where a1 in (select '1 - 03') or a2 < '9';
+a1
+1 - 00
+1 - 01
+1 - 02
+UNION subqueries are currently limited to only use IN-TO-EXISTS.
+explain
+select a1 from t1 where a1 in (select '1 - 03' UNION select '1 - 02');
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1      index   NULL    it1i1   8       NULL    3       Using where; Using index
+2       DEPENDENT SUBQUERY      NULL    NULL    NULL    NULL    NULL    NULL    NULL    No tables used
+3       DEPENDENT UNION NULL    NULL    NULL    NULL    NULL    NULL    NULL    No tables used
+NULL    UNION RESULT    <union2,3>      ALL     NULL    NULL    NULL    NULL    NULL    
+select a1 from t1 where a1 in (select '1 - 03' UNION select '1 - 02');
+a1
+1 - 02
+/* E.4 optimize_cond detects FALSE where/having clause. */
+explain
+select a1 from t1 where a1 in (select b1 from t2 where b1 = b2 and b2 = '1 - 03' and b1 = '1 - 02' ) or a2 < '9';
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1      index   it1i2   it1i3   16      NULL    3       Using where; Using index
+2       SUBQUERY        NULL    NULL    NULL    NULL    NULL    NULL    NULL    Impossible WHERE
+select a1 from t1 where a1 in (select b1 from t2 where b1 = b2 and b2 = '1 - 03' and b1 = '1 - 02' ) or a2 < '9';
+a1
+1 - 00
+1 - 01
+1 - 02
+/* E.5 opt_sum_query detects no matching min/max row or substitutes MIN/MAX with a const. */
+TODO this test produces wrong result due to missing logic to handle the case
+when JOIN::optimize detects an empty subquery result.
+explain
+select a1 from t1 where a1 in (select max(b1) from t2);
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1      index   NULL    it1i1   8       NULL    3       Using where; Using index
+2       DEPENDENT SUBQUERY      NULL    NULL    NULL    NULL    NULL    NULL    NULL    Select tables optimized away
+select a1 from t1 where a1 in (select max(b1) from t2);
+a1
+
+explain
+select a1 from t1 where a1 in (select max(b1) from t2 where b1 = '7 - 02');
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1      index   NULL    it1i1   8       NULL    3       Using where; Using index
+2       DEPENDENT SUBQUERY      NULL    NULL    NULL    NULL    NULL    NULL    NULL    No matching min/max row
+select a1 from t1 where a1 in (select max(b1) from t2 where b1 = '7 - 02');
+a1
+/* E.6 make_join_select detects impossible WHERE. *
+TODO
+/* E.7 constant optimization detects "no matching row in const table". */
+TODO
+/* E.8 Impossible WHERE noticed after reading const tables. */
+explain
+select '1 - 03' in (select b1 from t2 where b1 > '0' and b1 < '0');
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY NULL    NULL    NULL    NULL    NULL    NULL    NULL    No tables used
+2       DEPENDENT SUBQUERY      NULL    NULL    NULL    NULL    NULL    NULL    NULL    Impossible WHERE noticed after reading const tables
+select '1 - 03' in (select b1 from t2 where b1 > '0' and b1 < '0');
+'1 - 03' in (select b1 from t2 where b1 > '0' and b1 < '0')
+0
+
+/* F. UPDATE/DELETE with subqueries. */
+
+TODO
+
+call set_all_columns_nullable();
+
+/* 1.2 Materialization is cheaper */
+call add_materialization_data();
+call remove_t1_indexes();
+/* 1.2.1 non-indexed table access */
+call remove_t2_indexes();
+
+/* A. Subqueries in the SELECT clause. */
+explain
+select a1, a1 in (select b1 from t2 where b1 > '0') from t1;
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1      ALL     NULL    NULL    NULL    NULL    8       
+2       SUBQUERY        t2      ALL     NULL    NULL    NULL    NULL    5       Using where
+select a1, a1 in (select b1 from t2 where b1 > '0') from t1;
+a1      a1 in (select b1 from t2 where b1 > '0')
+1 - 00  0
+1 - 01  1
+1 - 02  1
+1 - 03  1
+1 - 04  0
+1 - 05  0
+1 - 06  0
+1 - 07  0
+
+explain
+select a1, a2, (a1, a2) in (select b1, b2 from t2 where b1 > '0') from t1;
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1      ALL     NULL    NULL    NULL    NULL    8       
+2       SUBQUERY        t2      ALL     NULL    NULL    NULL    NULL    5       Using where
+select a1, a2, (a1, a2) in (select b1, b2 from t2 where b1 > '0') from t1;
+a1      a2      (a1, a2) in (select b1, b2 from t2 where b1 > '0')
+1 - 00  2 - 00  0
+1 - 01  2 - 01  1
+1 - 02  2 - 02  1
+1 - 03  2 - 03  1
+1 - 04  2 - 04  0
+1 - 05  2 - 05  0
+1 - 06  2 - 06  0
+1 - 07  2 - 07  0
+
+explain
+select a1, a2, (a1, a2) in (select b1, b2 from t2 where b1 > '0' and b1 < '9') from t1;
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1      ALL     NULL    NULL    NULL    NULL    8       
+2       SUBQUERY        t2      ALL     NULL    NULL    NULL    NULL    5       Using where
+select a1, a2, (a1, a2) in (select b1, b2 from t2 where b1 > '0' and b1 < '9') from t1;
+a1      a2      (a1, a2) in (select b1, b2 from t2 where b1 > '0' and b1 < '9')
+1 - 00  2 - 00  0
+1 - 01  2 - 01  1
+1 - 02  2 - 02  1
+1 - 03  2 - 03  1
+1 - 04  2 - 04  0
+1 - 05  2 - 05  0
+1 - 06  2 - 06  0
+1 - 07  2 - 07  0
+
+/*
+B. "Natural" examples of subqueries without grouping that
+cannot be flattened into semijoin.
+*/
+explain
+select a1 from t1 where a1 in (select b2 from t2) or a2 < '9';
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1      ALL     NULL    NULL    NULL    NULL    8       Using where
+2       SUBQUERY        t2      ALL     NULL    NULL    NULL    NULL    5       
+select a1 from t1 where a1 in (select b2 from t2) or a2 < '9';
+a1
+1 - 00
+1 - 01
+1 - 02
+1 - 03
+1 - 04
+1 - 05
+1 - 06
+1 - 07
+
+explain
+select a1, a2 from t1 where (a1, a2) in (select b1, b2 from t2 where b1 > '0') or a2 < '9';
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1      ALL     NULL    NULL    NULL    NULL    8       Using where
+2       SUBQUERY        t2      ALL     NULL    NULL    NULL    NULL    5       Using where
+select a1, a2 from t1 where (a1, a2) in (select b1, b2 from t2 where b1 > '0') or a2 < '9';
+a1      a2
+1 - 00  2 - 00
+1 - 01  2 - 01
+1 - 02  2 - 02
+1 - 03  2 - 03
+1 - 04  2 - 04
+1 - 05  2 - 05
+1 - 06  2 - 06
+1 - 07  2 - 07
+UNION subqueries are currently limited to only use IN-TO-EXISTS.
+explain
+select a2 from t1 where a2 in (select b2 from t2 UNION select b3 from t2 as t3);
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1      ALL     NULL    NULL    NULL    NULL    8       Using where
+2       DEPENDENT SUBQUERY      t2      ALL     NULL    NULL    NULL    NULL    5       Using where
+3       DEPENDENT UNION t3      ALL     NULL    NULL    NULL    NULL    5       Using where
+NULL    UNION RESULT    <union2,3>      ALL     NULL    NULL    NULL    NULL    NULL    
+select a2 from t1 where a2 in (select b2 from t2 UNION select b3 from t2 as t3);
+a2
+2 - 01
+2 - 02
+2 - 03
+
+explain
+select a1 from t1 where a1 = '1 - 02' and a1 in (select max(b1) from t2 where b2 = '2 - 02');
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1      ALL     NULL    NULL    NULL    NULL    8       Using where
+2       SUBQUERY        t2      ALL     NULL    NULL    NULL    NULL    5       Using where
+select a1 from t1 where a1 = '1 - 02' and a1 in (select max(b1) from t2 where b2 = '2 - 02');
+a1
+1 - 02
+
+explain
+select a1, a2 from t1 where (a1, a2) in (select b1, b2 from t2 order by b3);
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1      ALL     NULL    NULL    NULL    NULL    8       Using where
+2       SUBQUERY        t2      ALL     NULL    NULL    NULL    NULL    5       
+select a1, a2 from t1 where (a1, a2) in (select b1, b2 from t2 order by b3);
+a1      a2
+1 - 01  2 - 01
+1 - 02  2 - 02
+1 - 03  2 - 03
+
+/* C. Subqueries in the WHERE clause with GROUP BY. */
+explain
+select * from t1 where a1 in (select b1 from t2 where b1 > '0' group by b1);
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1      ALL     NULL    NULL    NULL    NULL    8       Using where
+2       SUBQUERY        t2      ALL     NULL    NULL    NULL    NULL    5       Using where; Using temporary; Using filesort
+select * from t1 where a1 in (select b1 from t2 where b1 > '0' group by b1);
+a1      a2      a3      a4
+1 - 01  2 - 01  3 - 01  1
+1 - 02  2 - 02  3 - 02  2
+1 - 03  2 - 03  3 - 03  3
+
+explain
+select * from t1 where (a1, a2) in (select b1, b2 from t2 where b1 > '0' group by b1, b2);
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1      ALL     NULL    NULL    NULL    NULL    8       Using where
+2       SUBQUERY        t2      ALL     NULL    NULL    NULL    NULL    5       Using where; Using temporary; Using filesort
+select * from t1 where (a1, a2) in (select b1, b2 from t2 where b1 > '0' group by b1, b2);
+a1      a2      a3      a4
+1 - 01  2 - 01  3 - 01  1
+1 - 02  2 - 02  3 - 02  2
+1 - 03  2 - 03  3 - 03  3
+
+explain
+select * from t1 where (a1, a2) in (select b1, b2 from t2 where b1 > '0' group by b1, b2 having b2 <  '2 - 04');
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1      ALL     NULL    NULL    NULL    NULL    8       Using where
+2       SUBQUERY        t2      ALL     NULL    NULL    NULL    NULL    5       Using where; Using temporary; Using filesort
+select * from t1 where (a1, a2) in (select b1, b2 from t2 where b1 > '0' group by b1, b2 having b2 <  '2 - 04');
+a1      a2      a3      a4
+1 - 01  2 - 01  3 - 01  1
+1 - 02  2 - 02  3 - 02  2
+1 - 03  2 - 03  3 - 03  3
+
+explain
+select * from t1 where (a1, a2, a3) in (select b1, b2, b3 from t2 group by b1, b2, b3);
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1      ALL     NULL    NULL    NULL    NULL    8       Using where
+2       SUBQUERY        t2      ALL     NULL    NULL    NULL    NULL    5       Using temporary; Using filesort
+select * from t1 where (a1, a2, a3) in (select b1, b2, b3 from t2 group by b1, b2, b3);
+a1      a2      a3      a4
+1 - 01  2 - 01  3 - 01  1
+
+explain
+select * from t1 where (a1, a2, a3) in (select b1, b2, b3 from t2 where b3 = '3 - 02' group by b1, b2);
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1      ALL     NULL    NULL    NULL    NULL    8       Using where
+2       SUBQUERY        t2      ALL     NULL    NULL    NULL    NULL    5       Using where; Using temporary; Using filesort
+select * from t1 where (a1, a2, a3) in (select b1, b2, b3 from t2 where b3 = '3 - 02' group by b1, b2);
+a1      a2      a3      a4
+
+explain
+select * from t1 where (a1,a2,a3) in (select b1,b2,b3 from t2 where b1 = '1 - 01' group by b1,b2,b3);
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1      ALL     NULL    NULL    NULL    NULL    8       Using where
+2       SUBQUERY        t2      ALL     NULL    NULL    NULL    NULL    5       Using where; Using temporary; Using filesort
+select * from t1 where (a1,a2,a3) in (select b1,b2,b3 from t2 where b1 = '1 - 01' group by b1,b2,b3);
+a1      a2      a3      a4
+1 - 01  2 - 01  3 - 01  1
+
+/*
+D. Subqueries for which materialization is not possible, and the
+optimizer reverts to in-to-exists.
+*/
+explain
+select left(a1,7), left(a2,7) from t1_1024 where a1 in (select b1 from t2_1024 where b1 > '0') or a2 < '9';
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1_1024 ALL     NULL    NULL    NULL    NULL    3       Using where
+2       DEPENDENT SUBQUERY      t2_1024 ALL     NULL    NULL    NULL    NULL    4       Using where
+select left(a1,7), left(a2,7) from t1_1024 where a1 in (select b1 from t2_1024 where b1 > '0') or a2 < '9';
+left(a1,7)      left(a2,7)
+1 - 00x 2 - 00x
+1 - 01x 2 - 01x
+1 - 03x 2 - 03x
+explain
+select left(a1,7), left(a2,7) from t1_1024 where (a1,a2) in (select b1, b2 from t2_1024 where b1 > '0') or a2 < '9';
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1_1024 ALL     NULL    NULL    NULL    NULL    3       Using where
+2       DEPENDENT SUBQUERY      t2_1024 ALL     NULL    NULL    NULL    NULL    4       Using where
+select left(a1,7), left(a2,7) from t1_1024 where (a1,a2) in (select b1, b2 from t2_1024 where b1 > '0') or a2 < '9';
+left(a1,7)      left(a2,7)
+1 - 00x 2 - 00x
+1 - 01x 2 - 01x
+1 - 03x 2 - 03x
+
+explain
+select left(a1,7), left(a2,7) from t1_1024 where a1 in (select substring(b1,1,1024) from t2_1024 where b1 > '0') or a2 < '9';
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1_1024 ALL     NULL    NULL    NULL    NULL    3       Using where
+2       DEPENDENT SUBQUERY      t2_1024 ALL     NULL    NULL    NULL    NULL    4       Using where
+select left(a1,7), left(a2,7) from t1_1024 where a1 in (select substring(b1,1,1024) from t2_1024 where b1 > '0') or a2 < '9';
+left(a1,7)      left(a2,7)
+1 - 00x 2 - 00x
+1 - 01x 2 - 01x
+1 - 03x 2 - 03x
+explain
+select left(a1,7), left(a2,7) from t1_1024 where (a1,a2) in (select substring(b1,1,1024), substring(b2,1,1024) from t2_1024 where b1 > '0') or a2 < '9';
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1_1024 ALL     NULL    NULL    NULL    NULL    3       Using where
+2       DEPENDENT SUBQUERY      t2_1024 ALL     NULL    NULL    NULL    NULL    4       Using where
+select left(a1,7), left(a2,7) from t1_1024 where (a1,a2) in (select substring(b1,1,1024), substring(b2,1,1024) from t2_1024 where b1 > '0') or a2 < '9';
+left(a1,7)      left(a2,7)
+1 - 00x 2 - 00x
+1 - 01x 2 - 01x
+1 - 03x 2 - 03x
+
+
+/* E. Edge cases. */
+
+/* E.1 Both materialization and in_to_exists cannot be off. */
+set @save_optimizer_switch=@@optimizer_switch;
+set @@optimizer_switch = 'materialization=off,in_to_exists=off';
+select * from t1 where a1 in (select b1 from t2 where b1 > '0' group by b1);
+ERROR HY000: At least one of the 'in_to_exists' or 'materialization' optimizer_switch flags must be 'on'.
+set @@optimizer_switch = @save_optimizer_switch;
+/* E.2 Outer query without tables, always uses IN-TO-EXISTS. */
+explain
+select '1 - 03' in (select b1 from t2 where b1 > '0');
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY NULL    NULL    NULL    NULL    NULL    NULL    NULL    No tables used
+2       DEPENDENT SUBQUERY      t2      ALL     NULL    NULL    NULL    NULL    5       Using where
+select '1 - 03' in (select b1 from t2 where b1 > '0');
+'1 - 03' in (select b1 from t2 where b1 > '0')
+1
+/* E.3 Subqueries without tables. */
+explain
+select a1 from t1 where a1 in (select '1 - 03') or a2 < '9';
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1      ALL     NULL    NULL    NULL    NULL    8       Using where
+Warnings:
+Note    1249    Select 2 was reduced during optimization
+select a1 from t1 where a1 in (select '1 - 03') or a2 < '9';
+a1
+1 - 00
+1 - 01
+1 - 02
+1 - 03
+1 - 04
+1 - 05
+1 - 06
+1 - 07
+UNION subqueries are currently limited to only use IN-TO-EXISTS.
+explain
+select a1 from t1 where a1 in (select '1 - 03' UNION select '1 - 02');
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1      ALL     NULL    NULL    NULL    NULL    8       Using where
+2       DEPENDENT SUBQUERY      NULL    NULL    NULL    NULL    NULL    NULL    NULL    No tables used
+3       DEPENDENT UNION NULL    NULL    NULL    NULL    NULL    NULL    NULL    No tables used
+NULL    UNION RESULT    <union2,3>      ALL     NULL    NULL    NULL    NULL    NULL    
+select a1 from t1 where a1 in (select '1 - 03' UNION select '1 - 02');
+a1
+1 - 02
+1 - 03
+/* E.4 optimize_cond detects FALSE where/having clause. */
+explain
+select a1 from t1 where a1 in (select b1 from t2 where b1 = b2 and b2 = '1 - 03' and b1 = '1 - 02' ) or a2 < '9';
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1      ALL     NULL    NULL    NULL    NULL    8       Using where
+2       SUBQUERY        NULL    NULL    NULL    NULL    NULL    NULL    NULL    Impossible WHERE
+select a1 from t1 where a1 in (select b1 from t2 where b1 = b2 and b2 = '1 - 03' and b1 = '1 - 02' ) or a2 < '9';
+a1
+1 - 00
+1 - 01
+1 - 02
+1 - 03
+1 - 04
+1 - 05
+1 - 06
+1 - 07
+/* E.5 opt_sum_query detects no matching min/max row or substitutes MIN/MAX with a const. */
+TODO this test produces wrong result due to missing logic to handle the case
+when JOIN::optimize detects an empty subquery result.
+explain
+select a1 from t1 where a1 in (select max(b1) from t2);
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1      ALL     NULL    NULL    NULL    NULL    8       Using where
+2       SUBQUERY        t2      ALL     NULL    NULL    NULL    NULL    5       
+select a1 from t1 where a1 in (select max(b1) from t2);
+a1
+1 - 03
+
+explain
+select a1 from t1 where a1 in (select max(b1) from t2 where b1 = '7 - 02');
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1      ALL     NULL    NULL    NULL    NULL    8       Using where
+2       SUBQUERY        t2      ALL     NULL    NULL    NULL    NULL    5       Using where
+select a1 from t1 where a1 in (select max(b1) from t2 where b1 = '7 - 02');
+a1
+/* E.6 make_join_select detects impossible WHERE. *
+TODO
+/* E.7 constant optimization detects "no matching row in const table". */
+TODO
+/* E.8 Impossible WHERE noticed after reading const tables. */
+explain
+select '1 - 03' in (select b1 from t2 where b1 > '0' and b1 < '0');
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY NULL    NULL    NULL    NULL    NULL    NULL    NULL    No tables used
+2       DEPENDENT SUBQUERY      t2      ALL     NULL    NULL    NULL    NULL    5       Using where
+select '1 - 03' in (select b1 from t2 where b1 > '0' and b1 < '0');
+'1 - 03' in (select b1 from t2 where b1 > '0' and b1 < '0')
+0
+
+/* F. UPDATE/DELETE with subqueries. */
+
+TODO
+
+/* 1.2.2 indexed table access, nullabale columns. */
+call make_t2_indexes();
+
+/* A. Subqueries in the SELECT clause. */
+explain
+select a1, a1 in (select b1 from t2 where b1 > '0') from t1;
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1      ALL     NULL    NULL    NULL    NULL    8       
+2       SUBQUERY        t2      index   it2i4,it2i1,it2i3       it2i1   9       NULL    5       Using where; Using index
+select a1, a1 in (select b1 from t2 where b1 > '0') from t1;
+a1      a1 in (select b1 from t2 where b1 > '0')
+1 - 00  0
+1 - 01  1
+1 - 02  1
+1 - 03  1
+1 - 04  0
+1 - 05  0
+1 - 06  0
+1 - 07  0
+
+explain
+select a1, a2, (a1, a2) in (select b1, b2 from t2 where b1 > '0') from t1;
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1      ALL     NULL    NULL    NULL    NULL    8       
+2       SUBQUERY        t2      index   it2i4,it2i1,it2i2,it2i3 it2i3   18      NULL    5       Using where; Using index
+select a1, a2, (a1, a2) in (select b1, b2 from t2 where b1 > '0') from t1;
+a1      a2      (a1, a2) in (select b1, b2 from t2 where b1 > '0')
+1 - 00  2 - 00  0
+1 - 01  2 - 01  1
+1 - 02  2 - 02  1
+1 - 03  2 - 03  1
+1 - 04  2 - 04  0
+1 - 05  2 - 05  0
+1 - 06  2 - 06  0
+1 - 07  2 - 07  0
+
+explain
+select a1, a2, (a1, a2) in (select b1, b2 from t2 where b1 > '0' and b1 < '9') from t1;
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1      ALL     NULL    NULL    NULL    NULL    8       
+2       SUBQUERY        t2      range   it2i4,it2i1,it2i2,it2i3 it2i3   9       NULL    4       Using where; Using index
+select a1, a2, (a1, a2) in (select b1, b2 from t2 where b1 > '0' and b1 < '9') from t1;
+a1      a2      (a1, a2) in (select b1, b2 from t2 where b1 > '0' and b1 < '9')
+1 - 00  2 - 00  0
+1 - 01  2 - 01  1
+1 - 02  2 - 02  1
+1 - 03  2 - 03  1
+1 - 04  2 - 04  0
+1 - 05  2 - 05  0
+1 - 06  2 - 06  0
+1 - 07  2 - 07  0
+
+/*
+B. "Natural" examples of subqueries without grouping that
+cannot be flattened into semijoin.
+*/
+explain
+select a1 from t1 where a1 in (select b2 from t2) or a2 < '9';
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1      ALL     NULL    NULL    NULL    NULL    8       Using where
+2       SUBQUERY        t2      index   it2i2   it2i2   9       NULL    5       Using index
+select a1 from t1 where a1 in (select b2 from t2) or a2 < '9';
+a1
+1 - 00
+1 - 01
+1 - 02
+1 - 03
+1 - 04
+1 - 05
+1 - 06
+1 - 07
+
+explain
+select a1, a2 from t1 where (a1, a2) in (select b1, b2 from t2 where b1 > '0') or a2 < '9';
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1      ALL     NULL    NULL    NULL    NULL    8       Using where
+2       SUBQUERY        t2      index   it2i4,it2i1,it2i2,it2i3 it2i3   18      NULL    5       Using where; Using index
+select a1, a2 from t1 where (a1, a2) in (select b1, b2 from t2 where b1 > '0') or a2 < '9';
+a1      a2
+1 - 00  2 - 00
+1 - 01  2 - 01
+1 - 02  2 - 02
+1 - 03  2 - 03
+1 - 04  2 - 04
+1 - 05  2 - 05
+1 - 06  2 - 06
+1 - 07  2 - 07
+UNION subqueries are currently limited to only use IN-TO-EXISTS.
+explain
+select a2 from t1 where a2 in (select b2 from t2 UNION select b3 from t2 as t3);
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1      ALL     NULL    NULL    NULL    NULL    8       Using where
+2       DEPENDENT SUBQUERY      t2      ref     it2i2   it2i2   9       func    2       Using index
+3       DEPENDENT UNION t3      index   NULL    it2i4   27      NULL    5       Using where; Using index
+NULL    UNION RESULT    <union2,3>      ALL     NULL    NULL    NULL    NULL    NULL    
+select a2 from t1 where a2 in (select b2 from t2 UNION select b3 from t2 as t3);
+a2
+2 - 01
+2 - 02
+2 - 03
+
+explain
+select a1 from t1 where a1 = '1 - 02' and a1 in (select max(b1) from t2 where b2 = '2 - 02');
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1      ALL     NULL    NULL    NULL    NULL    8       Using where
+2       SUBQUERY        t2      ref     it2i2   it2i2   9       const   1       Using index condition
+select a1 from t1 where a1 = '1 - 02' and a1 in (select max(b1) from t2 where b2 = '2 - 02');
+a1
+1 - 02
+
+explain
+select a1, a2 from t1 where (a1, a2) in (select b1, b2 from t2 order by b3);
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1      ALL     NULL    NULL    NULL    NULL    8       Using where
+2       SUBQUERY        t2      index   it2i4,it2i1,it2i2,it2i3 it2i4   27      NULL    5       Using index
+select a1, a2 from t1 where (a1, a2) in (select b1, b2 from t2 order by b3);
+a1      a2
+1 - 01  2 - 01
+1 - 02  2 - 02
+1 - 03  2 - 03
+
+/* C. Subqueries in the WHERE clause with GROUP BY. */
+explain
+select * from t1 where a1 in (select b1 from t2 where b1 > '0' group by b1);
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1      ALL     NULL    NULL    NULL    NULL    8       Using where
+2       SUBQUERY        t2      index   it2i4,it2i1,it2i3       it2i1   9       NULL    5       Using where; Using index
+select * from t1 where a1 in (select b1 from t2 where b1 > '0' group by b1);
+a1      a2      a3      a4
+1 - 01  2 - 01  3 - 01  1
+1 - 02  2 - 02  3 - 02  2
+1 - 03  2 - 03  3 - 03  3
+
+explain
+select * from t1 where (a1, a2) in (select b1, b2 from t2 where b1 > '0' group by b1, b2);
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1      ALL     NULL    NULL    NULL    NULL    8       Using where
+2       SUBQUERY        t2      index   it2i4,it2i1,it2i3       it2i3   18      NULL    5       Using where; Using index
+select * from t1 where (a1, a2) in (select b1, b2 from t2 where b1 > '0' group by b1, b2);
+a1      a2      a3      a4
+1 - 01  2 - 01  3 - 01  1
+1 - 02  2 - 02  3 - 02  2
+1 - 03  2 - 03  3 - 03  3
+
+explain
+select * from t1 where (a1, a2) in (select b1, b2 from t2 where b1 > '0' group by b1, b2 having b2 <  '2 - 04');
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1      ALL     NULL    NULL    NULL    NULL    8       Using where
+2       SUBQUERY        t2      index   it2i4,it2i1,it2i3       it2i3   18      NULL    5       Using where; Using index
+select * from t1 where (a1, a2) in (select b1, b2 from t2 where b1 > '0' group by b1, b2 having b2 <  '2 - 04');
+a1      a2      a3      a4
+1 - 01  2 - 01  3 - 01  1
+1 - 02  2 - 02  3 - 02  2
+1 - 03  2 - 03  3 - 03  3
+
+explain
+select * from t1 where (a1, a2, a3) in (select b1, b2, b3 from t2 group by b1, b2, b3);
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1      ALL     NULL    NULL    NULL    NULL    8       Using where
+2       SUBQUERY        t2      range   NULL    it2i4   27      NULL    6       Using index for group-by
+select * from t1 where (a1, a2, a3) in (select b1, b2, b3 from t2 group by b1, b2, b3);
+a1      a2      a3      a4
+1 - 01  2 - 01  3 - 01  1
+
+explain
+select * from t1 where (a1, a2, a3) in (select b1, b2, b3 from t2 where b3 = '3 - 02' group by b1, b2);
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1      ALL     NULL    NULL    NULL    NULL    8       Using where
+2       SUBQUERY        t2      range   NULL    it2i4   27      NULL    6       Using where; Using index for group-by
+select * from t1 where (a1, a2, a3) in (select b1, b2, b3 from t2 where b3 = '3 - 02' group by b1, b2);
+a1      a2      a3      a4
+
+explain
+select * from t1 where (a1,a2,a3) in (select b1,b2,b3 from t2 where b1 = '1 - 01' group by b1,b2,b3);
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1      ALL     NULL    NULL    NULL    NULL    8       Using where
+2       SUBQUERY        t2      range   it2i4,it2i1,it2i3       it2i4   27      NULL    2       Using where; Using index for group-by
+select * from t1 where (a1,a2,a3) in (select b1,b2,b3 from t2 where b1 = '1 - 01' group by b1,b2,b3);
+a1      a2      a3      a4
+1 - 01  2 - 01  3 - 01  1
+
+/*
+D. Subqueries for which materialization is not possible, and the
+optimizer reverts to in-to-exists.
+*/
+explain
+select left(a1,7), left(a2,7) from t1_1024 where a1 in (select b1 from t2_1024 where b1 > '0') or a2 < '9';
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1_1024 ALL     NULL    NULL    NULL    NULL    3       Using where
+2       DEPENDENT SUBQUERY      t2_1024 index_subquery  it2_1024i1,it2_1024i3   it2_1024i3      9       func    1       Using where
+select left(a1,7), left(a2,7) from t1_1024 where a1 in (select b1 from t2_1024 where b1 > '0') or a2 < '9';
+left(a1,7)      left(a2,7)
+1 - 00x 2 - 00x
+1 - 01x 2 - 01x
+1 - 03x 2 - 03x
+explain
+select left(a1,7), left(a2,7) from t1_1024 where (a1,a2) in (select b1, b2 from t2_1024 where b1 > '0') or a2 < '9';
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1_1024 ALL     NULL    NULL    NULL    NULL    3       Using where
+2       DEPENDENT SUBQUERY      t2_1024 index_subquery  it2_1024i1,it2_1024i2,it2_1024i3        it2_1024i1      9       func    2       Using where
+select left(a1,7), left(a2,7) from t1_1024 where (a1,a2) in (select b1, b2 from t2_1024 where b1 > '0') or a2 < '9';
+left(a1,7)      left(a2,7)
+1 - 00x 2 - 00x
+1 - 01x 2 - 01x
+1 - 03x 2 - 03x
+
+explain
+select left(a1,7), left(a2,7) from t1_1024 where a1 in (select substring(b1,1,1024) from t2_1024 where b1 > '0') or a2 < '9';
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1_1024 ALL     NULL    NULL    NULL    NULL    3       Using where
+2       DEPENDENT SUBQUERY      t2_1024 range   it2_1024i1,it2_1024i3   it2_1024i1      9       NULL    4       Using where
+select left(a1,7), left(a2,7) from t1_1024 where a1 in (select substring(b1,1,1024) from t2_1024 where b1 > '0') or a2 < '9';
+left(a1,7)      left(a2,7)
+1 - 00x 2 - 00x
+1 - 01x 2 - 01x
+1 - 03x 2 - 03x
+explain
+select left(a1,7), left(a2,7) from t1_1024 where (a1,a2) in (select substring(b1,1,1024), substring(b2,1,1024) from t2_1024 where b1 > '0') or a2 < '9';
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1_1024 ALL     NULL    NULL    NULL    NULL    3       Using where
+2       DEPENDENT SUBQUERY      t2_1024 range   it2_1024i1,it2_1024i3   it2_1024i1      9       NULL    4       Using where
+select left(a1,7), left(a2,7) from t1_1024 where (a1,a2) in (select substring(b1,1,1024), substring(b2,1,1024) from t2_1024 where b1 > '0') or a2 < '9';
+left(a1,7)      left(a2,7)
+1 - 00x 2 - 00x
+1 - 01x 2 - 01x
+1 - 03x 2 - 03x
+
+
+/* E. Edge cases. */
+
+/* E.1 Both materialization and in_to_exists cannot be off. */
+set @save_optimizer_switch=@@optimizer_switch;
+set @@optimizer_switch = 'materialization=off,in_to_exists=off';
+select * from t1 where a1 in (select b1 from t2 where b1 > '0' group by b1);
+ERROR HY000: At least one of the 'in_to_exists' or 'materialization' optimizer_switch flags must be 'on'.
+set @@optimizer_switch = @save_optimizer_switch;
+/* E.2 Outer query without tables, always uses IN-TO-EXISTS. */
+explain
+select '1 - 03' in (select b1 from t2 where b1 > '0');
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY NULL    NULL    NULL    NULL    NULL    NULL    NULL    No tables used
+2       DEPENDENT SUBQUERY      t2      index_subquery  it2i4,it2i1,it2i3       it2i1   9       const   2       Using index; Using where
+select '1 - 03' in (select b1 from t2 where b1 > '0');
+'1 - 03' in (select b1 from t2 where b1 > '0')
+1
+/* E.3 Subqueries without tables. */
+explain
+select a1 from t1 where a1 in (select '1 - 03') or a2 < '9';
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1      ALL     NULL    NULL    NULL    NULL    8       Using where
+Warnings:
+Note    1249    Select 2 was reduced during optimization
+select a1 from t1 where a1 in (select '1 - 03') or a2 < '9';
+a1
+1 - 00
+1 - 01
+1 - 02
+1 - 03
+1 - 04
+1 - 05
+1 - 06
+1 - 07
+UNION subqueries are currently limited to only use IN-TO-EXISTS.
+explain
+select a1 from t1 where a1 in (select '1 - 03' UNION select '1 - 02');
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1      ALL     NULL    NULL    NULL    NULL    8       Using where
+2       DEPENDENT SUBQUERY      NULL    NULL    NULL    NULL    NULL    NULL    NULL    No tables used
+3       DEPENDENT UNION NULL    NULL    NULL    NULL    NULL    NULL    NULL    No tables used
+NULL    UNION RESULT    <union2,3>      ALL     NULL    NULL    NULL    NULL    NULL    
+select a1 from t1 where a1 in (select '1 - 03' UNION select '1 - 02');
+a1
+1 - 02
+1 - 03
+/* E.4 optimize_cond detects FALSE where/having clause. */
+explain
+select a1 from t1 where a1 in (select b1 from t2 where b1 = b2 and b2 = '1 - 03' and b1 = '1 - 02' ) or a2 < '9';
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1      ALL     NULL    NULL    NULL    NULL    8       Using where
+2       SUBQUERY        NULL    NULL    NULL    NULL    NULL    NULL    NULL    Impossible WHERE
+select a1 from t1 where a1 in (select b1 from t2 where b1 = b2 and b2 = '1 - 03' and b1 = '1 - 02' ) or a2 < '9';
+a1
+1 - 00
+1 - 01
+1 - 02
+1 - 03
+1 - 04
+1 - 05
+1 - 06
+1 - 07
+/* E.5 opt_sum_query detects no matching min/max row or substitutes MIN/MAX with a const. */
+TODO this test produces wrong result due to missing logic to handle the case
+when JOIN::optimize detects an empty subquery result.
+explain
+select a1 from t1 where a1 in (select max(b1) from t2);
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1      ALL     NULL    NULL    NULL    NULL    8       Using where
+2       DEPENDENT SUBQUERY      NULL    NULL    NULL    NULL    NULL    NULL    NULL    Select tables optimized away
+select a1 from t1 where a1 in (select max(b1) from t2);
+a1
+1 - 03
+
+explain
+select a1 from t1 where a1 in (select max(b1) from t2 where b1 = '7 - 02');
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1      ALL     NULL    NULL    NULL    NULL    8       Using where
+2       DEPENDENT SUBQUERY      NULL    NULL    NULL    NULL    NULL    NULL    NULL    No matching min/max row
+select a1 from t1 where a1 in (select max(b1) from t2 where b1 = '7 - 02');
+a1
+/* E.6 make_join_select detects impossible WHERE. *
+TODO
+/* E.7 constant optimization detects "no matching row in const table". */
+TODO
+/* E.8 Impossible WHERE noticed after reading const tables. */
+explain
+select '1 - 03' in (select b1 from t2 where b1 > '0' and b1 < '0');
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY NULL    NULL    NULL    NULL    NULL    NULL    NULL    No tables used
+2       DEPENDENT SUBQUERY      NULL    NULL    NULL    NULL    NULL    NULL    NULL    Impossible WHERE noticed after reading const tables
+select '1 - 03' in (select b1 from t2 where b1 > '0' and b1 < '0');
+'1 - 03' in (select b1 from t2 where b1 > '0' and b1 < '0')
+0
+
+/* F. UPDATE/DELETE with subqueries. */
+
+TODO
+
+/* 1.2.3 indexed table access, non-nullabale columns. */
+call set_all_columns_not_null();
+
+/* A. Subqueries in the SELECT clause. */
+explain
+select a1, a1 in (select b1 from t2 where b1 > '0') from t1;
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1      ALL     NULL    NULL    NULL    NULL    8       
+2       SUBQUERY        t2      index   it2i4,it2i1,it2i3       it2i1   8       NULL    5       Using where; Using index
+select a1, a1 in (select b1 from t2 where b1 > '0') from t1;
+a1      a1 in (select b1 from t2 where b1 > '0')
+1 - 00  0
+1 - 01  1
+1 - 02  1
+1 - 03  1
+1 - 04  0
+1 - 05  0
+1 - 06  0
+1 - 07  0
+
+explain
+select a1, a2, (a1, a2) in (select b1, b2 from t2 where b1 > '0') from t1;
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1      ALL     NULL    NULL    NULL    NULL    8       
+2       SUBQUERY        t2      index   it2i4,it2i1,it2i2,it2i3 it2i3   16      NULL    5       Using where; Using index
+select a1, a2, (a1, a2) in (select b1, b2 from t2 where b1 > '0') from t1;
+a1      a2      (a1, a2) in (select b1, b2 from t2 where b1 > '0')
+1 - 00  2 - 00  0
+1 - 01  2 - 01  1
+1 - 02  2 - 02  1
+1 - 03  2 - 03  1
+1 - 04  2 - 04  0
+1 - 05  2 - 05  0
+1 - 06  2 - 06  0
+1 - 07  2 - 07  0
+
+explain
+select a1, a2, (a1, a2) in (select b1, b2 from t2 where b1 > '0' and b1 < '9') from t1;
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1      ALL     NULL    NULL    NULL    NULL    8       
+2       SUBQUERY        t2      range   it2i4,it2i1,it2i2,it2i3 it2i3   8       NULL    4       Using where; Using index
+select a1, a2, (a1, a2) in (select b1, b2 from t2 where b1 > '0' and b1 < '9') from t1;
+a1      a2      (a1, a2) in (select b1, b2 from t2 where b1 > '0' and b1 < '9')
+1 - 00  2 - 00  0
+1 - 01  2 - 01  1
+1 - 02  2 - 02  1
+1 - 03  2 - 03  1
+1 - 04  2 - 04  0
+1 - 05  2 - 05  0
+1 - 06  2 - 06  0
+1 - 07  2 - 07  0
+
+/*
+B. "Natural" examples of subqueries without grouping that
+cannot be flattened into semijoin.
+*/
+explain
+select a1 from t1 where a1 in (select b2 from t2) or a2 < '9';
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1      ALL     NULL    NULL    NULL    NULL    8       Using where
+2       SUBQUERY        t2      index   it2i2   it2i2   8       NULL    5       Using index
+select a1 from t1 where a1 in (select b2 from t2) or a2 < '9';
+a1
+1 - 00
+1 - 01
+1 - 02
+1 - 03
+1 - 04
+1 - 05
+1 - 06
+1 - 07
+
+explain
+select a1, a2 from t1 where (a1, a2) in (select b1, b2 from t2 where b1 > '0') or a2 < '9';
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1      ALL     NULL    NULL    NULL    NULL    8       Using where
+2       SUBQUERY        t2      index   it2i4,it2i1,it2i2,it2i3 it2i3   16      NULL    5       Using where; Using index
+select a1, a2 from t1 where (a1, a2) in (select b1, b2 from t2 where b1 > '0') or a2 < '9';
+a1      a2
+1 - 00  2 - 00
+1 - 01  2 - 01
+1 - 02  2 - 02
+1 - 03  2 - 03
+1 - 04  2 - 04
+1 - 05  2 - 05
+1 - 06  2 - 06
+1 - 07  2 - 07
+UNION subqueries are currently limited to only use IN-TO-EXISTS.
+explain
+select a2 from t1 where a2 in (select b2 from t2 UNION select b3 from t2 as t3);
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1      ALL     NULL    NULL    NULL    NULL    8       Using where
+2       DEPENDENT SUBQUERY      t2      ref     it2i2   it2i2   8       func    2       Using index
+3       DEPENDENT UNION t3      index   NULL    it2i4   24      NULL    5       Using where; Using index
+NULL    UNION RESULT    <union2,3>      ALL     NULL    NULL    NULL    NULL    NULL    
+select a2 from t1 where a2 in (select b2 from t2 UNION select b3 from t2 as t3);
+a2
+2 - 01
+2 - 02
+2 - 03
+
+explain
+select a1 from t1 where a1 = '1 - 02' and a1 in (select max(b1) from t2 where b2 = '2 - 02');
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1      ALL     NULL    NULL    NULL    NULL    8       Using where
+2       SUBQUERY        t2      ref     it2i2   it2i2   8       const   1       Using index condition
+select a1 from t1 where a1 = '1 - 02' and a1 in (select max(b1) from t2 where b2 = '2 - 02');
+a1
+1 - 02
+
+explain
+select a1, a2 from t1 where (a1, a2) in (select b1, b2 from t2 order by b3);
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1      ALL     NULL    NULL    NULL    NULL    8       Using where
+2       SUBQUERY        t2      index   it2i4,it2i1,it2i2,it2i3 it2i4   24      NULL    5       Using index
+select a1, a2 from t1 where (a1, a2) in (select b1, b2 from t2 order by b3);
+a1      a2
+1 - 01  2 - 01
+1 - 02  2 - 02
+1 - 03  2 - 03
+
+/* C. Subqueries in the WHERE clause with GROUP BY. */
+explain
+select * from t1 where a1 in (select b1 from t2 where b1 > '0' group by b1);
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1      ALL     NULL    NULL    NULL    NULL    8       Using where
+2       SUBQUERY        t2      index   it2i4,it2i1,it2i3       it2i1   8       NULL    5       Using where; Using index
+select * from t1 where a1 in (select b1 from t2 where b1 > '0' group by b1);
+a1      a2      a3      a4
+1 - 01  2 - 01  3 - 01  1
+1 - 02  2 - 02  3 - 02  2
+1 - 03  2 - 03  3 - 03  3
+
+explain
+select * from t1 where (a1, a2) in (select b1, b2 from t2 where b1 > '0' group by b1, b2);
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1      ALL     NULL    NULL    NULL    NULL    8       Using where
+2       SUBQUERY        t2      index   it2i4,it2i1,it2i3       it2i3   16      NULL    5       Using where; Using index
+select * from t1 where (a1, a2) in (select b1, b2 from t2 where b1 > '0' group by b1, b2);
+a1      a2      a3      a4
+1 - 01  2 - 01  3 - 01  1
+1 - 02  2 - 02  3 - 02  2
+1 - 03  2 - 03  3 - 03  3
+
+explain
+select * from t1 where (a1, a2) in (select b1, b2 from t2 where b1 > '0' group by b1, b2 having b2 <  '2 - 04');
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1      ALL     NULL    NULL    NULL    NULL    8       Using where
+2       SUBQUERY        t2      index   it2i4,it2i1,it2i3       it2i3   16      NULL    5       Using where; Using index
+select * from t1 where (a1, a2) in (select b1, b2 from t2 where b1 > '0' group by b1, b2 having b2 <  '2 - 04');
+a1      a2      a3      a4
+1 - 01  2 - 01  3 - 01  1
+1 - 02  2 - 02  3 - 02  2
+1 - 03  2 - 03  3 - 03  3
+
+explain
+select * from t1 where (a1, a2, a3) in (select b1, b2, b3 from t2 group by b1, b2, b3);
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1      ALL     NULL    NULL    NULL    NULL    8       Using where
+2       SUBQUERY        t2      range   NULL    it2i4   24      NULL    6       Using index for group-by
+select * from t1 where (a1, a2, a3) in (select b1, b2, b3 from t2 group by b1, b2, b3);
+a1      a2      a3      a4
+1 - 01  2 - 01  3 - 01  1
+
+explain
+select * from t1 where (a1, a2, a3) in (select b1, b2, b3 from t2 where b3 = '3 - 02' group by b1, b2);
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1      ALL     NULL    NULL    NULL    NULL    8       Using where
+2       SUBQUERY        t2      range   NULL    it2i4   24      NULL    6       Using where; Using index for group-by
+select * from t1 where (a1, a2, a3) in (select b1, b2, b3 from t2 where b3 = '3 - 02' group by b1, b2);
+a1      a2      a3      a4
+
+explain
+select * from t1 where (a1,a2,a3) in (select b1,b2,b3 from t2 where b1 = '1 - 01' group by b1,b2,b3);
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1      ALL     NULL    NULL    NULL    NULL    8       Using where
+2       SUBQUERY        t2      range   it2i4,it2i1,it2i3       it2i4   24      NULL    2       Using where; Using index for group-by
+select * from t1 where (a1,a2,a3) in (select b1,b2,b3 from t2 where b1 = '1 - 01' group by b1,b2,b3);
+a1      a2      a3      a4
+1 - 01  2 - 01  3 - 01  1
+
+/*
+D. Subqueries for which materialization is not possible, and the
+optimizer reverts to in-to-exists.
+*/
+explain
+select left(a1,7), left(a2,7) from t1_1024 where a1 in (select b1 from t2_1024 where b1 > '0') or a2 < '9';
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1_1024 ALL     NULL    NULL    NULL    NULL    3       Using where
+2       DEPENDENT SUBQUERY      t2_1024 index_subquery  it2_1024i1,it2_1024i3   it2_1024i3      9       func    1       Using where
+select left(a1,7), left(a2,7) from t1_1024 where a1 in (select b1 from t2_1024 where b1 > '0') or a2 < '9';
+left(a1,7)      left(a2,7)
+1 - 00x 2 - 00x
+1 - 01x 2 - 01x
+1 - 03x 2 - 03x
+explain
+select left(a1,7), left(a2,7) from t1_1024 where (a1,a2) in (select b1, b2 from t2_1024 where b1 > '0') or a2 < '9';
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1_1024 ALL     NULL    NULL    NULL    NULL    3       Using where
+2       DEPENDENT SUBQUERY      t2_1024 index_subquery  it2_1024i1,it2_1024i2,it2_1024i3        it2_1024i1      9       func    2       Using where
+select left(a1,7), left(a2,7) from t1_1024 where (a1,a2) in (select b1, b2 from t2_1024 where b1 > '0') or a2 < '9';
+left(a1,7)      left(a2,7)
+1 - 00x 2 - 00x
+1 - 01x 2 - 01x
+1 - 03x 2 - 03x
+
+explain
+select left(a1,7), left(a2,7) from t1_1024 where a1 in (select substring(b1,1,1024) from t2_1024 where b1 > '0') or a2 < '9';
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1_1024 ALL     NULL    NULL    NULL    NULL    3       Using where
+2       DEPENDENT SUBQUERY      t2_1024 range   it2_1024i1,it2_1024i3   it2_1024i1      9       NULL    4       Using where
+select left(a1,7), left(a2,7) from t1_1024 where a1 in (select substring(b1,1,1024) from t2_1024 where b1 > '0') or a2 < '9';
+left(a1,7)      left(a2,7)
+1 - 00x 2 - 00x
+1 - 01x 2 - 01x
+1 - 03x 2 - 03x
+explain
+select left(a1,7), left(a2,7) from t1_1024 where (a1,a2) in (select substring(b1,1,1024), substring(b2,1,1024) from t2_1024 where b1 > '0') or a2 < '9';
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1_1024 ALL     NULL    NULL    NULL    NULL    3       Using where
+2       DEPENDENT SUBQUERY      t2_1024 range   it2_1024i1,it2_1024i3   it2_1024i1      9       NULL    4       Using where
+select left(a1,7), left(a2,7) from t1_1024 where (a1,a2) in (select substring(b1,1,1024), substring(b2,1,1024) from t2_1024 where b1 > '0') or a2 < '9';
+left(a1,7)      left(a2,7)
+1 - 00x 2 - 00x
+1 - 01x 2 - 01x
+1 - 03x 2 - 03x
+
+
+/* E. Edge cases. */
+
+/* E.1 Both materialization and in_to_exists cannot be off. */
+set @save_optimizer_switch=@@optimizer_switch;
+set @@optimizer_switch = 'materialization=off,in_to_exists=off';
+select * from t1 where a1 in (select b1 from t2 where b1 > '0' group by b1);
+ERROR HY000: At least one of the 'in_to_exists' or 'materialization' optimizer_switch flags must be 'on'.
+set @@optimizer_switch = @save_optimizer_switch;
+/* E.2 Outer query without tables, always uses IN-TO-EXISTS. */
+explain
+select '1 - 03' in (select b1 from t2 where b1 > '0');
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY NULL    NULL    NULL    NULL    NULL    NULL    NULL    No tables used
+2       DEPENDENT SUBQUERY      t2      index_subquery  it2i4,it2i1,it2i3       it2i1   8       const   5       Using index; Using where
+select '1 - 03' in (select b1 from t2 where b1 > '0');
+'1 - 03' in (select b1 from t2 where b1 > '0')
+1
+/* E.3 Subqueries without tables. */
+explain
+select a1 from t1 where a1 in (select '1 - 03') or a2 < '9';
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1      ALL     NULL    NULL    NULL    NULL    8       Using where
+Warnings:
+Note    1249    Select 2 was reduced during optimization
+select a1 from t1 where a1 in (select '1 - 03') or a2 < '9';
+a1
+1 - 00
+1 - 01
+1 - 02
+1 - 03
+1 - 04
+1 - 05
+1 - 06
+1 - 07
+UNION subqueries are currently limited to only use IN-TO-EXISTS.
+explain
+select a1 from t1 where a1 in (select '1 - 03' UNION select '1 - 02');
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1      ALL     NULL    NULL    NULL    NULL    8       Using where
+2       DEPENDENT SUBQUERY      NULL    NULL    NULL    NULL    NULL    NULL    NULL    No tables used
+3       DEPENDENT UNION NULL    NULL    NULL    NULL    NULL    NULL    NULL    No tables used
+NULL    UNION RESULT    <union2,3>      ALL     NULL    NULL    NULL    NULL    NULL    
+select a1 from t1 where a1 in (select '1 - 03' UNION select '1 - 02');
+a1
+1 - 02
+1 - 03
+/* E.4 optimize_cond detects FALSE where/having clause. */
+explain
+select a1 from t1 where a1 in (select b1 from t2 where b1 = b2 and b2 = '1 - 03' and b1 = '1 - 02' ) or a2 < '9';
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1      ALL     NULL    NULL    NULL    NULL    8       Using where
+2       SUBQUERY        NULL    NULL    NULL    NULL    NULL    NULL    NULL    Impossible WHERE
+select a1 from t1 where a1 in (select b1 from t2 where b1 = b2 and b2 = '1 - 03' and b1 = '1 - 02' ) or a2 < '9';
+a1
+1 - 00
+1 - 01
+1 - 02
+1 - 03
+1 - 04
+1 - 05
+1 - 06
+1 - 07
+/* E.5 opt_sum_query detects no matching min/max row or substitutes MIN/MAX with a const. */
+TODO this test produces wrong result due to missing logic to handle the case
+when JOIN::optimize detects an empty subquery result.
+explain
+select a1 from t1 where a1 in (select max(b1) from t2);
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1      ALL     NULL    NULL    NULL    NULL    8       Using where
+2       DEPENDENT SUBQUERY      NULL    NULL    NULL    NULL    NULL    NULL    NULL    Select tables optimized away
+select a1 from t1 where a1 in (select max(b1) from t2);
+a1
+1 - 03
+
+explain
+select a1 from t1 where a1 in (select max(b1) from t2 where b1 = '7 - 02');
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1      ALL     NULL    NULL    NULL    NULL    8       Using where
+2       DEPENDENT SUBQUERY      NULL    NULL    NULL    NULL    NULL    NULL    NULL    No matching min/max row
+select a1 from t1 where a1 in (select max(b1) from t2 where b1 = '7 - 02');
+a1
+/* E.6 make_join_select detects impossible WHERE. *
+TODO
+/* E.7 constant optimization detects "no matching row in const table". */
+TODO
+/* E.8 Impossible WHERE noticed after reading const tables. */
+explain
+select '1 - 03' in (select b1 from t2 where b1 > '0' and b1 < '0');
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY NULL    NULL    NULL    NULL    NULL    NULL    NULL    No tables used
+2       DEPENDENT SUBQUERY      NULL    NULL    NULL    NULL    NULL    NULL    NULL    Impossible WHERE noticed after reading const tables
+select '1 - 03' in (select b1 from t2 where b1 > '0' and b1 < '0');
+'1 - 03' in (select b1 from t2 where b1 > '0' and b1 < '0')
+0
+
+/* F. UPDATE/DELETE with subqueries. */
+
+TODO
+
+call set_all_columns_nullable();
+/******************************************************************************
+2. Materialization is OFF, in-to-exists is ON, materialization is cheaper.
+******************************************************************************/
+set @@optimizer_switch='materialization=off,in_to_exists=on';
+/* 2.1 non-indexed table access */
+call remove_t2_indexes();
+
+/* A. Subqueries in the SELECT clause. */
+explain
+select a1, a1 in (select b1 from t2 where b1 > '0') from t1;
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1      ALL     NULL    NULL    NULL    NULL    8       
+2       DEPENDENT SUBQUERY      t2      ALL     NULL    NULL    NULL    NULL    5       Using where
+select a1, a1 in (select b1 from t2 where b1 > '0') from t1;
+a1      a1 in (select b1 from t2 where b1 > '0')
+1 - 00  0
+1 - 01  1
+1 - 02  1
+1 - 03  1
+1 - 04  0
+1 - 05  0
+1 - 06  0
+1 - 07  0
+
+explain
+select a1, a2, (a1, a2) in (select b1, b2 from t2 where b1 > '0') from t1;
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1      ALL     NULL    NULL    NULL    NULL    8       
+2       DEPENDENT SUBQUERY      t2      ALL     NULL    NULL    NULL    NULL    5       Using where
+select a1, a2, (a1, a2) in (select b1, b2 from t2 where b1 > '0') from t1;
+a1      a2      (a1, a2) in (select b1, b2 from t2 where b1 > '0')
+1 - 00  2 - 00  0
+1 - 01  2 - 01  1
+1 - 02  2 - 02  1
+1 - 03  2 - 03  1
+1 - 04  2 - 04  0
+1 - 05  2 - 05  0
+1 - 06  2 - 06  0
+1 - 07  2 - 07  0
+
+explain
+select a1, a2, (a1, a2) in (select b1, b2 from t2 where b1 > '0' and b1 < '9') from t1;
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1      ALL     NULL    NULL    NULL    NULL    8       
+2       DEPENDENT SUBQUERY      t2      ALL     NULL    NULL    NULL    NULL    5       Using where
+select a1, a2, (a1, a2) in (select b1, b2 from t2 where b1 > '0' and b1 < '9') from t1;
+a1      a2      (a1, a2) in (select b1, b2 from t2 where b1 > '0' and b1 < '9')
+1 - 00  2 - 00  0
+1 - 01  2 - 01  1
+1 - 02  2 - 02  1
+1 - 03  2 - 03  1
+1 - 04  2 - 04  0
+1 - 05  2 - 05  0
+1 - 06  2 - 06  0
+1 - 07  2 - 07  0
+
+/*
+B. "Natural" examples of subqueries without grouping that
+cannot be flattened into semijoin.
+*/
+explain
+select a1 from t1 where a1 in (select b2 from t2) or a2 < '9';
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1      ALL     NULL    NULL    NULL    NULL    8       Using where
+2       DEPENDENT SUBQUERY      t2      ALL     NULL    NULL    NULL    NULL    5       Using where
+select a1 from t1 where a1 in (select b2 from t2) or a2 < '9';
+a1
+1 - 00
+1 - 01
+1 - 02
+1 - 03
+1 - 04
+1 - 05
+1 - 06
+1 - 07
+
+explain
+select a1, a2 from t1 where (a1, a2) in (select b1, b2 from t2 where b1 > '0') or a2 < '9';
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1      ALL     NULL    NULL    NULL    NULL    8       Using where
+2       DEPENDENT SUBQUERY      t2      ALL     NULL    NULL    NULL    NULL    5       Using where
+select a1, a2 from t1 where (a1, a2) in (select b1, b2 from t2 where b1 > '0') or a2 < '9';
+a1      a2
+1 - 00  2 - 00
+1 - 01  2 - 01
+1 - 02  2 - 02
+1 - 03  2 - 03
+1 - 04  2 - 04
+1 - 05  2 - 05
+1 - 06  2 - 06
+1 - 07  2 - 07
+UNION subqueries are currently limited to only use IN-TO-EXISTS.
+explain
+select a2 from t1 where a2 in (select b2 from t2 UNION select b3 from t2 as t3);
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1      ALL     NULL    NULL    NULL    NULL    8       Using where
+2       DEPENDENT SUBQUERY      t2      ALL     NULL    NULL    NULL    NULL    5       Using where
+3       DEPENDENT UNION t3      ALL     NULL    NULL    NULL    NULL    5       Using where
+NULL    UNION RESULT    <union2,3>      ALL     NULL    NULL    NULL    NULL    NULL    
+select a2 from t1 where a2 in (select b2 from t2 UNION select b3 from t2 as t3);
+a2
+2 - 01
+2 - 02
+2 - 03
+
+explain
+select a1 from t1 where a1 = '1 - 02' and a1 in (select max(b1) from t2 where b2 = '2 - 02');
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1      ALL     NULL    NULL    NULL    NULL    8       Using where
+2       DEPENDENT SUBQUERY      t2      ALL     NULL    NULL    NULL    NULL    5       Using where
+select a1 from t1 where a1 = '1 - 02' and a1 in (select max(b1) from t2 where b2 = '2 - 02');
+a1
+1 - 02
+
+explain
+select a1, a2 from t1 where (a1, a2) in (select b1, b2 from t2 order by b3);
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1      ALL     NULL    NULL    NULL    NULL    8       Using where
+2       DEPENDENT SUBQUERY      t2      ALL     NULL    NULL    NULL    NULL    5       Using where
+select a1, a2 from t1 where (a1, a2) in (select b1, b2 from t2 order by b3);
+a1      a2
+1 - 01  2 - 01
+1 - 02  2 - 02
+1 - 03  2 - 03
+
+/* C. Subqueries in the WHERE clause with GROUP BY. */
+explain
+select * from t1 where a1 in (select b1 from t2 where b1 > '0' group by b1);
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1      ALL     NULL    NULL    NULL    NULL    8       Using where
+2       DEPENDENT SUBQUERY      t2      ALL     NULL    NULL    NULL    NULL    5       Using where; Using temporary; Using filesort
+select * from t1 where a1 in (select b1 from t2 where b1 > '0' group by b1);
+a1      a2      a3      a4
+1 - 01  2 - 01  3 - 01  1
+1 - 02  2 - 02  3 - 02  2
+1 - 03  2 - 03  3 - 03  3
+
+explain
+select * from t1 where (a1, a2) in (select b1, b2 from t2 where b1 > '0' group by b1, b2);
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1      ALL     NULL    NULL    NULL    NULL    8       Using where
+2       DEPENDENT SUBQUERY      t2      ALL     NULL    NULL    NULL    NULL    5       Using where; Using temporary; Using filesort
+select * from t1 where (a1, a2) in (select b1, b2 from t2 where b1 > '0' group by b1, b2);
+a1      a2      a3      a4
+1 - 01  2 - 01  3 - 01  1
+1 - 02  2 - 02  3 - 02  2
+1 - 03  2 - 03  3 - 03  3
+
+explain
+select * from t1 where (a1, a2) in (select b1, b2 from t2 where b1 > '0' group by b1, b2 having b2 <  '2 - 04');
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1      ALL     NULL    NULL    NULL    NULL    8       Using where
+2       DEPENDENT SUBQUERY      t2      ALL     NULL    NULL    NULL    NULL    5       Using where; Using temporary; Using filesort
+select * from t1 where (a1, a2) in (select b1, b2 from t2 where b1 > '0' group by b1, b2 having b2 <  '2 - 04');
+a1      a2      a3      a4
+1 - 01  2 - 01  3 - 01  1
+1 - 02  2 - 02  3 - 02  2
+1 - 03  2 - 03  3 - 03  3
+
+explain
+select * from t1 where (a1, a2, a3) in (select b1, b2, b3 from t2 group by b1, b2, b3);
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1      ALL     NULL    NULL    NULL    NULL    8       Using where
+2       DEPENDENT SUBQUERY      t2      ALL     NULL    NULL    NULL    NULL    5       Using temporary; Using filesort
+select * from t1 where (a1, a2, a3) in (select b1, b2, b3 from t2 group by b1, b2, b3);
+a1      a2      a3      a4
+1 - 01  2 - 01  3 - 01  1
+
+explain
+select * from t1 where (a1, a2, a3) in (select b1, b2, b3 from t2 where b3 = '3 - 02' group by b1, b2);
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1      ALL     NULL    NULL    NULL    NULL    8       Using where
+2       DEPENDENT SUBQUERY      t2      ALL     NULL    NULL    NULL    NULL    5       Using where; Using temporary; Using filesort
+select * from t1 where (a1, a2, a3) in (select b1, b2, b3 from t2 where b3 = '3 - 02' group by b1, b2);
+a1      a2      a3      a4
+
+explain
+select * from t1 where (a1,a2,a3) in (select b1,b2,b3 from t2 where b1 = '1 - 01' group by b1,b2,b3);
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1      ALL     NULL    NULL    NULL    NULL    8       Using where
+2       DEPENDENT SUBQUERY      t2      ALL     NULL    NULL    NULL    NULL    5       Using where; Using temporary; Using filesort
+select * from t1 where (a1,a2,a3) in (select b1,b2,b3 from t2 where b1 = '1 - 01' group by b1,b2,b3);
+a1      a2      a3      a4
+1 - 01  2 - 01  3 - 01  1
+
+/*
+D. Subqueries for which materialization is not possible, and the
+optimizer reverts to in-to-exists.
+*/
+explain
+select left(a1,7), left(a2,7) from t1_1024 where a1 in (select b1 from t2_1024 where b1 > '0') or a2 < '9';
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1_1024 ALL     NULL    NULL    NULL    NULL    3       Using where
+2       DEPENDENT SUBQUERY      t2_1024 ALL     NULL    NULL    NULL    NULL    4       Using where
+select left(a1,7), left(a2,7) from t1_1024 where a1 in (select b1 from t2_1024 where b1 > '0') or a2 < '9';
+left(a1,7)      left(a2,7)
+1 - 00x 2 - 00x
+1 - 01x 2 - 01x
+1 - 03x 2 - 03x
+explain
+select left(a1,7), left(a2,7) from t1_1024 where (a1,a2) in (select b1, b2 from t2_1024 where b1 > '0') or a2 < '9';
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1_1024 ALL     NULL    NULL    NULL    NULL    3       Using where
+2       DEPENDENT SUBQUERY      t2_1024 ALL     NULL    NULL    NULL    NULL    4       Using where
+select left(a1,7), left(a2,7) from t1_1024 where (a1,a2) in (select b1, b2 from t2_1024 where b1 > '0') or a2 < '9';
+left(a1,7)      left(a2,7)
+1 - 00x 2 - 00x
+1 - 01x 2 - 01x
+1 - 03x 2 - 03x
+
+explain
+select left(a1,7), left(a2,7) from t1_1024 where a1 in (select substring(b1,1,1024) from t2_1024 where b1 > '0') or a2 < '9';
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1_1024 ALL     NULL    NULL    NULL    NULL    3       Using where
+2       DEPENDENT SUBQUERY      t2_1024 ALL     NULL    NULL    NULL    NULL    4       Using where
+select left(a1,7), left(a2,7) from t1_1024 where a1 in (select substring(b1,1,1024) from t2_1024 where b1 > '0') or a2 < '9';
+left(a1,7)      left(a2,7)
+1 - 00x 2 - 00x
+1 - 01x 2 - 01x
+1 - 03x 2 - 03x
+explain
+select left(a1,7), left(a2,7) from t1_1024 where (a1,a2) in (select substring(b1,1,1024), substring(b2,1,1024) from t2_1024 where b1 > '0') or a2 < '9';
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1_1024 ALL     NULL    NULL    NULL    NULL    3       Using where
+2       DEPENDENT SUBQUERY      t2_1024 ALL     NULL    NULL    NULL    NULL    4       Using where
+select left(a1,7), left(a2,7) from t1_1024 where (a1,a2) in (select substring(b1,1,1024), substring(b2,1,1024) from t2_1024 where b1 > '0') or a2 < '9';
+left(a1,7)      left(a2,7)
+1 - 00x 2 - 00x
+1 - 01x 2 - 01x
+1 - 03x 2 - 03x
+
+
+/* E. Edge cases. */
+
+/* E.1 Both materialization and in_to_exists cannot be off. */
+set @save_optimizer_switch=@@optimizer_switch;
+set @@optimizer_switch = 'materialization=off,in_to_exists=off';
+select * from t1 where a1 in (select b1 from t2 where b1 > '0' group by b1);
+ERROR HY000: At least one of the 'in_to_exists' or 'materialization' optimizer_switch flags must be 'on'.
+set @@optimizer_switch = @save_optimizer_switch;
+/* E.2 Outer query without tables, always uses IN-TO-EXISTS. */
+explain
+select '1 - 03' in (select b1 from t2 where b1 > '0');
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY NULL    NULL    NULL    NULL    NULL    NULL    NULL    No tables used
+2       DEPENDENT SUBQUERY      t2      ALL     NULL    NULL    NULL    NULL    5       Using where
+select '1 - 03' in (select b1 from t2 where b1 > '0');
+'1 - 03' in (select b1 from t2 where b1 > '0')
+1
+/* E.3 Subqueries without tables. */
+explain
+select a1 from t1 where a1 in (select '1 - 03') or a2 < '9';
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1      ALL     NULL    NULL    NULL    NULL    8       Using where
+Warnings:
+Note    1249    Select 2 was reduced during optimization
+select a1 from t1 where a1 in (select '1 - 03') or a2 < '9';
+a1
+1 - 00
+1 - 01
+1 - 02
+1 - 03
+1 - 04
+1 - 05
+1 - 06
+1 - 07
+UNION subqueries are currently limited to only use IN-TO-EXISTS.
+explain
+select a1 from t1 where a1 in (select '1 - 03' UNION select '1 - 02');
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1      ALL     NULL    NULL    NULL    NULL    8       Using where
+2       DEPENDENT SUBQUERY      NULL    NULL    NULL    NULL    NULL    NULL    NULL    No tables used
+3       DEPENDENT UNION NULL    NULL    NULL    NULL    NULL    NULL    NULL    No tables used
+NULL    UNION RESULT    <union2,3>      ALL     NULL    NULL    NULL    NULL    NULL    
+select a1 from t1 where a1 in (select '1 - 03' UNION select '1 - 02');
+a1
+1 - 02
+1 - 03
+/* E.4 optimize_cond detects FALSE where/having clause. */
+explain
+select a1 from t1 where a1 in (select b1 from t2 where b1 = b2 and b2 = '1 - 03' and b1 = '1 - 02' ) or a2 < '9';
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1      ALL     NULL    NULL    NULL    NULL    8       Using where
+2       SUBQUERY        NULL    NULL    NULL    NULL    NULL    NULL    NULL    Impossible WHERE
+select a1 from t1 where a1 in (select b1 from t2 where b1 = b2 and b2 = '1 - 03' and b1 = '1 - 02' ) or a2 < '9';
+a1
+1 - 00
+1 - 01
+1 - 02
+1 - 03
+1 - 04
+1 - 05
+1 - 06
+1 - 07
+/* E.5 opt_sum_query detects no matching min/max row or substitutes MIN/MAX with a const. */
+TODO this test produces wrong result due to missing logic to handle the case
+when JOIN::optimize detects an empty subquery result.
+explain
+select a1 from t1 where a1 in (select max(b1) from t2);
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1      ALL     NULL    NULL    NULL    NULL    8       Using where
+2       DEPENDENT SUBQUERY      t2      ALL     NULL    NULL    NULL    NULL    5       
+select a1 from t1 where a1 in (select max(b1) from t2);
+a1
+1 - 03
+
+explain
+select a1 from t1 where a1 in (select max(b1) from t2 where b1 = '7 - 02');
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1      ALL     NULL    NULL    NULL    NULL    8       Using where
+2       DEPENDENT SUBQUERY      t2      ALL     NULL    NULL    NULL    NULL    5       Using where
+select a1 from t1 where a1 in (select max(b1) from t2 where b1 = '7 - 02');
+a1
+/* E.6 make_join_select detects impossible WHERE. *
+TODO
+/* E.7 constant optimization detects "no matching row in const table". */
+TODO
+/* E.8 Impossible WHERE noticed after reading const tables. */
+explain
+select '1 - 03' in (select b1 from t2 where b1 > '0' and b1 < '0');
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY NULL    NULL    NULL    NULL    NULL    NULL    NULL    No tables used
+2       DEPENDENT SUBQUERY      t2      ALL     NULL    NULL    NULL    NULL    5       Using where
+select '1 - 03' in (select b1 from t2 where b1 > '0' and b1 < '0');
+'1 - 03' in (select b1 from t2 where b1 > '0' and b1 < '0')
+0
+
+/* F. UPDATE/DELETE with subqueries. */
+
+TODO
+
+/* 2.2 indexed table access, nullabale columns. */
+call make_t2_indexes();
+
+/* A. Subqueries in the SELECT clause. */
+explain
+select a1, a1 in (select b1 from t2 where b1 > '0') from t1;
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1      ALL     NULL    NULL    NULL    NULL    8       
+2       DEPENDENT SUBQUERY      t2      index_subquery  it2i4,it2i1,it2i3       it2i1   9       func    2       Using index; Using where; Full scan on NULL key
+select a1, a1 in (select b1 from t2 where b1 > '0') from t1;
+a1      a1 in (select b1 from t2 where b1 > '0')
+1 - 00  0
+1 - 01  1
+1 - 02  1
+1 - 03  1
+1 - 04  0
+1 - 05  0
+1 - 06  0
+1 - 07  0
+
+explain
+select a1, a2, (a1, a2) in (select b1, b2 from t2 where b1 > '0') from t1;
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1      ALL     NULL    NULL    NULL    NULL    8       
+2       DEPENDENT SUBQUERY      t2      index_subquery  it2i4,it2i1,it2i2,it2i3 it2i3   9       func    2       Using index; Using where; Full scan on NULL key
+select a1, a2, (a1, a2) in (select b1, b2 from t2 where b1 > '0') from t1;
+a1      a2      (a1, a2) in (select b1, b2 from t2 where b1 > '0')
+1 - 00  2 - 00  0
+1 - 01  2 - 01  1
+1 - 02  2 - 02  1
+1 - 03  2 - 03  1
+1 - 04  2 - 04  0
+1 - 05  2 - 05  0
+1 - 06  2 - 06  0
+1 - 07  2 - 07  0
+
+explain
+select a1, a2, (a1, a2) in (select b1, b2 from t2 where b1 > '0' and b1 < '9') from t1;
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1      ALL     NULL    NULL    NULL    NULL    8       
+2       DEPENDENT SUBQUERY      t2      index_subquery  it2i4,it2i1,it2i2,it2i3 it2i3   9       func    2       Using index; Using where; Full scan on NULL key
+select a1, a2, (a1, a2) in (select b1, b2 from t2 where b1 > '0' and b1 < '9') from t1;
+a1      a2      (a1, a2) in (select b1, b2 from t2 where b1 > '0' and b1 < '9')
+1 - 00  2 - 00  0
+1 - 01  2 - 01  1
+1 - 02  2 - 02  1
+1 - 03  2 - 03  1
+1 - 04  2 - 04  0
+1 - 05  2 - 05  0
+1 - 06  2 - 06  0
+1 - 07  2 - 07  0
+
+/*
+B. "Natural" examples of subqueries without grouping that
+cannot be flattened into semijoin.
+*/
+explain
+select a1 from t1 where a1 in (select b2 from t2) or a2 < '9';
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1      ALL     NULL    NULL    NULL    NULL    8       Using where
+2       DEPENDENT SUBQUERY      t2      index_subquery  it2i2   it2i2   9       func    2       Using index
+select a1 from t1 where a1 in (select b2 from t2) or a2 < '9';
+a1
+1 - 00
+1 - 01
+1 - 02
+1 - 03
+1 - 04
+1 - 05
+1 - 06
+1 - 07
+
+explain
+select a1, a2 from t1 where (a1, a2) in (select b1, b2 from t2 where b1 > '0') or a2 < '9';
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1      ALL     NULL    NULL    NULL    NULL    8       Using where
+2       DEPENDENT SUBQUERY      t2      index_subquery  it2i4,it2i1,it2i2,it2i3 it2i4   18      func,func       1       Using index; Using where
+select a1, a2 from t1 where (a1, a2) in (select b1, b2 from t2 where b1 > '0') or a2 < '9';
+a1      a2
+1 - 00  2 - 00
+1 - 01  2 - 01
+1 - 02  2 - 02
+1 - 03  2 - 03
+1 - 04  2 - 04
+1 - 05  2 - 05
+1 - 06  2 - 06
+1 - 07  2 - 07
+UNION subqueries are currently limited to only use IN-TO-EXISTS.
+explain
+select a2 from t1 where a2 in (select b2 from t2 UNION select b3 from t2 as t3);
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1      ALL     NULL    NULL    NULL    NULL    8       Using where
+2       DEPENDENT SUBQUERY      t2      ref     it2i2   it2i2   9       func    2       Using index
+3       DEPENDENT UNION t3      index   NULL    it2i4   27      NULL    5       Using where; Using index
+NULL    UNION RESULT    <union2,3>      ALL     NULL    NULL    NULL    NULL    NULL    
+select a2 from t1 where a2 in (select b2 from t2 UNION select b3 from t2 as t3);
+a2
+2 - 01
+2 - 02
+2 - 03
+
+explain
+select a1 from t1 where a1 = '1 - 02' and a1 in (select max(b1) from t2 where b2 = '2 - 02');
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1      ALL     NULL    NULL    NULL    NULL    8       Using where
+2       DEPENDENT SUBQUERY      t2      ref     it2i2   it2i2   9       const   1       Using index condition
+select a1 from t1 where a1 = '1 - 02' and a1 in (select max(b1) from t2 where b2 = '2 - 02');
+a1
+1 - 02
+
+explain
+select a1, a2 from t1 where (a1, a2) in (select b1, b2 from t2 order by b3);
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1      ALL     NULL    NULL    NULL    NULL    8       Using where
+2       DEPENDENT SUBQUERY      t2      index_subquery  it2i4,it2i1,it2i2,it2i3 it2i4   18      func,func       1       Using index; Using where
+select a1, a2 from t1 where (a1, a2) in (select b1, b2 from t2 order by b3);
+a1      a2
+1 - 01  2 - 01
+1 - 02  2 - 02
+1 - 03  2 - 03
+
+/* C. Subqueries in the WHERE clause with GROUP BY. */
+explain
+select * from t1 where a1 in (select b1 from t2 where b1 > '0' group by b1);
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1      ALL     NULL    NULL    NULL    NULL    8       Using where
+2       DEPENDENT SUBQUERY      t2      index   it2i4,it2i1,it2i3       it2i1   9       NULL    1       Using where; Using index
+select * from t1 where a1 in (select b1 from t2 where b1 > '0' group by b1);
+a1      a2      a3      a4
+1 - 01  2 - 01  3 - 01  1
+1 - 02  2 - 02  3 - 02  2
+1 - 03  2 - 03  3 - 03  3
+
+explain
+select * from t1 where (a1, a2) in (select b1, b2 from t2 where b1 > '0' group by b1, b2);
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1      ALL     NULL    NULL    NULL    NULL    8       Using where
+2       DEPENDENT SUBQUERY      t2      index   it2i4,it2i1,it2i3       it2i3   18      NULL    1       Using where; Using index
+select * from t1 where (a1, a2) in (select b1, b2 from t2 where b1 > '0' group by b1, b2);
+a1      a2      a3      a4
+1 - 01  2 - 01  3 - 01  1
+1 - 02  2 - 02  3 - 02  2
+1 - 03  2 - 03  3 - 03  3
+
+explain
+select * from t1 where (a1, a2) in (select b1, b2 from t2 where b1 > '0' group by b1, b2 having b2 <  '2 - 04');
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1      ALL     NULL    NULL    NULL    NULL    8       Using where
+2       DEPENDENT SUBQUERY      t2      index   it2i4,it2i1,it2i3       it2i3   18      NULL    1       Using where; Using index
+select * from t1 where (a1, a2) in (select b1, b2 from t2 where b1 > '0' group by b1, b2 having b2 <  '2 - 04');
+a1      a2      a3      a4
+1 - 01  2 - 01  3 - 01  1
+1 - 02  2 - 02  3 - 02  2
+1 - 03  2 - 03  3 - 03  3
+
+explain
+select * from t1 where (a1, a2, a3) in (select b1, b2, b3 from t2 group by b1, b2, b3);
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1      ALL     NULL    NULL    NULL    NULL    8       Using where
+2       DEPENDENT SUBQUERY      t2      index   NULL    it2i4   27      NULL    1       Using index
+select * from t1 where (a1, a2, a3) in (select b1, b2, b3 from t2 group by b1, b2, b3);
+a1      a2      a3      a4
+1 - 01  2 - 01  3 - 01  1
+
+explain
+select * from t1 where (a1, a2, a3) in (select b1, b2, b3 from t2 where b3 = '3 - 02' group by b1, b2);
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1      ALL     NULL    NULL    NULL    NULL    8       Using where
+2       DEPENDENT SUBQUERY      t2      index   NULL    it2i4   27      NULL    1       Using where; Using index
+select * from t1 where (a1, a2, a3) in (select b1, b2, b3 from t2 where b3 = '3 - 02' group by b1, b2);
+a1      a2      a3      a4
+
+explain
+select * from t1 where (a1,a2,a3) in (select b1,b2,b3 from t2 where b1 = '1 - 01' group by b1,b2,b3);
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1      ALL     NULL    NULL    NULL    NULL    8       Using where
+2       DEPENDENT SUBQUERY      t2      range   it2i4,it2i1,it2i3       it2i4   27      NULL    2       Using where; Using index for group-by
+select * from t1 where (a1,a2,a3) in (select b1,b2,b3 from t2 where b1 = '1 - 01' group by b1,b2,b3);
+a1      a2      a3      a4
+1 - 01  2 - 01  3 - 01  1
+
+/*
+D. Subqueries for which materialization is not possible, and the
+optimizer reverts to in-to-exists.
+*/
+explain
+select left(a1,7), left(a2,7) from t1_1024 where a1 in (select b1 from t2_1024 where b1 > '0') or a2 < '9';
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1_1024 ALL     NULL    NULL    NULL    NULL    3       Using where
+2       DEPENDENT SUBQUERY      t2_1024 index_subquery  it2_1024i1,it2_1024i3   it2_1024i3      9       func    1       Using where
+select left(a1,7), left(a2,7) from t1_1024 where a1 in (select b1 from t2_1024 where b1 > '0') or a2 < '9';
+left(a1,7)      left(a2,7)
+1 - 00x 2 - 00x
+1 - 01x 2 - 01x
+1 - 03x 2 - 03x
+explain
+select left(a1,7), left(a2,7) from t1_1024 where (a1,a2) in (select b1, b2 from t2_1024 where b1 > '0') or a2 < '9';
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1_1024 ALL     NULL    NULL    NULL    NULL    3       Using where
+2       DEPENDENT SUBQUERY      t2_1024 index_subquery  it2_1024i1,it2_1024i2,it2_1024i3        it2_1024i1      9       func    2       Using where
+select left(a1,7), left(a2,7) from t1_1024 where (a1,a2) in (select b1, b2 from t2_1024 where b1 > '0') or a2 < '9';
+left(a1,7)      left(a2,7)
+1 - 00x 2 - 00x
+1 - 01x 2 - 01x
+1 - 03x 2 - 03x
+
+explain
+select left(a1,7), left(a2,7) from t1_1024 where a1 in (select substring(b1,1,1024) from t2_1024 where b1 > '0') or a2 < '9';
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1_1024 ALL     NULL    NULL    NULL    NULL    3       Using where
+2       DEPENDENT SUBQUERY      t2_1024 range   it2_1024i1,it2_1024i3   it2_1024i1      9       NULL    4       Using where
+select left(a1,7), left(a2,7) from t1_1024 where a1 in (select substring(b1,1,1024) from t2_1024 where b1 > '0') or a2 < '9';
+left(a1,7)      left(a2,7)
+1 - 00x 2 - 00x
+1 - 01x 2 - 01x
+1 - 03x 2 - 03x
+explain
+select left(a1,7), left(a2,7) from t1_1024 where (a1,a2) in (select substring(b1,1,1024), substring(b2,1,1024) from t2_1024 where b1 > '0') or a2 < '9';
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1_1024 ALL     NULL    NULL    NULL    NULL    3       Using where
+2       DEPENDENT SUBQUERY      t2_1024 range   it2_1024i1,it2_1024i3   it2_1024i1      9       NULL    4       Using where
+select left(a1,7), left(a2,7) from t1_1024 where (a1,a2) in (select substring(b1,1,1024), substring(b2,1,1024) from t2_1024 where b1 > '0') or a2 < '9';
+left(a1,7)      left(a2,7)
+1 - 00x 2 - 00x
+1 - 01x 2 - 01x
+1 - 03x 2 - 03x
+
+
+/* E. Edge cases. */
+
+/* E.1 Both materialization and in_to_exists cannot be off. */
+set @save_optimizer_switch=@@optimizer_switch;
+set @@optimizer_switch = 'materialization=off,in_to_exists=off';
+select * from t1 where a1 in (select b1 from t2 where b1 > '0' group by b1);
+ERROR HY000: At least one of the 'in_to_exists' or 'materialization' optimizer_switch flags must be 'on'.
+set @@optimizer_switch = @save_optimizer_switch;
+/* E.2 Outer query without tables, always uses IN-TO-EXISTS. */
+explain
+select '1 - 03' in (select b1 from t2 where b1 > '0');
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY NULL    NULL    NULL    NULL    NULL    NULL    NULL    No tables used
+2       DEPENDENT SUBQUERY      t2      index_subquery  it2i4,it2i1,it2i3       it2i1   9       const   2       Using index; Using where
+select '1 - 03' in (select b1 from t2 where b1 > '0');
+'1 - 03' in (select b1 from t2 where b1 > '0')
+1
+/* E.3 Subqueries without tables. */
+explain
+select a1 from t1 where a1 in (select '1 - 03') or a2 < '9';
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1      ALL     NULL    NULL    NULL    NULL    8       Using where
+Warnings:
+Note    1249    Select 2 was reduced during optimization
+select a1 from t1 where a1 in (select '1 - 03') or a2 < '9';
+a1
+1 - 00
+1 - 01
+1 - 02
+1 - 03
+1 - 04
+1 - 05
+1 - 06
+1 - 07
+UNION subqueries are currently limited to only use IN-TO-EXISTS.
+explain
+select a1 from t1 where a1 in (select '1 - 03' UNION select '1 - 02');
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1      ALL     NULL    NULL    NULL    NULL    8       Using where
+2       DEPENDENT SUBQUERY      NULL    NULL    NULL    NULL    NULL    NULL    NULL    No tables used
+3       DEPENDENT UNION NULL    NULL    NULL    NULL    NULL    NULL    NULL    No tables used
+NULL    UNION RESULT    <union2,3>      ALL     NULL    NULL    NULL    NULL    NULL    
+select a1 from t1 where a1 in (select '1 - 03' UNION select '1 - 02');
+a1
+1 - 02
+1 - 03
+/* E.4 optimize_cond detects FALSE where/having clause. */
+explain
+select a1 from t1 where a1 in (select b1 from t2 where b1 = b2 and b2 = '1 - 03' and b1 = '1 - 02' ) or a2 < '9';
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1      ALL     NULL    NULL    NULL    NULL    8       Using where
+2       SUBQUERY        NULL    NULL    NULL    NULL    NULL    NULL    NULL    Impossible WHERE
+select a1 from t1 where a1 in (select b1 from t2 where b1 = b2 and b2 = '1 - 03' and b1 = '1 - 02' ) or a2 < '9';
+a1
+1 - 00
+1 - 01
+1 - 02
+1 - 03
+1 - 04
+1 - 05
+1 - 06
+1 - 07
+/* E.5 opt_sum_query detects no matching min/max row or substitutes MIN/MAX with a const. */
+TODO this test produces wrong result due to missing logic to handle the case
+when JOIN::optimize detects an empty subquery result.
+explain
+select a1 from t1 where a1 in (select max(b1) from t2);
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1      ALL     NULL    NULL    NULL    NULL    8       Using where
+2       DEPENDENT SUBQUERY      NULL    NULL    NULL    NULL    NULL    NULL    NULL    Select tables optimized away
+select a1 from t1 where a1 in (select max(b1) from t2);
+a1
+1 - 03
+
+explain
+select a1 from t1 where a1 in (select max(b1) from t2 where b1 = '7 - 02');
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1      ALL     NULL    NULL    NULL    NULL    8       Using where
+2       DEPENDENT SUBQUERY      NULL    NULL    NULL    NULL    NULL    NULL    NULL    No matching min/max row
+select a1 from t1 where a1 in (select max(b1) from t2 where b1 = '7 - 02');
+a1
+/* E.6 make_join_select detects impossible WHERE. *
+TODO
+/* E.7 constant optimization detects "no matching row in const table". */
+TODO
+/* E.8 Impossible WHERE noticed after reading const tables. */
+explain
+select '1 - 03' in (select b1 from t2 where b1 > '0' and b1 < '0');
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY NULL    NULL    NULL    NULL    NULL    NULL    NULL    No tables used
+2       DEPENDENT SUBQUERY      NULL    NULL    NULL    NULL    NULL    NULL    NULL    Impossible WHERE noticed after reading const tables
+select '1 - 03' in (select b1 from t2 where b1 > '0' and b1 < '0');
+'1 - 03' in (select b1 from t2 where b1 > '0' and b1 < '0')
+0
+
+/* F. UPDATE/DELETE with subqueries. */
+
+TODO
+
+/* 2.3 indexed table access, non-nullabale columns. */
+call set_all_columns_not_null();
+
+/* A. Subqueries in the SELECT clause. */
+explain
+select a1, a1 in (select b1 from t2 where b1 > '0') from t1;
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1      ALL     NULL    NULL    NULL    NULL    8       
+2       DEPENDENT SUBQUERY      t2      index_subquery  it2i4,it2i1,it2i3       it2i4   8       func    1       Using index; Using where
+select a1, a1 in (select b1 from t2 where b1 > '0') from t1;
+a1      a1 in (select b1 from t2 where b1 > '0')
+1 - 00  0
+1 - 01  1
+1 - 02  1
+1 - 03  1
+1 - 04  0
+1 - 05  0
+1 - 06  0
+1 - 07  0
+
+explain
+select a1, a2, (a1, a2) in (select b1, b2 from t2 where b1 > '0') from t1;
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1      ALL     NULL    NULL    NULL    NULL    8       
+2       DEPENDENT SUBQUERY      t2      ref     it2i4,it2i1,it2i2,it2i3 it2i4   8       func    2       Using where; Using index
+select a1, a2, (a1, a2) in (select b1, b2 from t2 where b1 > '0') from t1;
+a1      a2      (a1, a2) in (select b1, b2 from t2 where b1 > '0')
+1 - 00  2 - 00  0
+1 - 01  2 - 01  1
+1 - 02  2 - 02  1
+1 - 03  2 - 03  1
+1 - 04  2 - 04  0
+1 - 05  2 - 05  0
+1 - 06  2 - 06  0
+1 - 07  2 - 07  0
+
+explain
+select a1, a2, (a1, a2) in (select b1, b2 from t2 where b1 > '0' and b1 < '9') from t1;
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1      ALL     NULL    NULL    NULL    NULL    8       
+2       DEPENDENT SUBQUERY      t2      ref     it2i4,it2i1,it2i2,it2i3 it2i4   8       func    2       Using where; Using index
+select a1, a2, (a1, a2) in (select b1, b2 from t2 where b1 > '0' and b1 < '9') from t1;
+a1      a2      (a1, a2) in (select b1, b2 from t2 where b1 > '0' and b1 < '9')
+1 - 00  2 - 00  0
+1 - 01  2 - 01  1
+1 - 02  2 - 02  1
+1 - 03  2 - 03  1
+1 - 04  2 - 04  0
+1 - 05  2 - 05  0
+1 - 06  2 - 06  0
+1 - 07  2 - 07  0
+
+/*
+B. "Natural" examples of subqueries without grouping that
+cannot be flattened into semijoin.
+*/
+explain
+select a1 from t1 where a1 in (select b2 from t2) or a2 < '9';
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1      ALL     NULL    NULL    NULL    NULL    8       Using where
+2       DEPENDENT SUBQUERY      t2      index_subquery  it2i2   it2i2   8       func    2       Using index
+select a1 from t1 where a1 in (select b2 from t2) or a2 < '9';
+a1
+1 - 00
+1 - 01
+1 - 02
+1 - 03
+1 - 04
+1 - 05
+1 - 06
+1 - 07
+
+explain
+select a1, a2 from t1 where (a1, a2) in (select b1, b2 from t2 where b1 > '0') or a2 < '9';
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1      ALL     NULL    NULL    NULL    NULL    8       Using where
+2       DEPENDENT SUBQUERY      t2      index_subquery  it2i4,it2i1,it2i2,it2i3 it2i4   16      func,func       1       Using index; Using where
+select a1, a2 from t1 where (a1, a2) in (select b1, b2 from t2 where b1 > '0') or a2 < '9';
+a1      a2
+1 - 00  2 - 00
+1 - 01  2 - 01
+1 - 02  2 - 02
+1 - 03  2 - 03
+1 - 04  2 - 04
+1 - 05  2 - 05
+1 - 06  2 - 06
+1 - 07  2 - 07
+UNION subqueries are currently limited to only use IN-TO-EXISTS.
+explain
+select a2 from t1 where a2 in (select b2 from t2 UNION select b3 from t2 as t3);
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1      ALL     NULL    NULL    NULL    NULL    8       Using where
+2       DEPENDENT SUBQUERY      t2      ref     it2i2   it2i2   8       func    2       Using index
+3       DEPENDENT UNION t3      index   NULL    it2i4   24      NULL    5       Using where; Using index
+NULL    UNION RESULT    <union2,3>      ALL     NULL    NULL    NULL    NULL    NULL    
+select a2 from t1 where a2 in (select b2 from t2 UNION select b3 from t2 as t3);
+a2
+2 - 01
+2 - 02
+2 - 03
+
+explain
+select a1 from t1 where a1 = '1 - 02' and a1 in (select max(b1) from t2 where b2 = '2 - 02');
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1      ALL     NULL    NULL    NULL    NULL    8       Using where
+2       DEPENDENT SUBQUERY      t2      ref     it2i2   it2i2   8       const   1       Using index condition
+select a1 from t1 where a1 = '1 - 02' and a1 in (select max(b1) from t2 where b2 = '2 - 02');
+a1
+1 - 02
+
+explain
+select a1, a2 from t1 where (a1, a2) in (select b1, b2 from t2 order by b3);
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1      ALL     NULL    NULL    NULL    NULL    8       Using where
+2       DEPENDENT SUBQUERY      t2      index_subquery  it2i4,it2i1,it2i2,it2i3 it2i4   16      func,func       1       Using index; Using where
+select a1, a2 from t1 where (a1, a2) in (select b1, b2 from t2 order by b3);
+a1      a2
+1 - 01  2 - 01
+1 - 02  2 - 02
+1 - 03  2 - 03
+
+/* C. Subqueries in the WHERE clause with GROUP BY. */
+explain
+select * from t1 where a1 in (select b1 from t2 where b1 > '0' group by b1);
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1      ALL     NULL    NULL    NULL    NULL    8       Using where
+2       DEPENDENT SUBQUERY      t2      index   it2i4,it2i1,it2i3       it2i1   8       NULL    1       Using where; Using index
+select * from t1 where a1 in (select b1 from t2 where b1 > '0' group by b1);
+a1      a2      a3      a4
+1 - 01  2 - 01  3 - 01  1
+1 - 02  2 - 02  3 - 02  2
+1 - 03  2 - 03  3 - 03  3
+
+explain
+select * from t1 where (a1, a2) in (select b1, b2 from t2 where b1 > '0' group by b1, b2);
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1      ALL     NULL    NULL    NULL    NULL    8       Using where
+2       DEPENDENT SUBQUERY      t2      index   it2i4,it2i1,it2i3       it2i3   16      NULL    1       Using where; Using index
+select * from t1 where (a1, a2) in (select b1, b2 from t2 where b1 > '0' group by b1, b2);
+a1      a2      a3      a4
+1 - 01  2 - 01  3 - 01  1
+1 - 02  2 - 02  3 - 02  2
+1 - 03  2 - 03  3 - 03  3
+
+explain
+select * from t1 where (a1, a2) in (select b1, b2 from t2 where b1 > '0' group by b1, b2 having b2 <  '2 - 04');
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1      ALL     NULL    NULL    NULL    NULL    8       Using where
+2       DEPENDENT SUBQUERY      t2      index   it2i4,it2i1,it2i3       it2i3   16      NULL    1       Using where; Using index
+select * from t1 where (a1, a2) in (select b1, b2 from t2 where b1 > '0' group by b1, b2 having b2 <  '2 - 04');
+a1      a2      a3      a4
+1 - 01  2 - 01  3 - 01  1
+1 - 02  2 - 02  3 - 02  2
+1 - 03  2 - 03  3 - 03  3
+
+explain
+select * from t1 where (a1, a2, a3) in (select b1, b2, b3 from t2 group by b1, b2, b3);
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1      ALL     NULL    NULL    NULL    NULL    8       Using where
+2       DEPENDENT SUBQUERY      t2      index   NULL    it2i4   24      NULL    1       Using index
+select * from t1 where (a1, a2, a3) in (select b1, b2, b3 from t2 group by b1, b2, b3);
+a1      a2      a3      a4
+1 - 01  2 - 01  3 - 01  1
+
+explain
+select * from t1 where (a1, a2, a3) in (select b1, b2, b3 from t2 where b3 = '3 - 02' group by b1, b2);
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1      ALL     NULL    NULL    NULL    NULL    8       Using where
+2       DEPENDENT SUBQUERY      t2      index   NULL    it2i4   24      NULL    1       Using where; Using index
+select * from t1 where (a1, a2, a3) in (select b1, b2, b3 from t2 where b3 = '3 - 02' group by b1, b2);
+a1      a2      a3      a4
+
+explain
+select * from t1 where (a1,a2,a3) in (select b1,b2,b3 from t2 where b1 = '1 - 01' group by b1,b2,b3);
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1      ALL     NULL    NULL    NULL    NULL    8       Using where
+2       DEPENDENT SUBQUERY      t2      range   it2i4,it2i1,it2i3       it2i4   24      NULL    2       Using where; Using index for group-by
+select * from t1 where (a1,a2,a3) in (select b1,b2,b3 from t2 where b1 = '1 - 01' group by b1,b2,b3);
+a1      a2      a3      a4
+1 - 01  2 - 01  3 - 01  1
+
+/*
+D. Subqueries for which materialization is not possible, and the
+optimizer reverts to in-to-exists.
+*/
+explain
+select left(a1,7), left(a2,7) from t1_1024 where a1 in (select b1 from t2_1024 where b1 > '0') or a2 < '9';
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1_1024 ALL     NULL    NULL    NULL    NULL    3       Using where
+2       DEPENDENT SUBQUERY      t2_1024 index_subquery  it2_1024i1,it2_1024i3   it2_1024i3      9       func    1       Using where
+select left(a1,7), left(a2,7) from t1_1024 where a1 in (select b1 from t2_1024 where b1 > '0') or a2 < '9';
+left(a1,7)      left(a2,7)
+1 - 00x 2 - 00x
+1 - 01x 2 - 01x
+1 - 03x 2 - 03x
+explain
+select left(a1,7), left(a2,7) from t1_1024 where (a1,a2) in (select b1, b2 from t2_1024 where b1 > '0') or a2 < '9';
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1_1024 ALL     NULL    NULL    NULL    NULL    3       Using where
+2       DEPENDENT SUBQUERY      t2_1024 index_subquery  it2_1024i1,it2_1024i2,it2_1024i3        it2_1024i1      9       func    2       Using where
+select left(a1,7), left(a2,7) from t1_1024 where (a1,a2) in (select b1, b2 from t2_1024 where b1 > '0') or a2 < '9';
+left(a1,7)      left(a2,7)
+1 - 00x 2 - 00x
+1 - 01x 2 - 01x
+1 - 03x 2 - 03x
+
+explain
+select left(a1,7), left(a2,7) from t1_1024 where a1 in (select substring(b1,1,1024) from t2_1024 where b1 > '0') or a2 < '9';
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1_1024 ALL     NULL    NULL    NULL    NULL    3       Using where
+2       DEPENDENT SUBQUERY      t2_1024 range   it2_1024i1,it2_1024i3   it2_1024i1      9       NULL    4       Using where
+select left(a1,7), left(a2,7) from t1_1024 where a1 in (select substring(b1,1,1024) from t2_1024 where b1 > '0') or a2 < '9';
+left(a1,7)      left(a2,7)
+1 - 00x 2 - 00x
+1 - 01x 2 - 01x
+1 - 03x 2 - 03x
+explain
+select left(a1,7), left(a2,7) from t1_1024 where (a1,a2) in (select substring(b1,1,1024), substring(b2,1,1024) from t2_1024 where b1 > '0') or a2 < '9';
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1_1024 ALL     NULL    NULL    NULL    NULL    3       Using where
+2       DEPENDENT SUBQUERY      t2_1024 range   it2_1024i1,it2_1024i3   it2_1024i1      9       NULL    4       Using where
+select left(a1,7), left(a2,7) from t1_1024 where (a1,a2) in (select substring(b1,1,1024), substring(b2,1,1024) from t2_1024 where b1 > '0') or a2 < '9';
+left(a1,7)      left(a2,7)
+1 - 00x 2 - 00x
+1 - 01x 2 - 01x
+1 - 03x 2 - 03x
+
+
+/* E. Edge cases. */
+
+/* E.1 Both materialization and in_to_exists cannot be off. */
+set @save_optimizer_switch=@@optimizer_switch;
+set @@optimizer_switch = 'materialization=off,in_to_exists=off';
+select * from t1 where a1 in (select b1 from t2 where b1 > '0' group by b1);
+ERROR HY000: At least one of the 'in_to_exists' or 'materialization' optimizer_switch flags must be 'on'.
+set @@optimizer_switch = @save_optimizer_switch;
+/* E.2 Outer query without tables, always uses IN-TO-EXISTS. */
+explain
+select '1 - 03' in (select b1 from t2 where b1 > '0');
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY NULL    NULL    NULL    NULL    NULL    NULL    NULL    No tables used
+2       DEPENDENT SUBQUERY      t2      index_subquery  it2i4,it2i1,it2i3       it2i1   8       const   5       Using index; Using where
+select '1 - 03' in (select b1 from t2 where b1 > '0');
+'1 - 03' in (select b1 from t2 where b1 > '0')
+1
+/* E.3 Subqueries without tables. */
+explain
+select a1 from t1 where a1 in (select '1 - 03') or a2 < '9';
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1      ALL     NULL    NULL    NULL    NULL    8       Using where
+Warnings:
+Note    1249    Select 2 was reduced during optimization
+select a1 from t1 where a1 in (select '1 - 03') or a2 < '9';
+a1
+1 - 00
+1 - 01
+1 - 02
+1 - 03
+1 - 04
+1 - 05
+1 - 06
+1 - 07
+UNION subqueries are currently limited to only use IN-TO-EXISTS.
+explain
+select a1 from t1 where a1 in (select '1 - 03' UNION select '1 - 02');
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1      ALL     NULL    NULL    NULL    NULL    8       Using where
+2       DEPENDENT SUBQUERY      NULL    NULL    NULL    NULL    NULL    NULL    NULL    No tables used
+3       DEPENDENT UNION NULL    NULL    NULL    NULL    NULL    NULL    NULL    No tables used
+NULL    UNION RESULT    <union2,3>      ALL     NULL    NULL    NULL    NULL    NULL    
+select a1 from t1 where a1 in (select '1 - 03' UNION select '1 - 02');
+a1
+1 - 02
+1 - 03
+/* E.4 optimize_cond detects FALSE where/having clause. */
+explain
+select a1 from t1 where a1 in (select b1 from t2 where b1 = b2 and b2 = '1 - 03' and b1 = '1 - 02' ) or a2 < '9';
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1      ALL     NULL    NULL    NULL    NULL    8       Using where
+2       SUBQUERY        NULL    NULL    NULL    NULL    NULL    NULL    NULL    Impossible WHERE
+select a1 from t1 where a1 in (select b1 from t2 where b1 = b2 and b2 = '1 - 03' and b1 = '1 - 02' ) or a2 < '9';
+a1
+1 - 00
+1 - 01
+1 - 02
+1 - 03
+1 - 04
+1 - 05
+1 - 06
+1 - 07
+/* E.5 opt_sum_query detects no matching min/max row or substitutes MIN/MAX with a const. */
+TODO this test produces wrong result due to missing logic to handle the case
+when JOIN::optimize detects an empty subquery result.
+explain
+select a1 from t1 where a1 in (select max(b1) from t2);
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1      ALL     NULL    NULL    NULL    NULL    8       Using where
+2       DEPENDENT SUBQUERY      NULL    NULL    NULL    NULL    NULL    NULL    NULL    Select tables optimized away
+select a1 from t1 where a1 in (select max(b1) from t2);
+a1
+1 - 03
+
+explain
+select a1 from t1 where a1 in (select max(b1) from t2 where b1 = '7 - 02');
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1      ALL     NULL    NULL    NULL    NULL    8       Using where
+2       DEPENDENT SUBQUERY      NULL    NULL    NULL    NULL    NULL    NULL    NULL    No matching min/max row
+select a1 from t1 where a1 in (select max(b1) from t2 where b1 = '7 - 02');
+a1
+/* E.6 make_join_select detects impossible WHERE. *
+TODO
+/* E.7 constant optimization detects "no matching row in const table". */
+TODO
+/* E.8 Impossible WHERE noticed after reading const tables. */
+explain
+select '1 - 03' in (select b1 from t2 where b1 > '0' and b1 < '0');
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY NULL    NULL    NULL    NULL    NULL    NULL    NULL    No tables used
+2       DEPENDENT SUBQUERY      NULL    NULL    NULL    NULL    NULL    NULL    NULL    Impossible WHERE noticed after reading const tables
+select '1 - 03' in (select b1 from t2 where b1 > '0' and b1 < '0');
+'1 - 03' in (select b1 from t2 where b1 > '0' and b1 < '0')
+0
+
+/* F. UPDATE/DELETE with subqueries. */
+
+TODO
+
+call set_all_columns_nullable();
+/******************************************************************************
+3. Materialization is ON, in-to-exists is OFF, in-to-exists is cheaper.
+******************************************************************************/
+set @@optimizer_switch='materialization=on,in_to_exists=off';
+call delete_materialization_data();
+call make_t1_indexes();
+/* 3.1 non-indexed table access */
+call remove_t2_indexes();
+
+/* A. Subqueries in the SELECT clause. */
+explain
+select a1, a1 in (select b1 from t2 where b1 > '0') from t1;
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1      index   NULL    it1i1   9       NULL    3       Using index
+2       SUBQUERY        t2      ALL     NULL    NULL    NULL    NULL    5       Using where
+select a1, a1 in (select b1 from t2 where b1 > '0') from t1;
+a1      a1 in (select b1 from t2 where b1 > '0')
+1 - 00  0
+1 - 01  1
+1 - 02  1
+
+explain
+select a1, a2, (a1, a2) in (select b1, b2 from t2 where b1 > '0') from t1;
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1      index   NULL    it1i3   18      NULL    3       Using index
+2       SUBQUERY        t2      ALL     NULL    NULL    NULL    NULL    5       Using where
+select a1, a2, (a1, a2) in (select b1, b2 from t2 where b1 > '0') from t1;
+a1      a2      (a1, a2) in (select b1, b2 from t2 where b1 > '0')
+1 - 00  2 - 00  0
+1 - 01  2 - 01  1
+1 - 02  2 - 02  1
+
+explain
+select a1, a2, (a1, a2) in (select b1, b2 from t2 where b1 > '0' and b1 < '9') from t1;
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1      index   NULL    it1i3   18      NULL    3       Using index
+2       SUBQUERY        t2      ALL     NULL    NULL    NULL    NULL    5       Using where
+select a1, a2, (a1, a2) in (select b1, b2 from t2 where b1 > '0' and b1 < '9') from t1;
+a1      a2      (a1, a2) in (select b1, b2 from t2 where b1 > '0' and b1 < '9')
+1 - 00  2 - 00  0
+1 - 01  2 - 01  1
+1 - 02  2 - 02  1
+
+/*
+B. "Natural" examples of subqueries without grouping that
+cannot be flattened into semijoin.
+*/
+explain
+select a1 from t1 where a1 in (select b2 from t2) or a2 < '9';
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1      index   it1i2   it1i3   18      NULL    3       Using where; Using index
+2       SUBQUERY        t2      ALL     NULL    NULL    NULL    NULL    5       
+select a1 from t1 where a1 in (select b2 from t2) or a2 < '9';
+a1
+1 - 00
+1 - 01
+1 - 02
+
+explain
+select a1, a2 from t1 where (a1, a2) in (select b1, b2 from t2 where b1 > '0') or a2 < '9';
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1      index   it1i2   it1i3   18      NULL    3       Using where; Using index
+2       SUBQUERY        t2      ALL     NULL    NULL    NULL    NULL    5       Using where
+select a1, a2 from t1 where (a1, a2) in (select b1, b2 from t2 where b1 > '0') or a2 < '9';
+a1      a2
+1 - 00  2 - 00
+1 - 01  2 - 01
+1 - 02  2 - 02
+UNION subqueries are currently limited to only use IN-TO-EXISTS.
+explain
+select a2 from t1 where a2 in (select b2 from t2 UNION select b3 from t2 as t3);
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1      index   NULL    it1i2   9       NULL    3       Using where; Using index
+2       DEPENDENT SUBQUERY      t2      ALL     NULL    NULL    NULL    NULL    5       Using where
+3       DEPENDENT UNION t3      ALL     NULL    NULL    NULL    NULL    5       Using where
+NULL    UNION RESULT    <union2,3>      ALL     NULL    NULL    NULL    NULL    NULL    
+select a2 from t1 where a2 in (select b2 from t2 UNION select b3 from t2 as t3);
+a2
+2 - 01
+2 - 02
+
+explain
+select a1 from t1 where a1 = '1 - 02' and a1 in (select max(b1) from t2 where b2 = '2 - 02');
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1      ref     it1i1,it1i3     it1i1   9       const   1       Using where; Using index
+2       SUBQUERY        t2      ALL     NULL    NULL    NULL    NULL    5       Using where
+select a1 from t1 where a1 = '1 - 02' and a1 in (select max(b1) from t2 where b2 = '2 - 02');
+a1
+1 - 02
+
+explain
+select a1, a2 from t1 where (a1, a2) in (select b1, b2 from t2 order by b3);
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1      index   NULL    it1i3   18      NULL    3       Using where; Using index
+2       SUBQUERY        t2      ALL     NULL    NULL    NULL    NULL    5       
+select a1, a2 from t1 where (a1, a2) in (select b1, b2 from t2 order by b3);
+a1      a2
+1 - 01  2 - 01
+1 - 02  2 - 02
+
+/* C. Subqueries in the WHERE clause with GROUP BY. */
+explain
+select * from t1 where a1 in (select b1 from t2 where b1 > '0' group by b1);
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1      ALL     NULL    NULL    NULL    NULL    3       Using where
+2       SUBQUERY        t2      ALL     NULL    NULL    NULL    NULL    5       Using where; Using temporary; Using filesort
+select * from t1 where a1 in (select b1 from t2 where b1 > '0' group by b1);
+a1      a2      a3      a4
+1 - 01  2 - 01  3 - 01  1
+1 - 02  2 - 02  3 - 02  2
+
+explain
+select * from t1 where (a1, a2) in (select b1, b2 from t2 where b1 > '0' group by b1, b2);
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1      ALL     NULL    NULL    NULL    NULL    3       Using where
+2       SUBQUERY        t2      ALL     NULL    NULL    NULL    NULL    5       Using where; Using temporary; Using filesort
+select * from t1 where (a1, a2) in (select b1, b2 from t2 where b1 > '0' group by b1, b2);
+a1      a2      a3      a4
+1 - 01  2 - 01  3 - 01  1
+1 - 02  2 - 02  3 - 02  2
+
+explain
+select * from t1 where (a1, a2) in (select b1, b2 from t2 where b1 > '0' group by b1, b2 having b2 <  '2 - 04');
+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    5       Using where; Using temporary; Using filesort
+select * from t1 where (a1, a2) in (select b1, b2 from t2 where b1 > '0' group by b1, b2 having b2 <  '2 - 04');
+a1      a2      a3      a4
+1 - 01  2 - 01  3 - 01  1
+1 - 02  2 - 02  3 - 02  2
+
+explain
+select * from t1 where (a1, a2, a3) in (select b1, b2, b3 from t2 group by b1, b2, b3);
+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    5       Using temporary; Using filesort
+select * from t1 where (a1, a2, a3) in (select b1, b2, b3 from t2 group by b1, b2, b3);
+a1      a2      a3      a4
+1 - 01  2 - 01  3 - 01  1
+
+explain
+select * from t1 where (a1, a2, a3) in (select b1, b2, b3 from t2 where b3 = '3 - 02' group by b1, b2);
+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    5       Using where; Using temporary; Using filesort
+select * from t1 where (a1, a2, a3) in (select b1, b2, b3 from t2 where b3 = '3 - 02' group by b1, b2);
+a1      a2      a3      a4
+
+explain
+select * from t1 where (a1,a2,a3) in (select b1,b2,b3 from t2 where b1 = '1 - 01' group by b1,b2,b3);
+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    5       Using where; Using temporary; Using filesort
+select * from t1 where (a1,a2,a3) in (select b1,b2,b3 from t2 where b1 = '1 - 01' group by b1,b2,b3);
+a1      a2      a3      a4
+1 - 01  2 - 01  3 - 01  1
+
+/*
+D. Subqueries for which materialization is not possible, and the
+optimizer reverts to in-to-exists.
+*/
+explain
+select left(a1,7), left(a2,7) from t1_1024 where a1 in (select b1 from t2_1024 where b1 > '0') or a2 < '9';
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1_1024 ALL     it1_1024i2      NULL    NULL    NULL    2       Using where
+2       DEPENDENT SUBQUERY      t2_1024 ALL     NULL    NULL    NULL    NULL    4       Using where
+select left(a1,7), left(a2,7) from t1_1024 where a1 in (select b1 from t2_1024 where b1 > '0') or a2 < '9';
+left(a1,7)      left(a2,7)
+1 - 00x 2 - 00x
+1 - 01x 2 - 01x
+explain
+select left(a1,7), left(a2,7) from t1_1024 where (a1,a2) in (select b1, b2 from t2_1024 where b1 > '0') or a2 < '9';
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1_1024 ALL     it1_1024i2      NULL    NULL    NULL    2       Using where
+2       DEPENDENT SUBQUERY      t2_1024 ALL     NULL    NULL    NULL    NULL    4       Using where
+select left(a1,7), left(a2,7) from t1_1024 where (a1,a2) in (select b1, b2 from t2_1024 where b1 > '0') or a2 < '9';
+left(a1,7)      left(a2,7)
+1 - 00x 2 - 00x
+1 - 01x 2 - 01x
+
+explain
+select left(a1,7), left(a2,7) from t1_1024 where a1 in (select substring(b1,1,1024) from t2_1024 where b1 > '0') or a2 < '9';
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1_1024 ALL     it1_1024i2      NULL    NULL    NULL    2       Using where
+2       DEPENDENT SUBQUERY      t2_1024 ALL     NULL    NULL    NULL    NULL    4       Using where
+select left(a1,7), left(a2,7) from t1_1024 where a1 in (select substring(b1,1,1024) from t2_1024 where b1 > '0') or a2 < '9';
+left(a1,7)      left(a2,7)
+1 - 00x 2 - 00x
+1 - 01x 2 - 01x
+explain
+select left(a1,7), left(a2,7) from t1_1024 where (a1,a2) in (select substring(b1,1,1024), substring(b2,1,1024) from t2_1024 where b1 > '0') or a2 < '9';
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1_1024 ALL     it1_1024i2      NULL    NULL    NULL    2       Using where
+2       DEPENDENT SUBQUERY      t2_1024 ALL     NULL    NULL    NULL    NULL    4       Using where
+select left(a1,7), left(a2,7) from t1_1024 where (a1,a2) in (select substring(b1,1,1024), substring(b2,1,1024) from t2_1024 where b1 > '0') or a2 < '9';
+left(a1,7)      left(a2,7)
+1 - 00x 2 - 00x
+1 - 01x 2 - 01x
+
+
+/* E. Edge cases. */
+
+/* E.1 Both materialization and in_to_exists cannot be off. */
+set @save_optimizer_switch=@@optimizer_switch;
+set @@optimizer_switch = 'materialization=off,in_to_exists=off';
+select * from t1 where a1 in (select b1 from t2 where b1 > '0' group by b1);
+ERROR HY000: At least one of the 'in_to_exists' or 'materialization' optimizer_switch flags must be 'on'.
+set @@optimizer_switch = @save_optimizer_switch;
+/* E.2 Outer query without tables, always uses IN-TO-EXISTS. */
+explain
+select '1 - 03' in (select b1 from t2 where b1 > '0');
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY NULL    NULL    NULL    NULL    NULL    NULL    NULL    No tables used
+2       DEPENDENT SUBQUERY      t2      ALL     NULL    NULL    NULL    NULL    5       Using where
+select '1 - 03' in (select b1 from t2 where b1 > '0');
+'1 - 03' in (select b1 from t2 where b1 > '0')
+1
+/* E.3 Subqueries without tables. */
+explain
+select a1 from t1 where a1 in (select '1 - 03') or a2 < '9';
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1      index   it1i1,it1i2,it1i3       it1i3   18      NULL    3       Using where; Using index
+Warnings:
+Note    1249    Select 2 was reduced during optimization
+select a1 from t1 where a1 in (select '1 - 03') or a2 < '9';
+a1
+1 - 00
+1 - 01
+1 - 02
+UNION subqueries are currently limited to only use IN-TO-EXISTS.
+explain
+select a1 from t1 where a1 in (select '1 - 03' UNION select '1 - 02');
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1      index   NULL    it1i1   9       NULL    3       Using where; Using index
+2       DEPENDENT SUBQUERY      NULL    NULL    NULL    NULL    NULL    NULL    NULL    No tables used
+3       DEPENDENT UNION NULL    NULL    NULL    NULL    NULL    NULL    NULL    No tables used
+NULL    UNION RESULT    <union2,3>      ALL     NULL    NULL    NULL    NULL    NULL    
+select a1 from t1 where a1 in (select '1 - 03' UNION select '1 - 02');
+a1
+1 - 02
+/* E.4 optimize_cond detects FALSE where/having clause. */
+explain
+select a1 from t1 where a1 in (select b1 from t2 where b1 = b2 and b2 = '1 - 03' and b1 = '1 - 02' ) or a2 < '9';
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1      index   it1i2   it1i3   18      NULL    3       Using where; Using index
+2       SUBQUERY        NULL    NULL    NULL    NULL    NULL    NULL    NULL    Impossible WHERE
+select a1 from t1 where a1 in (select b1 from t2 where b1 = b2 and b2 = '1 - 03' and b1 = '1 - 02' ) or a2 < '9';
+a1
+1 - 00
+1 - 01
+1 - 02
+/* E.5 opt_sum_query detects no matching min/max row or substitutes MIN/MAX with a const. */
+TODO this test produces wrong result due to missing logic to handle the case
+when JOIN::optimize detects an empty subquery result.
+explain
+select a1 from t1 where a1 in (select max(b1) from t2);
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1      index   NULL    it1i1   9       NULL    3       Using where; Using index
+2       SUBQUERY        t2      ALL     NULL    NULL    NULL    NULL    5       
+select a1 from t1 where a1 in (select max(b1) from t2);
+a1
+
+explain
+select a1 from t1 where a1 in (select max(b1) from t2 where b1 = '7 - 02');
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1      index   NULL    it1i1   9       NULL    3       Using where; Using index
+2       SUBQUERY        t2      ALL     NULL    NULL    NULL    NULL    5       Using where
+select a1 from t1 where a1 in (select max(b1) from t2 where b1 = '7 - 02');
+a1
+/* E.6 make_join_select detects impossible WHERE. *
+TODO
+/* E.7 constant optimization detects "no matching row in const table". */
+TODO
+/* E.8 Impossible WHERE noticed after reading const tables. */
+explain
+select '1 - 03' in (select b1 from t2 where b1 > '0' and b1 < '0');
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY NULL    NULL    NULL    NULL    NULL    NULL    NULL    No tables used
+2       DEPENDENT SUBQUERY      t2      ALL     NULL    NULL    NULL    NULL    5       Using where
+select '1 - 03' in (select b1 from t2 where b1 > '0' and b1 < '0');
+'1 - 03' in (select b1 from t2 where b1 > '0' and b1 < '0')
+0
+
+/* F. UPDATE/DELETE with subqueries. */
+
+TODO
+
+/* 3.2 indexed table access, nullabale columns. */
+call make_t2_indexes();
+
+/* A. Subqueries in the SELECT clause. */
+explain
+select a1, a1 in (select b1 from t2 where b1 > '0') from t1;
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1      index   NULL    it1i1   9       NULL    3       Using index
+2       SUBQUERY        t2      index   it2i4,it2i1,it2i3       it2i1   9       NULL    5       Using where; Using index
+select a1, a1 in (select b1 from t2 where b1 > '0') from t1;
+a1      a1 in (select b1 from t2 where b1 > '0')
+1 - 00  0
+1 - 01  1
+1 - 02  1
+
+explain
+select a1, a2, (a1, a2) in (select b1, b2 from t2 where b1 > '0') from t1;
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1      index   NULL    it1i3   18      NULL    3       Using index
+2       SUBQUERY        t2      index   it2i4,it2i1,it2i3       it2i3   18      NULL    5       Using where; Using index
+select a1, a2, (a1, a2) in (select b1, b2 from t2 where b1 > '0') from t1;
+a1      a2      (a1, a2) in (select b1, b2 from t2 where b1 > '0')
+1 - 00  2 - 00  0
+1 - 01  2 - 01  1
+1 - 02  2 - 02  1
+
+explain
+select a1, a2, (a1, a2) in (select b1, b2 from t2 where b1 > '0' and b1 < '9') from t1;
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1      index   NULL    it1i3   18      NULL    3       Using index
+2       SUBQUERY        t2      range   it2i4,it2i1,it2i3       it2i3   9       NULL    4       Using where; Using index
+select a1, a2, (a1, a2) in (select b1, b2 from t2 where b1 > '0' and b1 < '9') from t1;
+a1      a2      (a1, a2) in (select b1, b2 from t2 where b1 > '0' and b1 < '9')
+1 - 00  2 - 00  0
+1 - 01  2 - 01  1
+1 - 02  2 - 02  1
+
+/*
+B. "Natural" examples of subqueries without grouping that
+cannot be flattened into semijoin.
+*/
+explain
+select a1 from t1 where a1 in (select b2 from t2) or a2 < '9';
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1      index   it1i2   it1i3   18      NULL    3       Using where; Using index
+2       SUBQUERY        t2      index   NULL    it2i2   9       NULL    5       Using index
+select a1 from t1 where a1 in (select b2 from t2) or a2 < '9';
+a1
+1 - 00
+1 - 01
+1 - 02
+
+explain
+select a1, a2 from t1 where (a1, a2) in (select b1, b2 from t2 where b1 > '0') or a2 < '9';
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1      index   it1i2   it1i3   18      NULL    3       Using where; Using index
+2       SUBQUERY        t2      index   it2i4,it2i1,it2i3       it2i3   18      NULL    5       Using where; Using index
+select a1, a2 from t1 where (a1, a2) in (select b1, b2 from t2 where b1 > '0') or a2 < '9';
+a1      a2
+1 - 00  2 - 00
+1 - 01  2 - 01
+1 - 02  2 - 02
+UNION subqueries are currently limited to only use IN-TO-EXISTS.
+explain
+select a2 from t1 where a2 in (select b2 from t2 UNION select b3 from t2 as t3);
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1      index   NULL    it1i2   9       NULL    3       Using where; Using index
+2       DEPENDENT SUBQUERY      t2      ref     it2i2   it2i2   9       func    2       Using index
+3       DEPENDENT UNION t3      index   NULL    it2i4   27      NULL    5       Using where; Using index
+NULL    UNION RESULT    <union2,3>      ALL     NULL    NULL    NULL    NULL    NULL    
+select a2 from t1 where a2 in (select b2 from t2 UNION select b3 from t2 as t3);
+a2
+2 - 01
+2 - 02
+
+explain
+select a1 from t1 where a1 = '1 - 02' and a1 in (select max(b1) from t2 where b2 = '2 - 02');
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1      ref     it1i1,it1i3     it1i1   9       const   1       Using where; Using index
+2       SUBQUERY        t2      ref     it2i2   it2i2   9       const   1       Using index condition
+select a1 from t1 where a1 = '1 - 02' and a1 in (select max(b1) from t2 where b2 = '2 - 02');
+a1
+1 - 02
+
+explain
+select a1, a2 from t1 where (a1, a2) in (select b1, b2 from t2 order by b3);
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1      index   NULL    it1i3   18      NULL    3       Using where; Using index
+2       SUBQUERY        t2      index   NULL    it2i4   27      NULL    5       Using index
+select a1, a2 from t1 where (a1, a2) in (select b1, b2 from t2 order by b3);
+a1      a2
+1 - 01  2 - 01
+1 - 02  2 - 02
+
+/* C. Subqueries in the WHERE clause with GROUP BY. */
+explain
+select * from t1 where a1 in (select b1 from t2 where b1 > '0' group by b1);
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1      ALL     NULL    NULL    NULL    NULL    3       Using where
+2       SUBQUERY        t2      index   it2i4,it2i1,it2i3       it2i1   9       NULL    5       Using where; Using index
+select * from t1 where a1 in (select b1 from t2 where b1 > '0' group by b1);
+a1      a2      a3      a4
+1 - 01  2 - 01  3 - 01  1
+1 - 02  2 - 02  3 - 02  2
+
+explain
+select * from t1 where (a1, a2) in (select b1, b2 from t2 where b1 > '0' group by b1, b2);
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1      ALL     NULL    NULL    NULL    NULL    3       Using where
+2       SUBQUERY        t2      index   it2i4,it2i1,it2i3       it2i3   18      NULL    5       Using where; Using index
+select * from t1 where (a1, a2) in (select b1, b2 from t2 where b1 > '0' group by b1, b2);
+a1      a2      a3      a4
+1 - 01  2 - 01  3 - 01  1
+1 - 02  2 - 02  3 - 02  2
+
+explain
+select * from t1 where (a1, a2) in (select b1, b2 from t2 where b1 > '0' group by b1, b2 having b2 <  '2 - 04');
+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      index   it2i4,it2i1,it2i3       it2i3   18      NULL    5       Using where; Using index
+select * from t1 where (a1, a2) in (select b1, b2 from t2 where b1 > '0' group by b1, b2 having b2 <  '2 - 04');
+a1      a2      a3      a4
+1 - 01  2 - 01  3 - 01  1
+1 - 02  2 - 02  3 - 02  2
+
+explain
+select * from t1 where (a1, a2, a3) in (select b1, b2, b3 from t2 group by b1, b2, b3);
+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      range   NULL    it2i4   27      NULL    6       Using index for group-by
+select * from t1 where (a1, a2, a3) in (select b1, b2, b3 from t2 group by b1, b2, b3);
+a1      a2      a3      a4
+1 - 01  2 - 01  3 - 01  1
+
+explain
+select * from t1 where (a1, a2, a3) in (select b1, b2, b3 from t2 where b3 = '3 - 02' group by b1, b2);
+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      range   NULL    it2i4   27      NULL    6       Using where; Using index for group-by
+select * from t1 where (a1, a2, a3) in (select b1, b2, b3 from t2 where b3 = '3 - 02' group by b1, b2);
+a1      a2      a3      a4
+
+explain
+select * from t1 where (a1,a2,a3) in (select b1,b2,b3 from t2 where b1 = '1 - 01' group by b1,b2,b3);
+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      range   it2i4,it2i1,it2i3       it2i4   27      NULL    2       Using where; Using index for group-by
+select * from t1 where (a1,a2,a3) in (select b1,b2,b3 from t2 where b1 = '1 - 01' group by b1,b2,b3);
+a1      a2      a3      a4
+1 - 01  2 - 01  3 - 01  1
+
+/*
+D. Subqueries for which materialization is not possible, and the
+optimizer reverts to in-to-exists.
+*/
+explain
+select left(a1,7), left(a2,7) from t1_1024 where a1 in (select b1 from t2_1024 where b1 > '0') or a2 < '9';
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1_1024 ALL     it1_1024i2      NULL    NULL    NULL    2       Using where
+2       DEPENDENT SUBQUERY      t2_1024 index_subquery  it2_1024i1,it2_1024i3   it2_1024i3      9       func    1       Using where
+select left(a1,7), left(a2,7) from t1_1024 where a1 in (select b1 from t2_1024 where b1 > '0') or a2 < '9';
+left(a1,7)      left(a2,7)
+1 - 00x 2 - 00x
+1 - 01x 2 - 01x
+explain
+select left(a1,7), left(a2,7) from t1_1024 where (a1,a2) in (select b1, b2 from t2_1024 where b1 > '0') or a2 < '9';
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1_1024 ALL     it1_1024i2      NULL    NULL    NULL    2       Using where
+2       DEPENDENT SUBQUERY      t2_1024 index_subquery  it2_1024i1,it2_1024i2,it2_1024i3        it2_1024i1      9       func    2       Using where
+select left(a1,7), left(a2,7) from t1_1024 where (a1,a2) in (select b1, b2 from t2_1024 where b1 > '0') or a2 < '9';
+left(a1,7)      left(a2,7)
+1 - 00x 2 - 00x
+1 - 01x 2 - 01x
+
+explain
+select left(a1,7), left(a2,7) from t1_1024 where a1 in (select substring(b1,1,1024) from t2_1024 where b1 > '0') or a2 < '9';
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1_1024 ALL     it1_1024i2      NULL    NULL    NULL    2       Using where
+2       DEPENDENT SUBQUERY      t2_1024 range   it2_1024i1,it2_1024i3   it2_1024i1      9       NULL    4       Using where
+select left(a1,7), left(a2,7) from t1_1024 where a1 in (select substring(b1,1,1024) from t2_1024 where b1 > '0') or a2 < '9';
+left(a1,7)      left(a2,7)
+1 - 00x 2 - 00x
+1 - 01x 2 - 01x
+explain
+select left(a1,7), left(a2,7) from t1_1024 where (a1,a2) in (select substring(b1,1,1024), substring(b2,1,1024) from t2_1024 where b1 > '0') or a2 < '9';
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1_1024 ALL     it1_1024i2      NULL    NULL    NULL    2       Using where
+2       DEPENDENT SUBQUERY      t2_1024 range   it2_1024i1,it2_1024i3   it2_1024i1      9       NULL    4       Using where
+select left(a1,7), left(a2,7) from t1_1024 where (a1,a2) in (select substring(b1,1,1024), substring(b2,1,1024) from t2_1024 where b1 > '0') or a2 < '9';
+left(a1,7)      left(a2,7)
+1 - 00x 2 - 00x
+1 - 01x 2 - 01x
+
+
+/* E. Edge cases. */
+
+/* E.1 Both materialization and in_to_exists cannot be off. */
+set @save_optimizer_switch=@@optimizer_switch;
+set @@optimizer_switch = 'materialization=off,in_to_exists=off';
+select * from t1 where a1 in (select b1 from t2 where b1 > '0' group by b1);
+ERROR HY000: At least one of the 'in_to_exists' or 'materialization' optimizer_switch flags must be 'on'.
+set @@optimizer_switch = @save_optimizer_switch;
+/* E.2 Outer query without tables, always uses IN-TO-EXISTS. */
+explain
+select '1 - 03' in (select b1 from t2 where b1 > '0');
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY NULL    NULL    NULL    NULL    NULL    NULL    NULL    No tables used
+2       DEPENDENT SUBQUERY      t2      index_subquery  it2i4,it2i1,it2i3       it2i1   9       const   2       Using index; Using where
+select '1 - 03' in (select b1 from t2 where b1 > '0');
+'1 - 03' in (select b1 from t2 where b1 > '0')
+1
+/* E.3 Subqueries without tables. */
+explain
+select a1 from t1 where a1 in (select '1 - 03') or a2 < '9';
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1      index   it1i1,it1i2,it1i3       it1i3   18      NULL    3       Using where; Using index
+Warnings:
+Note    1249    Select 2 was reduced during optimization
+select a1 from t1 where a1 in (select '1 - 03') or a2 < '9';
+a1
+1 - 00
+1 - 01
+1 - 02
+UNION subqueries are currently limited to only use IN-TO-EXISTS.
+explain
+select a1 from t1 where a1 in (select '1 - 03' UNION select '1 - 02');
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1      index   NULL    it1i1   9       NULL    3       Using where; Using index
+2       DEPENDENT SUBQUERY      NULL    NULL    NULL    NULL    NULL    NULL    NULL    No tables used
+3       DEPENDENT UNION NULL    NULL    NULL    NULL    NULL    NULL    NULL    No tables used
+NULL    UNION RESULT    <union2,3>      ALL     NULL    NULL    NULL    NULL    NULL    
+select a1 from t1 where a1 in (select '1 - 03' UNION select '1 - 02');
+a1
+1 - 02
+/* E.4 optimize_cond detects FALSE where/having clause. */
+explain
+select a1 from t1 where a1 in (select b1 from t2 where b1 = b2 and b2 = '1 - 03' and b1 = '1 - 02' ) or a2 < '9';
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1      index   it1i2   it1i3   18      NULL    3       Using where; Using index
+2       SUBQUERY        NULL    NULL    NULL    NULL    NULL    NULL    NULL    Impossible WHERE
+select a1 from t1 where a1 in (select b1 from t2 where b1 = b2 and b2 = '1 - 03' and b1 = '1 - 02' ) or a2 < '9';
+a1
+1 - 00
+1 - 01
+1 - 02
+/* E.5 opt_sum_query detects no matching min/max row or substitutes MIN/MAX with a const. */
+TODO this test produces wrong result due to missing logic to handle the case
+when JOIN::optimize detects an empty subquery result.
+explain
+select a1 from t1 where a1 in (select max(b1) from t2);
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1      index   NULL    it1i1   9       NULL    3       Using where; Using index
+2       DEPENDENT SUBQUERY      NULL    NULL    NULL    NULL    NULL    NULL    NULL    Select tables optimized away
+select a1 from t1 where a1 in (select max(b1) from t2);
+a1
+
+explain
+select a1 from t1 where a1 in (select max(b1) from t2 where b1 = '7 - 02');
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1      index   NULL    it1i1   9       NULL    3       Using where; Using index
+2       DEPENDENT SUBQUERY      NULL    NULL    NULL    NULL    NULL    NULL    NULL    No matching min/max row
+select a1 from t1 where a1 in (select max(b1) from t2 where b1 = '7 - 02');
+a1
+/* E.6 make_join_select detects impossible WHERE. *
+TODO
+/* E.7 constant optimization detects "no matching row in const table". */
+TODO
+/* E.8 Impossible WHERE noticed after reading const tables. */
+explain
+select '1 - 03' in (select b1 from t2 where b1 > '0' and b1 < '0');
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY NULL    NULL    NULL    NULL    NULL    NULL    NULL    No tables used
+2       DEPENDENT SUBQUERY      NULL    NULL    NULL    NULL    NULL    NULL    NULL    Impossible WHERE noticed after reading const tables
+select '1 - 03' in (select b1 from t2 where b1 > '0' and b1 < '0');
+'1 - 03' in (select b1 from t2 where b1 > '0' and b1 < '0')
+0
+
+/* F. UPDATE/DELETE with subqueries. */
+
+TODO
+
+/* 3.3 indexed table access, non-nullabale columns. */
+call set_all_columns_not_null();
+
+/* A. Subqueries in the SELECT clause. */
+explain
+select a1, a1 in (select b1 from t2 where b1 > '0') from t1;
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1      index   NULL    it1i1   8       NULL    3       Using index
+2       SUBQUERY        t2      index   it2i4,it2i1,it2i3       it2i1   8       NULL    5       Using where; Using index
+select a1, a1 in (select b1 from t2 where b1 > '0') from t1;
+a1      a1 in (select b1 from t2 where b1 > '0')
+1 - 00  0
+1 - 01  1
+1 - 02  1
+
+explain
+select a1, a2, (a1, a2) in (select b1, b2 from t2 where b1 > '0') from t1;
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1      index   NULL    it1i3   16      NULL    3       Using index
+2       SUBQUERY        t2      index   it2i4,it2i1,it2i3       it2i3   16      NULL    5       Using where; Using index
+select a1, a2, (a1, a2) in (select b1, b2 from t2 where b1 > '0') from t1;
+a1      a2      (a1, a2) in (select b1, b2 from t2 where b1 > '0')
+1 - 00  2 - 00  0
+1 - 01  2 - 01  1
+1 - 02  2 - 02  1
+
+explain
+select a1, a2, (a1, a2) in (select b1, b2 from t2 where b1 > '0' and b1 < '9') from t1;
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1      index   NULL    it1i3   16      NULL    3       Using index
+2       SUBQUERY        t2      range   it2i4,it2i1,it2i3       it2i3   8       NULL    4       Using where; Using index
+select a1, a2, (a1, a2) in (select b1, b2 from t2 where b1 > '0' and b1 < '9') from t1;
+a1      a2      (a1, a2) in (select b1, b2 from t2 where b1 > '0' and b1 < '9')
+1 - 00  2 - 00  0
+1 - 01  2 - 01  1
+1 - 02  2 - 02  1
+
+/*
+B. "Natural" examples of subqueries without grouping that
+cannot be flattened into semijoin.
+*/
+explain
+select a1 from t1 where a1 in (select b2 from t2) or a2 < '9';
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1      index   it1i2   it1i3   16      NULL    3       Using where; Using index
+2       SUBQUERY        t2      index   NULL    it2i2   8       NULL    5       Using index
+select a1 from t1 where a1 in (select b2 from t2) or a2 < '9';
+a1
+1 - 00
+1 - 01
+1 - 02
+
+explain
+select a1, a2 from t1 where (a1, a2) in (select b1, b2 from t2 where b1 > '0') or a2 < '9';
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1      index   it1i2   it1i3   16      NULL    3       Using where; Using index
+2       SUBQUERY        t2      index   it2i4,it2i1,it2i3       it2i3   16      NULL    5       Using where; Using index
+select a1, a2 from t1 where (a1, a2) in (select b1, b2 from t2 where b1 > '0') or a2 < '9';
+a1      a2
+1 - 00  2 - 00
+1 - 01  2 - 01
+1 - 02  2 - 02
+UNION subqueries are currently limited to only use IN-TO-EXISTS.
+explain
+select a2 from t1 where a2 in (select b2 from t2 UNION select b3 from t2 as t3);
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1      index   NULL    it1i2   8       NULL    3       Using where; Using index
+2       DEPENDENT SUBQUERY      t2      ref     it2i2   it2i2   8       func    2       Using index
+3       DEPENDENT UNION t3      index   NULL    it2i4   24      NULL    5       Using where; Using index
+NULL    UNION RESULT    <union2,3>      ALL     NULL    NULL    NULL    NULL    NULL    
+select a2 from t1 where a2 in (select b2 from t2 UNION select b3 from t2 as t3);
+a2
+2 - 01
+2 - 02
+
+explain
+select a1 from t1 where a1 = '1 - 02' and a1 in (select max(b1) from t2 where b2 = '2 - 02');
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1      ref     it1i1,it1i3     it1i1   8       const   1       Using where; Using index
+2       SUBQUERY        t2      ref     it2i2   it2i2   8       const   1       Using index condition
+select a1 from t1 where a1 = '1 - 02' and a1 in (select max(b1) from t2 where b2 = '2 - 02');
+a1
+1 - 02
+
+explain
+select a1, a2 from t1 where (a1, a2) in (select b1, b2 from t2 order by b3);
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1      index   NULL    it1i3   16      NULL    3       Using where; Using index
+2       SUBQUERY        t2      index   NULL    it2i4   24      NULL    5       Using index
+select a1, a2 from t1 where (a1, a2) in (select b1, b2 from t2 order by b3);
+a1      a2
+1 - 01  2 - 01
+1 - 02  2 - 02
+
+/* C. Subqueries in the WHERE clause with GROUP BY. */
+explain
+select * from t1 where a1 in (select b1 from t2 where b1 > '0' group by b1);
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1      ALL     NULL    NULL    NULL    NULL    3       Using where
+2       SUBQUERY        t2      index   it2i4,it2i1,it2i3       it2i1   8       NULL    5       Using where; Using index
+select * from t1 where a1 in (select b1 from t2 where b1 > '0' group by b1);
+a1      a2      a3      a4
+1 - 01  2 - 01  3 - 01  1
+1 - 02  2 - 02  3 - 02  2
+
+explain
+select * from t1 where (a1, a2) in (select b1, b2 from t2 where b1 > '0' group by b1, b2);
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1      ALL     NULL    NULL    NULL    NULL    3       Using where
+2       SUBQUERY        t2      index   it2i4,it2i1,it2i3       it2i3   16      NULL    5       Using where; Using index
+select * from t1 where (a1, a2) in (select b1, b2 from t2 where b1 > '0' group by b1, b2);
+a1      a2      a3      a4
+1 - 01  2 - 01  3 - 01  1
+1 - 02  2 - 02  3 - 02  2
+
+explain
+select * from t1 where (a1, a2) in (select b1, b2 from t2 where b1 > '0' group by b1, b2 having b2 <  '2 - 04');
+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      index   it2i4,it2i1,it2i3       it2i3   16      NULL    5       Using where; Using index
+select * from t1 where (a1, a2) in (select b1, b2 from t2 where b1 > '0' group by b1, b2 having b2 <  '2 - 04');
+a1      a2      a3      a4
+1 - 01  2 - 01  3 - 01  1
+1 - 02  2 - 02  3 - 02  2
+
+explain
+select * from t1 where (a1, a2, a3) in (select b1, b2, b3 from t2 group by b1, b2, b3);
+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      range   NULL    it2i4   24      NULL    6       Using index for group-by
+select * from t1 where (a1, a2, a3) in (select b1, b2, b3 from t2 group by b1, b2, b3);
+a1      a2      a3      a4
+1 - 01  2 - 01  3 - 01  1
+
+explain
+select * from t1 where (a1, a2, a3) in (select b1, b2, b3 from t2 where b3 = '3 - 02' group by b1, b2);
+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      range   NULL    it2i4   24      NULL    6       Using where; Using index for group-by
+select * from t1 where (a1, a2, a3) in (select b1, b2, b3 from t2 where b3 = '3 - 02' group by b1, b2);
+a1      a2      a3      a4
+
+explain
+select * from t1 where (a1,a2,a3) in (select b1,b2,b3 from t2 where b1 = '1 - 01' group by b1,b2,b3);
+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      range   it2i4,it2i1,it2i3       it2i4   24      NULL    2       Using where; Using index for group-by
+select * from t1 where (a1,a2,a3) in (select b1,b2,b3 from t2 where b1 = '1 - 01' group by b1,b2,b3);
+a1      a2      a3      a4
+1 - 01  2 - 01  3 - 01  1
+
+/*
+D. Subqueries for which materialization is not possible, and the
+optimizer reverts to in-to-exists.
+*/
+explain
+select left(a1,7), left(a2,7) from t1_1024 where a1 in (select b1 from t2_1024 where b1 > '0') or a2 < '9';
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1_1024 ALL     it1_1024i2      NULL    NULL    NULL    2       Using where
+2       DEPENDENT SUBQUERY      t2_1024 index_subquery  it2_1024i1,it2_1024i3   it2_1024i3      9       func    1       Using where
+select left(a1,7), left(a2,7) from t1_1024 where a1 in (select b1 from t2_1024 where b1 > '0') or a2 < '9';
+left(a1,7)      left(a2,7)
+1 - 00x 2 - 00x
+1 - 01x 2 - 01x
+explain
+select left(a1,7), left(a2,7) from t1_1024 where (a1,a2) in (select b1, b2 from t2_1024 where b1 > '0') or a2 < '9';
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1_1024 ALL     it1_1024i2      NULL    NULL    NULL    2       Using where
+2       DEPENDENT SUBQUERY      t2_1024 index_subquery  it2_1024i1,it2_1024i2,it2_1024i3        it2_1024i1      9       func    2       Using where
+select left(a1,7), left(a2,7) from t1_1024 where (a1,a2) in (select b1, b2 from t2_1024 where b1 > '0') or a2 < '9';
+left(a1,7)      left(a2,7)
+1 - 00x 2 - 00x
+1 - 01x 2 - 01x
+
+explain
+select left(a1,7), left(a2,7) from t1_1024 where a1 in (select substring(b1,1,1024) from t2_1024 where b1 > '0') or a2 < '9';
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1_1024 ALL     it1_1024i2      NULL    NULL    NULL    2       Using where
+2       DEPENDENT SUBQUERY      t2_1024 range   it2_1024i1,it2_1024i3   it2_1024i1      9       NULL    4       Using where
+select left(a1,7), left(a2,7) from t1_1024 where a1 in (select substring(b1,1,1024) from t2_1024 where b1 > '0') or a2 < '9';
+left(a1,7)      left(a2,7)
+1 - 00x 2 - 00x
+1 - 01x 2 - 01x
+explain
+select left(a1,7), left(a2,7) from t1_1024 where (a1,a2) in (select substring(b1,1,1024), substring(b2,1,1024) from t2_1024 where b1 > '0') or a2 < '9';
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1_1024 ALL     it1_1024i2      NULL    NULL    NULL    2       Using where
+2       DEPENDENT SUBQUERY      t2_1024 range   it2_1024i1,it2_1024i3   it2_1024i1      9       NULL    4       Using where
+select left(a1,7), left(a2,7) from t1_1024 where (a1,a2) in (select substring(b1,1,1024), substring(b2,1,1024) from t2_1024 where b1 > '0') or a2 < '9';
+left(a1,7)      left(a2,7)
+1 - 00x 2 - 00x
+1 - 01x 2 - 01x
+
+
+/* E. Edge cases. */
+
+/* E.1 Both materialization and in_to_exists cannot be off. */
+set @save_optimizer_switch=@@optimizer_switch;
+set @@optimizer_switch = 'materialization=off,in_to_exists=off';
+select * from t1 where a1 in (select b1 from t2 where b1 > '0' group by b1);
+ERROR HY000: At least one of the 'in_to_exists' or 'materialization' optimizer_switch flags must be 'on'.
+set @@optimizer_switch = @save_optimizer_switch;
+/* E.2 Outer query without tables, always uses IN-TO-EXISTS. */
+explain
+select '1 - 03' in (select b1 from t2 where b1 > '0');
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY NULL    NULL    NULL    NULL    NULL    NULL    NULL    No tables used
+2       DEPENDENT SUBQUERY      t2      index_subquery  it2i4,it2i1,it2i3       it2i1   8       const   5       Using index; Using where
+select '1 - 03' in (select b1 from t2 where b1 > '0');
+'1 - 03' in (select b1 from t2 where b1 > '0')
+1
+/* E.3 Subqueries without tables. */
+explain
+select a1 from t1 where a1 in (select '1 - 03') or a2 < '9';
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1      index   it1i1,it1i2,it1i3       it1i3   16      NULL    3       Using where; Using index
+Warnings:
+Note    1249    Select 2 was reduced during optimization
+select a1 from t1 where a1 in (select '1 - 03') or a2 < '9';
+a1
+1 - 00
+1 - 01
+1 - 02
+UNION subqueries are currently limited to only use IN-TO-EXISTS.
+explain
+select a1 from t1 where a1 in (select '1 - 03' UNION select '1 - 02');
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1      index   NULL    it1i1   8       NULL    3       Using where; Using index
+2       DEPENDENT SUBQUERY      NULL    NULL    NULL    NULL    NULL    NULL    NULL    No tables used
+3       DEPENDENT UNION NULL    NULL    NULL    NULL    NULL    NULL    NULL    No tables used
+NULL    UNION RESULT    <union2,3>      ALL     NULL    NULL    NULL    NULL    NULL    
+select a1 from t1 where a1 in (select '1 - 03' UNION select '1 - 02');
+a1
+1 - 02
+/* E.4 optimize_cond detects FALSE where/having clause. */
+explain
+select a1 from t1 where a1 in (select b1 from t2 where b1 = b2 and b2 = '1 - 03' and b1 = '1 - 02' ) or a2 < '9';
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1      index   it1i2   it1i3   16      NULL    3       Using where; Using index
+2       SUBQUERY        NULL    NULL    NULL    NULL    NULL    NULL    NULL    Impossible WHERE
+select a1 from t1 where a1 in (select b1 from t2 where b1 = b2 and b2 = '1 - 03' and b1 = '1 - 02' ) or a2 < '9';
+a1
+1 - 00
+1 - 01
+1 - 02
+/* E.5 opt_sum_query detects no matching min/max row or substitutes MIN/MAX with a const. */
+TODO this test produces wrong result due to missing logic to handle the case
+when JOIN::optimize detects an empty subquery result.
+explain
+select a1 from t1 where a1 in (select max(b1) from t2);
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1      index   NULL    it1i1   8       NULL    3       Using where; Using index
+2       DEPENDENT SUBQUERY      NULL    NULL    NULL    NULL    NULL    NULL    NULL    Select tables optimized away
+select a1 from t1 where a1 in (select max(b1) from t2);
+a1
+
+explain
+select a1 from t1 where a1 in (select max(b1) from t2 where b1 = '7 - 02');
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY t1      index   NULL    it1i1   8       NULL    3       Using where; Using index
+2       DEPENDENT SUBQUERY      NULL    NULL    NULL    NULL    NULL    NULL    NULL    No matching min/max row
+select a1 from t1 where a1 in (select max(b1) from t2 where b1 = '7 - 02');
+a1
+/* E.6 make_join_select detects impossible WHERE. *
+TODO
+/* E.7 constant optimization detects "no matching row in const table". */
+TODO
+/* E.8 Impossible WHERE noticed after reading const tables. */
+explain
+select '1 - 03' in (select b1 from t2 where b1 > '0' and b1 < '0');
+id      select_type     table   type    possible_keys   key     key_len ref     rows    Extra
+1       PRIMARY NULL    NULL    NULL    NULL    NULL    NULL    NULL    No tables used
+2       DEPENDENT SUBQUERY      NULL    NULL    NULL    NULL    NULL    NULL    NULL    Impossible WHERE noticed after reading const tables
+select '1 - 03' in (select b1 from t2 where b1 > '0' and b1 < '0');
+'1 - 03' in (select b1 from t2 where b1 > '0' and b1 < '0')
+0
+
+/* F. UPDATE/DELETE with subqueries. */
+
+TODO
+
+call set_all_columns_nullable();
+drop procedure make_t1_indexes;
+drop procedure make_t2_indexes;
+drop procedure remove_t1_indexes;
+drop procedure remove_t2_indexes;
+drop procedure add_materialization_data;
+drop procedure delete_materialization_data;
+drop procedure set_all_columns_not_null;
+drop procedure set_all_columns_nullable;
+drop table t1, t2, t1_1024, t2_1024;
+#
+# LP BUG#643424 valgrind warning in choose_subquery_plan()
+#
+CREATE TABLE t1 (
+pk int(11) NOT NULL AUTO_INCREMENT,
+c1 int(11) DEFAULT NULL,
+c2 int(11) DEFAULT NULL,
+PRIMARY KEY (pk),
+KEY c2 (c2));
+INSERT INTO t1 VALUES (1,NULL,2);
+INSERT INTO t1 VALUES (2,7,9);
+INSERT INTO t1 VALUES (9,NULL,8);
+CREATE TABLE t2 (
+pk int(11) NOT NULL AUTO_INCREMENT,
+c1 int(11) DEFAULT NULL,
+c2 int(11) DEFAULT NULL,
+PRIMARY KEY (pk),
+KEY c2 (c2));
+INSERT INTO t2 VALUES (1,1,7);
+set @save_optimizer_switch=@@optimizer_switch;
+set @@optimizer_switch='materialization=on,in_to_exists=on,semijoin=off';
+SELECT pk FROM t1 WHERE (c2, c1) IN (SELECT c2, c2 FROM t2);
+pk
+set session optimizer_switch=@save_optimizer_switch;
+drop table t1, t2;
+#
+# LP BUG#652727 Crash in create_ref_for_key()
+#
+CREATE TABLE t2 (
+pk int(11) NOT NULL AUTO_INCREMENT,
+c1 int(11) DEFAULT NULL,
+PRIMARY KEY (pk));
+INSERT INTO t2 VALUES (10,7);
+INSERT INTO t2 VALUES (11,1);
+INSERT INTO t2 VALUES (17,NULL);
+CREATE TABLE t1 (
+pk int(11) NOT NULL AUTO_INCREMENT,
+c1 int(11) DEFAULT NULL,
+PRIMARY KEY (pk));
+INSERT INTO t1 VALUES (15,1);
+INSERT INTO t1 VALUES (19,NULL);
+CREATE TABLE t3 (c2 int(11) DEFAULT NULL, KEY c2 (c2));
+INSERT INTO t3 VALUES (1);
+set @save_optimizer_switch=@@optimizer_switch;
+set @@optimizer_switch='materialization=on,in_to_exists=on,semijoin=off';
+SELECT c2
+FROM t3
+WHERE (2, 6) IN (SELECT t1.c1, t1.c1 FROM t1 STRAIGHT_JOIN t2 ON t2.pk = t1.pk);
+c2
+set session optimizer_switch=@save_optimizer_switch;
+drop table t1, t2, t3;
+#
+# LP BUG#641245 Crash in Item_equal::contains
+#
+CREATE TABLE t1 (
+pk int(11) NOT NULL AUTO_INCREMENT,
+c1 int(11) DEFAULT NULL,
+c2 int(11) DEFAULT NULL,
+c3 varchar(1) DEFAULT NULL,
+c4 varchar(1) DEFAULT NULL,
+PRIMARY KEY (pk),
+KEY c2 (c2),
+KEY c3 (c3,c2));
+INSERT INTO t1 VALUES (10,7,8,'v','v');
+INSERT INTO t1 VALUES (11,1,9,'r','r');
+INSERT INTO t1 VALUES (12,5,9,'a','a');
+create table t1a like t1;
+insert into t1a select * from t1;
+create table t1b like t1;
+insert into t1b select * from t1;
+CREATE TABLE t2 (
+pk int(11) NOT NULL AUTO_INCREMENT,
+c1 int(11) DEFAULT NULL,
+c2 int(11) DEFAULT NULL,
+c3 varchar(1) DEFAULT NULL,
+c4 varchar(1) DEFAULT NULL,
+PRIMARY KEY (pk),
+KEY c2 (c2),
+KEY c3 (c3,c2));
+INSERT INTO t2 VALUES (1,NULL,2,'w','w');
+INSERT INTO t2 VALUES (2,7,9,'m','m');
+set @@optimizer_switch='materialization=off,in_to_exists=on,semijoin=off';
+EXPLAIN EXTENDED SELECT pk
+FROM t1
+WHERE c1 IN
+(SELECT t1a.c1
+FROM (t1b JOIN t2 ON t2.c3 = t1b.c4) LEFT JOIN
+t1a ON (t1a.c2 = t1b.pk AND 2)
+WHERE t1.pk) ;
+id      select_type     table   type    possible_keys   key     key_len ref     rows    filtered        Extra
+1       PRIMARY t1      ALL     NULL    NULL    NULL    NULL    3       100.00  Using where
+2       DEPENDENT SUBQUERY      t2      index   c3      c3      9       NULL    2       100.00  Using index
+2       DEPENDENT SUBQUERY      t1b     ALL     NULL    NULL    NULL    NULL    3       100.00  Using where; Using join buffer
+2       DEPENDENT SUBQUERY      t1a     ref     c2      c2      5       test.t1b.pk     2       100.00  Using where
+Warnings:
+Note    1276    Field or reference 'test.t1.pk' of SELECT #2 was resolved in SELECT #1
+Note    1003    select `test`.`t1`.`pk` AS `pk` from `test`.`t1` where <expr_cache><`test`.`t1`.`c1`,(`test`.`t1`.`pk` and (`test`.`t1b`.`c4` = `test`.`t2`.`c3`) and (<cache>(`test`.`t1`.`c1`) = `test`.`t1a`.`c1`))>(<in_optimizer>(`test`.`t1`.`c1`,<exists>(select `test`.`t1a`.`c1` from `test`.`t1b` join `test`.`t2` left join `test`.`t1a` on((2 and (`test`.`t1a`.`c2` = `test`.`t1b`.`pk`))) where (`test`.`t1`.`pk` and (`test`.`t1b`.`c4` = `test`.`t2`.`c3`) and (<cache>(`test`.`t1`.`c1`) = `test`.`t1a`.`c1`)))))
+SELECT pk
+FROM t1
+WHERE c1 IN
+(SELECT t1a.c1
+FROM (t1b JOIN t2 ON t2.c3 = t1b.c4) LEFT JOIN
+t1a ON (t1a.c2 = t1b.pk AND 2)
+WHERE t1.pk) ;
+pk
+DROP TABLE t1, t1a, t1b, t2;

=== modified file 'mysql-test/t/subselect3.test'
--- a/mysql-test/t/subselect3.test	2010-07-10 10:37:30 +0000
+++ b/mysql-test/t/subselect3.test	2010-09-30 15:32:44 +0000
@@ -681,7 +681,8 @@ SELECT a, ROW(11, 12) = (SELECT a, 12), 
 # The x alias is used below to workaround bug #40674.
 # Regression tests for sum function on outer column in subselect from dual:
 SELECT a AS x, ROW(11, 12) = (SELECT MAX(x), 22), ROW(11, 12) IN (SELECT MAX(x), 22) FROM t1;
---echo # 2nd and 3rd columns should be same for x == 11 only
+--echo # 2nd and 3rd columns should be same
+EXPLAIN SELECT a AS x, ROW(11, 12) = (SELECT MAX(x), 12), ROW(11, 12) IN (SELECT MAX(x), 12) FROM t1;
 SELECT a AS x, ROW(11, 12) = (SELECT MAX(x), 12), ROW(11, 12) IN (SELECT MAX(x), 12) FROM t1;
 
 DROP TABLE t1;

=== modified file 'mysql-test/t/subselect4.test'
--- a/mysql-test/t/subselect4.test	2010-10-28 17:04:23 +0000
+++ b/mysql-test/t/subselect4.test	2010-11-11 16:42:55 +0000
@@ -370,3 +370,29 @@ WHERE 
 SET SESSION engine_condition_pushdown=@old_icp;
 
 DROP TABLE t1,t2;
+
+--echo #
+--echo # LP BUG#641203 Query returns rows where no result is expected (impossible WHERE)
+--echo #
+
+CREATE TABLE t1 (c1 varchar(1) DEFAULT NULL);
+CREATE TABLE t2 (c1 varchar(1) DEFAULT NULL);
+INSERT INTO t2 VALUES ('k'), ('d');
+CREATE TABLE t3 (c1 varchar(1) DEFAULT NULL);
+INSERT INTO t3 VALUES ('a'), ('b'), ('c');
+CREATE TABLE t4 (c1 varchar(1) primary key);
+INSERT INTO t4 VALUES ('k'), ('d');
+
+EXPLAIN
+SELECT * FROM t1 RIGHT JOIN t2 ON t1.c1 WHERE 's' IN (SELECT c1 FROM t2);
+SELECT * FROM t1 RIGHT JOIN t2 ON t1.c1 WHERE 's' IN (SELECT c1 FROM t2);
+EXPLAIN
+SELECT * FROM t2 LEFT JOIN t1 ON t1.c1 WHERE 's' IN (SELECT c1 FROM t2);
+SELECT * FROM t2 LEFT JOIN t1 ON t1.c1 WHERE 's' IN (SELECT c1 FROM t2);
+EXPLAIN
+SELECT * FROM (t2 LEFT JOIN t1 ON t1.c1) LEFT JOIN t3 on t3.c1 WHERE 's' IN (SELECT c1 FROM t2);
+SELECT * FROM (t2 LEFT JOIN t1 ON t1.c1) LEFT JOIN t3 on t3.c1 WHERE 's' IN (SELECT c1 FROM t2);
+EXPLAIN
+SELECT * FROM t4 LEFT JOIN t2 ON t4.c1 WHERE 's' IN (SELECT c1 FROM t2);
+SELECT * FROM t4 LEFT JOIN t2 ON t4.c1 WHERE 's' IN (SELECT c1 FROM t2);
+drop table t1, t2, t3, t4;

=== modified file 'mysql-test/t/subselect_mat.test'
--- a/mysql-test/t/subselect_mat.test	2010-10-25 20:48:43 +0000
+++ b/mysql-test/t/subselect_mat.test	2010-11-05 12:42:58 +0000
@@ -48,7 +48,7 @@ insert into t2i select * from t2;
 insert into t3i select * from t3;
 
 # force the use of materialization
-set @@optimizer_switch='semijoin=off';
+set @@optimizer_switch='materialization=on,in_to_exists=off,semijoin=off';
 
 /******************************************************************************
 * Simple tests.
@@ -111,22 +111,22 @@ select * from t1 where (a1, a2) in (sele
 
 # test re-optimization/re-execution with different execution methods
 # prepare once, exec with different modes
-set @@optimizer_switch='default,semijoin=off';
+set @@optimizer_switch='materialization=on,in_to_exists=off,semijoin=on';
 prepare st1 from
 "select * from t1 where (a1, a2) in (select b1, min(b2) from t2 where b1 > '0' group by b1)";
-set @@optimizer_switch='default,materialization=off';
+set @@optimizer_switch='materialization=off,in_to_exists=on,semijoin=on';
 execute st1;
-set @@optimizer_switch='default,semijoin=off';
+set @@optimizer_switch='materialization=on,in_to_exists=off,semijoin=on';
 execute st1;
 
-set @@optimizer_switch='default,materialization=off';
+set @@optimizer_switch='materialization=off,in_to_exists=on,semijoin=on';
 prepare st1 from
 "select * from t1 where (a1, a2) in (select b1, min(b2) from t2 where b1 > '0' group by b1)";
-set @@optimizer_switch='default,semijoin=off';
+set @@optimizer_switch='materialization=on,in_to_exists=off,semijoin=off';
 execute st1;
-set @@optimizer_switch='default,materialization=off';
+set @@optimizer_switch='materialization=off,in_to_exists=on,semijoin=on';
 execute st1;
-set @@optimizer_switch='default,semijoin=off';
+set @@optimizer_switch='materialization=on,in_to_exists=off,semijoin=off';
 
 # materialize the result of ORDER BY
 # non-indexed fields
@@ -327,7 +327,7 @@ select * from t1 order by (select col fr
   Test that BLOBs are not materialized (except when arguments of some functions).
 */
 # force materialization to be always considered
-set @@optimizer_switch='semijoin=off';
+set @@optimizer_switch='materialization=on,in_to_exists=off,semijoin=off';
 set @prefix_len = 6;
 
 # BLOB == 16 (small blobs that could be stored in HEAP tables)
@@ -680,7 +680,7 @@ insert into t2bit values (b'001', b'101'
 insert into t2bit values (b'010', b'110');
 insert into t2bit values (b'110', b'111');
 
-set @@optimizer_switch='semijoin=off';
+set @@optimizer_switch='materialization=on,in_to_exists=off,semijoin=off';
 
 explain extended select bin(a1), bin(a2)
 from t1bit
@@ -718,7 +718,7 @@ drop table t1, t2, t3, t1i, t2i, t3i, co
 /******************************************************************************
 * Test the cache of the left operand of IN.
 ******************************************************************************/
-set @@optimizer_switch='semijoin=off';
+set @@optimizer_switch='materialization=on,in_to_exists=off,semijoin=off';
 
 # Test that default values of Cached_item are not used for comparison
 create table t1 (s1 int);
@@ -812,23 +812,28 @@ drop table t2;
 create table t1 (a1 int key);
 create table t2 (b1 int);
 insert into t1 values (5);
-
+-- echo Only the last query returns correct result. Filed as BUG#40037.
 # Query with group by, executed via materialization
+set @@optimizer_switch='materialization=on,in_to_exists=off,semijoin=off';
 explain select min(a1) from t1 where 7 in (select b1 from t2 group by b1);
 select min(a1) from t1 where 7 in (select b1 from t2 group by b1);
 # Query with group by, executed via IN=>EXISTS
-set @@optimizer_switch='default,materialization=off';
+set @@optimizer_switch='materialization=off,in_to_exists=on,semijoin=off';
 explain select min(a1) from t1 where 7 in (select b1 from t2 group by b1);
 select min(a1) from t1 where 7 in (select b1 from t2 group by b1);
 
 # Executed with materialization
-set @@optimizer_switch='default,semijoin=off';
+set @@optimizer_switch='materialization=on,in_to_exists=off,semijoin=off';
+explain select min(a1) from t1 where 7 in (select b1 from t2);
+select min(a1) from t1 where 7 in (select b1 from t2);
+# Executed via IN=>EXISTS
+set @@optimizer_switch='materialization=off,in_to_exists=on,semijoin=off';
 explain select min(a1) from t1 where 7 in (select b1 from t2);
 select min(a1) from t1 where 7 in (select b1 from t2);
 # Executed with semi-join. Notice, this time we get a different result (NULL).
-# This is the only correct result of all four queries. This difference is
+# This is the only correct result of all five queries. This difference is
 # filed as BUG#40037.
-set @@optimizer_switch='default,materialization=off';
+set @@optimizer_switch='materialization=off,in_to_exists=off,semijoin=on';
 explain select min(a1) from t1 where 7 in (select b1 from t2);
 select min(a1) from t1 where 7 in (select b1 from t2);
 drop table t1,t2;
@@ -840,7 +845,7 @@ create table t1 (a char(2), b varchar(10
 insert into t1 values ('a',  'aaa');
 insert into t1 values ('aa', 'aaaa');
 
-set @@optimizer_switch='default,semijoin=off';
+set @@optimizer_switch='materialization=on,in_to_exists=off,semijoin=off';
 explain select a,b from t1 where b in (select a from t1);
 select a,b from t1 where b in (select a from t1);
 prepare st1 from "select a,b from t1 where b in (select a from t1)";
@@ -861,7 +866,7 @@ CREATE TABLE t2 LIKE t1;
 INSERT INTO t2 VALUES (1, 1.789);
 INSERT INTO t2 VALUES (13, 1.454);
 
-SET @@optimizer_switch='default,semijoin=on,materialization=on';
+SET @@optimizer_switch='materialization=on,in_to_exists=off,semijoin=on';
 EXPLAIN SELECT COUNT(*) FROM t1 WHERE (f1,f2) IN (SELECT f1,f2 FROM t2);
 SELECT COUNT(*) FROM t1 WHERE (f1,f2) IN (SELECT f1,f2 FROM t2);
 
@@ -883,7 +888,7 @@ INSERT INTO t1 VALUES (1,'o','ffff','fff
 CREATE TABLE t2 LIKE t1;
 INSERT INTO t2 VALUES (1,'i','iiii','iiii','iiii'),(2,'f','ffff','ffff','ffff');
 
-SET @@optimizer_switch='default,semijoin=on,materialization=on';
+SET @@optimizer_switch='materialization=on,in_to_exists=off,semijoin=on';
 EXPLAIN SELECT pk FROM t1 WHERE (a) IN (SELECT a FROM t2 WHERE pk > 0);
 SELECT pk FROM t1 WHERE (a) IN (SELECT a FROM t2 WHERE pk > 0);
 SELECT pk FROM t1 WHERE (b,c,d) IN (SELECT b,c,d FROM t2 WHERE pk > 0);
@@ -900,7 +905,7 @@ create table t3(i int);
 insert into t3 values (1), (2), (3), (4), (5), (6), (7), (8), (9), (10);
 select * from t1 where t1.i in (select t2.i from t2 join t3 where t2.i + t3.i = 5);
 set @save_optimizer_switch=@@optimizer_switch;
-set session optimizer_switch='materialization=off';
+set session optimizer_switch='materialization=off,in_to_exists=on';
 select * from t1 where t1.i in (select t2.i from t2 join t3 where t2.i + t3.i = 5);
 set session optimizer_switch=@save_optimizer_switch;
 drop table t1, t2, t3;

=== added file 'mysql-test/t/subselect_mat_cost.test'
--- a/mysql-test/t/subselect_mat_cost.test	1970-01-01 00:00:00 +0000
+++ b/mysql-test/t/subselect_mat_cost.test	2010-11-05 15:10:48 +0000
@@ -0,0 +1,324 @@
+#
+# Tets of cost-based choice between the materialization and in-to-exists
+# subquery execution strategies (MWL#89)
+#
+
+--disable_warnings
+drop table if exists t1, t2, t1_1024, t2_1024;
+drop procedure if exists make_t1_indexes;
+drop procedure if exists make_t2_indexes;
+drop procedure if exists remove_t1_indexes;
+drop procedure if exists remove_t2_indexes;
+drop procedure if exists add_materialization_data;
+drop procedure if exists delete_materialization_data;
+drop procedure if exists set_all_columns_not_null;
+drop procedure if exists set_all_columns_nullable;
+--enable_warnings
+
+create table t1 (a1 char(8), a2 char(8), a3 char(8), a4 int);
+insert into t1 values ('1 - 00', '2 - 00', '3 - 00', 0);
+insert into t1 values ('1 - 01', '2 - 01', '3 - 01', 1);
+insert into t1 values ('1 - 02', '2 - 02', '3 - 02', 2);
+
+create table t2 (b1 char(8), b2 char(8), b3 char(8), b4 int);
+insert into t2 values ('1 - 01', '2 - 01', '3 - 01', 1);
+insert into t2 values ('1 - 01', '2 - 01', '3 - 02', 2);
+insert into t2 values ('1 - 02', '2 - 02', '3 - 03', 3);
+insert into t2 values ('1 - 02', '2 - 02', '3 - 04', 4);
+insert into t2 values ('1 - 03', '2 - 03', '3 - 05', 5);
+
+create table t1_1024 (a1 blob(1024), a2 blob(1024));
+insert into t1_1024 values (concat('1 - 00', repeat('x', 1018)), concat('2 - 00', repeat('x', 1018)));
+insert into t1_1024 values (concat('1 - 01', repeat('x', 1018)), concat('2 - 01', repeat('x', 1018)));
+
+create table t2_1024 (b1 blob(1024), b2 blob(1024));
+insert into t2_1024 values (concat('1 - 01', repeat('x', 1018)), concat('2 - 01', repeat('x', 1018)));
+insert into t2_1024 values (concat('1 - 02', repeat('x', 1018)), concat('2 - 02', repeat('x', 1018)));
+insert into t2_1024 values (concat('1 - 03', repeat('x', 1018)), concat('2 - 03', repeat('x', 1018)));
+insert into t2_1024 values (concat('1 - 04', repeat('x', 1018)), concat('2 - 04', repeat('x', 1018)));
+
+delimiter |;
+create procedure make_t1_indexes()
+begin
+  create index it1i1 on t1 (a1);
+  create index it1i2 on t1 (a2);
+  create index it1i3 on t1 (a1, a2);
+  create index it1_1024i1 on t1_1024 (a1(6));
+  create index it1_1024i2 on t1_1024 (a2(6));
+  create index it1_1024i3 on t1_1024 (a1(6), a2(6));
+end|
+
+create procedure make_t2_indexes()
+begin
+  create index it2i1 on t2 (b1);
+  create index it2i2 on t2 (b2);
+  create index it2i3 on t2 (b1, b2);
+  create unique index it2i4 on t2 (b1, b2, b3);
+  create index it2_1024i1 on t2_1024 (b1(6));
+  create index it2_1024i2 on t2_1024 (b2(6));
+  create index it2_1024i3 on t2_1024 (b1(6), b2(6));
+end|
+
+create procedure remove_t1_indexes()
+begin
+  drop index it1i1 on t1;
+  drop index it1i2 on t1;
+  drop index it1i3 on t1;
+  drop index it1_1024i1 on t1_1024;
+  drop index it1_1024i2 on t1_1024;
+  drop index it1_1024i3 on t1_1024;
+end|
+
+create procedure remove_t2_indexes()
+begin
+  drop index it2i1 on t2;
+  drop index it2i2 on t2;
+  drop index it2i3 on t2;
+  drop index it2i4 on t2;
+  drop index it2_1024i1 on t2_1024;
+  drop index it2_1024i2 on t2_1024;
+  drop index it2_1024i3 on t2_1024;
+end|
+
+create procedure add_materialization_data()
+begin
+insert into t1 values ('1 - 03', '2 - 03', '3 - 03', 3);
+insert into t1 values ('1 - 04', '2 - 04', '3 - 04', 4);
+insert into t1 values ('1 - 05', '2 - 05', '3 - 05', 5);
+insert into t1 values ('1 - 06', '2 - 06', '3 - 06', 6);
+insert into t1 values ('1 - 07', '2 - 07', '3 - 07', 7);
+insert into t1_1024 values (concat('1 - 03', repeat('x', 1018)), concat('2 - 03', repeat('x', 1018)));
+end|
+
+create procedure delete_materialization_data()
+begin
+delete from t1 where a1 >= '1 - 03';
+delete from t1_1024 where a1 >= '1 - 03';
+end|
+
+create procedure set_all_columns_not_null()
+begin
+alter table t1 modify a1 char(8) not null, modify a2 char(8) not null, modify a3 char(8) not null;
+alter table t2 modify b1 char(8) not null, modify b2 char(8) not null, modify b3 char(8) not null;
+end|
+
+create procedure set_all_columns_nullable()
+begin
+alter table t1 modify a1 char(8) null, modify a2 char(8) null, modify a3 char(8) null;
+alter table t2 modify b1 char(8) null, modify b2 char(8) null, modify b3 char(8) null;
+end|
+
+delimiter ;|
+-- echo
+
+-- echo /******************************************************************************
+-- echo 1. Both materialization and in-to-exists are ON, make a cost-based choice.
+-- echo ******************************************************************************/
+set @@optimizer_switch='materialization=on,in_to_exists=on';
+-- echo 
+-- echo /* 1.1 In-to-exists is cheaper */
+call make_t1_indexes();
+
+-- echo /* 1.1.1 non-indexed table access */
+-- source include/subselect_mat_cost.inc
+
+-- echo /* 1.1.2 indexed table access, nullabale columns. */
+call make_t2_indexes();
+-- source include/subselect_mat_cost.inc
+
+-- echo /* 1.1.3 indexed table access, non-nullabale columns. */
+call set_all_columns_not_null();
+-- source include/subselect_mat_cost.inc
+call set_all_columns_nullable();
+
+-- echo 
+-- echo /* 1.2 Materialization is cheaper */
+# make materialization cheaper
+call add_materialization_data();
+call remove_t1_indexes();
+
+-- echo /* 1.2.1 non-indexed table access */
+call remove_t2_indexes();
+-- source include/subselect_mat_cost.inc
+
+-- echo /* 1.2.2 indexed table access, nullabale columns. */
+call make_t2_indexes();
+-- source include/subselect_mat_cost.inc
+
+-- echo /* 1.2.3 indexed table access, non-nullabale columns. */
+call set_all_columns_not_null();
+-- source include/subselect_mat_cost.inc
+call set_all_columns_nullable();
+
+
+-- echo /******************************************************************************
+-- echo 2. Materialization is OFF, in-to-exists is ON, materialization is cheaper.
+-- echo ******************************************************************************/
+set @@optimizer_switch='materialization=off,in_to_exists=on';
+
+-- echo /* 2.1 non-indexed table access */
+call remove_t2_indexes();
+-- source include/subselect_mat_cost.inc
+
+-- echo /* 2.2 indexed table access, nullabale columns. */
+call make_t2_indexes();
+-- source include/subselect_mat_cost.inc
+
+-- echo /* 2.3 indexed table access, non-nullabale columns. */
+call set_all_columns_not_null();
+-- source include/subselect_mat_cost.inc
+call set_all_columns_nullable();
+
+
+-- echo /******************************************************************************
+-- echo 3. Materialization is ON, in-to-exists is OFF, in-to-exists is cheaper.
+-- echo ******************************************************************************/
+set @@optimizer_switch='materialization=on,in_to_exists=off';
+# make IN-TO-EXISTS cheaper
+call delete_materialization_data();
+call make_t1_indexes();
+
+-- echo /* 3.1 non-indexed table access */
+call remove_t2_indexes();
+-- source include/subselect_mat_cost.inc
+
+-- echo /* 3.2 indexed table access, nullabale columns. */
+call make_t2_indexes();
+-- source include/subselect_mat_cost.inc
+
+-- echo /* 3.3 indexed table access, non-nullabale columns. */
+call set_all_columns_not_null();
+-- source include/subselect_mat_cost.inc
+call set_all_columns_nullable();
+
+
+drop procedure make_t1_indexes;
+drop procedure make_t2_indexes;
+drop procedure remove_t1_indexes;
+drop procedure remove_t2_indexes;
+drop procedure add_materialization_data;
+drop procedure delete_materialization_data;
+drop procedure set_all_columns_not_null;
+drop procedure set_all_columns_nullable;
+drop table t1, t2, t1_1024, t2_1024;
+
+--echo #
+--echo # LP BUG#643424 valgrind warning in choose_subquery_plan()
+--echo #
+
+CREATE TABLE t1 (
+  pk int(11) NOT NULL AUTO_INCREMENT,
+  c1 int(11) DEFAULT NULL,
+  c2 int(11) DEFAULT NULL,
+  PRIMARY KEY (pk),
+  KEY c2 (c2));
+
+INSERT INTO t1 VALUES (1,NULL,2);
+INSERT INTO t1 VALUES (2,7,9);
+INSERT INTO t1 VALUES (9,NULL,8);
+
+CREATE TABLE t2 (
+  pk int(11) NOT NULL AUTO_INCREMENT,
+  c1 int(11) DEFAULT NULL,
+  c2 int(11) DEFAULT NULL,
+  PRIMARY KEY (pk),
+  KEY c2 (c2));
+
+INSERT INTO t2 VALUES (1,1,7);
+
+set @save_optimizer_switch=@@optimizer_switch;
+set @@optimizer_switch='materialization=on,in_to_exists=on,semijoin=off';
+
+SELECT pk FROM t1 WHERE (c2, c1) IN (SELECT c2, c2 FROM t2);
+
+set session optimizer_switch=@save_optimizer_switch;
+
+drop table t1, t2;
+
+
+--echo #
+--echo # LP BUG#652727 Crash in create_ref_for_key()
+--echo #
+
+CREATE TABLE t2 (
+  pk int(11) NOT NULL AUTO_INCREMENT,
+  c1 int(11) DEFAULT NULL,
+  PRIMARY KEY (pk));
+
+INSERT INTO t2 VALUES (10,7);
+INSERT INTO t2 VALUES (11,1);
+INSERT INTO t2 VALUES (17,NULL);
+
+CREATE TABLE t1 (
+  pk int(11) NOT NULL AUTO_INCREMENT,
+  c1 int(11) DEFAULT NULL,
+  PRIMARY KEY (pk));
+
+INSERT INTO t1 VALUES (15,1);
+INSERT INTO t1 VALUES (19,NULL);
+
+CREATE TABLE t3 (c2 int(11) DEFAULT NULL, KEY c2 (c2));
+INSERT INTO t3 VALUES (1);
+
+set @save_optimizer_switch=@@optimizer_switch;
+set @@optimizer_switch='materialization=on,in_to_exists=on,semijoin=off';
+
+SELECT c2
+FROM t3
+WHERE (2, 6) IN (SELECT t1.c1, t1.c1 FROM t1 STRAIGHT_JOIN t2 ON t2.pk = t1.pk);
+
+set session optimizer_switch=@save_optimizer_switch;
+drop table t1, t2, t3;
+
+
+--echo #
+--echo # LP BUG#641245 Crash in Item_equal::contains
+--echo #
+
+CREATE TABLE t1 (
+  pk int(11) NOT NULL AUTO_INCREMENT,
+  c1 int(11) DEFAULT NULL,
+  c2 int(11) DEFAULT NULL,
+  c3 varchar(1) DEFAULT NULL,
+  c4 varchar(1) DEFAULT NULL,
+  PRIMARY KEY (pk),
+  KEY c2 (c2),
+  KEY c3 (c3,c2));
+
+INSERT INTO t1 VALUES (10,7,8,'v','v');
+INSERT INTO t1 VALUES (11,1,9,'r','r');
+INSERT INTO t1 VALUES (12,5,9,'a','a');
+
+create table t1a like t1;
+insert into t1a select * from t1;
+
+create table t1b like t1;
+insert into t1b select * from t1;
+
+CREATE TABLE t2 (
+  pk int(11) NOT NULL AUTO_INCREMENT,
+  c1 int(11) DEFAULT NULL,
+  c2 int(11) DEFAULT NULL,
+  c3 varchar(1) DEFAULT NULL,
+  c4 varchar(1) DEFAULT NULL,
+  PRIMARY KEY (pk),
+  KEY c2 (c2),
+  KEY c3 (c3,c2));
+
+INSERT INTO t2 VALUES (1,NULL,2,'w','w');
+INSERT INTO t2 VALUES (2,7,9,'m','m');
+
+set @@optimizer_switch='materialization=off,in_to_exists=on,semijoin=off';
+
+let $query=
+SELECT pk
+FROM t1
+WHERE c1 IN
+      (SELECT t1a.c1
+       FROM (t1b JOIN t2 ON t2.c3 = t1b.c4) LEFT JOIN
+            t1a ON (t1a.c2 = t1b.pk AND 2)
+       WHERE t1.pk) ;
+eval EXPLAIN EXTENDED $query;
+eval $query;
+
+DROP TABLE t1, t1a, t1b, t2;

=== modified file 'sql/item.h'
--- a/sql/item.h	2010-10-10 14:18:11 +0000
+++ b/sql/item.h	2010-11-05 12:42:58 +0000
@@ -948,6 +948,8 @@ public:
   virtual bool register_field_in_read_map(uchar *arg) { return 0; }
   virtual bool enumerate_field_refs_processor(uchar *arg) { return 0; }
   virtual bool mark_as_eliminated_processor(uchar *arg) { return 0; }
+  virtual bool eliminate_subselect_processor(uchar *arg) { return 0; }
+  virtual bool set_fake_select_as_master_processor(uchar *arg) { return 0; }
 
   /* To call bool function for all arguments */
   struct bool_func_call_args

=== modified file 'sql/item_cmpfunc.cc'
--- a/sql/item_cmpfunc.cc	2010-10-28 17:04:23 +0000
+++ b/sql/item_cmpfunc.cc	2010-11-05 12:42:58 +0000
@@ -1985,6 +1985,18 @@ Item *Item_in_optimizer::transform(Item_
 }
 
 
+bool Item_in_optimizer::is_expensive_processor(uchar *arg)
+{
+  return args[1]->is_expensive_processor(arg);
+}
+
+
+bool Item_in_optimizer::is_expensive()
+{
+  return args[1]->is_expensive();
+}
+
+
 longlong Item_func_eq::val_int()
 {
   DBUG_ASSERT(fixed == 1);
@@ -4650,12 +4662,6 @@ Item *and_expressions(Item *a, Item *b, 
 longlong Item_func_isnull::val_int()
 {
   DBUG_ASSERT(fixed == 1);
-  /*
-    Handle optimization if the argument can't be null
-    This has to be here because of the test in update_used_tables().
-  */
-  if (!used_tables_cache && !with_subselect)
-    return cached_value;
   return args[0]->is_null() ? 1: 0;
 }
 
@@ -4663,12 +4669,6 @@ longlong Item_is_not_null_test::val_int(
 {
   DBUG_ASSERT(fixed == 1);
   DBUG_ENTER("Item_is_not_null_test::val_int");
-  if (!used_tables_cache && !with_subselect)
-  {
-    owner->was_null|= (!cached_value);
-    DBUG_PRINT("info", ("cached: %ld", (long) cached_value));
-    DBUG_RETURN(cached_value);
-  }
   if (args[0]->is_null())
   {
     DBUG_PRINT("info", ("null"));
@@ -4685,19 +4685,9 @@ longlong Item_is_not_null_test::val_int(
 void Item_is_not_null_test::update_used_tables()
 {
   if (!args[0]->maybe_null)
-  {
     used_tables_cache= 0;                       /* is always true */
-    cached_value= (longlong) 1;
-  }
   else
-  {
     args[0]->update_used_tables();
-    if (!(used_tables_cache=args[0]->used_tables()) && !with_subselect)
-    {
-      /* Remember if the value is always NULL or never NULL */
-      cached_value= (longlong) !args[0]->is_null();
-    }
-  }
 }
 
 
@@ -5373,7 +5363,7 @@ Item *Item_func_nop_all::neg_transformer
   /* "NOT (e $cmp$ ANY (SELECT ...)) -> e $rev_cmp$" ALL (SELECT ...) */
   Item_func_not_all *new_item= new Item_func_not_all(args[0]);
   Item_allany_subselect *allany= (Item_allany_subselect*)args[0];
-  allany->func= allany->func_creator(FALSE);
+  allany->create_comp_func(FALSE);
   allany->all= !allany->all;
   allany->upper_item= new_item;
   return new_item;
@@ -5385,7 +5375,7 @@ Item *Item_func_not_all::neg_transformer
   Item_func_nop_all *new_item= new Item_func_nop_all(args[0]);
   Item_allany_subselect *allany= (Item_allany_subselect*)args[0];
   allany->all= !allany->all;
-  allany->func= allany->func_creator(TRUE);
+  allany->create_comp_func(TRUE);
   allany->upper_item= new_item;
   return new_item;
 }
@@ -5668,6 +5658,9 @@ longlong Item_equal::val_int()
   Item_field *item_field;
   if (cond_false)
     return 0;
+  /* If there is a single constant and no fields, the equality is TRUE. */
+  if (const_item && !fields.elements)
+    return 1;
   List_iterator_fast<Item_field> it(fields);
   Item *item= const_item ? const_item : it++;
   if ((null_value= item->is_null()))
@@ -5688,6 +5681,15 @@ longlong Item_equal::val_int()
 void Item_equal::fix_length_and_dec()
 {
   Item *item= get_first(NULL);
+  if (!item)
+  {
+    /*
+      If there are no fields, there must be at least a constant, in which
+      case Item_equal::val_int evaluates to TRUE.
+    */
+    DBUG_ASSERT(const_item);
+    return;
+  }
   eval_item= cmp_item::get_comparator(item->result_type(),
                                       item->collation.collation);
 }

=== modified file 'sql/item_cmpfunc.h'
--- a/sql/item_cmpfunc.h	2010-10-10 14:18:11 +0000
+++ b/sql/item_cmpfunc.h	2010-11-05 12:42:58 +0000
@@ -266,6 +266,8 @@ public:
   void keep_top_level_cache();
   Item *transform(Item_transformer transformer, uchar *arg);
   virtual Item *expr_cache_insert_transformer(uchar *thd_arg);
+  bool is_expensive_processor(uchar *arg);
+  bool is_expensive();
 };
 
 class Comp_creator
@@ -1303,8 +1305,6 @@ public:
 
 class Item_func_isnull :public Item_bool_func
 {
-protected:
-  longlong cached_value;
 public:
   Item_func_isnull(Item *a) :Item_bool_func(a) {}
   longlong val_int();
@@ -1322,18 +1322,9 @@ public:
     {
       used_tables_cache= 0;                     /* is always false */
       const_item_cache= 1;
-      cached_value= (longlong) 0;
     }
     else
-    {
       args[0]->update_used_tables();
-      if ((const_item_cache= !(used_tables_cache= args[0]->used_tables()) &&
-          !with_subselect))
-      {
-        /* Remember if the value is always NULL or never NULL */
-        cached_value= (longlong) args[0]->is_null();
-      }
-    }
   }
   table_map not_null_tables() const { return 0; }
   optimize_type select_optimize() const { return OPTIMIZE_NULL; }

=== modified file 'sql/item_subselect.cc'
--- a/sql/item_subselect.cc	2010-11-02 09:03:33 +0000
+++ b/sql/item_subselect.cc	2010-11-05 12:42:58 +0000
@@ -36,8 +36,8 @@ Item_subselect::Item_subselect():
   Item_result_field(), value_assigned(0), thd(0), substitution(0),
   expr_cache(0), engine(0), old_engine(0), used_tables_cache(0),
   have_to_be_excluded(0), const_item_cache(1), inside_first_fix_fields(0),
-  done_first_fix_fields(FALSE), eliminated(FALSE), engine_changed(0),
-  changed(0), is_correlated(FALSE)
+  done_first_fix_fields(FALSE), forced_const(FALSE), eliminated(FALSE),
+  engine_changed(0), changed(0), is_correlated(FALSE)
 {
   with_subselect= 1;
   reset();
@@ -60,7 +60,7 @@ void Item_subselect::init(st_select_lex 
   DBUG_ENTER("Item_subselect::init");
   DBUG_PRINT("enter", ("select_lex: 0x%lx", (long) select_lex));
   unit= select_lex->master_unit();
-
+  thd= unit->thd;
   if (unit->item)
   {
     /*
@@ -121,6 +121,7 @@ void Item_subselect::cleanup()
   reset();
   value_assigned= 0;
   expr_cache= 0;
+  forced_const= FALSE;
   DBUG_VOID_RETURN;
 }
 
@@ -158,7 +159,9 @@ void Item_in_subselect::cleanup()
     left_expr_cache= NULL;
   }
   first_execution= TRUE;
-  is_constant= FALSE;
+  if (in_strategy & SUBS_MATERIALIZATION)
+    in_strategy= 0;
+  pushed_cond_guards= NULL;
   Item_subselect::cleanup();
   DBUG_VOID_RETURN;
 }
@@ -166,6 +169,7 @@ void Item_in_subselect::cleanup()
 Item_subselect::~Item_subselect()
 {
   delete engine;
+  engine= NULL;
 }
 
 Item_subselect::trans_res
@@ -183,7 +187,8 @@ bool Item_subselect::fix_fields(THD *thd
   bool res;
 
   DBUG_ASSERT(fixed == 0);
-  engine->set_thd((thd= thd_param));
+  DBUG_ASSERT(thd == thd_param); /* thd can't change during execution. */
+  engine->set_thd(thd);
   if (!done_first_fix_fields)
   {
     done_first_fix_fields= TRUE;
@@ -206,11 +211,7 @@ bool Item_subselect::fix_fields(THD *thd
   {
     // all transformation is done (used by prepared statements)
     changed= 1;
-  inside_first_fix_fields= FALSE;
-
-
-    // all transformation is done (used by prepared statements)
-    changed= 1;
+    inside_first_fix_fields= FALSE;
 
     /*
       Substitute the current item with an Item_in_optimizer that was
@@ -220,11 +221,14 @@ bool Item_subselect::fix_fields(THD *thd
     */
     if (substitution)
     {
-      // did we changed top item of WHERE condition
+      /*
+        If the top item of the WHERE/HAVING condition changed,
+        set correct WHERE/HAVING for PS.
+      */
       if (unit->outer_select()->where == (*ref))
-        unit->outer_select()->where= substitution; // correct WHERE for PS
+        thd->change_item_tree(&(unit->outer_select()->where), substitution);
       else if (unit->outer_select()->having == (*ref))
-        unit->outer_select()->having= substitution; // correct HAVING for PS
+        thd->change_item_tree(&(unit->outer_select()->having), substitution);
 
       (*ref)= substitution;
       substitution->name= name;
@@ -284,6 +288,73 @@ bool Item_subselect::mark_as_eliminated_
 }
 
 
+/**
+  Remove a subselect item from its unit so that the unit no longer
+  represents a subquery.
+
+  @param arg  unused parameter
+
+  @return
+    FALSE to force the evaluation of the processor for the subsequent items.
+*/
+
+bool Item_subselect::eliminate_subselect_processor(uchar *arg)
+{
+  unit->item= NULL;
+  unit->exclude_from_tree();
+  eliminated= TRUE;
+  return FALSE;
+}
+
+
+/**
+  Adjust the master select of the subquery to be the fake_select which
+  represents the whole UNION right above the subquery, instead of the
+  last query of the UNION.
+
+  @param arg  pointer to the fake select
+
+  @return
+    FALSE to force the evaluation of the processor for the subsequent items.
+*/
+
+bool Item_subselect::set_fake_select_as_master_processor(uchar *arg)
+{
+  SELECT_LEX *fake_select= (SELECT_LEX*) arg;
+  /*
+    Move the st_select_lex_unit of a subquery from a global ORDER BY clause to
+    become a direct child of the fake_select of a UNION. In this way the
+    ORDER BY is applied to the temporary table that contains the result of the
+    whole UNION, and all columns in the subquery are resolved against this table.
+
+    Apply the transformation only for immediate child subqueries of a
+    UNION query.
+  */
+  if (unit->outer_select()->master_unit()->fake_select_lex == fake_select)
+  {
+    /*
+      Set the master of the subquery to be the fake select (i.e. the whole UNION),
+      instead of the last query in the UNION.
+      TODO:
+      This is a hack, instead we should call: unit->include_down(fake_select);
+      However, this call results in an infinite loop where
+      some_select_lex->master == some_select_lex.
+    */
+    unit->set_master(fake_select);
+    /* Adjust the name resolution context hierarchy accordingly. */
+    for (SELECT_LEX *sl= unit->first_select(); sl; sl= sl->next_select())
+      sl->context.outer_context= &(fake_select->context);
+    /*
+      Undo Item_subselect::eliminate_subselect_processor because at that phase
+      we don't know yet that the ORDER clause will be moved to the fake select.
+    */
+    unit->item= this;
+    eliminated= FALSE;
+  }
+  return FALSE;
+}
+
+
 bool Item_subselect::mark_as_dependent(THD *thd, st_select_lex *select, 
                                        Item *item)
 {
@@ -558,7 +629,7 @@ bool Item_in_subselect::exec()
     - on a cost-based basis, that takes into account the cost of a cache
       lookup, the cache hit rate, and the savings per cache hit.
   */
-  if (!left_expr_cache && exec_method == MATERIALIZATION)
+  if (!left_expr_cache && (in_strategy & SUBS_MATERIALIZATION))
     init_left_expr_cache();
 
   /*
@@ -611,12 +682,15 @@ Item *Item_subselect::get_tmp_table_item
 
 void Item_subselect::update_used_tables()
 {
-  recalc_used_tables(parent_select, FALSE);
-  if (!engine->uncacheable())
+  if (!forced_const)
   {
-    // did all used tables become static?
-    if (!(used_tables_cache & ~engine->upper_select_const_tables()))
-      const_item_cache= 1;
+    recalc_used_tables(parent_select, FALSE);
+    if (!engine->uncacheable())
+    {
+      // did all used tables become static?
+      if (!(used_tables_cache & ~engine->upper_select_const_tables()))
+        const_item_cache= 1;
+    }
   }
 }
 
@@ -682,7 +756,7 @@ Item_maxmin_subselect::Item_maxmin_subse
     of Items belonged to subquery, which will be not repeated
   */
   used_tables_cache= parent->get_used_tables_cache();
-  const_item_cache= parent->get_const_item_cache();
+  const_item_cache= parent->const_item();
 
   /*
     this subquery always creates during preparation, so we can assign
@@ -720,8 +794,7 @@ void Item_maxmin_subselect::print(String
 
 void Item_singlerow_subselect::reset()
 {
-  eliminated= FALSE;
-  null_value= 1;
+  Item_subselect::reset();
   if (value)
     value->null_value= 1;
 }
@@ -1015,11 +1088,11 @@ 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_cache(0), first_execution(TRUE),
-  is_constant(FALSE), optimizer(0), pushed_cond_guards(NULL),
-  exec_method(NOT_TRANSFORMED), upper_item(0)
+  optimizer(0), pushed_cond_guards(NULL), in_strategy(0), upper_item(0)
 {
   DBUG_ENTER("Item_in_subselect::Item_in_subselect");
   left_expr= left_exp;
+  func= &eq_creator;
   init(select_lex, new select_exists_subselect(this));
   max_columns= UINT_MAX;
   maybe_null= 1;
@@ -1249,7 +1322,7 @@ bool Item_in_subselect::val_bool()
 {
   DBUG_ASSERT(fixed == 1);
   null_value= 0;
-  if (is_constant)
+  if (forced_const)
     return value;
   if (exec())
   {
@@ -1289,59 +1362,37 @@ my_decimal *Item_in_subselect::val_decim
 }
 
 
-/* 
-  Rewrite a single-column IN/ALL/ANY subselect
-
-  SYNOPSIS
-    Item_in_subselect::single_value_transformer()
-      join  Join object of the subquery (i.e. 'child' join).
-      func  Subquery comparison creator
-
-  DESCRIPTION
-    Rewrite a single-column subquery using rule-based approach. The subquery
-    
-       oe $cmp$ (SELECT ie FROM ... WHERE subq_where ... HAVING subq_having)
-    
-    First, try to convert the subquery to scalar-result subquery in one of
-    the forms:
-    
-       - oe $cmp$ (SELECT MAX(...) )  // handled by Item_singlerow_subselect
-       - oe $cmp$ <max>(SELECT ...)   // handled by Item_maxmin_subselect
-   
-    If that fails, the subquery will be handled with class Item_in_optimizer, 
-    Inject the predicates into subquery, i.e. convert it to:
-
-    - If the subquery has aggregates, GROUP BY, or HAVING, convert to
+/**
+  Rewrite a single-column IN/ALL/ANY subselect.
 
-       SELECT ie FROM ...  HAVING subq_having AND 
-                                   trigcond(oe $cmp$ ref_or_null_helper<ie>)
-                                   
-      the addition is wrapped into trigger only when we want to distinguish
-      between NULL and FALSE results.
+  @param join  Join object of the subquery (i.e. 'child' join).
 
-    - Otherwise (no aggregates/GROUP BY/HAVING) convert it to one of the
-      following:
+  @details
+  Rewrite a single-column subquery using rule-based approach. The subquery
 
-      = If we don't need to distinguish between NULL and FALSE subquery:
-        
-        SELECT 1 FROM ... WHERE (oe $cmp$ ie) AND subq_where
+     oe $cmp$ (SELECT ie FROM ... WHERE subq_where ... HAVING subq_having)
 
-      = If we need to distinguish between those:
+  First, try to convert the subquery to scalar-result subquery in one of
+  the forms:
+    
+     - oe $cmp$ (SELECT MAX(...) )  // handled by Item_singlerow_subselect
+     - oe $cmp$ <max>(SELECT ...)   // handled by Item_maxmin_subselect
 
-        SELECT 1 FROM ...
-          WHERE  subq_where AND trigcond((oe $cmp$ ie) OR (ie IS NULL))
-          HAVING trigcond(<is_not_null_test>(ie))
+  If that fails, check if the subquery is a single select without tables,
+  and substitute the subquery predicate with "oe $cmp$ ie".
+   
+  If that fails, the subquery predicate is wrapped into an Item_in_optimizer.
+  Later the query optimization phase chooses whether the subquery under the
+  Item_in_optimizer will be further transformed into an equivalent correlated
+  EXISTS by injecting additional predicates, or will be executed via subquery
+  materialization in its unmodified form.
 
-  RETURN
-    RES_OK     Either subquery was transformed, or appopriate
-                       predicates where injected into it.
-    RES_REDUCE The subquery was reduced to non-subquery
-    RES_ERROR  Error
+  @retval RES_OK     The subquery was transformed
+  @retval RES_ERROR  Error
 */
 
 Item_subselect::trans_res
-Item_in_subselect::single_value_transformer(JOIN *join,
-                                            Comp_creator *func)
+Item_in_subselect::single_value_transformer(JOIN *join)
 {
   SELECT_LEX *select_lex= join->select_lex;
   DBUG_ENTER("Item_in_subselect::single_value_transformer");
@@ -1373,7 +1424,7 @@ Item_in_subselect::single_value_transfor
   {
     if (substitution)
     {
-      // It is second (third, ...) SELECT of UNION => All is done
+      /* It is second (third, ...) SELECT of UNION => All is done */
       DBUG_RETURN(RES_OK);
     }
 
@@ -1439,6 +1490,36 @@ Item_in_subselect::single_value_transfor
     DBUG_RETURN(RES_OK);
   }
 
+  Item* join_having= join->having ? join->having : join->tmp_having;
+  if (!(join_having || select_lex->with_sum_func ||
+        select_lex->group_list.elements) &&
+      select_lex->table_list.elements == 0 &&
+      !select_lex->master_unit()->is_union())
+  {
+    Item *where_item= (Item*) select_lex->item_list.head();
+    /*
+      it is single select without tables => possible optimization
+      remove the dependence mark since the item is moved to upper
+      select and is not outer anymore.
+    */
+    where_item->walk(&Item::remove_dependence_processor, 0,
+                     (uchar *) select_lex->outer_select());
+    substitution= func->create(left_expr, where_item);
+    have_to_be_excluded= 1;
+    if (thd->lex->describe)
+    {
+      char warn_buff[MYSQL_ERRMSG_SIZE];
+      sprintf(warn_buff, ER(ER_SELECT_REDUCED), select_lex->select_number);
+      push_warning(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
+                   ER_SELECT_REDUCED, warn_buff);
+    }
+    DBUG_RETURN(RES_OK);
+  }
+
+  /*
+    Wrap the current IN predicate in an Item_in_optimizer. The actual
+    substitution in the Item tree takes place in Item_subselect::fix_fields.
+  */
   if (!substitution)
   {
     /* We're invoked for the 1st (or the only) SELECT in the subquery UNION */
@@ -1469,33 +1550,37 @@ Item_in_subselect::single_value_transfor
                               (char *)in_left_expr_name);
 
     master_unit->uncacheable|= UNCACHEABLE_DEPENDENT;
-    //psergey: placed then removed: select_lex->uncacheable|= UNCACHEABLE_DEPENDENT;
+    // TODO: do we need to set both?
+    // select_lex->uncacheable|= UNCACHEABLE_DEPENDENT;
   }
 
-  if (!abort_on_null && left_expr->maybe_null && !pushed_cond_guards)
-  {
-    if (!(pushed_cond_guards= (bool*)join->thd->alloc(sizeof(bool))))
-      DBUG_RETURN(RES_ERROR);
-    pushed_cond_guards[0]= TRUE;
-  }
+  DBUG_RETURN(RES_OK);
+}
 
-  /*
-    If this IN predicate can be computed via materialization, do not
-    perform the IN -> EXISTS transformation.
-  */
-  if (exec_method == MATERIALIZATION)
-    DBUG_RETURN(RES_OK);
 
-  /* Perform the IN=>EXISTS transformation. */
-  DBUG_RETURN(single_value_in_to_exists_transformer(join, func));
+bool Item_in_subselect::fix_having(Item *having, SELECT_LEX *select_lex)
+{
+  bool fix_res= 0;
+  if (!having->fixed)
+  {
+    select_lex->having_fix_field= 1;
+    fix_res= having->fix_fields(thd, 0);
+    select_lex->having_fix_field= 0;
+  }
+  return fix_res;
 }
 
 
 /**
-  Transofrm an IN predicate into EXISTS via predicate injection.
+  Create the predicates needed to transform a single-column IN/ALL/ANY
+  subselect into a correlated EXISTS via predicate injection.
+
+  @param join[in]  Join object of the subquery (i.e. 'child' join).
+  @param where_item[out]   the in-to-exists addition to the where clause
+  @param having_item[out]  the in-to-exists addition to the having clause
 
-  @details The transformation injects additional predicates into the subquery
-  (and makes the subquery correlated) as follows.
+  @details
+  The correlated predicates are created as follows:
 
   - If the subquery has aggregates, GROUP BY, or HAVING, convert to
 
@@ -1510,34 +1595,38 @@ Item_in_subselect::single_value_transfor
 
     = If we don't need to distinguish between NULL and FALSE subquery:
         
-      SELECT 1 FROM ... WHERE (oe $cmp$ ie) AND subq_where
+      SELECT ie FROM ... WHERE subq_where AND (oe $cmp$ ie)
 
     = If we need to distinguish between those:
 
-      SELECT 1 FROM ...
+      SELECT ie FROM ...
         WHERE  subq_where AND trigcond((oe $cmp$ ie) OR (ie IS NULL))
         HAVING trigcond(<is_not_null_test>(ie))
 
-    @param join  Join object of the subquery (i.e. 'child' join).
-    @param func  Subquery comparison creator
-
-    @retval RES_OK     Either subquery was transformed, or appopriate
-                       predicates where injected into it.
-    @retval RES_REDUCE The subquery was reduced to non-subquery
-    @retval RES_ERROR  Error
+  @retval RES_OK     If the new conditions were created successfully
+  @retval RES_ERROR  Error
 */
 
 Item_subselect::trans_res
-Item_in_subselect::single_value_in_to_exists_transformer(JOIN * join, Comp_creator *func)
+Item_in_subselect::create_single_in_to_exists_cond(JOIN * join,
+                                                   Item **where_item,
+                                                   Item **having_item)
 {
   SELECT_LEX *select_lex= join->select_lex;
-  DBUG_ENTER("Item_in_subselect::single_value_in_to_exists_transformer");
+  /*
+    The non-transformed HAVING clause of 'join' may be stored in two ways
+    during JOIN::optimize: this->tmp_having= this->having; this->having= 0;
+  */
+  Item* join_having= join->having ? join->having : join->tmp_having;
+
+  DBUG_ENTER("Item_in_subselect::create_single_in_to_exists_cond");
 
-  select_lex->uncacheable|= UNCACHEABLE_DEPENDENT;
-  if (join->having || select_lex->with_sum_func ||
+  *where_item= NULL;
+  *having_item= NULL;
+
+  if (join_having || select_lex->with_sum_func ||
       select_lex->group_list.elements)
   {
-    bool tmp;
     Item *item= func->create(expr,
                              new Item_ref_null_helper(&select_lex->context,
                                                       this,
@@ -1553,24 +1642,12 @@ Item_in_subselect::single_value_in_to_ex
       */
       item= new Item_func_trig_cond(item, get_cond_guard(0));
     }
-    
-    /*
-      AND and comparison functions can't be changed during fix_fields()
-      we can assign select_lex->having here, and pass 0 as last
-      argument (reference) to fix_fields()
-    */
-    select_lex->having= join->having= and_items(join->having, item);
-    if (join->having == item)
-      item->name= (char*)in_having_cond;
-    select_lex->having_fix_field= 1;
-    /*
-      we do not check join->having->fixed, because Item_and (from and_items)
-      or comparison function (from func->create) can't be fixed after creation
-    */
-    tmp= join->having->fix_fields(thd, 0);
-    select_lex->having_fix_field= 0;
-    if (tmp)
+
+    if (!join_having)
+      item->name= (char*) in_having_cond;
+    if (fix_having(item, select_lex))
       DBUG_RETURN(RES_ERROR);
+    *having_item= item;
   }
   else
   {
@@ -1578,13 +1655,8 @@ Item_in_subselect::single_value_in_to_ex
 
     if (select_lex->table_list.elements)
     {
-      bool tmp;
-      Item *having= item, *orig_item= item;
-      select_lex->item_list.empty();
-      select_lex->item_list.push_back(new Item_int("Not_used",
-                                                   (longlong) 1,
-                                                   MY_INT64_NUM_DECIMAL_DIGITS));
-      select_lex->ref_pointer_array[0]= select_lex->item_list.head();
+      Item *having= item;
+      Item *orig_item= item;
        
       item= func->create(expr, item);
       if (!abort_on_null && orig_item->maybe_null)
@@ -1596,23 +1668,11 @@ Item_in_subselect::single_value_in_to_ex
                                                 get_cond_guard(0))))
             DBUG_RETURN(RES_ERROR);
         }
-        /*
-          Item_is_not_null_test can't be changed during fix_fields()
-          we can assign select_lex->having here, and pass 0 as last
-          argument (reference) to fix_fields()
-        */
-        having->name= (char*)in_having_cond;
-        select_lex->having= join->having= having;
-        select_lex->having_fix_field= 1;
-        /*
-          we do not check join->having->fixed, because Item_and (from
-          and_items) or comparison function (from func->create) can't be
-          fixed after creation
-        */
-        tmp= join->having->fix_fields(thd, 0);
-        select_lex->having_fix_field= 0;
-        if (tmp)
-          DBUG_RETURN(RES_ERROR);
+        having->name= (char*) in_having_cond;
+        if (fix_having(having, select_lex))
+          DBUG_RETURN(RES_ERROR);
+        *having_item= having;
+
         item= new Item_cond_or(item,
                                new Item_func_isnull(orig_item));
       }
@@ -1630,32 +1691,15 @@ Item_in_subselect::single_value_in_to_ex
         single_value_transformer but there is no corresponding action in
         row_value_transformer?
       */
-      item->name= (char *)in_additional_cond;
-
-      /*
-        AND can't be changed during fix_fields()
-        we can assign select_lex->having here, and pass 0 as last
-        argument (reference) to fix_fields()
-      */
-      select_lex->where= join->conds= and_items(join->conds, item);
-      select_lex->where->top_level_item();
-      /*
-        we do not check join->conds->fixed, because Item_and can't be fixed
-        after creation
-      */
-      if (join->conds->fix_fields(thd, 0))
-        DBUG_RETURN(RES_ERROR);
+      item->name= (char *) in_additional_cond;
+      if (!item->fixed && item->fix_fields(thd, 0))
+        DBUG_RETURN(RES_ERROR);
+      *where_item= item;
     }
     else
     {
-      bool tmp;
       if (select_lex->master_unit()->is_union())
       {
-        /*
-          comparison functions can't be changed during fix_fields()
-          we can assign select_lex->having here, and pass 0 as last
-          argument (reference) to fix_fields()
-        */
         Item *new_having=
           func->create(expr,
                        new Item_ref_null_helper(&select_lex->context, this,
@@ -1668,39 +1712,14 @@ Item_in_subselect::single_value_in_to_ex
                                                     get_cond_guard(0))))
             DBUG_RETURN(RES_ERROR);
         }
-        new_having->name= (char*)in_having_cond;
-        select_lex->having= join->having= new_having;
-        select_lex->having_fix_field= 1;
-        
-        /*
-          we do not check join->having->fixed, because comparison function
-          (from func->create) can't be fixed after creation
-        */
-        tmp= join->having->fix_fields(thd, 0);
-        select_lex->having_fix_field= 0;
-        if (tmp)
-          DBUG_RETURN(RES_ERROR);
+
+        new_having->name= (char*) in_having_cond;
+        if (fix_having(new_having, select_lex))
+          DBUG_RETURN(RES_ERROR);
+        *having_item= new_having;
       }
       else
-      {
-        // it is single select without tables => possible optimization
-        // remove the dependence mark since the item is moved to upper
-        // select and is not outer anymore.
-        item->walk(&Item::remove_dependence_processor, 0,
-                           (uchar *) select_lex->outer_select());
-        item= func->create(left_expr, item);
-        // fix_field of item will be done in time of substituting
-        substitution= item;
-        have_to_be_excluded= 1;
-        if (thd->lex->describe)
-        {
-          char warn_buff[MYSQL_ERRMSG_SIZE];
-          sprintf(warn_buff, ER(ER_SELECT_REDUCED), select_lex->select_number);
-          push_warning(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
-                       ER_SELECT_REDUCED, warn_buff);
-        }
-        DBUG_RETURN(RES_REDUCE);
-      }
+        DBUG_ASSERT(FALSE);
     }
   }
 
@@ -1708,6 +1727,22 @@ Item_in_subselect::single_value_in_to_ex
 }
 
 
+/**
+  Wrap a multi-column IN/ALL/ANY subselect into an Item_in_optimizer.
+
+  @param join  Join object of the subquery (i.e. 'child' join).
+
+  @details
+  The subquery predicate is wrapped into an Item_in_optimizer. Later the query
+  optimization phase chooses whether the subquery under the Item_in_optimizer
+  will be further transformed into an equivalent correlated EXISTS by injecting
+  additional predicates, or will be executed via subquery materialization in its
+  unmodified form.
+
+  @retval RES_OK     The subquery was transformed
+  @retval RES_ERROR  Error
+*/
+
 Item_subselect::trans_res
 Item_in_subselect::row_value_transformer(JOIN *join)
 {
@@ -1747,60 +1782,51 @@ Item_in_subselect::row_value_transformer
 
     thd->lex->current_select= current;
     master_unit->uncacheable|= UNCACHEABLE_DEPENDENT;
-
-    if (!abort_on_null && left_expr->maybe_null && !pushed_cond_guards)
-    {
-      if (!(pushed_cond_guards= (bool*)join->thd->alloc(sizeof(bool) *
-                                                        left_expr->cols())))
-        DBUG_RETURN(RES_ERROR);
-      for (uint i= 0; i < cols_num; i++)
-        pushed_cond_guards[i]= TRUE;
-    }
+    // TODO: do we need to set both?
+    //select_lex->uncacheable|= UNCACHEABLE_DEPENDENT;
   }
 
-  /*
-    If this IN predicate can be computed via materialization, do not
-    perform the IN -> EXISTS transformation.
-  */
-  if (exec_method == MATERIALIZATION)
-    DBUG_RETURN(RES_OK);
-
-  /* Perform the IN=>EXISTS transformation. */
-  DBUG_RETURN(row_value_in_to_exists_transformer(join));
+  DBUG_RETURN(RES_OK);
 }
 
 
 /**
-  Tranform a (possibly non-correlated) IN subquery into a correlated EXISTS.
+  Create the predicates needed to transform a multi-column IN/ALL/ANY
+  subselect into a correlated EXISTS via predicate injection.
 
-  @todo
-  The IF-ELSE below can be refactored so that there is no duplication of the
-  statements that create the new conditions. For this we have to invert the IF
-  and the FOR statements as this:
-  for (each left operand)
-    create the equi-join condition
-    if (is_having_used || !abort_on_null)
-      create the "is null" and is_not_null_test items
-    if (is_having_used)
-      add the equi-join and the null tests to HAVING
-    else
-      add the equi-join and the "is null" to WHERE
-      add the is_not_null_test to HAVING
+  @details
+  There are two cases - either the subquery has aggregates, GROUP BY,
+  or HAVING, or not. Both cases are described inline in the code.
+
+  @param join[in]  Join object of the subquery (i.e. 'child' join).
+  @param where_item[out]   the in-to-exists addition to the where clause
+  @param having_item[out]  the in-to-exists addition to the having clause
+
+  @retval RES_OK     If the new conditions were created successfully
+  @retval RES_ERROR  Error
 */
 
 Item_subselect::trans_res
-Item_in_subselect::row_value_in_to_exists_transformer(JOIN * join)
+Item_in_subselect::create_row_in_to_exists_cond(JOIN * join,
+                                                Item **where_item,
+                                                Item **having_item)
 {
   SELECT_LEX *select_lex= join->select_lex;
-  Item *having_item= 0;
   uint cols_num= left_expr->cols();
-  bool is_having_used= (join->having || select_lex->with_sum_func ||
+  /*
+    The non-transformed HAVING clause of 'join' may be stored in two ways
+    during JOIN::optimize: this->tmp_having= this->having; this->having= 0;
+  */
+  Item* join_having= join->having ? join->having : join->tmp_having;
+  bool is_having_used= (join_having || select_lex->with_sum_func ||
                         select_lex->group_list.first ||
                         !select_lex->table_list.elements);
 
-  DBUG_ENTER("Item_in_subselect::row_value_in_to_exists_transformer");
+  DBUG_ENTER("Item_in_subselect::create_row_in_to_exists_cond");
+
+  *where_item= NULL;
+  *having_item= NULL;
 
-  select_lex->uncacheable|= UNCACHEABLE_DEPENDENT;
   if (is_having_used)
   {
     /*
@@ -1853,8 +1880,8 @@ Item_in_subselect::row_value_in_to_exist
         if (!(col_item= new Item_func_trig_cond(col_item, get_cond_guard(i))))
           DBUG_RETURN(RES_ERROR);
       }
-      having_item= and_items(having_item, col_item);
-      
+      *having_item= and_items(*having_item, col_item);
+
       Item *item_nnull_test= 
          new Item_is_not_null_test(this,
                                    new Item_ref(&select_lex->context,
@@ -1871,8 +1898,7 @@ Item_in_subselect::row_value_in_to_exist
       item_having_part2= and_items(item_having_part2, item_nnull_test);
       item_having_part2->top_level_item();
     }
-    having_item= and_items(having_item, item_having_part2);
-    having_item->top_level_item();
+    *having_item= and_items(*having_item, item_having_part2);
   }
   else
   {
@@ -1893,7 +1919,6 @@ Item_in_subselect::row_value_in_to_exist
                                (l2 = v2) and
                                (l3 = v3)
     */
-    Item *where_item= 0;
     for (uint i= 0; i < cols_num; i++)
     {
       Item *item, *item_isnull;
@@ -1951,39 +1976,26 @@ Item_in_subselect::row_value_in_to_exist
                   new Item_func_trig_cond(having_col_item, get_cond_guard(i))))
             DBUG_RETURN(RES_ERROR);
         }
-        having_item= and_items(having_item, having_col_item);
+        *having_item= and_items(*having_item, having_col_item);
       }
-      where_item= and_items(where_item, item);
+      *where_item= and_items(*where_item, item);
     }
-    /*
-      AND can't be changed during fix_fields()
-      we can assign select_lex->where here, and pass 0 as last
-      argument (reference) to fix_fields()
-    */
-    select_lex->where= join->conds= and_items(join->conds, where_item);
-    select_lex->where->top_level_item();
-    if (join->conds->fix_fields(thd, 0))
+  }
+
+  if (*where_item)
+  {
+    if (!(*where_item)->fixed && (*where_item)->fix_fields(thd, 0))
       DBUG_RETURN(RES_ERROR);
+    (*where_item)->top_level_item();
   }
-  if (having_item)
+
+  if (*having_item)
   {
-    bool res;
-    select_lex->having= join->having= and_items(join->having, having_item);
-    if (having_item == select_lex->having)
-      having_item->name= (char*)in_having_cond;
-    select_lex->having->top_level_item();
-    /*
-      AND can't be changed during fix_fields()
-      we can assign select_lex->having here, and pass 0 as last
-      argument (reference) to fix_fields()
-    */
-    select_lex->having_fix_field= 1;
-    res= join->having->fix_fields(thd, 0);
-    select_lex->having_fix_field= 0;
-    if (res)
-    {
+    if (!join_having)
+      (*having_item)->name= (char*) in_having_cond;
+    if (fix_having(*having_item, select_lex))
       DBUG_RETURN(RES_ERROR);
-    }
+    (*having_item)->top_level_item();
   }
 
   DBUG_RETURN(RES_OK);
@@ -1993,7 +2005,89 @@ Item_in_subselect::row_value_in_to_exist
 Item_subselect::trans_res
 Item_in_subselect::select_transformer(JOIN *join)
 {
-  return select_in_like_transformer(join, &eq_creator);
+  return select_in_like_transformer(join);
+}
+
+
+/**
+  Create the predicates needed to transform an IN/ALL/ANY subselect into a
+  correlated EXISTS via predicate injection.
+
+  @param join_arg  Join object of the subquery.
+
+  @retval FALSE  ok
+  @retval TRUE   error
+*/
+
+bool Item_in_subselect::create_in_to_exists_cond(JOIN *join_arg)
+{
+  Item_subselect::trans_res res;
+
+  DBUG_ASSERT(engine->engine_type() == subselect_engine::SINGLE_SELECT_ENGINE ||
+              engine->engine_type() == subselect_engine::UNION_ENGINE);
+  /*
+    TODO: the call to init_cond_guards allocates and initializes an
+    array of booleans that may not be used later because we may choose
+    materialization.
+    The two calls below to create_XYZ_cond depend on this boolean array.
+    If the dependency is removed, the call can be moved to a later phase.
+  */
+  init_cond_guards();
+  join_arg->select_lex->uncacheable|= UNCACHEABLE_DEPENDENT;
+  if (left_expr->cols() == 1)
+    res= create_single_in_to_exists_cond(join_arg,
+                                         &(join_arg->in_to_exists_where),
+                                         &(join_arg->in_to_exists_having));
+  else
+    res= create_row_in_to_exists_cond(join_arg,
+                                      &(join_arg->in_to_exists_where),
+                                      &(join_arg->in_to_exists_having));
+  return (res != RES_OK);
+}
+
+
+/**
+  Transform an IN/ALL/ANY subselect into a correlated EXISTS via injecting
+  correlated in-to-exists predicates.
+
+  @param join_arg  Join object of the subquery.
+
+  @retval FALSE  ok
+  @retval TRUE   error
+*/
+
+bool Item_in_subselect::inject_in_to_exists_cond(JOIN *join_arg)
+{
+  SELECT_LEX *select_lex= join_arg->select_lex;
+  Item *where_item= join_arg->in_to_exists_where;
+  Item *having_item= join_arg->in_to_exists_having;
+
+  DBUG_ENTER("Item_in_subselect::inject_in_to_exists_cond");
+
+  if (where_item)
+  {
+    where_item= and_items(join_arg->conds, where_item);
+    if (!where_item->fixed && where_item->fix_fields(thd, 0))
+      DBUG_RETURN(true);
+    // TIMOUR TODO: call optimize_cond() for the new where clause
+    thd->change_item_tree(&select_lex->where, where_item);
+    select_lex->where->top_level_item();
+    join_arg->conds= select_lex->where;
+  }
+
+  if (having_item)
+  {
+    Item* join_having= join_arg->having ? join_arg->having:join_arg->tmp_having;
+    having_item= and_items(join_having, having_item);
+    if (fix_having(having_item, select_lex))
+      DBUG_RETURN(true);
+    // TIMOUR TODO: call optimize_cond() for the new having clause
+    thd->change_item_tree(&select_lex->having, having_item);
+    select_lex->having->top_level_item();
+    join_arg->having= select_lex->having;
+  }
+
+  DBUG_RETURN(false);
 }
 
 
@@ -2001,25 +2095,20 @@ Item_in_subselect::select_transformer(JO
   Prepare IN/ALL/ANY/SOME subquery transformation and call appropriate
   transformation function.
 
-    To decide which transformation procedure (scalar or row) applicable here
-    we have to call fix_fields() for left expression to be able to call
-    cols() method on it. Also this method make arena management for
-    underlying transformation methods.
-
   @param join    JOIN object of transforming subquery
-  @param func    creator of condition function of subquery
 
-  @retval
-    RES_OK      OK
-  @retval
-    RES_REDUCE  OK, and current subquery was reduced during
-    transformation
-  @retval
-    RES_ERROR   Error
+  @notes
+  To decide which transformation procedure (scalar or row) applicable here
+  we have to call fix_fields() for left expression to be able to call
+  cols() method on it. Also this method make arena management for
+  underlying transformation methods.
+
+  @retval  RES_OK      OK
+  @retval  RES_ERROR   Error
 */
 
 Item_subselect::trans_res
-Item_in_subselect::select_in_like_transformer(JOIN *join, Comp_creator *func)
+Item_in_subselect::select_in_like_transformer(JOIN *join)
 {
   Query_arena *arena, backup;
   SELECT_LEX *current= thd->lex->current_select;
@@ -2075,22 +2164,15 @@ Item_in_subselect::select_in_like_transf
     goto err;
 
   /*
-    If we didn't choose an execution method up to this point, we choose
-    the IN=>EXISTS transformation.
-  */
-  if (exec_method == NOT_TRANSFORMED)
-    exec_method= IN_TO_EXISTS;
-  arena= thd->activate_stmt_arena_if_needed(&backup);
-
-  /*
     Both transformers call fix_fields() only for Items created inside them,
     and all that items do not make permanent changes in current item arena
     which allow to us call them with changed arena (if we do not know nature
     of Item, we have to call fix_fields() for it only with original arena to
     avoid memory leack)
   */
+  arena= thd->activate_stmt_arena_if_needed(&backup);
   if (left_expr->cols() == 1)
-    res= single_value_transformer(join, func);
+    res= single_value_transformer(join);
   else
   {
     /* we do not support row operation for ALL/ANY/SOME */
@@ -2113,7 +2195,7 @@ err:
 
 void Item_in_subselect::print(String *str, enum_query_type query_type)
 {
-  if (exec_method == IN_TO_EXISTS)
+  if (in_strategy & SUBS_IN_TO_EXISTS)
     str->append(STRING_WITH_LEN("<exists>"));
   else
   {
@@ -2129,7 +2211,7 @@ bool Item_in_subselect::fix_fields(THD *
   uint outer_cols_num;
   List<Item> *inner_cols;
 
-  if (exec_method == SEMI_JOIN)
+  if (in_strategy & SUBS_SEMI_JOIN)
     return !( (*ref)= new Item_int(1));
 
   /*
@@ -2202,99 +2284,47 @@ void Item_in_subselect::update_used_tabl
   used_tables_cache |= left_expr->used_tables();
 }
 
+
 /**
-  Try to create an engine to compute the subselect via materialization,
-  and if this fails, revert to execution via the IN=>EXISTS transformation.
+  Try to create and initialize an engine to compute a subselect via
+  materialization.
 
   @details
-    The purpose of this method is to hide the implementation details
-    of this Item's execution. The method creates a new engine for
-    materialized execution, and initializes the engine.
-
-    If this initialization fails
-    - either because it wasn't possible to create the needed temporary table
-      and its index,
-    - or because of a memory allocation error,
-    then we revert back to execution via the IN=>EXISTS tranformation.
-
-    The initialization of the new engine is divided in two parts - a permanent
-    one that lives across prepared statements, and one that is repeated for each
-    execution.
+  The method creates a new engine for materialized execution, and initializes
+  the engine. The initialization may fail
+  - either because it wasn't possible to create the needed temporary table
+    and its index,
+  - or because of a memory allocation error,
 
   @returns
     @retval TRUE  memory allocation error occurred
     @retval FALSE an execution method was chosen successfully
 */
 
-bool Item_in_subselect::setup_engine()
+bool Item_in_subselect::setup_mat_engine()
 {
-  subselect_hash_sj_engine *new_engine= NULL;
-  bool res= FALSE;
-
-  DBUG_ENTER("Item_in_subselect::setup_engine");
+  subselect_hash_sj_engine       *mat_engine= NULL;
+  subselect_single_select_engine *select_engine;
 
-  if (engine->engine_type() == subselect_engine::SINGLE_SELECT_ENGINE)
-  {
-    /* Create/initialize objects in permanent memory. */
-    subselect_single_select_engine *old_engine;
-    Query_arena *arena= thd->stmt_arena, backup;
+  DBUG_ENTER("Item_in_subselect::setup_mat_engine");
 
-    old_engine= (subselect_single_select_engine*) engine;
-
-    if (arena->is_conventional())
-      arena= 0;
-    else
-      thd->set_n_backup_active_arena(arena, &backup);
-
-    if (!(new_engine= new subselect_hash_sj_engine(thd, this,
-                                                   old_engine)) ||
-        new_engine->init_permanent(unit->get_unit_column_types()))
-    {
-      Item_subselect::trans_res trans_res;
-      /*
-        If for some reason we cannot use materialization for this IN predicate,
-        delete all materialization-related objects, and apply the IN=>EXISTS
-        transformation.
-      */
-      delete new_engine;
-      new_engine= NULL;
-      exec_method= NOT_TRANSFORMED;
-      if (left_expr->cols() == 1)
-        trans_res= single_value_in_to_exists_transformer(old_engine->join,
-                                                         &eq_creator);
-      else
-        trans_res= row_value_in_to_exists_transformer(old_engine->join);
-      res= (trans_res != Item_subselect::RES_OK);
-    }
-    if (new_engine)
-      engine= new_engine;
+  /*
+    The select_engine (that executes transformed IN=>EXISTS subselects) is
+    pre-created at parse time, and is stored in statment memory (preserved
+    across PS executions).
+  */
+  DBUG_ASSERT(engine->engine_type() == subselect_engine::SINGLE_SELECT_ENGINE);
+  select_engine= (subselect_single_select_engine*) engine;
 
-    if (arena)
-      thd->restore_active_arena(arena, &backup);
-  }
-  else
-  {
-    DBUG_ASSERT(engine->engine_type() == subselect_engine::HASH_SJ_ENGINE);
-    new_engine= (subselect_hash_sj_engine*) engine;
-  }
+  /* Create/initialize execution objects. */
+  if (!(mat_engine= new subselect_hash_sj_engine(thd, this, select_engine)))
+    DBUG_RETURN(TRUE);
 
-  /* Initilizations done in runtime memory, repeated for each execution. */
-  if (new_engine)
-  {
-    /*
-      Reset the LIMIT 1 set in Item_exists_subselect::fix_length_and_dec.
-      TODO:
-      Currently we set the subquery LIMIT to infinity, and this is correct
-      because we forbid at parse time LIMIT inside IN subqueries (see
-      Item_in_subselect::test_limit). However, once we allow this, here
-      we should set the correct limit if given in the query.
-    */
-    unit->global_parameters->select_limit= NULL;
-    if ((res= new_engine->init_runtime()))
-      DBUG_RETURN(res);
-  }
+  if (mat_engine->init(&select_engine->join->fields_list))
+    DBUG_RETURN(TRUE);
 
-  DBUG_RETURN(res);
+  engine= mat_engine;
+  DBUG_RETURN(FALSE);
 }
 
 
@@ -2337,14 +2367,25 @@ bool Item_in_subselect::init_left_expr_c
 }
 
 
+bool Item_in_subselect::init_cond_guards()
+{
+  uint cols_num= left_expr->cols();
+  if (!abort_on_null && left_expr->maybe_null && !pushed_cond_guards)
+  {
+    if (!(pushed_cond_guards= (bool*)thd->alloc(sizeof(bool) * cols_num)))
+        return TRUE;
+    for (uint i= 0; i < cols_num; i++)
+      pushed_cond_guards[i]= TRUE;
+  }
+  return FALSE;
+}
+
+
 /*
   Callback to test if an IN predicate is expensive.
 
   @details
-    IN predicates are considered expensive only if they will be executed via
-    materialization. The return value affects the behavior of
-    make_cond_for_table() in such a way that it is unchanged when we use
-    the IN=>EXISTS transformation to compute IN.
+    The return value affects the behavior of make_cond_for_table().
 
   @retval TRUE  if the predicate is expensive
   @retval FALSE otherwise
@@ -2352,7 +2393,8 @@ bool Item_in_subselect::init_left_expr_c
 
 bool Item_in_subselect::is_expensive_processor(uchar *arg)
 {
-  return exec_method == MATERIALIZATION;
+  /* TIMOUR: TODO: decide on a cost basis whether it is expensive or not. */
+  return TRUE;
 }
 
 
@@ -2360,16 +2402,16 @@ Item_subselect::trans_res
 Item_allany_subselect::select_transformer(JOIN *join)
 {
   DBUG_ENTER("Item_allany_subselect::select_transformer");
-  exec_method= IN_TO_EXISTS;
+  in_strategy= SUBS_IN_TO_EXISTS;
   if (upper_item)
     upper_item->show= 1;
-  DBUG_RETURN(select_in_like_transformer(join, func));
+  DBUG_RETURN(select_in_like_transformer(join));
 }
 
 
 void Item_allany_subselect::print(String *str, enum_query_type query_type)
 {
-  if (exec_method == IN_TO_EXISTS)
+  if (in_strategy & SUBS_IN_TO_EXISTS)
     str->append(STRING_WITH_LEN("<exists>"));
   else
   {
@@ -3795,13 +3837,14 @@ bitmap_init_memroot(MY_BITMAP *map, uint
   @retval FALSE otherwise
 */
 
-bool subselect_hash_sj_engine::init_permanent(List<Item> *tmp_columns)
+bool subselect_hash_sj_engine::init(List<Item> *tmp_columns)
 {
+  select_union *result_sink;
   /* Options to create_tmp_table. */
   ulonglong tmp_create_options= thd->options | TMP_TABLE_ALL_COLUMNS;
                              /* | TMP_TABLE_FORCE_MYISAM; TIMOUR: force MYISAM */
 
-  DBUG_ENTER("subselect_hash_sj_engine::init_permanent");
+  DBUG_ENTER("subselect_hash_sj_engine::init");
 
   if (bitmap_init_memroot(&non_null_key_parts, tmp_columns->elements,
                             thd->mem_root) ||
@@ -3830,15 +3873,16 @@ bool subselect_hash_sj_engine::init_perm
     DBUG_RETURN(TRUE);
   }
 */
-  if (!(result= new select_materialize_with_stats))
+  if (!(result_sink= new select_materialize_with_stats))
     DBUG_RETURN(TRUE);
-
-  if (((select_union*) result)->create_result_table(
-                         thd, tmp_columns, TRUE, tmp_create_options,
-                         "materialized subselect", TRUE))
+  result_sink->get_tmp_table_param()->materialized_subquery= true;
+  if (result_sink->create_result_table(thd, tmp_columns, TRUE,
+                                       tmp_create_options,
+                                       "materialized subselect", TRUE))
     DBUG_RETURN(TRUE);
 
-  tmp_table= ((select_union*) result)->table;
+  tmp_table= result_sink->table;
+  result= result_sink;
 
   /*
     If the subquery has blobs, or the total key lenght is bigger than
@@ -3875,6 +3919,17 @@ bool subselect_hash_sj_engine::init_perm
       !(lookup_engine= make_unique_engine()))
     DBUG_RETURN(TRUE);
 
+  /*
+    Repeat name resolution for 'cond' since cond is not part of any
+    clause of the query, and it is not 'fixed' during JOIN::prepare.
+  */
+  if (semi_join_conds && !semi_join_conds->fixed &&
+      semi_join_conds->fix_fields(thd, (Item**)&semi_join_conds))
+    DBUG_RETURN(TRUE);
+  /* Let our engine reuse this query plan for materialization. */
+  materialize_join= materialize_engine->join;
+  materialize_join->change_result(result);
+
   DBUG_RETURN(FALSE);
 }
 
@@ -3987,41 +4042,22 @@ subselect_hash_sj_engine::make_unique_en
 }
 
 
-/**
-  Initialize members of the engine that need to be re-initilized at each
-  execution.
+subselect_hash_sj_engine::~subselect_hash_sj_engine()
+{
+  delete lookup_engine;
+  delete result;
+  if (tmp_table)
+    free_tmp_table(thd, tmp_table);
+}
 
-  @retval TRUE  if a memory allocation error occurred
-  @retval FALSE if success
-*/
 
-bool subselect_hash_sj_engine::init_runtime()
+int subselect_hash_sj_engine::prepare()
 {
   /*
     Create and optimize the JOIN that will be used to materialize
     the subquery if not yet created.
   */
-  materialize_engine->prepare();
-  /*
-    Repeat name resolution for 'cond' since cond is not part of any
-    clause of the query, and it is not 'fixed' during JOIN::prepare.
-  */
-  if (semi_join_conds && !semi_join_conds->fixed &&
-      semi_join_conds->fix_fields(thd, (Item**)&semi_join_conds))
-    return TRUE;
-  /* Let our engine reuse this query plan for materialization. */
-  materialize_join= materialize_engine->join;
-  materialize_join->change_result(result);
-  return FALSE;
-}
-
-
-subselect_hash_sj_engine::~subselect_hash_sj_engine()
-{
-  delete lookup_engine;
-  delete result;
-  if (tmp_table)
-    free_tmp_table(thd, tmp_table);
+  return materialize_engine->prepare();
 }
 
 
@@ -4042,6 +4078,12 @@ void subselect_hash_sj_engine::cleanup()
   count_null_only_columns= 0;
   strategy= UNDEFINED;
   materialize_engine->cleanup();
+  /*
+    Restore the original Item_in_subselect engine. This engine is created once
+    at parse time and stored across executions, while all other materialization
+    related engines are created and chosen for each execution.
+  */
+  ((Item_in_subselect *) item)->engine= materialize_engine;
   if (lookup_engine_type == TABLE_SCAN_ENGINE ||
       lookup_engine_type == ROWID_MERGE_ENGINE)
   {
@@ -4058,6 +4100,9 @@ void subselect_hash_sj_engine::cleanup()
   DBUG_ASSERT(lookup_engine->engine_type() == UNIQUESUBQUERY_ENGINE);
   lookup_engine->cleanup();
   result->cleanup(); /* Resets the temp table as well. */
+  DBUG_ASSERT(tmp_table);
+  free_tmp_table(thd, tmp_table);
+  tmp_table= NULL;
 }
 
 
@@ -4086,9 +4131,8 @@ int subselect_hash_sj_engine::exec()
     the subquery predicate.
   */
   thd->lex->current_select= materialize_engine->select_lex;
-  if ((res= materialize_join->optimize()))
-    goto err; /* purecov: inspected */
-  DBUG_ASSERT(!is_materialized); /* We should materialize only once. */
+  /* The subquery should be optimized, and materialized only once. */
+  DBUG_ASSERT(materialize_join->optimized && !is_materialized);
   materialize_join->exec();
   if ((res= test(materialize_join->error || thd->is_fatal_error)))
     goto err;
@@ -4112,11 +4156,10 @@ int subselect_hash_sj_engine::exec()
   tmp_table->file->info(HA_STATUS_VARIABLE);
   if (!tmp_table->file->stats.records)
   {
-    item_in->value= FALSE;
     /* The value of IN will not change during this execution. */
-    item_in->is_constant= TRUE;
+    item_in->reset();
+    item_in->make_const();
     item_in->set_first_execution();
-    /* TIMOUR: check if we need this: item_in->null_value= FALSE; */
     DBUG_RETURN(FALSE);
   }
 

=== modified file 'sql/item_subselect.h'
--- a/sql/item_subselect.h	2010-10-10 14:18:11 +0000
+++ b/sql/item_subselect.h	2010-11-05 12:42:58 +0000
@@ -71,6 +71,13 @@ protected:
   
   bool inside_first_fix_fields;
   bool done_first_fix_fields;
+  /*
+    Set to TRUE if at optimization or execution time we determine that this
+    item's value is a constant. We need this member because it is not possible
+    to substitute 'this' with a constant item.
+  */
+  bool forced_const;
+
 public:
   /* A reference from inside subquery predicate to somewhere outside of it */
   class Ref_to_outside : public Sql_alloc
@@ -119,6 +126,12 @@ public:
   Item_subselect();
 
   virtual subs_type substype() { return UNKNOWN_SUBS; }
+  bool is_in_predicate()
+  {
+    return (substype() == Item_subselect::IN_SUBS ||
+            substype() == Item_subselect::ALL_SUBS ||
+            substype() == Item_subselect::ANY_SUBS);
+  }
 
   /*
     We need this method, because some compilers do not allow 'this'
@@ -149,12 +162,21 @@ public:
   void fix_after_pullout(st_select_lex *new_parent, Item **ref);
   void recalc_used_tables(st_select_lex *new_parent, bool after_pullout);
   virtual bool exec();
+  /*
+    If subquery optimization or execution determines that the subquery has
+    an empty result, mark the subquery predicate as a constant value.
+  */
+  void make_const()
+  { 
+    used_tables_cache= 0;
+    const_item_cache= 0;
+    forced_const= TRUE; 
+  }
   virtual void fix_length_and_dec();
   table_map used_tables() const;
   table_map not_null_tables() const { return 0; }
   bool const_item() const;
   inline table_map get_used_tables_cache() { return used_tables_cache; }
-  inline bool get_const_item_cache() { return const_item_cache; }
   Item *get_tmp_table_item(THD *thd);
   void update_used_tables();
   virtual void print(String *str, enum_query_type query_type);
@@ -181,6 +203,8 @@ public:
   enum_parsing_place place() { return parsing_place; }
   bool walk(Item_processor processor, bool walk_subquery, uchar *arg);
   bool mark_as_eliminated_processor(uchar *arg);
+  bool eliminate_subselect_processor(uchar *arg);
+  bool set_fake_select_as_master_processor(uchar *arg);
   bool enumerate_field_refs_processor(uchar *arg);
   bool check_vcol_func_processor(uchar *int_arg) 
   {
@@ -313,6 +337,18 @@ public:
 };
 
 
+/*
+  Possible methods to execute an IN predicate. These are set by the optimizer
+  based on user-set optimizer switches, semantic analysis and cost comparison.
+*/
+#define SUBS_NOT_TRANSFORMED 0 /* No execution method was chosen for this IN. */
+#define SUBS_SEMI_JOIN 1       /* IN was converted to semi-join. */
+#define SUBS_IN_TO_EXISTS 2    /* IN was converted to correlated EXISTS. */
+#define SUBS_MATERIALIZATION 4 /* Execute IN via subquery materialization. */
+/* Partial matching substrategies of MATERIALIZATION. */
+#define SUBS_PARTIAL_MATCH_ROWID_MERGE 8
+#define SUBS_PARTIAL_MATCH_TABLE_SCAN 16
+
 /**
   Representation of IN subquery predicates of the form
   "left_expr IN (SELECT ...)".
@@ -330,8 +366,6 @@ public:
 
 class Item_in_subselect :public Item_exists_subselect
 {
-public:
-  Item *left_expr;
 protected:
   /*
     Cache of the left operand of the subquery predicate. Allocated in the
@@ -339,12 +373,6 @@ protected:
   */
   List<Cached_item> *left_expr_cache;
   bool first_execution;
-  /*
-    Set to TRUE if at query execution time we determine that this item's
-    value is a constant during this execution. We need this member because
-    it is not possible to substitute 'this' with a constant item.
-  */
-  bool is_constant;
 
   /*
     expr & optimizer used in subselect rewriting to store Item for
@@ -354,10 +382,24 @@ protected:
   Item_in_optimizer *optimizer;
   bool was_null;
   bool abort_on_null;
-public:
   /* Used to trigger on/off conditions that were pushed down to subselect */
   bool *pushed_cond_guards;
-  
+  Comp_creator *func;
+
+protected:
+  bool init_cond_guards();
+  trans_res select_in_like_transformer(JOIN *join);
+  trans_res single_value_transformer(JOIN *join);
+  trans_res row_value_transformer(JOIN * join);
+  bool fix_having(Item *having, st_select_lex *select_lex);
+  trans_res create_single_in_to_exists_cond(JOIN * join,
+                                            Item **where_item,
+                                            Item **having_item);
+  trans_res create_row_in_to_exists_cond(JOIN * join,
+                                         Item **where_item,
+                                         Item **having_item);
+public:
+  Item *left_expr;
   /* Priority of this predicate in the convert-to-semi-join-nest process. */
   int sj_convert_priority;
   /*
@@ -388,14 +430,8 @@ public:
   */
   bool sjm_scan_allowed;
 
-  /* The method chosen to execute the IN predicate.  */
-  enum enum_exec_method {
-    NOT_TRANSFORMED, /* No execution method was chosen for this IN. */
-    SEMI_JOIN,   /* IN was converted to semi-join nest and should be removed. */
-    IN_TO_EXISTS, /* IN was converted to correlated EXISTS. */
-    MATERIALIZATION /* IN will be executed via subquery materialization. */
-  };
-  enum_exec_method exec_method;
+  /* A bitmap of possible execution strategies for an IN predicate. */
+  uchar in_strategy;
 
   bool *get_cond_guard(int i)
   {
@@ -413,9 +449,10 @@ public:
   Item_in_subselect(Item * left_expr, st_select_lex *select_lex);
   Item_in_subselect()
     :Item_exists_subselect(), left_expr_cache(0), first_execution(TRUE),
-    is_constant(FALSE), optimizer(0), abort_on_null(0),
-    pushed_cond_guards(NULL), exec_method(NOT_TRANSFORMED), upper_item(0)
-  {}
+    optimizer(0), abort_on_null(0),
+    pushed_cond_guards(NULL), func(NULL), in_strategy(0),
+    upper_item(0)
+    {}
   void cleanup();
   subs_type substype() { return IN_SUBS; }
   void reset() 
@@ -426,12 +463,9 @@ public:
     was_null= 0;
   }
   trans_res select_transformer(JOIN *join);
-  trans_res select_in_like_transformer(JOIN *join, Comp_creator *func);
-  trans_res single_value_transformer(JOIN *join, Comp_creator *func);
-  trans_res row_value_transformer(JOIN * join);
-  trans_res single_value_in_to_exists_transformer(JOIN * join,
-                                                  Comp_creator *func);
-  trans_res row_value_in_to_exists_transformer(JOIN * join);
+  bool create_in_to_exists_cond(JOIN *join_arg);
+  bool inject_in_to_exists_cond(JOIN *join_arg);
+
   virtual bool exec();
   longlong val_int();
   double val_real();
@@ -446,11 +480,12 @@ public:
   bool fix_fields(THD *thd, Item **ref);
   void fix_after_pullout(st_select_lex *new_parent, Item **ref);
   void update_used_tables();
-  bool setup_engine();
+  bool setup_mat_engine();
   bool init_left_expr_cache();
   /* Inform 'this' that it was computed, and contains a valid result. */
   void set_first_execution() { if (first_execution) first_execution= FALSE; }
   bool is_expensive_processor(uchar *arg);
+  bool is_expensive() { return TRUE; }
   bool expr_cache_is_needed(THD *thd);
   
   /* 
@@ -472,7 +507,6 @@ class Item_allany_subselect :public Item
 {
 public:
   chooser_compare_func_creator func_creator;
-  Comp_creator *func;
   bool all;
 
   Item_allany_subselect(Item * left_expr, chooser_compare_func_creator fc,
@@ -481,6 +515,7 @@ public:
   // only ALL subquery has upper not
   subs_type substype() { return all?ALL_SUBS:ANY_SUBS; }
   trans_res select_transformer(JOIN *join);
+  void create_comp_func(bool invert) { func= func_creator(invert); }
   virtual void print(String *str, enum_query_type query_type);
 };
 
@@ -821,10 +856,9 @@ public:
   }
   ~subselect_hash_sj_engine();
 
-  bool init_permanent(List<Item> *tmp_columns);
-  bool init_runtime();
+  bool init(List<Item> *tmp_columns);
   void cleanup();
-  int prepare() { return 0; } /* Override virtual function in base class. */
+  int prepare();
   int exec();
   virtual void print(String *str, enum_query_type query_type);
   uint cols()

=== modified file 'sql/mysql_priv.h'
--- a/sql/mysql_priv.h	2010-11-05 10:37:51 +0000
+++ b/sql/mysql_priv.h	2010-11-16 06:51:38 +0000
@@ -566,20 +565,26 @@ protected:
 #define OPTIMIZER_SWITCH_FIRSTMATCH 32
 #define OPTIMIZER_SWITCH_LOOSE_SCAN 64
 #define OPTIMIZER_SWITCH_MATERIALIZATION 128
-#define OPTIMIZER_SWITCH_SEMIJOIN 256
-#define OPTIMIZER_SWITCH_PARTIAL_MATCH_ROWID_MERGE 512
-#define OPTIMIZER_SWITCH_PARTIAL_MATCH_TABLE_SCAN 1024
-#define OPTIMIZER_SWITCH_SUBQUERY_CACHE (1<<11)
+#define OPTIMIZER_SWITCH_IN_TO_EXISTS 256
+#define OPTIMIZER_SWITCH_SEMIJOIN 512
+#define OPTIMIZER_SWITCH_PARTIAL_MATCH_ROWID_MERGE 1024
+#define OPTIMIZER_SWITCH_PARTIAL_MATCH_TABLE_SCAN  (1<<11)
+#define OPTIMIZER_SWITCH_SUBQUERY_CACHE  (1<<12)
 
 #ifdef DBUG_OFF
-#  define OPTIMIZER_SWITCH_LAST (1<<12)
-#else
-#  define OPTIMIZER_SWITCH_TABLE_ELIMINATION (1<<12)
 #  define OPTIMIZER_SWITCH_LAST (1<<13)
+#else
+#  define OPTIMIZER_SWITCH_TABLE_ELIMINATION (1<<13)
+#  define OPTIMIZER_SWITCH_LAST (1<<14)
 #endif
 
 #ifdef DBUG_OFF 
 /* The following must be kept in sync with optimizer_switch_str in mysqld.cc */
+/*
+TODO: Materialization is off by default to mimic 5.1/5.2 behavior.
+Once cost based choice between materialization and in-to-exists should be
+enabled by default, add OPTIMIZER_SWITCH_MATERIALIZATION
+*/
 #  define OPTIMIZER_SWITCH_DEFAULT (OPTIMIZER_SWITCH_INDEX_MERGE | \
                                     OPTIMIZER_SWITCH_INDEX_MERGE_UNION | \
                                     OPTIMIZER_SWITCH_INDEX_MERGE_SORT_UNION | \
@@ -587,7 +592,7 @@ protected:
                                     OPTIMIZER_SWITCH_INDEX_COND_PUSHDOWN | \
                                     OPTIMIZER_SWITCH_FIRSTMATCH | \
                                     OPTIMIZER_SWITCH_LOOSE_SCAN | \
-                                    OPTIMIZER_SWITCH_MATERIALIZATION | \
+                                    OPTIMIZER_SWITCH_IN_TO_EXISTS | \
                                     OPTIMIZER_SWITCH_SEMIJOIN | \
                                     OPTIMIZER_SWITCH_PARTIAL_MATCH_ROWID_MERGE|\
                                     OPTIMIZER_SWITCH_PARTIAL_MATCH_TABLE_SCAN|\
@@ -601,7 +606,7 @@ protected:
                                     OPTIMIZER_SWITCH_TABLE_ELIMINATION | \
                                     OPTIMIZER_SWITCH_FIRSTMATCH | \
                                     OPTIMIZER_SWITCH_LOOSE_SCAN | \
-                                    OPTIMIZER_SWITCH_MATERIALIZATION | \
+                                    OPTIMIZER_SWITCH_IN_TO_EXISTS | \
                                     OPTIMIZER_SWITCH_SEMIJOIN | \
                                     OPTIMIZER_SWITCH_PARTIAL_MATCH_ROWID_MERGE|\
                                     OPTIMIZER_SWITCH_PARTIAL_MATCH_TABLE_SCAN|\

=== modified file 'sql/mysqld.cc'
--- a/sql/mysqld.cc	2010-11-05 10:37:51 +0000
+++ b/sql/mysqld.cc	2010-11-16 06:51:38 +0000
@@ -341,7 +341,7 @@ static const char *optimizer_switch_name
   "index_merge","index_merge_union","index_merge_sort_union",
   "index_merge_intersection",
   "index_condition_pushdown",
-  "firstmatch","loosescan","materialization", "semijoin",
+  "firstmatch","loosescan","materialization","in_to_exists","semijoin",
   "partial_match_rowid_merge",
   "partial_match_table_scan",
   "subquery_cache",
@@ -362,6 +362,7 @@ static const unsigned int optimizer_swit
   sizeof("firstmatch") - 1,
   sizeof("loosescan") - 1,
   sizeof("materialization") - 1,
+  sizeof("in_to_exists") - 1,
   sizeof("semijoin") - 1,
   sizeof("partial_match_rowid_merge") - 1,
   sizeof("partial_match_table_scan") - 1,
@@ -460,7 +461,8 @@ static const char *optimizer_switch_str=
                                         "index_condition_pushdown=on,"
                                         "firstmatch=on,"
                                         "loosescan=on,"
-                                        "materialization=on,"
+                                        "materialization=off,"
+                                        "in_to_exists=on,"
                                         "semijoin=on,"
                                         "partial_match_rowid_merge=on,"
                                         "partial_match_table_scan=on,"
@@ -7379,7 +7381,7 @@ thread is in the relay logs.",
   {"optimizer_switch", OPT_OPTIMIZER_SWITCH,
    "optimizer_switch=option=val[,option=val...], where option={index_merge, "
    "index_merge_union, index_merge_sort_union, index_merge_intersection, "
-   "index_condition_pushdown, firstmatch, loosescan, materialization, "
+   "index_condition_pushdown, firstmatch, loosescan, materialization, in_to_exists, "
    "semijoin, partial_match_rowid_merge, partial_match_table_scan, "
    "subquery_cache"
 #ifndef DBUG_OFF

=== modified file 'sql/opt_subselect.cc'
--- a/sql/opt_subselect.cc	2010-11-02 18:13:53 +0000
+++ b/sql/opt_subselect.cc	2010-11-16 06:51:38 +0000
@@ -67,6 +67,7 @@ int check_and_do_in_subquery_rewrites(JO
 {
   THD *thd=join->thd;
   st_select_lex *select_lex= join->select_lex;
+  st_select_lex_unit* parent_unit= select_lex->master_unit();
   DBUG_ENTER("check_and_do_in_subquery_rewrites");
   /*
     If 
@@ -84,8 +85,8 @@ int check_and_do_in_subquery_rewrites(JO
     TODO: for PS, make the whole block execute only on the first execution
   */
   Item_subselect *subselect;
-  if (!thd->lex->view_prepare_mode &&                  // (1)
-    (subselect= select_lex->master_unit()->item))      // (2)
+  if (!thd->lex->view_prepare_mode &&            // (1)
+      (subselect= parent_unit->item))            // (2)
   {
     Item_in_subselect *in_subs= NULL;
     if (subselect->substype() == Item_subselect::IN_SUBS)
@@ -129,6 +130,15 @@ int check_and_do_in_subquery_rewrites(JO
       if (failure)
         DBUG_RETURN(-1); /* purecov: deadcode */
     }
+    if (select_lex == parent_unit->fake_select_lex)
+    {
+      /*
+        The join and its select_lex object represent the 'fake' select used
+        to compute the result of a UNION.
+      */
+      DBUG_RETURN(0);
+    }
+
     DBUG_PRINT("info", ("Checking if subq can be converted to semi-join"));
     /*
       Check if we're in subquery that is a candidate for flattening into a
@@ -154,8 +164,8 @@ int check_and_do_in_subquery_rewrites(JO
         !join->having && !select_lex->with_sum_func &&                // 4
         thd->thd_marker.emb_on_expr_nest &&                           // 5
         select_lex->outer_select()->join &&                           // 6
-        select_lex->master_unit()->first_select()->leaf_tables &&     // 7
-        in_subs->exec_method == Item_in_subselect::NOT_TRANSFORMED && // 8
+        parent_unit->first_select()->leaf_tables &&                   // 7
+        !in_subs->in_strategy &&                                      // 8
         select_lex->outer_select()->leaf_tables &&                    // 9
         !((join->select_options |                                     // 10
            select_lex->outer_select()->join->select_options)          // 10
@@ -175,63 +185,82 @@ int check_and_do_in_subquery_rewrites(JO
     else
     {
       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 subq 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 table-less SELECT ... FROM DUAL. We
+      /* Test if the user has set a legal combination of optimizer switches. */
+      if (!optimizer_flag(thd, OPTIMIZER_SWITCH_IN_TO_EXISTS) &&
+          !optimizer_flag(thd, OPTIMIZER_SWITCH_MATERIALIZATION))
+        my_error(ER_ILLEGAL_SUBQUERY_OPTIMIZER_SWITCHES, MYF(0));
+
+      if (in_subs)
+      {
+        /* Subquery predicate is an IN/=ANY predicate. */
+        if (optimizer_flag(thd, OPTIMIZER_SWITCH_IN_TO_EXISTS))
+          in_subs->in_strategy|= SUBS_IN_TO_EXISTS;
+        if (optimizer_flag(thd, OPTIMIZER_SWITCH_MATERIALIZATION))
+          in_subs->in_strategy|= SUBS_MATERIALIZATION;
+
+        /*
+          Check if the subquery predicate can be executed via materialization.
+          The required conditions are:
+          1. Subquery is a single SELECT (not a UNION)
+          2. Subquery is not a table-less query. In this case there is no
+             point in materializing.
+          2A The upper query is not a table-less 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. Either the subquery predicate is a top-level predicate, or at
-           least one partial match strategy is enabled. If no partial match
-           strategy is enabled, then materialization cannot be used for
-           non-top-level queries because it cannot handle NULLs correctly.
-        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).
-
-        (*) The subquery must be part of a SELECT statement. The current
-             condition also excludes multi-table update statements.
-
-        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.
-      */
-      if (optimizer_flag(thd, OPTIMIZER_SWITCH_MATERIALIZATION)  && 
-          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 &&                     // *
-          select_lex->outer_select()->leaf_tables &&                    // 3A
-          subquery_types_allow_materialization(in_subs) &&
-          // psergey-todo: duplicated_subselect_card_check: where it's done?
-          (in_subs->is_top_level_item() ||
-           optimizer_flag(thd, OPTIMIZER_SWITCH_PARTIAL_MATCH_ROWID_MERGE) ||
-           optimizer_flag(thd, OPTIMIZER_SWITCH_PARTIAL_MATCH_TABLE_SCAN)) &&//4
-          !in_subs->is_correlated &&                                  // 5
-          in_subs->exec_method == Item_in_subselect::NOT_TRANSFORMED) // 6
-      {
-          in_subs->exec_method= Item_in_subselect::MATERIALIZATION;
+          3. Either the subquery predicate is a top-level predicate, or at
+             least one partial match strategy is enabled. If no partial match
+             strategy is enabled, then materialization cannot be used for
+             non-top-level queries because it cannot handle NULLs correctly.
+          4. 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"))
+
+          (*) The subquery must be part of a SELECT statement. The current
+               condition also excludes multi-table update statements.
+        */
+        if (!(in_subs->in_strategy & SUBS_MATERIALIZATION && 
+              !select_lex->is_part_of_union() &&                            // 1
+              parent_unit->first_select()->leaf_tables &&                   // 2
+              thd->lex->sql_command == SQLCOM_SELECT &&                     // *
+              select_lex->outer_select()->leaf_tables &&                    // 2A
+              subquery_types_allow_materialization(in_subs) &&
+              // psergey-todo: duplicated_subselect_card_check: where it's done?
+              (in_subs->is_top_level_item() ||                               //3
+               optimizer_flag(thd,
+                              OPTIMIZER_SWITCH_PARTIAL_MATCH_ROWID_MERGE) || //3
+               optimizer_flag(thd,
+                              OPTIMIZER_SWITCH_PARTIAL_MATCH_TABLE_SCAN)) && //3
+              !in_subs->is_correlated))                                      //4
+        {
+          /* Materialization is not possible based on syntactic properties. */
+          in_subs->in_strategy&= ~SUBS_MATERIALIZATION;
+        }
+
+        if (!in_subs->in_strategy)
+        {
+          /*
+            If neither materialization is possible, nor the user chose
+            IN-TO-EXISTS, choose IN-TO-EXISTS as the only universal strategy.
+          */
+          in_subs->in_strategy|= SUBS_IN_TO_EXISTS;
+        }
       }
 
+      /*
+        Transform each subquery predicate according to its overloaded
+        transformer.
+      */
       Item_subselect::trans_res trans_res;
       if ((trans_res= subselect->select_transformer(join)) !=
           Item_subselect::RES_OK)
-      {
         DBUG_RETURN((trans_res == Item_subselect::RES_ERROR));
-      }
     }
   }
   DBUG_RETURN(0);
@@ -509,6 +538,15 @@ skip_conversion:
                                      FALSE))
         DBUG_RETURN(TRUE);
     }
+    /*
+      Revert to the IN->EXISTS strategy in the rare case when the subquery could
+      not be flattened.
+      TODO: This is a limitation done for simplicity. Such subqueries could also
+      be executed via materialization. In order to determine this, we should
+      re-run the test for materialization that was done in
+      check_and_do_in_subquery_rewrites.
+    */
+    (*in_subq)->in_strategy= SUBS_IN_TO_EXISTS;
   }
 
   if (arena)
@@ -769,8 +807,7 @@ static bool convert_subq_to_sj(JOIN *par
   /* 3. Remove the original subquery predicate from the WHERE/ON */
 
   // The subqueries were replaced for Item_int(1) earlier
-  subq_pred->exec_method=
-    Item_in_subselect::SEMI_JOIN;         // for subsequent executions
+  subq_pred->in_strategy= SUBS_SEMI_JOIN;         // for subsequent executions
   /*TODO: also reset the 'with_subselect' there. */
 
   /* n. Adjust the parent_join->tables counter */
@@ -1167,8 +1204,8 @@ bool optimize_semijoin_nests(JOIN *join,
         sjm->tables= n_tables;
         sjm->is_used= FALSE;
         double subjoin_out_rows, subjoin_read_time;
-        get_partial_join_cost(join, n_tables,
-                              &subjoin_read_time, &subjoin_out_rows);
+        join->get_partial_join_cost(n_tables + join->const_tables,
+                                    &subjoin_read_time, &subjoin_out_rows);
 
         sjm->materialization_cost.convert_from_cost(subjoin_read_time);
         sjm->rows= subjoin_out_rows;
@@ -3359,6 +3397,19 @@ int rewrite_to_index_subquery_engine(JOI
   /*
     is this simple IN subquery?
   */
+  /* TODO: In order to use these more efficient subquery engines in more cases,
+     the following problems need to be solved:
+     - the code that removes GROUP BY (group_list), also adds an ORDER BY
+       (order), thus GROUP BY queries (almost?) never pass through this branch.
+       Solution: remove the test below '!join->order', because we remove the
+       ORDER clase for subqueries anyway.
+     - in order to set a more efficient engine, the optimizer needs to both
+       decide to remove GROUP BY, *and* select one of the JT_[EQ_]REF[_OR_NULL]
+       access methods, *and* loose scan should be more expensive or
+       inapliccable. When is that possible?
+     - Consider expanding the applicability of this rewrite for loose scan
+       for group by queries.
+  */
   if (!join->group_list && !join->order &&
       join->unit->item && 
       join->unit->item->substype() == Item_subselect::IN_SUBS &&
@@ -3499,3 +3550,332 @@ static void remove_subq_pushed_predicate
 }
 
 
+/**
+  Optimize all subqueries of a query that have were flattened into a semijoin.
+
+  @details
+  Optimize all immediate children subqueries of a query.
+
+  This phase must be called after substitute_for_best_equal_field() because
+  that function may replace items with other items from a multiple equality,
+  and we need to reference the correct items in the index access method of the
+  IN predicate.
+
+  @return Operation status
+  @retval FALSE     success.
+  @retval TRUE      error occurred.
+*/
+
+bool JOIN::optimize_unflattened_subqueries()
+{
+  return select_lex->optimize_unflattened_subqueries();
+}
+
+
+/**
+  Choose an optimal strategy to execute an IN/ALL/ANY subquery predicate
+  based on cost.
+
+  @param join_tables  the set of tables joined in the subquery
+
+  @notes
+  The method chooses between the materialization and IN=>EXISTS rewrite
+  strategies for the execution of a non-flattened subquery IN predicate.
+  The cost-based decision is made as follows:
+
+  1. compute materialize_strategy_cost based on the unmodified subquery
+  2. reoptimize the subquery taking into account the IN-EXISTS predicates
+  3. compute in_exists_strategy_cost based on the reoptimized plan
+  4. compare and set the cheaper strategy
+     if (materialize_strategy_cost >= in_exists_strategy_cost)
+       in_strategy = MATERIALIZATION
+     else
+       in_strategy = IN_TO_EXISTS
+  5. if in_strategy = MATERIALIZATION and it is not possible to initialize it
+       revert to IN_TO_EXISTS
+  6. if (in_strategy == MATERIALIZATION)
+       revert the subquery plan to the original one before reoptimizing
+     else
+       inject the IN=>EXISTS predicates into the new EXISTS subquery plan
+
+  The implementation itself is a bit more complicated because it takes into
+  account two more factors:
+  - whether the user allowed both strategies through an optimizer_switch, and
+  - if materialization was the cheaper strategy, whether it can be executed
+    or not.
+
+  @retval FALSE     success.
+  @retval TRUE      error occurred.
+*/
+
+bool JOIN::choose_subquery_plan(table_map join_tables)
+{  /* The original QEP of the subquery. */
+  DYNAMIC_ARRAY save_keyuse; /* Copy of the JOIN::keyuse array. */
+  POSITION save_best_positions[MAX_TABLES+1]; /* Copy of JOIN::best_positions */
+  /* Copies of the JOIN_TAB::keyuse pointers for each JOIN_TAB. */
+  KEYUSE *save_join_tab_keyuse[MAX_TABLES];
+  /* Copies of JOIN_TAB::checked_keys for each JOIN_TAB. */
+  key_map save_join_tab_checked_keys[MAX_TABLES];
+  enum_reopt_result reopt_result= REOPT_NONE;
+  Item_in_subselect *in_subs;
+
+  if (select_lex->master_unit()->item &&
+      select_lex->master_unit()->item->is_in_predicate())
+  {
+    in_subs= (Item_in_subselect*) select_lex->master_unit()->item;
+    if (in_subs->create_in_to_exists_cond(this))
+      return true;
+  }
+  else
+    return false;
+
+  DBUG_ASSERT(in_subs->in_strategy); /* A strategy must be chosen earlier. */
+  DBUG_ASSERT(in_to_exists_where || in_to_exists_having);
+  DBUG_ASSERT(!in_to_exists_where || in_to_exists_where->fixed);
+  DBUG_ASSERT(!in_to_exists_having || in_to_exists_having->fixed);
+
+  save_keyuse.elements= 0;
+  save_keyuse.buffer= NULL;
+
+  /*
+    Compute and compare the costs of materialization and in-exists if both
+    strategies are possible and allowed by the user (checked during the prepare
+    phase.
+  */
+  if (in_subs->in_strategy & SUBS_MATERIALIZATION &&
+      in_subs->in_strategy & SUBS_IN_TO_EXISTS)
+  {
+    JOIN *outer_join= unit->outer_select() ? unit->outer_select()->join : NULL;
+    JOIN *inner_join= this;
+    /* Cost of the outer JOIN. */
+    double outer_read_time, outer_record_count;
+    /* Cost of the unmodified subquery. */
+    double inner_read_time_1, inner_record_count_1;
+    /* Cost of the subquery with injected IN-EXISTS predicates. */
+    double inner_read_time_2, inner_record_count_2;
+    /* The cost to compute IN via materialization. */
+    double materialize_strategy_cost;
+    /* The cost of the IN->EXISTS strategy. */
+    double in_exists_strategy_cost;
+
+    if (outer_join)
+      outer_join->get_partial_join_cost(outer_join->tables,
+                                        &outer_read_time, &outer_record_count);
+    else
+    {
+      /*
+        TODO: outer_join can be NULL for DELETE statements.
+        How to compute its cost?
+      */
+      outer_read_time= 1; /* TODO */
+      outer_record_count= 1;  /* TODO */
+    }
+
+    inner_join->get_partial_join_cost(inner_join->tables,
+                                      &inner_read_time_1, &inner_record_count_1);
+
+    if (in_to_exists_where && const_tables != tables)
+    {
+      /*
+        Re-optimize and cost the subquery taking into account the IN-EXISTS
+        conditions.
+      */
+      if (save_query_plan(&save_keyuse, save_best_positions,
+                          save_join_tab_keyuse, save_join_tab_checked_keys))
+        return TRUE;
+      reopt_result= reoptimize(in_to_exists_where, join_tables);
+      if (reopt_result == REOPT_OLD_PLAN)
+        restore_query_plan(&save_keyuse, save_best_positions,
+                           save_join_tab_keyuse, save_join_tab_checked_keys);
+      else if (reopt_result == REOPT_ERROR)
+        return TRUE;
+
+      inner_join->get_partial_join_cost(inner_join->tables,
+                                        &inner_read_time_2, &inner_record_count_2);
+    }
+    else
+    {
+      /* Reoptimization would not produce any better plan. */
+      inner_read_time_2= inner_read_time_1;
+      inner_record_count_2= inner_record_count_1;
+    }
+
+    /* Compute execution costs. */
+    /*
+      1. Compute the cost of the materialization strategy.
+    */
+    double materialization_cost; /* The cost of executing the subquery and */
+                                 /* storing its result in an indexed temp table.*/
+    /* The cost of a lookup into the unique index of the materialized table. */
+    double lookup_cost;
+    double write_row_cost= 1; /* TODO: what is the real cost to write a row? */
+    materialization_cost= inner_read_time_1 +
+                          inner_record_count_1 * write_row_cost;
+    /*
+      The cost of a hash/btree lookup into a unique index of a materialized
+      subquery.
+      TIMOUR: TODO: the block of code below is exact copy/paste from
+      opt_subselect.cc:optimize_semi_join_nests() - refactor it.
+    */
+    uint rowlen= get_tmp_table_rec_length(unit->first_select()->item_list);
+    if (rowlen * inner_record_count_1 < thd->variables.max_heap_table_size)
+      lookup_cost= HEAP_TEMPTABLE_LOOKUP_COST;
+    else
+      lookup_cost= DISK_TEMPTABLE_LOOKUP_COST;
+    materialize_strategy_cost= materialization_cost +
+                               outer_record_count * lookup_cost;
+
+    /*
+      2. Compute the cost of the IN=>EXISTS strategy.
+    */
+    in_exists_strategy_cost= outer_record_count * inner_read_time_2;
+
+    /* Compare the costs and choose the cheaper strategy. */
+    if (materialize_strategy_cost >= in_exists_strategy_cost)
+      in_subs->in_strategy&= ~SUBS_MATERIALIZATION;
+    else
+      in_subs->in_strategy&= ~SUBS_IN_TO_EXISTS;
+  }
+
+  /*
+    If (1) materialization is a possible strategy based on semantic analysis
+    during the prepare phase, then if
+      (2) it is more expensive than the IN->EXISTS transformation, and
+      (3) it is not possible to create usable indexes for the materialization
+          strategy,
+      fall back to IN->EXISTS.
+    otherwise
+      use materialization.
+  */
+  if (in_subs->in_strategy & SUBS_MATERIALIZATION &&
+      in_subs->setup_mat_engine())
+  {
+    /*
+      If materialization was the cheaper or the only user-selected strategy,
+      but it is not possible to execute it due to limitations in the
+      implementation, fall back to IN-TO-EXISTS.
+    */
+    in_subs->in_strategy&= ~SUBS_MATERIALIZATION;
+    in_subs->in_strategy|= SUBS_IN_TO_EXISTS;
+  }
+
+  if (in_subs->in_strategy & SUBS_MATERIALIZATION)
+  {
+    /* Restore the original query plan used for materialization. */
+    if (reopt_result == REOPT_NEW_PLAN)
+      restore_query_plan(&save_keyuse, save_best_positions,
+                         save_join_tab_keyuse, save_join_tab_checked_keys);
+
+    /* TODO: should we set/unset this flag for both select_lex and its unit? */
+    in_subs->unit->uncacheable&= ~UNCACHEABLE_DEPENDENT;
+    select_lex->uncacheable&= ~UNCACHEABLE_DEPENDENT;
+
+    /*
+      Reset the "LIMIT 1" set in Item_exists_subselect::fix_length_and_dec.
+      TODO:
+      Currently we set the subquery LIMIT to infinity, and this is correct
+      because we forbid at parse time LIMIT inside IN subqueries (see
+      Item_in_subselect::test_limit). However, once we allow this, here
+      we should set the correct limit if given in the query.
+    */
+    in_subs->unit->global_parameters->select_limit= NULL;
+    in_subs->unit->set_limit(unit->global_parameters);
+    /*
+      Set the limit of this JOIN object as well, because normally its being
+      set in the beginning of JOIN::optimize, which was already done.
+    */
+    select_limit= in_subs->unit->select_limit_cnt;
+  }
+  else if (in_subs->in_strategy & SUBS_IN_TO_EXISTS)
+  {
+    /* Keep the new query plan with injected conditions, delete the old plan. */
+    if (reopt_result == REOPT_NEW_PLAN)
+      delete_dynamic(&save_keyuse);
+
+    if (reopt_result == REOPT_NONE && in_to_exists_where && const_tables != tables)
+    {
+      /*
+        The subquery was not reoptimized either because the user allowed only the
+        IN-EXISTS strategy, or because materialization was not possible based on
+        semantic analysis. Clenup the original plan and reoptimize.
+      */
+      for (uint i= 0; i < tables; i++)
+      {
+        join_tab[i].keyuse= NULL;
+        join_tab[i].checked_keys.clear_all();
+      }
+      if ((reopt_result= reoptimize(in_to_exists_where, join_tables)) ==
+          REOPT_ERROR)
+        return TRUE;
+    }
+
+    if (in_subs->inject_in_to_exists_cond(this))
+      return TRUE;
+  }
+  else
+    DBUG_ASSERT(FALSE);
+
+  return FALSE;
+}
+
+
+/**
+  Choose a query plan for a table-less subquery.
+
+  @notes
+
+  @retval FALSE     success.
+  @retval TRUE      error occurred.
+*/
+
+bool JOIN::choose_tableless_subquery_plan()
+{
+  DBUG_ASSERT(!tables_list || !tables);
+  if (select_lex->master_unit()->item)
+  {
+    DBUG_ASSERT(select_lex->master_unit()->item->type() ==
+                Item::SUBSELECT_ITEM);
+    Item_subselect *subs_predicate= select_lex->master_unit()->item;
+
+    /*
+      If the optimizer determined that his query has an empty result,
+      in most cases the subquery predicate is a known constant value -
+      either FALSE or NULL. The implementation of Item_subselect::reset()
+      determines which one.
+    */
+    if (zero_result_cause)
+    {
+      if (!implicit_grouping)
+      {
+        /*
+          Both group by queries and non-group by queries without aggregate
+          functions produce empty subquery result.
+        */
+        subs_predicate->reset();
+        subs_predicate->make_const();
+        return FALSE;
+      }
+
+      /* TODO:
+         A further optimization is possible when a non-group query with
+         MIN/MAX/COUNT is optimized by opt_sum_query. Then, if there are
+         only MIN/MAX functions over an empty result set, the subquery
+         result is a NULL value/row, thus the value of subs_predicate is
+         NULL.
+      */
+    }
+
+    if (subs_predicate->is_in_predicate())
+    {
+      Item_in_subselect *in_subs;
+      in_subs= (Item_in_subselect*) subs_predicate;
+      in_subs->in_strategy= SUBS_IN_TO_EXISTS;
+      if (in_subs->create_in_to_exists_cond(this) ||
+          in_subs->inject_in_to_exists_cond(this))
+        return TRUE;
+      tmp_having= having;
+    }
+  }
+  return FALSE;
+}
+

=== modified file 'sql/share/errmsg.txt'
--- a/sql/share/errmsg.txt	2010-09-12 12:29:54 +0000
+++ b/sql/share/errmsg.txt	2010-11-05 12:42:58 +0000
@@ -6247,3 +6247,5 @@ ER_UNKNOWN_OPTION
         eng "Unknown option '%-.64s'"
 ER_BAD_OPTION_VALUE
         eng "Incorrect value '%-.64s' for option '%-.64s'"
+ER_ILLEGAL_SUBQUERY_OPTIMIZER_SWITCHES
+        eng "At least one of the 'in_to_exists' or 'materialization' optimizer_switch flags must be 'on'."

=== modified file 'sql/sql_class.cc'
--- a/sql/sql_class.cc	2010-11-05 10:37:51 +0000
+++ b/sql/sql_class.cc	2010-11-16 06:51:38 +0000
@@ -2656,6 +2656,7 @@ void Query_arena::free_items()
   for (; free_list; free_list= next)
   {
     next= free_list->next;
+    DBUG_ASSERT(free_list != next);
     free_list->delete_self();
   }
   /* Postcondition: free_list is 0 */
@@ -3092,6 +3093,7 @@ void TMP_TABLE_PARAM::init()
   table_charset= 0;
   precomputed_group_by= 0;
   bit_fields_as_long= 0;
+  materialized_subquery= 0;
   skip_create_table= 0;
   DBUG_VOID_RETURN;
 }

=== modified file 'sql/sql_class.h'
--- a/sql/sql_class.h	2010-11-05 10:37:51 +0000
+++ b/sql/sql_class.h	2010-11-16 06:51:38 +0000
@@ -2933,6 +2933,8 @@ public:
   uint  convert_blob_length;
   CHARSET_INFO *table_charset;
   bool schema_table;
+  /* TRUE if the temp table is created for subquery materialization. */
+  bool materialized_subquery;
   /*
     True if GROUP BY and its aggregate functions are already computed
     by a table access method (e.g. by loose index scan). In this case
@@ -2956,8 +2958,8 @@ public:
   TMP_TABLE_PARAM()
     :copy_field(0), group_parts(0),
      group_length(0), group_null_parts(0), convert_blob_length(0),
-     schema_table(0), precomputed_group_by(0), force_copy_fields(0),
-     bit_fields_as_long(0), skip_create_table(0)
+    schema_table(0), materialized_subquery(0), precomputed_group_by(0),
+    force_copy_fields(0), bit_fields_as_long(0), skip_create_table(0)
   {}
   ~TMP_TABLE_PARAM()
   {
@@ -2990,6 +2992,7 @@ public:
   virtual bool create_result_table(THD *thd, List<Item> *column_types,
                                    bool is_distinct, ulonglong options,
                                    const char *alias, bool bit_fields_as_long);
+  TMP_TABLE_PARAM *get_tmp_table_param() { return &tmp_table_param; }
 };
 
 /* Base subselect interface class */
@@ -3053,7 +3056,7 @@ protected:
   void reset();
 
 public:
-  select_materialize_with_stats() {}
+  select_materialize_with_stats() { tmp_table_param.init(); }
   virtual bool create_result_table(THD *thd, List<Item> *column_types,
                                    bool is_distinct, ulonglong options,
                                    const char *alias, bool bit_fields_as_long);

=== modified file 'sql/sql_delete.cc'
--- a/sql/sql_delete.cc	2010-10-01 15:27:32 +0000
+++ b/sql/sql_delete.cc	2010-11-05 12:42:58 +0000
@@ -92,6 +92,10 @@ bool mysql_delete(THD *thd, TABLE_LIST *
     }
   }
 
+  /* Apply the IN=>EXISTS transformation to all subqueries and optimize them. */
+  if (select_lex->optimize_unflattened_subqueries())
+    DBUG_RETURN(TRUE);
+
   const_cond= (!conds || conds->const_item());
   safe_update=test(thd->options & OPTION_SAFE_UPDATES);
   if (safe_update && const_cond)

=== modified file 'sql/sql_lex.cc'
--- a/sql/sql_lex.cc	2010-10-10 14:18:11 +0000
+++ b/sql/sql_lex.cc	2010-11-05 12:42:58 +0000
@@ -1735,17 +1735,29 @@ void st_select_lex_node::fast_exclude()
   
 }
 
+
+/*
+  Exclude a node from the tree lex structure, but leave it in the global
+  list of nodes.
+*/
+
+void st_select_lex_node::exclude_from_tree()
+{
+  if ((*prev= next))
+    next->prev= prev;
+}
+
+
 /*
-  excluding select_lex structure (except first (first select can't be
+  Exclude select_lex structure (except first (first select can't be
   deleted, because it is most upper select))
 */
 void st_select_lex_node::exclude()
 {
-  //exclude from global list
+  /* exclude from global list */
   fast_exclude();
-  //exclude from other structures
-  if ((*prev= next))
-    next->prev= prev;
+  /* exclude from other structures */
+  exclude_from_tree();
   /* 
      We do not need following statements, because prev pointer of first 
      list element point to master->slave
@@ -2145,8 +2157,8 @@ void st_select_lex::print_limit(THD *thd
                     select_limit == 1, and there should be no offset_limit.
                   */
                   (((subs_type == Item_subselect::IN_SUBS) &&
-                    ((Item_in_subselect*)item)->exec_method ==
-                    Item_in_subselect::MATERIALIZATION) ?
+                    ((Item_in_subselect*)item)->in_strategy &
+                    SUBS_MATERIALIZATION) ?
                    TRUE :
                    (select_limit->val_int() == 1LL) &&
                    offset_limit == 0));
@@ -3076,6 +3088,70 @@ bool st_select_lex::add_index_hint (THD 
                                             str, length));
 }
 
+
+bool st_select_lex::optimize_unflattened_subqueries()
+{
+  for (SELECT_LEX_UNIT *un= first_inner_unit(); un; un= un->next_unit())
+  {
+    Item_subselect *subquery_predicate= un->item;
+    if (subquery_predicate)
+    {
+      for (SELECT_LEX *sl= un->first_select(); sl; sl= sl->next_select())
+      {
+        JOIN *inner_join= sl->join;
+        SELECT_LEX *save_select= un->thd->lex->current_select;
+        ulonglong save_options;
+        int res;
+        /* We need only 1 row to determine existence */
+        un->set_limit(un->global_parameters);
+        un->thd->lex->current_select= sl;
+        save_options= inner_join->select_options;
+        if (un->outer_select()->options & SELECT_DESCRIBE)
+        {
+          /* Optimize the subquery in the context of EXPLAIN. */
+          set_explain_type();
+          inner_join->select_options= options;
+        }
+        res= inner_join->optimize();
+        inner_join->select_options= save_options;
+        un->thd->lex->current_select= save_select;
+        if (res)
+          return TRUE;
+      }
+    }
+  }
+  return FALSE;
+}
+
+
+/**
+  Set the EXPLAIN type for this subquery.
+*/
+
+void st_select_lex::set_explain_type()
+{
+  SELECT_LEX *first= master_unit()->first_select();
+  /* drop UNCACHEABLE_EXPLAIN, because it is for internal usage only */
+  uint8 is_uncacheable= (uncacheable & ~UNCACHEABLE_EXPLAIN);
+
+  type= ((&master_unit()->thd->lex->select_lex == this) ?
+         (first_inner_unit() || next_select() ?
+          "PRIMARY" : "SIMPLE") :
+         ((this == first) ?
+          ((linkage == DERIVED_TABLE_TYPE) ?
+           "DERIVED" :
+           ((is_uncacheable & UNCACHEABLE_DEPENDENT) ?
+            "DEPENDENT SUBQUERY" :
+            (is_uncacheable ? "UNCACHEABLE SUBQUERY" :
+             "SUBQUERY"))) :
+          ((is_uncacheable & UNCACHEABLE_DEPENDENT) ?
+           "DEPENDENT UNION":
+           is_uncacheable ? "UNCACHEABLE UNION":
+           "UNION")));
+  options|= SELECT_DESCRIBE;
+}
+
+
 /**
   A routine used by the parser to decide whether we are specifying a full
   partitioning or if only partitions to add or to split.

=== modified file 'sql/sql_lex.h'
--- a/sql/sql_lex.h	2010-10-28 17:04:23 +0000
+++ b/sql/sql_lex.h	2010-11-05 12:42:58 +0000
@@ -439,6 +439,7 @@ public:
   st_select_lex_node(): linkage(UNSPECIFIED_TYPE) {}
   virtual ~st_select_lex_node() {}
   inline st_select_lex_node* get_master() { return master; }
+  inline void set_master(st_select_lex_node* master_arg) { master= master_arg; }
   virtual void init_query();
   virtual void init_select();
   void include_down(st_select_lex_node *upper);
@@ -446,6 +447,7 @@ public:
   void include_standalone(st_select_lex_node *sel, st_select_lex_node **ref);
   void include_global(st_select_lex_node **plink);
   void exclude();
+  void exclude_from_tree();
 
   virtual st_select_lex_unit* master_unit()= 0;
   virtual st_select_lex* outer_select()= 0;
@@ -846,6 +848,15 @@ public:
 
   void clear_index_hints(void) { index_hints= NULL; }
   bool is_part_of_union() { return master_unit()->is_union(); }
+  /*
+    Optimize all subqueries that have not been flattened into semi-joins.
+    This functionality is a method of SELECT_LEX instead of JOIN because
+    some SQL statements as DELETE do not have a corresponding JOIN object.
+  */
+  bool optimize_unflattened_subqueries();
+  /* Set the EXPLAIN type for this subquery. */
+  void set_explain_type();
+
 private:  
   /* current index hint kind. used in filling up index_hints */
   enum index_hint_type current_index_hint_type;

=== modified file 'sql/sql_select.cc'
--- a/sql/sql_select.cc	2010-11-11 12:35:26 +0000
+++ b/sql/sql_select.cc	2010-11-16 06:51:38 +0000
@@ -59,9 +59,9 @@ static bool make_join_statistics(JOIN *j
 static bool update_ref_and_keys(THD *thd, DYNAMIC_ARRAY *keyuse,
                                 JOIN_TAB *join_tab,
                                 uint tables, COND *conds,
-                                COND_EQUAL *cond_equal,
                                 table_map table_map, SELECT_LEX *select_lex,
                                 st_sargable_param **sargables);
+static bool sort_and_filter_keyuse(DYNAMIC_ARRAY *keyuse);
 static int sort_keyuse(KEYUSE *a,KEYUSE *b);
 static bool create_ref_for_key(JOIN *join, JOIN_TAB *j, KEYUSE *org_keyuse,
                                table_map used_tables);
@@ -178,13 +178,10 @@ static int join_ft_read_next(READ_RECORD
 int join_read_always_key_or_null(JOIN_TAB *tab);
 int join_read_next_same_or_null(READ_RECORD *info);
 static COND *make_cond_for_table(Item *cond,table_map table,
-                                 table_map used_table,
-                                 bool exclude_expensive_cond);
+                                 table_map used_table);
 static COND *make_cond_for_table_from_pred(Item *root_cond, Item *cond,
                                            table_map tables,
-                                           table_map used_table,
-                                           bool exclude_expensive_cond);
-
+                                           table_map used_table);
 static Item* part_of_refkey(TABLE *form,Field *field);
 uint find_shortest_key(TABLE *table, const key_map *usable_keys);
 static bool test_if_skip_sort_order(JOIN_TAB *tab,ORDER *order,
@@ -236,8 +233,6 @@ static bool update_sum_func(Item_sum **f
 static void select_describe(JOIN *join, bool need_tmp_table,bool need_order,
                             bool distinct, const char *message=NullS);
 static void add_group_and_distinct_keys(JOIN *join, JOIN_TAB *join_tab);
-void get_partial_join_cost(JOIN *join, uint idx, double *read_time_arg,
-                           double *record_count_arg);
 static uint make_join_orderinfo(JOIN *join);
 static int
 join_read_record_no_init(JOIN_TAB *tab);
@@ -843,6 +838,7 @@ JOIN::optimize()
                            "Impossible HAVING" : "Impossible WHERE";
       tables= 0;
       error= 0;
+      choose_tableless_subquery_plan();
       goto setup_subq_exit;
     }
   }
@@ -887,12 +883,13 @@ JOIN::optimize()
     */
     if ((res=opt_sum_query(select_lex->leaf_tables, all_fields, conds)))
     {
-      if (res == HA_ERR_KEY_NOT_FOUND)
+      if (res == HA_ERR_KEY_NOT_FOUND || res < 0)
       {
         DBUG_PRINT("info",("No matching min/max row"));
         zero_result_cause= "No matching min/max row";
         tables= 0;
         error=0;
+        choose_tableless_subquery_plan();
         goto setup_subq_exit;
       }
       if (res > 1)
@@ -901,14 +898,7 @@ JOIN::optimize()
         DBUG_PRINT("error",("Error from opt_sum_query"));
         DBUG_RETURN(1);
       }
-      if (res < 0)
-      {
-        DBUG_PRINT("info",("No matching min/max row"));
-        zero_result_cause= "No matching min/max row";
-        tables= 0;
-        error=0;
-        goto setup_subq_exit;
-      }
+
       DBUG_PRINT("info",("Select tables optimized away"));
       zero_result_cause= "Select tables optimized away";
       tables_list= 0;                           // All tables resolved
@@ -926,24 +916,21 @@ JOIN::optimize()
       if (conds && !(thd->lex->describe & DESCRIBE_EXTENDED))
       {
         COND *table_independent_conds=
-          make_cond_for_table(conds, PSEUDO_TABLE_BITS, 0, FALSE);
+          make_cond_for_table(conds, PSEUDO_TABLE_BITS, 0);
         DBUG_EXECUTE("where",
                      print_where(table_independent_conds,
                                  "where after opt_sum_query()",
                                  QT_ORDINARY););
         conds= table_independent_conds;
       }
-      goto setup_subq_exit;
     }
   }
   if (!tables_list)
   {
     DBUG_PRINT("info",("No tables"));
     error= 0;
-    /* Create all structures needed for materialized subquery execution. */
-    if (setup_subquery_materialization())
-      DBUG_RETURN(1);
-    DBUG_RETURN(0);
+    choose_tableless_subquery_plan();
+    goto setup_subq_exit;
   }
   error= -1;                                    // Error is sent to client
   sort_by_table= get_sort_by_table(order, group_list, select_lex->leaf_tables);
@@ -1300,8 +1287,7 @@ JOIN::optimize()
   if (!(select_options & SELECT_DESCRIBE))
     init_ftfuncs(thd, select_lex, test(order));
 
-  /* Create all structures needed for materialized subquery execution. */
-  if (setup_subquery_materialization())
+  if (optimize_unflattened_subqueries())
     DBUG_RETURN(1);
   
   int res;
@@ -1396,6 +1382,32 @@ JOIN::optimize()
   if (join_tab->is_using_loose_index_scan())
     tmp_table_param.precomputed_group_by= TRUE;
 
+  error= 0;
+  DBUG_RETURN(0);
+
+setup_subq_exit:
+  /*
+    Even with zero matching rows, subqueries in the HAVING clause may
+    need to be evaluated if there are aggregate functions in the query.
+  */
+  if (optimize_unflattened_subqueries())
+    DBUG_RETURN(1);
+  error= 0;
+  DBUG_RETURN(0);
+}
+
+
+/**
+  Create and initialize objects neeed for the execution of a query plan.
+*/
+
+int JOIN::init_execution()
+{
+  DBUG_ENTER("JOIN::init_execution");
+
+  DBUG_ASSERT(optimized);
+  initialized= true;
+
   /* Create a tmp table if distinct or if the sort is too complicated */
   if (need_tmp)
   {
@@ -1428,7 +1440,7 @@ JOIN::optimize()
                            select_options,
                            tmp_rows_limit,
                            (char *) "")))
-                {
+    {
       DBUG_RETURN(1);
     }
 
@@ -1514,19 +1526,6 @@ JOIN::optimize()
       DBUG_RETURN(-1);                         /* purecov: inspected */
   }
 
-  error= 0;
-  DBUG_RETURN(0);
-
-setup_subq_exit:
-  /*
-    Even with zero matching rows, subqueries in the HAVING clause may
-    need to be evaluated if there are aggregate functions in the
-    query. If we have planned to materialize the subquery, we need to
-    set it up properly before prematurely leaving optimize().
-  */
-  if (setup_subquery_materialization())
-    DBUG_RETURN(1);
-  error= 0;
   DBUG_RETURN(0);
 }
 
@@ -1745,6 +1744,9 @@ JOIN::exec()
   int      tmp_error;
   DBUG_ENTER("JOIN::exec");
 
+  if (!initialized && init_execution())
+    DBUG_VOID_RETURN;
+
   thd_proc_info(thd, "executing");
   error= 0;
   if (procedure)
@@ -1816,6 +1818,9 @@ JOIN::exec()
   if (tables)
     thd->limit_found_rows= 0;
 
+  if (exec_const_cond && !exec_const_cond->val_int())
+    zero_result_cause= "Impossible WHERE noticed after reading const tables";
+
   if (zero_result_cause)
   {
     (void) return_zero_rows(this, result, select_lex->leaf_tables,
@@ -2197,7 +2202,7 @@ JOIN::exec()
 
       Item* sort_table_cond= make_cond_for_table(curr_join->tmp_having,
                                                  used_tables,
-                                                 (table_map)0, FALSE);
+                                                 (table_map)0);
       if (sort_table_cond)
       {
         if (!curr_table->select)
@@ -2228,7 +2233,7 @@ JOIN::exec()
                                          QT_ORDINARY););
         curr_join->tmp_having= make_cond_for_table(curr_join->tmp_having,
                                                    ~ (table_map) 0,
-                                                   ~used_tables, FALSE);
+                                                   ~used_tables);
         DBUG_EXECUTE("where",print_where(curr_join->tmp_having,
                                          "having after sort",
                                          QT_ORDINARY););
@@ -2569,51 +2574,6 @@ err:
 }
 
 
-/**
-  Setup for execution all subqueries of a query, for which the optimizer
-  chose hash semi-join.
-
-  @details Iterate over all subqueries of the query, and if they are under an
-  IN predicate, and the optimizer chose to compute it via hash semi-join:
-  - try to initialize all data structures needed for the materialized execution
-    of the IN predicate,
-  - if this fails, then perform the IN=>EXISTS transformation which was
-    previously blocked during JOIN::prepare.
-
-  This method is part of the "code generation" query processing phase.
-
-  This phase must be called after substitute_for_best_equal_field() because
-  that function may replace items with other items from a multiple equality,
-  and we need to reference the correct items in the index access method of the
-  IN predicate.
-
-  @return Operation status
-  @retval FALSE     success.
-  @retval TRUE      error occurred.
-*/
-
-bool JOIN::setup_subquery_materialization()
-{
-  for (SELECT_LEX_UNIT *un= select_lex->first_inner_unit(); un;
-       un= un->next_unit())
-  {
-    for (SELECT_LEX *sl= un->first_select(); sl; sl= sl->next_select())
-    {
-      Item_subselect *subquery_predicate= sl->master_unit()->item;
-      if (subquery_predicate &&
-          subquery_predicate->substype() == Item_subselect::IN_SUBS)
-      {
-        Item_in_subselect *in_subs= (Item_in_subselect*) subquery_predicate;
-        if (in_subs->exec_method == Item_in_subselect::MATERIALIZATION &&
-            in_subs->setup_engine())
-          return TRUE;
-      }
-    }
-  }
-  return FALSE;
-}
-
-
 /*****************************************************************************
   Create JOIN_TABS, make a guess about the table types,
   Approximate how many records will be used in each table
@@ -2838,10 +2798,14 @@ make_join_statistics(JOIN *join, TABLE_L
   }
 
   if (conds || outer_join)
+  {
     if (update_ref_and_keys(join->thd, keyuse_array, stat, join->tables,
-                            conds, join->cond_equal,
-                            ~outer_join, join->select_lex, &sargables))
+                            conds, ~outer_join, join->select_lex, &sargables))
       goto error;
+    if (keyuse_array->elements && sort_and_filter_keyuse(keyuse_array))
+      goto error;
+    DBUG_EXECUTE("opt", print_keyuse_array(keyuse_array););
+  }
 
   join->const_table_map= no_rows_const_tables;
   join->const_tables= const_count;
@@ -3144,6 +3108,9 @@ make_join_statistics(JOIN *join, TABLE_L
            sizeof(POSITION)*join->const_tables);
     join->best_read=1.0;
   }
+  if (join->choose_subquery_plan(all_table_map & ~join->const_table_map))
+    goto error;
+
   /* Generate an execution plan from the found optimal join order. */
   DBUG_RETURN(join->thd->killed || get_best_combination(join));
 
@@ -4094,11 +4061,10 @@ static void add_key_fields_for_nj(JOIN *
 
 static bool
 update_ref_and_keys(THD *thd, DYNAMIC_ARRAY *keyuse,JOIN_TAB *join_tab,
-                    uint tables, COND *cond, COND_EQUAL *cond_equal,
-                    table_map normal_tables, SELECT_LEX *select_lex,
-                    SARGABLE_PARAM **sargables)
+                    uint tables, COND *cond, table_map normal_tables,
+                    SELECT_LEX *select_lex, SARGABLE_PARAM **sargables)
 {
-  uint  and_level,i,found_eq_constant;
+  uint  and_level,i;
   KEY_FIELD *key_fields, *end, *field;
   uint sz;
   uint m= max(select_lex->max_equal_elems,1);
@@ -4194,64 +4160,72 @@ update_ref_and_keys(THD *thd, DYNAMIC_AR
       return TRUE;
   }
 
-  /*
-    Sort the array of possible keys and remove the following key parts:
-    - ref if there is a keypart which is a ref and a const.
-      (e.g. if there is a key(a,b) and the clause is a=3 and b=7 and b=t2.d,
-      then we skip the key part corresponding to b=t2.d)
-    - keyparts without previous keyparts
-      (e.g. if there is a key(a,b,c) but only b < 5 (or a=2 and c < 3) is
-      used in the query, we drop the partial key parts from consideration).
-    Special treatment for ft-keys.
-  */
-  if (keyuse->elements)
-  {
-    KEYUSE key_end,*prev,*save_pos,*use;
+  return FALSE;
+}
 
-    my_qsort(keyuse->buffer,keyuse->elements,sizeof(KEYUSE),
-          (qsort_cmp) sort_keyuse);
 
-    bzero((char*) &key_end,sizeof(key_end));    /* Add for easy testing */
-    if (insert_dynamic(keyuse,(uchar*) &key_end))
-      return TRUE;
+/**
+  Sort the array of possible keys and remove the following key parts:
+  - ref if there is a keypart which is a ref and a const.
+    (e.g. if there is a key(a,b) and the clause is a=3 and b=7 and b=t2.d,
+    then we skip the key part corresponding to b=t2.d)
+  - keyparts without previous keyparts
+    (e.g. if there is a key(a,b,c) but only b < 5 (or a=2 and c < 3) is
+    used in the query, we drop the partial key parts from consideration).
+  Special treatment for ft-keys.
+*/
+
+static bool sort_and_filter_keyuse(DYNAMIC_ARRAY *keyuse)
+{
+  KEYUSE key_end, *prev, *save_pos, *use;
+  uint found_eq_constant, i;
+
+  DBUG_ASSERT(keyuse->elements);
 
-    use=save_pos=dynamic_element(keyuse,0,KEYUSE*);
-    prev= &key_end;
-    found_eq_constant=0;
-    for (i=0 ; i < keyuse->elements-1 ; i++,use++)
-    {
-      if (!use->used_tables && use->optimize != KEY_OPTIMIZE_REF_OR_NULL)
-        use->table->const_key_parts[use->key]|= use->keypart_map;
-      if (use->keypart != FT_KEYPART)
+  my_qsort(keyuse->buffer, keyuse->elements, sizeof(KEYUSE),
+           (qsort_cmp) sort_keyuse);
+
+  bzero((char*) &key_end, sizeof(key_end));    /* Add for easy testing */
+  if (insert_dynamic(keyuse, (uchar*) &key_end))
+    return TRUE;
+
+  use= save_pos= dynamic_element(keyuse,0,KEYUSE*);
+  prev= &key_end;
+  found_eq_constant= 0;
+
+  for (i=0 ; i < keyuse->elements-1 ; i++,use++)
+  {
+    if (!use->used_tables && use->optimize != KEY_OPTIMIZE_REF_OR_NULL)
+      use->table->const_key_parts[use->key]|= use->keypart_map;
+    if (use->keypart != FT_KEYPART)
+    {
+      if (use->key == prev->key && use->table == prev->table)
       {
-        if (use->key == prev->key && use->table == prev->table)
-        {
-          if (prev->keypart+1 < use->keypart ||
-              (prev->keypart == use->keypart && found_eq_constant))
-            continue;                           /* remove */
-        }
-        else if (use->keypart != 0)             // First found must be 0
-          continue;
+        if (prev->keypart+1 < use->keypart ||
+            (prev->keypart == use->keypart && found_eq_constant))
+          continue;                             /* remove */
       }
+      else if (use->keypart != 0)               // First found must be 0
+        continue;
+    }
 
 #ifdef HAVE_valgrind
-      /* Valgrind complains about overlapped memcpy when save_pos==use. */
-      if (save_pos != use)
+    /* Valgrind complains about overlapped memcpy when save_pos==use. */
+    if (save_pos != use)
 #endif
-        *save_pos= *use;
-      prev=use;
-      found_eq_constant= !use->used_tables;
-      /* Save ptr to first use */
-      if (!use->table->reginfo.join_tab->keyuse)
-        use->table->reginfo.join_tab->keyuse=save_pos;
-      use->table->reginfo.join_tab->checked_keys.set_bit(use->key);
-      save_pos++;
-    }
-    i=(uint) (save_pos-(KEYUSE*) keyuse->buffer);
-    VOID(set_dynamic(keyuse,(uchar*) &key_end,i));
-    keyuse->elements=i;
-  }
-  DBUG_EXECUTE("opt", print_keyuse_array(keyuse););
+      *save_pos= *use;
+    prev= use;
+    found_eq_constant= !use->used_tables;
+    /* Save ptr to first use */
+    if (!use->table->reginfo.join_tab->keyuse)
+      use->table->reginfo.join_tab->keyuse=save_pos;
+    use->table->reginfo.join_tab->checked_keys.set_bit(use->key);
+    save_pos++;
+  }
+  i= (uint) (save_pos-(KEYUSE*) keyuse->buffer);
+  VOID(set_dynamic(keyuse,(uchar*) &key_end,i));
+  keyuse->elements= i;
+
   return FALSE;
 }
 
@@ -5441,40 +5416,43 @@ greedy_search(JOIN      *join,
 }
 
 
-/*
-  Calculate a cost of given partial join order
+/**
+  Calculate a cost of given partial join order in join->positions.
  
-  SYNOPSIS
-    get_partial_join_cost()
-      join               IN    Join to use. join->positions holds the
-                               partial join order
-      idx                IN    # tables in the partial join order
-      read_time_arg      OUT   Store read time here 
-      record_count_arg   OUT   Store record count here
-
-  DESCRIPTION
+  @param n_tables[in]      # tables in the partial join order after the last
+                           constant table
+  @param read_time_arg[out]    store read time here 
+  @param record_count_arg[out] store record count here
 
-    This is needed for semi-join materialization code. The idea is that 
-    we detect sj-materialization after we've put all sj-inner tables into
-    the join prefix
+  @note
+    When used by semi-join materialization code the idea is that we
+    detect sj-materialization after we've put all sj-inner tables into
+    the join prefix.
 
       prefix-tables semi-join-inner-tables  tN
                                              ^--we're here
 
     and we'll need to get the cost of prefix-tables prefix again.
+
+    When used with non-flattened subqueries, the method computes the
+    total cost of query plan.
+
+  @returns
+    read_time_arg and record_count_arg contain the computed cost.
 */
 
-void get_partial_join_cost(JOIN *join, uint n_tables, double *read_time_arg,
-                           double *record_count_arg)
+void JOIN::get_partial_join_cost(uint n_tables,
+                                 double *read_time_arg, double *record_count_arg)
 {
   double record_count= 1;
   double read_time= 0.0;
-  for (uint i= join->const_tables; i < n_tables + join->const_tables ; i++)
+
+  for (uint i= const_tables; i < n_tables; i++)
   {
-    if (join->best_positions[i].records_read)
+    if (best_positions[i].records_read)
     {
-      record_count *= join->best_positions[i].records_read;
-      read_time += join->best_positions[i].read_time;
+      record_count *= best_positions[i].records_read;
+      read_time += best_positions[i].read_time;
     }
   }
   *read_time_arg= read_time;// + record_count / TIME_FOR_COMPARE;
@@ -6657,11 +6635,12 @@ make_join_select(JOIN *join,SQL_SELECT *
            there inside the triggers.
       */
       {                                         // Check const tables
-        COND *const_cond=
+        join->exec_const_cond=
           make_cond_for_table(cond,
                               join->const_table_map,
-                              (table_map) 0, TRUE);
-        DBUG_EXECUTE("where",print_where(const_cond,"constants", QT_ORDINARY););
+                              (table_map) 0);
+        DBUG_EXECUTE("where",print_where(join->exec_const_cond, "constants",
+                                         QT_ORDINARY););
         for (JOIN_TAB *tab= join->join_tab+join->const_tables;
              tab < join->join_tab+join->tables ; tab++)
         {
@@ -6670,7 +6649,7 @@ make_join_select(JOIN *join,SQL_SELECT *
             JOIN_TAB *cond_tab= tab->first_inner;
             COND *tmp= make_cond_for_table(*tab->on_expr_ref,
                                            join->const_table_map,
-                                         (  table_map) 0, FALSE);
+                                           (table_map) 0);
             if (!tmp)
               continue;
             tmp= new Item_func_trig_cond(tmp, &cond_tab->not_null_compl);
@@ -6686,10 +6665,13 @@ make_join_select(JOIN *join,SQL_SELECT *
             cond_tab->select_cond->quick_fix_field();
           }       
         }
-        if (const_cond && !const_cond->val_int())
+
+        if (join->exec_const_cond && !join->exec_const_cond->is_expensive() &&
+            !join->exec_const_cond->val_int())
         {
-          DBUG_PRINT("info",("Found impossible WHERE condition"));
-          DBUG_RETURN(1);        // Impossible const condition
+          DBUG_PRINT("info",("Found impossible WHERE condition"));
+          join->exec_const_cond= NULL;
+          DBUG_RETURN(1);        // Impossible const condition
         }
       }
     }
@@ -6758,7 +6740,7 @@ make_join_select(JOIN *join,SQL_SELECT *
 
       tmp= NULL;
       if (cond)
-        tmp= make_cond_for_table(cond, used_tables, current_map, FALSE);
+        tmp= make_cond_for_table(cond, used_tables, current_map);
       if (cond && !tmp && tab->quick)
       {                                         // Outer join
         if (tab->type != JT_ALL)
@@ -6813,8 +6795,7 @@ make_join_select(JOIN *join,SQL_SELECT *
           tab->table->file->pushed_cond= NULL;
           if (thd->variables.engine_condition_pushdown && !first_inner_tab)
           {
-            COND *push_cond= 
-              make_cond_for_table(tmp, current_map, current_map, FALSE);
+            COND *push_cond= make_cond_for_table(tmp, current_map, current_map);
             if (push_cond)
             {
               /* Push condition to handler */
@@ -6942,8 +6923,7 @@ make_join_select(JOIN *join,SQL_SELECT *
             if (cond &&
                 (tmp=make_cond_for_table(cond,
                                          join->const_table_map |
-                                         current_map,
-                                         current_map, FALSE)))
+                                         current_map, current_map)))
             {
               DBUG_EXECUTE("where",print_where(tmp,"cache", QT_ORDINARY););
               tab->cache_select=(SQL_SELECT*)
@@ -6973,7 +6953,7 @@ make_join_select(JOIN *join,SQL_SELECT *
           JOIN_TAB *cond_tab= join_tab->first_inner;
           COND *tmp= make_cond_for_table(*join_tab->on_expr_ref,
                                          join->const_table_map,
-                                         (table_map) 0, FALSE);
+                                         (table_map) 0);
           if (!tmp)
             continue;
           tmp= new Item_func_trig_cond(tmp, &cond_tab->not_null_compl);
@@ -7008,7 +6988,7 @@ make_join_select(JOIN *join,SQL_SELECT *
           current_map= tab->table->map;
           used_tables2|= current_map;
           COND *tmp_cond= make_cond_for_table(on_expr, used_tables2,
-                                              current_map, FALSE);
+                                              current_map);
           if (tmp_cond)
           {
             JOIN_TAB *cond_tab= tab < first_inner_tab ? first_inner_tab : tab;
@@ -11432,10 +11412,30 @@ create_tmp_table(THD *thd,TMP_TABLE_PARA
       {
         if (thd->is_fatal_error)
           goto err;                             // Got OOM
-        continue;                               // Some kindf of const item
+        continue;                               // Some kind of const item
       }
       if (type == Item::SUM_FUNC_ITEM)
-        ((Item_sum *) item)->result_field= new_field;
+      {
+        Item_sum *agg_item= (Item_sum *) item;
+        /*
+          Update the result field only if it has never been set, or if the
+          created temporary table is not to be used for subquery
+          materialization.
+
+          The reason is that for subqueries that require materialization as part
+          of their plan, we create the 'external' temporary table needed for IN
+          execution, after the 'internal' temporary table needed for grouping.
+          Since both the external and the internal temporary tables are created
+          for the same list of SELECT fields of the subquery, setting
+          'result_field' for each invocation of create_tmp_table overrides the
+           previous value of 'result_field'.
+
+          The condition below prevents the creation of the external temp table
+          to override the 'result_field' that was set for the internal temp table.
+        */
+        if (!agg_item->result_field || !param->materialized_subquery)
+          agg_item->result_field= new_field;
+      }
       tmp_from_field++;
       reclength+=new_field->pack_length();
       if (!(new_field->flags & NOT_NULL_FLAG))
@@ -14663,33 +14662,16 @@ bool test_if_ref(Item *root_cond, Item_f
 */
 
 static Item *
-make_cond_for_table(Item *cond, table_map tables, table_map used_table,
-                    bool exclude_expensive_cond)
+make_cond_for_table(Item *cond, table_map tables, table_map used_table)
 {
-  return make_cond_for_table_from_pred(cond, cond, tables, used_table,
-                                       exclude_expensive_cond);
+  return make_cond_for_table_from_pred(cond, cond, tables, used_table);
 }
                
 static Item *
 make_cond_for_table_from_pred(Item *root_cond, Item *cond,
-                              table_map tables, table_map used_table,
-                              bool exclude_expensive_cond)
+                              table_map tables, table_map used_table)
 
 {
-  if (used_table && !(cond->used_tables() & used_table) &&
-      /*
-        Exclude constant conditions not checked at optimization time if
-        the table we are pushing conditions to is the first one.
-        As a result, such conditions are not considered as already checked
-        and will be checked at execution time, attached to the first table.
-
-        psergey: TODO: "used_table & 1" doesn't make sense in nearly any
-        context. Look at setup_table_map(), table bits reflect the order 
-        the tables were encountered by the parser. Check what we should
-        replace this condition with.
-      */
-      !((used_table & 1) && cond->is_expensive()))
-    return (COND*) 0;                           // Already checked
   if (cond->type() == Item::COND_ITEM)
   {
     if (((Item_cond*) cond)->functype() == Item_func::COND_AND_FUNC)
@@ -14703,8 +14685,7 @@ make_cond_for_table_from_pred(Item *root
       while ((item=li++))
       {
         Item *fix=make_cond_for_table_from_pred(root_cond, item, 
-                                                tables, used_table,
-                                                exclude_expensive_cond);
+                                                tables, used_table);
         if (fix)
           new_cond->argument_list()->push_back(fix);
       }
@@ -14735,8 +14716,7 @@ make_cond_for_table_from_pred(Item *root
       while ((item=li++))
       {
         Item *fix=make_cond_for_table_from_pred(root_cond, item,
-                                                tables, 0L,
-                                                exclude_expensive_cond);
+                                                tables, 0L);
         if (!fix)
           return (COND*) 0;                     // Always true
         new_cond->argument_list()->push_back(fix);
@@ -14757,12 +14737,7 @@ make_cond_for_table_from_pred(Item *root
     table_count times, we mark each item that we have examined with the result
     of the test
   */
-  if (cond->marker == 3 || (cond->used_tables() & ~tables) ||
-      /*
-        When extracting constant conditions, treat expensive conditions as
-        non-constant, so that they are not evaluated at optimization time.
-      */
-      (!used_table && exclude_expensive_cond && cond->is_expensive()))
+  if (cond->marker == 3 || (cond->used_tables() & ~tables))
     return (COND*) 0;                           // Can't check this yet
   if (cond->marker == 2 || cond->eq_cmp_result() == Item::COND_OK)
     return cond;                                // Not boolean op
@@ -15962,8 +15936,7 @@ static bool fix_having(JOIN *join, Item 
   table_map used_tables= join->const_table_map | table->table->map;
 
   DBUG_EXECUTE("where",print_where(*having,"having", QT_ORDINARY););
-  Item* sort_table_cond=make_cond_for_table(*having, used_tables, used_tables,
-                                            FALSE);
+  Item* sort_table_cond=make_cond_for_table(*having, used_tables, used_tables);
   if (sort_table_cond)
   {
     if (!table->select)
@@ -15981,7 +15954,7 @@ static bool fix_having(JOIN *join, Item 
     DBUG_EXECUTE("where",print_where(table->select_cond,
                                      "select and having",
                                      QT_ORDINARY););
-    *having= make_cond_for_table(*having,~ (table_map) 0,~used_tables, FALSE);
+    *having= make_cond_for_table(*having,~ (table_map) 0,~used_tables);
     DBUG_EXECUTE("where",
                  print_where(*having,"having after make_cond", QT_ORDINARY););
   }
@@ -18899,28 +18872,9 @@ bool mysql_explain_union(THD *thd, SELEC
   bool res= 0;
   SELECT_LEX *first= unit->first_select();
 
-  for (SELECT_LEX *sl= first;
-       sl;
-       sl= sl->next_select())
-  {
-    // drop UNCACHEABLE_EXPLAIN, because it is for internal usage only
-    uint8 uncacheable= (sl->uncacheable & ~UNCACHEABLE_EXPLAIN);
-    sl->type= (((&thd->lex->select_lex)==sl)?
-               (sl->first_inner_unit() || sl->next_select() ? 
-                "PRIMARY" : "SIMPLE"):
-               ((sl == first)?
-                ((sl->linkage == DERIVED_TABLE_TYPE) ?
-                 "DERIVED":
-                 ((uncacheable & UNCACHEABLE_DEPENDENT) ?
-                  "DEPENDENT SUBQUERY":
-                  (uncacheable?"UNCACHEABLE SUBQUERY":
-                   "SUBQUERY"))):
-                ((uncacheable & UNCACHEABLE_DEPENDENT) ?
-                 "DEPENDENT UNION":
-                 uncacheable?"UNCACHEABLE UNION":
-                 "UNION")));
-    sl->options|= SELECT_DESCRIBE;
-  }
+  for (SELECT_LEX *sl= first; sl; sl= sl->next_select())
+    sl->set_explain_type();
+
   if (unit->is_union())
   {
     unit->fake_select_lex->select_number= UINT_MAX; // jost for initialization
@@ -19338,6 +19292,8 @@ bool JOIN::change_result(select_result *
 {
   DBUG_ENTER("JOIN::change_result");
   result= res;
+  if (tmp_join)
+    tmp_join->result= res;
   if (!procedure && (result->prepare(fields_list, select_lex->master_unit()) ||
                      result->prepare2()))
   {
@@ -19346,6 +19302,155 @@ bool JOIN::change_result(select_result *
   DBUG_RETURN(FALSE);
 }
 
+
+/**
+  Save a query execution plan so that the caller can revert to it if needed,
+  and reset the current query plan so that it can be reoptimized.
+
+  @param save_keyuse[out]  a KEYUSE array to save JOIN::keyuse
+  @param save_best_positions[out]  array to save JOIN::best_positions
+  @param save_join_tab_keyuse[out] array of KEYUSE pointers to save each
+                                   JOIN_TAB::keyuse pointer
+  @param save_join_tab_checked_keys[out] an array of bitmaps to save
+                                         each JOIN_TAB::checked_keys
+
+  @retval 0  OK
+  @retval 1  memory allocation error
+*/
+int JOIN::save_query_plan(DYNAMIC_ARRAY *save_keyuse,
+                          POSITION *save_best_positions,
+                          KEYUSE **save_join_tab_keyuse,
+                          key_map *save_join_tab_checked_keys)
+{
+  if (keyuse.elements)
+  {
+    DYNAMIC_ARRAY tmp_keyuse;
+    if (my_init_dynamic_array(save_keyuse, sizeof(KEYUSE), 20, 64))
+      return 1;
+    /* Swap the current and the backup keyuse arrays. */
+    tmp_keyuse= keyuse;
+    keyuse= (*save_keyuse);
+    (*save_keyuse)= tmp_keyuse;
+
+    for (uint i= 0; i < tables; i++)
+    {
+      save_join_tab_keyuse[i]= join_tab[i].keyuse;
+      join_tab[i].keyuse= NULL;
+      save_join_tab_checked_keys[i]= join_tab[i].checked_keys;
+      join_tab[i].checked_keys.clear_all();
+    }
+  }
+  memcpy((uchar*) save_best_positions, (uchar*) best_positions,
+         sizeof(POSITION) * (tables + 1));
+  memset(best_positions, 0, sizeof(POSITION) * (tables + 1));
+  return 0;
+}
+
+
+/**
+  Restore a query plan previously saved by the caller.
+
+  @param save_keyuse  a KEYUSE array to restore into JOIN::keyuse
+  @param save_best_positions  array to restore into JOIN::best_positions
+  @param save_join_tab_keyuse array of KEYUSE pointers to restore each
+                                   JOIN_TAB::keyuse pointer
+  @param save_join_tab_checked_keys an array of bitmaps to restore
+                                         each JOIN_TAB::checked_keys
+*/
+
+void JOIN::restore_query_plan(DYNAMIC_ARRAY *save_keyuse,
+                              POSITION *save_best_positions,
+                              KEYUSE **save_join_tab_keyuse,
+                              key_map *save_join_tab_checked_keys)
+{
+  if (save_keyuse->elements)
+  {
+    DYNAMIC_ARRAY tmp_keyuse;
+    tmp_keyuse= keyuse;
+    keyuse= (*save_keyuse);
+    (*save_keyuse)= tmp_keyuse;
+    delete_dynamic(save_keyuse);
+
+    for (uint i= 0; i < tables; i++)
+    {
+      join_tab[i].keyuse= save_join_tab_keyuse[i];
+      join_tab[i].checked_keys= save_join_tab_checked_keys[i];
+    }
+
+  }
+  memcpy((uchar*) best_positions, (uchar*) save_best_positions,
+         sizeof(POSITION) * (tables + 1));
+}
+
+
+/**
+  Reoptimize a query plan taking into account an additional conjunct to the
+  WHERE clause.
+
+  @param added_where  An extra conjunct to the WHERE clause to reoptimize with
+  @param join_tables  The set of tables to reoptimize
+  @param save_best_positions  The join order of the original plan to restore to
+                              if needed.
+
+  @notes
+  Given a query plan that already optimized taking into account some WHERE clause
+  'C', reoptimize this plan with a new WHERE clause 'C AND added_where'. The
+  reoptimization works as follows:
+
+  1. Call update_ref_and_keys *only* for the new conditions 'added_where'
+     that are about to be injected into the query.
+  2. Expand if necessary the original KEYUSE array JOIN::keyuse to
+     accommodate the new REF accesses computed for the 'added_where' condition.
+  3. Add the new KEYUSEs into JOIN::keyuse.
+  4. Re-sort and re-filter the JOIN::keyuse array with the newly added
+     KEYUSE elements. 
+ 
+  @retval REOPT_NEW_PLAN  there is a new plan.
+  @retval REOPT_OLD_PLAN  no new improved plan was produced, use the old one.
+  @retval REOPT_ERROR     an irrecovarable error occured during reoptimization.
+*/
+
+JOIN::enum_reopt_result JOIN::reoptimize(Item *added_where, table_map join_tables)
+{
+  DYNAMIC_ARRAY added_keyuse;
+  SARGABLE_PARAM *sargables= 0; /* Used only as a dummy parameter. */
+
+  /* Re-run the REF optimizer to take into account the new conditions. */
+  if (update_ref_and_keys(thd, &added_keyuse, join_tab, tables, added_where,
+                          ~outer_join, select_lex, &sargables))
+  {
+    delete_dynamic(&added_keyuse);
+    return REOPT_ERROR;
+  }
+
+  if (!added_keyuse.elements)
+    return REOPT_OLD_PLAN;
+
+  /* Add the new access methods to the keyuse array. */
+  if (!keyuse.buffer &&
+      my_init_dynamic_array(&keyuse, sizeof(KEYUSE), 20, 64))
+  {
+    delete_dynamic(&added_keyuse);
+    return REOPT_ERROR;
+  }
+  allocate_dynamic(&keyuse, keyuse.elements + added_keyuse.elements);
+  memcpy(keyuse.buffer + keyuse.elements * keyuse.size_of_element,
+         added_keyuse.buffer,
+         (size_t) added_keyuse.elements * added_keyuse.size_of_element);
+  keyuse.elements+= added_keyuse.elements;
+  delete_dynamic(&added_keyuse);
+
+  if (sort_and_filter_keyuse(&keyuse))
+    return REOPT_ERROR;
+  optimize_keyuse(this, &keyuse);
+
+  /* Re-run the join optimizer to compute a new query plan. */
+  if (choose_plan(this, join_tables))
+    return REOPT_ERROR;
+
+  return REOPT_NEW_PLAN;
+}
+
 /**
   @} (end of group Query_Optimizer)
 */

=== modified file 'sql/sql_select.h'
--- a/sql/sql_select.h	2010-11-11 12:35:26 +0000
+++ b/sql/sql_select.h	2010-11-16 06:51:38 +0000
@@ -1369,8 +1369,30 @@ inline bool sj_is_materialize_strategy(u
 
 class JOIN :public Sql_alloc
 {
+private:
   JOIN(const JOIN &rhs);                        /**< not implemented */
   JOIN& operator=(const JOIN &rhs);             /**< not implemented */
+
+protected:
+  /* Results of reoptimizing a JOIN via JOIN::reoptimize(). */
+  enum enum_reopt_result {
+    REOPT_NEW_PLAN, /* there is a new reoptimized plan */
+    REOPT_OLD_PLAN, /* no new improved plan can be found, use the old one */
+    REOPT_ERROR,    /* an irrecovarable error occured during reoptimization */
+    REOPT_NONE      /* not yet reoptimized */
+  };
+
+  /* Support for plan reoptimization with rewritten conditions. */
+  enum_reopt_result reoptimize(Item *added_where, table_map join_tables);
+  int save_query_plan(DYNAMIC_ARRAY *save_keyuse, POSITION *save_positions,
+                      KEYUSE **save_join_tab_keyuse,
+                      key_map *save_join_tab_checked_keys);
+  void restore_query_plan(DYNAMIC_ARRAY *save_keyuse, POSITION *save_positions,
+                      KEYUSE **save_join_tab_keyuse,
+                      key_map *save_join_tab_checked_keys);
+  /* Choose a subquery plan for a table-less subquery. */
+  bool choose_tableless_subquery_plan();
+
 public:
   JOIN_TAB *join_tab,**best_ref;
   JOIN_TAB **map2table;    ///< mapping between table indexes and JOIN_TABs
@@ -1571,6 +1593,12 @@ public:
   List<TABLE_LIST> *join_list;       ///< list of joined tables in reverse order
   COND_EQUAL *cond_equal;
   COND_EQUAL *having_equal;
+  /*
+    Constant codition computed during optimization, but evaluated during
+    join execution. Typically expensive conditions that should not be
+    evaluated at optimization time.
+  */
+  Item *exec_const_cond;
   SQL_SELECT *select;                ///<created in optimisation phase
   JOIN_TAB *return_tab;              ///<used only for outer joins
   Item **ref_pointer_array; ///<used pointer reference for this select
@@ -1581,8 +1609,15 @@ public:
   
   bool union_part; ///< this subselect is part of union 
   bool optimized; ///< flag to avoid double optimization in EXPLAIN
+  bool initialized; ///< flag to avoid double init_execution calls
 
   Array<Item_in_subselect> sj_subselects;
+  /*
+    Additional WHERE and HAVING predicates to be considered for IN=>EXISTS
+    subquery transformation of a JOIN object.
+  */
+  Item *in_to_exists_where;
+  Item *in_to_exists_having;
 
   /* Temporary tables used to weed-out semi-join duplicates */
   List<TABLE> sj_tmp_tables;
@@ -1657,8 +1692,10 @@ public:
     ref_pointer_array_size= 0;
     zero_result_cause= 0;
     optimized= 0;
+    initialized= 0;
     cond_equal= 0;
     having_equal= 0;
+    exec_const_cond= 0;
     group_optimized_away= 0;
     no_rows_in_result_called= 0;
 
@@ -1672,6 +1709,8 @@ public:
 
     no_const_tables= FALSE;
     first_select= sub_select;
+    in_to_exists_where= NULL;
+    in_to_exists_having= NULL;
   }
 
   int prepare(Item ***rref_pointer_array, TABLE_LIST *tables, uint wind_num,
@@ -1680,12 +1719,13 @@ public:
               SELECT_LEX_UNIT *unit);
   int optimize();
   int reinit();
+  int init_execution();
   void exec();
   int destroy();
   void restore_tmp();
   bool alloc_func_list();
   bool flatten_subqueries();
-  bool setup_subquery_materialization();
+  bool optimize_unflattened_subqueries();
   bool make_sum_func_list(List<Item> &all_fields, List<Item> &send_fields,
                           bool before_group_by, bool recompute= FALSE);
 
@@ -1745,6 +1785,9 @@ public:
               NULL : join_tab+const_tables;
   }
   bool setup_subquery_caches();
+  bool choose_subquery_plan(table_map join_tables);
+  void get_partial_join_cost(uint n_tables,
+                             double *read_time_arg, double *record_count_arg);
 private:
   /**
     TRUE if the query contains an aggregate function but has no GROUP
@@ -1977,8 +2020,6 @@ inline Item * and_items(Item* cond, Item
   return (cond? (new Item_cond_and(cond, item)) : item);
 }
 bool choose_plan(JOIN *join,table_map join_tables);
-void get_partial_join_cost(JOIN *join, uint n_tables, double *read_time_arg,
-                           double *record_count_arg);
 void optimize_wo_join_buffering(JOIN *join, uint first_tab, uint last_tab, 
                                 table_map last_remaining_tables, 
                                 bool first_alt, uint no_jbuf_before,

=== modified file 'sql/sql_union.cc'
--- a/sql/sql_union.cc	2010-10-10 14:18:11 +0000
+++ b/sql/sql_union.cc	2010-11-05 12:42:58 +0000
@@ -185,6 +185,8 @@ st_select_lex_unit::init_prepare_fake_se
   {
     (*order->item)->walk(&Item::change_context_processor, 0,
                          (uchar*) &fake_select_lex->context);
+    (*order->item)->walk(&Item::set_fake_select_as_master_processor, 0,
+                         (uchar*) fake_select_lex);
   }
 }
 
@@ -270,6 +272,18 @@ bool st_select_lex_unit::prepare(THD *th
 
     can_skip_order_by= is_union_select && !(sl->braces && sl->explicit_limit);
 
+    /*
+      Remove all references from the select_lex_units to the subqueries that
+      are inside the ORDER BY clause.
+    */
+    if (can_skip_order_by)
+    {
+      for (ORDER *ord= (ORDER *)sl->order_list.first; ord; ord= ord->next)
+      {
+        (*ord->item)->walk(&Item::eliminate_subselect_processor, FALSE, NULL);
+      }
+    }
+
     saved_error= join->prepare(&sl->ref_pointer_array,
                                sl->table_list.first,
                                sl->with_wild,

=== modified file 'sql/sql_update.cc'
--- a/sql/sql_update.cc	2010-11-05 09:54:42 +0000
+++ b/sql/sql_update.cc	2010-11-16 06:51:38 +0000
@@ -290,6 +290,10 @@ int mysql_update(THD *thd,
     DBUG_RETURN(1);                             /* purecov: inspected */
   }
 
+  /* Apply the IN=>EXISTS transformation to all subqueries and optimize them. */
+  if (select_lex->optimize_unflattened_subqueries())
+    DBUG_RETURN(TRUE);
+
   if (select_lex->inner_refs_list.elements &&
     fix_inner_refs(thd, all_fields, select_lex, select_lex->ref_pointer_array))
     DBUG_RETURN(1);



More information about the commits mailing list