=== added file 'mysql-test/r/innodb_explain.result'
--- mysql-test/r/innodb_explain.result	1970-01-01 00:00:00 +0000
+++ mysql-test/r/innodb_explain.result	2013-05-21 05:27:36 +0000
@@ -0,0 +1,34 @@
+drop table if exists t1,t2,t3;
+#
+# MDEV-4536: sql_base.cc:1598: bool close_thread_table(THD*, TABLE**):
+#  Assertion `table->key_read == 0' failed.
+#
+set optimizer_switch="index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,index_merge_sort_intersection=off,engine_condition_pushdown=off,index_condition_pushdown=on,derived_merge=on,derived_with_keys=on,firstmatch=on,loosescan=on,materialization=on,in_to_exists=on,semijoin=on,partial_match_rowid_merge=on,partial_match_table_scan=on,subquery_cache=on,mrr=off,mrr_cost_based=off,mrr_sort_keys=off,outer_join_with_cache=on,semijoin_with_cache=on,join_cache_incremental=on,join_cache_hashed=on,join_cache_bka=on,optimize_join_buffer_size=off,table_elimination=on,extended_keys=off";
+CREATE TABLE t1 (pk int auto_increment primary key, `col_int_key` int(11),
+key `col_int_key` (`col_int_key`),`col_varchar_key` varchar(128), key
+(col_varchar_key)) engine=innodb;
+EXPLAIN SELECT 1 FROM t1 AS alias1 WHERE EXISTS ( SELECT SQ2_alias1 . col_int_key AS SQ2_field1 FROM ( t1 AS SQ2_alias1 RIGHT OUTER JOIN t1 AS SQ2_alias2 ON (SQ2_alias2 . col_int_key = SQ2_alias1 . col_int_key ) ) GROUP BY SQ2_field1 HAVING SQ2_alias1 . col_int_key >= 7 );
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	PRIMARY	NULL	NULL	NULL	NULL	NULL	NULL	NULL	Impossible WHERE
+2	SUBQUERY	SQ2_alias2	index	NULL	col_int_key	5	NULL	1	Using index; Using temporary; Using filesort
+2	SUBQUERY	SQ2_alias1	ref	col_int_key	col_int_key	5	test.SQ2_alias2.col_int_key	1	Using where; Using index
+set optimizer_switch=default;
+drop table t1;
+#
+# MDEV-4042: Assertion `table->key_read == 0' fails in
+# close_thread_table on EXPLAIN with GROUP BY and HAVING in
+# EXISTS SQ, InnoDB
+#
+CREATE TABLE t1 (a INT) ENGINE=InnoDB;
+CREATE TABLE t2 (b INT PRIMARY KEY, c INT) ENGINE=InnoDB;
+CREATE TABLE t3 (d INT) ENGINE=InnoDB;
+EXPLAIN 
+SELECT * FROM t1
+WHERE EXISTS ( SELECT b FROM t2, t3
+GROUP BY b
+HAVING b != 3 );
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	PRIMARY	NULL	NULL	NULL	NULL	NULL	NULL	NULL	Impossible WHERE
+2	SUBQUERY	t2	index	NULL	PRIMARY	4	NULL	1	Using index; Using temporary; Using filesort
+2	SUBQUERY	t3	ALL	NULL	NULL	NULL	NULL	1	Using join buffer (flat, BNL join)
+drop table t1,t2,t3;

=== added file 'mysql-test/t/innodb_explain.test'
--- mysql-test/t/innodb_explain.test	1970-01-01 00:00:00 +0000
+++ mysql-test/t/innodb_explain.test	2013-05-21 05:26:02 +0000
@@ -0,0 +1,41 @@
+#
+# Innodb specific explains
+
+--disable_warnings
+drop table if exists t1,t2,t3;
+--enable_warnings
+
+--source include/have_innodb.inc
+
+--echo #
+--echo # MDEV-4536: sql_base.cc:1598: bool close_thread_table(THD*, TABLE**):
+--echo #  Assertion `table->key_read == 0' failed.
+--echo #
+set optimizer_switch="index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,index_merge_sort_intersection=off,engine_condition_pushdown=off,index_condition_pushdown=on,derived_merge=on,derived_with_keys=on,firstmatch=on,loosescan=on,materialization=on,in_to_exists=on,semijoin=on,partial_match_rowid_merge=on,partial_match_table_scan=on,subquery_cache=on,mrr=off,mrr_cost_based=off,mrr_sort_keys=off,outer_join_with_cache=on,semijoin_with_cache=on,join_cache_incremental=on,join_cache_hashed=on,join_cache_bka=on,optimize_join_buffer_size=off,table_elimination=on,extended_keys=off";
+
+CREATE TABLE t1 (pk int auto_increment primary key, `col_int_key` int(11),
+key `col_int_key` (`col_int_key`),`col_varchar_key` varchar(128), key
+(col_varchar_key)) engine=innodb;
+
+EXPLAIN SELECT 1 FROM t1 AS alias1 WHERE EXISTS ( SELECT SQ2_alias1 . col_int_key AS SQ2_field1 FROM ( t1 AS SQ2_alias1 RIGHT OUTER JOIN t1 AS SQ2_alias2 ON (SQ2_alias2 . col_int_key = SQ2_alias1 . col_int_key ) ) GROUP BY SQ2_field1 HAVING SQ2_alias1 . col_int_key >= 7 );
+
+
+set optimizer_switch=default;
+drop table t1;
+
+--echo #
+--echo # MDEV-4042: Assertion `table->key_read == 0' fails in
+--echo # close_thread_table on EXPLAIN with GROUP BY and HAVING in
+--echo # EXISTS SQ, InnoDB
+--echo #
+CREATE TABLE t1 (a INT) ENGINE=InnoDB;
+CREATE TABLE t2 (b INT PRIMARY KEY, c INT) ENGINE=InnoDB;
+CREATE TABLE t3 (d INT) ENGINE=InnoDB;
+
+EXPLAIN 
+SELECT * FROM t1
+WHERE EXISTS ( SELECT b FROM t2, t3
+GROUP BY b
+HAVING b != 3 );
+
+drop table t1,t2,t3;

=== modified file 'sql/sql_select.cc'
--- sql/sql_select.cc	2013-05-07 11:05:09 +0000
+++ sql/sql_select.cc	2013-06-20 07:17:18 +0000
@@ -3680,8 +3680,8 @@ make_join_statistics(JOIN *join, List<TA
   if (pull_out_semijoin_tables(join))
     DBUG_RETURN(TRUE);
 
-  join->join_tab=stat;
-  join->top_join_tab_count= table_count;
+  join->table_access_tabs= join->join_tab=stat;
+  join->top_table_access_tabs_count= join->top_join_tab_count= table_count;
   join->map2table=stat_ref;
   join->table= table_vector;
   join->const_tables=const_count;
@@ -7380,6 +7380,13 @@ JOIN_TAB *first_linear_tab(JOIN *join, e
   return NULL; /* All tables were const tables */
 }
 
+static
+JOIN_TAB *first_optimizer_linear_tab(JOIN *join)
+{
+  return (join->top_table_access_tabs_count == 0 ?
+          NULL : join->table_access_tabs);
+}
+
 
 /*
   A helper function to loop over all join's join_tab in sequential fashion
@@ -7441,6 +7448,33 @@ JOIN_TAB *next_linear_tab(JOIN* join, JO
   return tab;
 }
 
+static
+JOIN_TAB *next_optimizer_linear_tab(JOIN* join, JOIN_TAB* tab)
+{
+  if (tab->bush_children)
+  {
+    /* This JOIN_TAB is a SJM nest; Start from first table in nest */
+    return tab->bush_children->start;
+  }
+
+  DBUG_ASSERT(!tab->last_leaf_in_bush || tab->bush_root_tab);
+
+  if (tab->bush_root_tab)       /* Are we inside an SJM nest */
+  {
+    /* Inside SJM nest */
+    if (!tab->last_leaf_in_bush)
+      return tab+1;              /* Return next in nest */
+    /* Continue from the sjm on the top level */
+    tab= tab->bush_root_tab;
+  }
+
+  /* If no more JOIN_TAB's on the top level */
+  if (++tab == join->table_access_tabs + join->top_table_access_tabs_count)
+    return NULL;
+
+  return tab;
+}
+
 
 /*
   Start to iterate over all join tables in bush-children-first order, excluding 
@@ -10646,6 +10680,14 @@ void JOIN::cleanup(bool full)
       {
 	tab->cleanup();
       }
+      if (join_tab != table_access_tabs)
+      {
+        for (tab= first_optimizer_linear_tab(this); tab;
+             tab= next_optimizer_linear_tab(this, tab))
+        {
+          tab->cleanup();
+        }
+      }
       cleaned= true;
     }
     else

