diff --git a/mysql-test/r/subselect4.result b/mysql-test/r/subselect4.result
index e22a0f04a4ddc8e0cdfb2ef2b6ce18e4b35d8ea2..a8102486f6bf8d95983a93a6a04bd1221d4348a0 100644
--- a/mysql-test/r/subselect4.result
+++ b/mysql-test/r/subselect4.result
@@ -1655,52 +1655,6 @@ f1b	f2b	f3b
 set @@optimizer_switch=@save_optimizer_switch;
 drop table t0,t1,t2;
 #                                                                                                                                                     
-# LP BUG#718593 Crash in substitute_for_best_equal_field -> eliminate_item_equal -> 
-# Item_field::find_item_equal -> Item_equal::contains
-#                                                                                                                                                     
-set @save_optimizer_switch=@@optimizer_switch;
-SET @@optimizer_switch = 'semijoin=off';
-CREATE TABLE t1 ( f3 int(11), f10 varchar(1), f11 varchar(1)) ;
-INSERT IGNORE INTO t1 VALUES (6,'f','f'),(2,'d','d');
-CREATE TABLE t2 ( f12 int(11), f13 int(11)) ;
-insert into t2 values (1,2), (3,4);
-EXPLAIN
-SELECT * FROM t2
-WHERE ( f12 ) IN (
-SELECT alias2.f3
-FROM t1 AS alias1 JOIN t1 AS alias2 ON alias2.f10 = alias1.f11
-WHERE alias1.f11 OR alias1.f3 = 50 AND alias1.f10
-);
-id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
-1	PRIMARY	t2	ALL	NULL	NULL	NULL	NULL	2	Using where
-2	DEPENDENT SUBQUERY	alias1	ALL	NULL	NULL	NULL	NULL	2	Using where
-2	DEPENDENT SUBQUERY	alias2	ALL	NULL	NULL	NULL	NULL	2	Using where; Using join buffer (flat, BNL join)
-SELECT * FROM t2
-WHERE ( f12 ) IN (
-SELECT alias2.f3
-FROM t1 AS alias1 JOIN t1 AS alias2 ON alias2.f10 = alias1.f11
-WHERE alias1.f11 OR alias1.f3 = 50 AND alias1.f10
-);
-f12	f13
-EXPLAIN
-SELECT * FROM t2
-WHERE ( f12 ) IN (
-SELECT alias2.f3
-FROM t1 AS alias1, t1 AS alias2
-WHERE (alias2.f10 = alias1.f11) AND (alias1.f11 OR alias1.f3 = 50 AND alias1.f10));
-id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
-1	PRIMARY	t2	ALL	NULL	NULL	NULL	NULL	2	Using where
-2	DEPENDENT SUBQUERY	alias1	ALL	NULL	NULL	NULL	NULL	2	Using where
-2	DEPENDENT SUBQUERY	alias2	ALL	NULL	NULL	NULL	NULL	2	Using where; Using join buffer (flat, BNL join)
-SELECT * FROM t2
-WHERE ( f12 ) IN (
-SELECT alias2.f3
-FROM t1 AS alias1, t1 AS alias2
-WHERE (alias2.f10 = alias1.f11) AND (alias1.f11 OR alias1.f3 = 50 AND alias1.f10));
-f12	f13
-set @@optimizer_switch=@save_optimizer_switch;
-drop table t1, t2;
-#                                                                                                                                                     
 # LP BUG#715759 Wrong result with in_to_exists=on in maria-5.3-mwl89
 #                                                                                                                                                     
 set @save_optimizer_switch=@@optimizer_switch;
diff --git a/mysql-test/r/subselect_mat_cost_bugs.result b/mysql-test/r/subselect_mat_cost_bugs.result
index cd0a6c716bba1707a27895444e996a46ea385eb5..18f2513a76da47dd6373fd23b30822433fda213a 100644
--- a/mysql-test/r/subselect_mat_cost_bugs.result
+++ b/mysql-test/r/subselect_mat_cost_bugs.result
@@ -229,6 +229,72 @@ FROM t1 STRAIGHT_JOIN t2 ON t2.f3 = t1.f3
 WHERE t2.f3 = 'c');
 f2	f3
 drop table t1,t2,t3;
+#                                                                                                                                                     
+# LP BUG#718593 Crash in substitute_for_best_equal_field -> eliminate_item_equal -> 
+# Item_field::find_item_equal -> Item_equal::contains
 #
-# LP BUG#718593 Crash in substitute_for_best_equal_field ->
-# eliminate_item_equal -> Item_field::find_item_equal -> Item_equal::contains
+set @save_optimizer_switch=@@optimizer_switch;
+SET @@optimizer_switch = 'semijoin=off';
+CREATE TABLE t1 ( f3 int(11), f10 varchar(1), f11 varchar(1)) ;
+INSERT IGNORE INTO t1 VALUES (6,'f','f'),(2,'d','d');
+CREATE TABLE t2 ( f12 int(11), f13 int(11)) ;
+insert into t2 values (1,2), (3,4);
+EXPLAIN
+SELECT * FROM t2
+WHERE ( f12 ) IN (
+SELECT alias2.f3
+FROM t1 AS alias1 JOIN t1 AS alias2 ON alias2.f10 = alias1.f11
+WHERE alias1.f11 OR alias1.f3 = 50 AND alias1.f10
+);
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	PRIMARY	t2	ALL	NULL	NULL	NULL	NULL	2	Using where
+2	SUBQUERY	alias1	ALL	NULL	NULL	NULL	NULL	2	Using where
+2	SUBQUERY	alias2	ALL	NULL	NULL	NULL	NULL	2	Using where; Using join buffer (flat, BNL join)
+SELECT * FROM t2
+WHERE ( f12 ) IN (
+SELECT alias2.f3
+FROM t1 AS alias1 JOIN t1 AS alias2 ON alias2.f10 = alias1.f11
+WHERE alias1.f11 OR alias1.f3 = 50 AND alias1.f10
+);
+f12	f13
+EXPLAIN
+SELECT * FROM t2
+WHERE ( f12 ) IN (
+SELECT alias2.f3
+FROM t1 AS alias1, t1 AS alias2
+WHERE (alias2.f10 = alias1.f11) AND (alias1.f11 OR alias1.f3 = 50 AND alias1.f10));
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	PRIMARY	t2	ALL	NULL	NULL	NULL	NULL	2	Using where
+2	SUBQUERY	alias1	ALL	NULL	NULL	NULL	NULL	2	Using where
+2	SUBQUERY	alias2	ALL	NULL	NULL	NULL	NULL	2	Using where; Using join buffer (flat, BNL join)
+SELECT * FROM t2
+WHERE ( f12 ) IN (
+SELECT alias2.f3
+FROM t1 AS alias1, t1 AS alias2
+WHERE (alias2.f10 = alias1.f11) AND (alias1.f11 OR alias1.f3 = 50 AND alias1.f10));
+f12	f13
+set @@optimizer_switch=@save_optimizer_switch;
+drop table t1, t2;
+#
+# MWL#89: test introduced after Sergey Petrunia's review - test that
+# keyparts wihtout index prefix are used with the IN-EXISTS strategy.
+# 
+create table t1 (c1 int);
+insert into t1 values (1), (2), (3);
+create table t2 (kp1 int, kp2 int, c2 int, filler char(100));
+insert into t2 values (0,0,0,'filler'),(0,1,1,'filler'),(0,2,2,'filler'),(0,3,3,'filler');
+create index key1 on t2 (kp1, kp2);
+create index key2 on t2 (kp1);
+create index key3 on t2 (kp2);
+set session optimizer_switch='default';
+analyze table t2;
+Table	Op	Msg_type	Msg_text
+test.t2	analyze	status	OK
+explain
+select c1 from t1 where c1 in (select kp1 from t2 where kp2 = 10 and c2 = 4) or c1 > 7;
+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_subquery	key1,key2,key3	key1	10	func,const	1	Using where
+select c1 from t1 where c1 in (select kp1 from t2 where kp2 = 10 and c2 = 4) or c1 > 7;
+c1
+drop table t1, t2;
diff --git a/mysql-test/t/subselect4.test b/mysql-test/t/subselect4.test
index 33a6329401f3f605328c4ebd180b5142aacb5e98..768f37c1b6c225a0017cbce99cd7eda1bea6eded 100644
--- a/mysql-test/t/subselect4.test
+++ b/mysql-test/t/subselect4.test
@@ -1328,48 +1328,6 @@ set @@optimizer_switch=@save_optimizer_switch;
 
 drop table t0,t1,t2;
 
---echo #                                                                                                                                                     
---echo # LP BUG#718593 Crash in substitute_for_best_equal_field -> eliminate_item_equal -> 
---echo # Item_field::find_item_equal -> Item_equal::contains
---echo #                                                                                                                                                     
-
-set @save_optimizer_switch=@@optimizer_switch;
-SET @@optimizer_switch = 'semijoin=off';
-
-CREATE TABLE t1 ( f3 int(11), f10 varchar(1), f11 varchar(1)) ;
-INSERT IGNORE INTO t1 VALUES (6,'f','f'),(2,'d','d');
-
-CREATE TABLE t2 ( f12 int(11), f13 int(11)) ;
-insert into t2 values (1,2), (3,4);
-
-EXPLAIN
-SELECT * FROM t2
-WHERE ( f12 ) IN (
-        SELECT alias2.f3
-        FROM t1 AS alias1 JOIN t1 AS alias2 ON alias2.f10 = alias1.f11
-        WHERE alias1.f11 OR alias1.f3 = 50 AND alias1.f10
-);
-SELECT * FROM t2
-WHERE ( f12 ) IN (
-        SELECT alias2.f3
-        FROM t1 AS alias1 JOIN t1 AS alias2 ON alias2.f10 = alias1.f11
-        WHERE alias1.f11 OR alias1.f3 = 50 AND alias1.f10
-);
-
-EXPLAIN
-SELECT * FROM t2
-WHERE ( f12 ) IN (
-        SELECT alias2.f3
-        FROM t1 AS alias1, t1 AS alias2
-        WHERE (alias2.f10 = alias1.f11) AND (alias1.f11 OR alias1.f3 = 50 AND alias1.f10));
-SELECT * FROM t2
-WHERE ( f12 ) IN (
-        SELECT alias2.f3
-        FROM t1 AS alias1, t1 AS alias2
-        WHERE (alias2.f10 = alias1.f11) AND (alias1.f11 OR alias1.f3 = 50 AND alias1.f10));
-
-set @@optimizer_switch=@save_optimizer_switch;
-drop table t1, t2;
 
 --echo #                                                                                                                                                     
 --echo # LP BUG#715759 Wrong result with in_to_exists=on in maria-5.3-mwl89
diff --git a/mysql-test/t/subselect_mat_cost_bugs.test b/mysql-test/t/subselect_mat_cost_bugs.test
index bc052ea04b109572e5536f3b49f1aa311d493290..0240c9203b3f4b7399c841b5faf4cba5c40bdd64 100644
--- a/mysql-test/t/subselect_mat_cost_bugs.test
+++ b/mysql-test/t/subselect_mat_cost_bugs.test
@@ -258,54 +258,71 @@ WHERE ( f2 ) IN (SELECT t1.f1
 drop table t1,t2,t3;
 
 
+--echo #                                                                                                                                                     
+--echo # LP BUG#718593 Crash in substitute_for_best_equal_field -> eliminate_item_equal -> 
+--echo # Item_field::find_item_equal -> Item_equal::contains
 --echo #
---echo # LP BUG#718593 Crash in substitute_for_best_equal_field ->
---echo # eliminate_item_equal -> Item_field::find_item_equal -> Item_equal::contains
 
---disable_parsing # not yet fixed
+set @save_optimizer_switch=@@optimizer_switch;
+SET @@optimizer_switch = 'semijoin=off';
 
 CREATE TABLE t1 ( f3 int(11), f10 varchar(1), f11 varchar(1)) ;
 INSERT IGNORE INTO t1 VALUES (6,'f','f'),(2,'d','d');
 
 CREATE TABLE t2 ( f12 int(11), f13 int(11)) ;
-
-set @save_optimizer_switch=@@optimizer_switch;
-set @@optimizer_switch='materialization=off,in_to_exists=on,semijoin=off';
+insert into t2 values (1,2), (3,4);
 
 EXPLAIN
 SELECT * FROM t2
-WHERE (f12) IN (
-      SELECT alias2.f3
-      FROM t1 AS alias1, t1 AS alias2
-      WHERE (alias2.f10 = alias1.f11) AND
-             (alias1.f11 OR alias1.f3 = 50 AND alias1.f10));
-
+WHERE ( f12 ) IN (
+        SELECT alias2.f3
+        FROM t1 AS alias1 JOIN t1 AS alias2 ON alias2.f10 = alias1.f11
+        WHERE alias1.f11 OR alias1.f3 = 50 AND alias1.f10
+);
 SELECT * FROM t2
-WHERE (f12) IN (
-      SELECT alias2.f3
-      FROM t1 AS alias1, t1 AS alias2
-      WHERE (alias2.f10 = alias1.f11) AND
-             (alias1.f11 OR alias1.f3 = 50 AND alias1.f10));
-
-insert into t2 values (1,2), (3,4);
+WHERE ( f12 ) IN (
+        SELECT alias2.f3
+        FROM t1 AS alias1 JOIN t1 AS alias2 ON alias2.f10 = alias1.f11
+        WHERE alias1.f11 OR alias1.f3 = 50 AND alias1.f10
+);
 
 EXPLAIN
 SELECT * FROM t2
-WHERE (f12) IN (
-      SELECT alias2.f3
-      FROM t1 AS alias1, t1 AS alias2
-      WHERE (alias2.f10 = alias1.f11) AND
-             (alias1.f11 OR alias1.f3 = 50 AND alias1.f10));
-
+WHERE ( f12 ) IN (
+        SELECT alias2.f3
+        FROM t1 AS alias1, t1 AS alias2
+        WHERE (alias2.f10 = alias1.f11) AND (alias1.f11 OR alias1.f3 = 50 AND alias1.f10));
 SELECT * FROM t2
-WHERE (f12) IN (
-      SELECT alias2.f3
-      FROM t1 AS alias1, t1 AS alias2
-      WHERE (alias2.f10 = alias1.f11) AND
-             (alias1.f11 OR alias1.f3 = 50 AND alias1.f10));
-
-set session optimizer_switch=@save_optimizer_switch;
+WHERE ( f12 ) IN (
+        SELECT alias2.f3
+        FROM t1 AS alias1, t1 AS alias2
+        WHERE (alias2.f10 = alias1.f11) AND (alias1.f11 OR alias1.f3 = 50 AND alias1.f10));
 
+set @@optimizer_switch=@save_optimizer_switch;
 drop table t1, t2;
 
---enable_parsing
+
+--echo #
+--echo # MWL#89: test introduced after Sergey Petrunia's review - test that
+--echo # keyparts wihtout index prefix are used with the IN-EXISTS strategy.
+--echo # 
+
+create table t1 (c1 int);
+insert into t1 values (1), (2), (3);
+
+create table t2 (kp1 int, kp2 int, c2 int, filler char(100));
+insert into t2 values (0,0,0,'filler'),(0,1,1,'filler'),(0,2,2,'filler'),(0,3,3,'filler');
+
+create index key1 on t2 (kp1, kp2);
+create index key2 on t2 (kp1);
+create index key3 on t2 (kp2);
+
+set session optimizer_switch='default';
+
+analyze table t2;
+
+explain
+select c1 from t1 where c1 in (select kp1 from t2 where kp2 = 10 and c2 = 4) or c1 > 7;
+select c1 from t1 where c1 in (select kp1 from t2 where kp2 = 10 and c2 = 4) or c1 > 7;
+
+drop table t1, t2;
diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc
index c2c592e2b885bac491556224b3d71b008d903f47..ab3c2422f30f3fd57de12b76825dcb5200d25a44 100644
--- a/sql/item_cmpfunc.cc
+++ b/sql/item_cmpfunc.cc
@@ -2096,13 +2096,14 @@ Item *Item_in_optimizer::transform(Item_transformer transformer, uchar *argument
 
 bool Item_in_optimizer::is_expensive_processor(uchar *arg)
 {
-  return args[1]->is_expensive_processor(arg);
+  return args[0]->is_expensive_processor(arg) ||
+         args[1]->is_expensive_processor(arg);
 }
 
 
 bool Item_in_optimizer::is_expensive()
 {
-  return args[1]->is_expensive();
+  return args[0]->is_expensive() || args[1]->is_expensive();
 }
 
 
diff --git a/sql/item_subselect.h b/sql/item_subselect.h
index 8d2580cc604d1391e4857a748498c5c1c6e08928..1b1be973cf9bf2a2ebf6a0d568b88567af967546 100644
--- a/sql/item_subselect.h
+++ b/sql/item_subselect.h
@@ -464,7 +464,7 @@ class Item_in_subselect :public Item_exists_subselect
   Item_in_subselect()
     :Item_exists_subselect(), left_expr_cache(0), first_execution(TRUE),
     optimizer(0), abort_on_null(0),
-    pushed_cond_guards(NULL), func(NULL), in_strategy(0),
+    pushed_cond_guards(NULL), func(NULL), in_strategy(SUBS_NOT_TRANSFORMED),
     upper_item(0)
     {}
   void cleanup();
diff --git a/sql/opt_subselect.cc b/sql/opt_subselect.cc
index a9aa4d0b11dd9e9de7b9b8e3ec77c8aeabe4d663..fb573e3b31c7a982a6f6422f4b27448003f73877 100644
--- a/sql/opt_subselect.cc
+++ b/sql/opt_subselect.cc
@@ -205,18 +205,19 @@ int check_and_do_in_subquery_rewrites(JOIN *join)
           !optimizer_flag(thd, OPTIMIZER_SWITCH_MATERIALIZATION))
         my_error(ER_ILLEGAL_SUBQUERY_OPTIMIZER_SWITCHES, MYF(0));
 
+      /*
+        If the subquery predicate is IN/=ANY, analyse and set all possible
+        subquery execution strategies based on optimizer switches and syntactic
+        properties.
+      */
       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)
+          0. The materialization optimizer switch was set.
+          1. Subquery is a single SELECT (not a UNION).
+             TODO: this is a limitation that can be fixed
           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
@@ -230,7 +231,7 @@ int check_and_do_in_subquery_rewrites(JOIN *join)
              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:
+             This condition is too restrictive (limitation). 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 &&
@@ -240,30 +241,30 @@ int check_and_do_in_subquery_rewrites(JOIN *join)
           (*) The subquery must be part of a SELECT statement. The current
                condition also excludes multi-table update statements.
         */
-        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
+        if (optimizer_flag(thd, OPTIMIZER_SWITCH_MATERIALIZATION) &&      // 0
+            !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) &&
+            (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;
+          in_subs->in_strategy|= SUBS_MATERIALIZATION;
         }
 
-        if (!in_subs->in_strategy)
+        /*
+          IN-TO-EXISTS is the only universal strategy. Choose it if the user
+          allowed it via an optimizer switch, or if materialization is not
+          possible.
+        */
+        if (optimizer_flag(thd, OPTIMIZER_SWITCH_IN_TO_EXISTS) ||
+            !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;
         }
       }
@@ -3703,10 +3704,9 @@ bool JOIN::choose_subquery_plan(table_map join_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())
+  if (is_in_subquery())
   {
-    in_subs= (Item_in_subselect*) select_lex->master_unit()->item;
+    in_subs= (Item_in_subselect*) unit->item;
     if (in_subs->create_in_to_exists_cond(this))
       return true;
   }
@@ -3943,11 +3943,10 @@ bool JOIN::choose_subquery_plan(table_map join_tables)
 bool JOIN::choose_tableless_subquery_plan()
 {
   DBUG_ASSERT(!tables_list || !tables);
-  if (select_lex->master_unit()->item)
+  if (unit->item)
   {
-    DBUG_ASSERT(select_lex->master_unit()->item->type() ==
-                Item::SUBSELECT_ITEM);
-    Item_subselect *subs_predicate= select_lex->master_unit()->item;
+    DBUG_ASSERT(unit->item->type() == Item::SUBSELECT_ITEM);
+    Item_subselect *subs_predicate= unit->item;
 
     /*
       If the optimizer determined that his query has an empty result,
@@ -3990,4 +3989,3 @@ bool JOIN::choose_tableless_subquery_plan()
   }
   return FALSE;
 }
-
diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc
index f1640bf71ed3718cc12ae58a5c9978110c8188f1..48f0b3bdbd59826916b88101ebb190c90de56a1d 100644
--- a/sql/sql_lex.cc
+++ b/sql/sql_lex.cc
@@ -3123,7 +3123,7 @@ bool st_select_lex::optimize_unflattened_subqueries()
         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)
+        if (options & SELECT_DESCRIBE)
         {
           /* Optimize the subquery in the context of EXPLAIN. */
           sl->set_explain_type();
diff --git a/sql/sql_list.h b/sql/sql_list.h
index e90f16aeb999df9ed02058da70770a2cf3cc39bc..7d9f3d26ec85e9028ce29d5be53c5d02bec784fc 100644
--- a/sql/sql_list.h
+++ b/sql/sql_list.h
@@ -260,7 +260,7 @@ class base_list :public Sql_alloc
     list_node *node= first;
     list_node *list_first= list->first;
     elements=0;
-    while (node->info && node != list_first)
+    while (node != &end_of_list && node != list_first)
     {
       prev= &node->next;
       node= node->next;
diff --git a/sql/sql_select.cc b/sql/sql_select.cc
index 151c22dc52c07455e608621591b86b728290c0b3..76b1e182eb0e398a752393f05c4ca3e09e9d3363 100644
--- a/sql/sql_select.cc
+++ b/sql/sql_select.cc
@@ -57,7 +57,8 @@ static bool update_ref_and_keys(THD *thd, DYNAMIC_ARRAY *keyuse,
                                 uint tables, COND *conds,
                                 table_map table_map, SELECT_LEX *select_lex,
                                 st_sargable_param **sargables);
-static bool sort_and_filter_keyuse(DYNAMIC_ARRAY *keyuse);
+static bool sort_and_filter_keyuse(DYNAMIC_ARRAY *keyuse,
+                                   bool skip_unprefixed_keyparts);
 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);
@@ -914,7 +915,6 @@ JOIN::optimize()
                            "Impossible HAVING" : "Impossible WHERE";
       tables= 0;
       error= 0;
-      choose_tableless_subquery_plan();
       goto setup_subq_exit;
     }
   }
@@ -966,7 +966,6 @@ JOIN::optimize()
 	zero_result_cause= "No matching min/max row";
         tables= 0;
 	error=0;
-        choose_tableless_subquery_plan();
         goto setup_subq_exit;
       }
       if (res > 1)
@@ -1007,7 +1006,6 @@ JOIN::optimize()
   {
     DBUG_PRINT("info",("No tables"));
     error= 0;
-    choose_tableless_subquery_plan();
     goto setup_subq_exit;
   }
   error= -1;					// Error is sent to client
@@ -1508,6 +1506,9 @@ JOIN::optimize()
   DBUG_RETURN(0);
 
 setup_subq_exit:
+  /* Choose an execution strategy for this JOIN. */
+  if (!tables_list || !tables)
+    choose_tableless_subquery_plan();
   /*
     Even with zero matching rows, subqueries in the HAVING clause may
     need to be evaluated if there are aggregate functions in the query.
@@ -3036,7 +3037,16 @@ make_join_statistics(JOIN *join, TABLE_LIST *tables_arg, COND *conds,
     if (update_ref_and_keys(join->thd, keyuse_array, stat, join->tables,
                             conds, ~outer_join, join->select_lex, &sargables))
       goto error;
-    if (keyuse_array->elements && sort_and_filter_keyuse(keyuse_array))
+    /*
+      Keyparts without prefixes may be useful if this JOIN is a subquery, and
+      if the subquery may be executed via the IN-EXISTS strategy.
+    */
+    bool skip_unprefixed_keyparts=
+      !(join->is_in_subquery() &&
+        ((Item_in_subselect*)join->unit->item)->in_strategy & SUBS_IN_TO_EXISTS);
+
+    if (keyuse_array->elements &&
+        sort_and_filter_keyuse(keyuse_array, skip_unprefixed_keyparts))
       goto error;
     DBUG_EXECUTE("opt", print_keyuse_array(keyuse_array););
   }
@@ -4505,7 +4515,8 @@ update_ref_and_keys(THD *thd, DYNAMIC_ARRAY *keyuse,JOIN_TAB *join_tab,
   Special treatment for ft-keys.
 */
 
-static bool sort_and_filter_keyuse(DYNAMIC_ARRAY *keyuse)
+static bool sort_and_filter_keyuse(DYNAMIC_ARRAY *keyuse, 
+                                   bool skip_unprefixed_keyparts)
 {
   KEYUSE key_end, *prev, *save_pos, *use;
   uint found_eq_constant, i;
@@ -4532,12 +4543,12 @@ static bool sort_and_filter_keyuse(DYNAMIC_ARRAY *keyuse)
       {
         if (use->key == prev->key && use->table == prev->table)
         {
-          if (prev->keypart+1 < use->keypart ||
+          if ((prev->keypart+1 < use->keypart && skip_unprefixed_keyparts) ||
               (prev->keypart == use->keypart && found_eq_constant))
             continue;				/* remove */
         }
-        else if (use->keypart != 0)		// First found must be 0
-          continue;
+        else if (use->keypart != 0 && skip_unprefixed_keyparts)
+          continue; /* remove - first found must be 0 */
       }
 
       prev= use;
@@ -16938,8 +16949,7 @@ create_sort_index(THD *thd, JOIN *join, ORDER *order,
   select= tab->select;
 
   /* Currently ORDER BY ... LIMIT is not supported in subqueries. */
-  DBUG_ASSERT(join->group_list ||
-              !(join->unit->item && join->unit->item->is_in_predicate()));
+  DBUG_ASSERT(join->group_list || !join->is_in_subquery());
 
   /*
     When there is SQL_BIG_RESULT do not sort using index for GROUP BY,
@@ -20659,7 +20669,7 @@ JOIN::reoptimize(Item *added_where, table_map join_tables,
   /* added_keyuse contents is copied, and it is no longer needed. */
   delete_dynamic(&added_keyuse);
 
-  if (sort_and_filter_keyuse(&keyuse))
+  if (sort_and_filter_keyuse(&keyuse, true))
     return REOPT_ERROR;
   optimize_keyuse(this, &keyuse);
 
diff --git a/sql/sql_select.h b/sql/sql_select.h
index f6862c4655497361ee39bfa3acd2a1a6c5611816..f67b79fff471a91ac098964e8d7b2dc6714d44cb 100644
--- a/sql/sql_select.h
+++ b/sql/sql_select.h
@@ -1083,6 +1083,11 @@ class JOIN :public Sql_alloc
                              double *read_time_arg, double *record_count_arg);
   /* defined in opt_subselect.cc */
   bool transform_max_min_subquery();
+  /* True if this JOIN is a subquery under an IN predicate. */
+  bool is_in_subquery()
+  {
+    return (unit->item && unit->item->is_in_predicate());
+  }
 
 private:
   /**