← Back to team overview

maria-developers team mailing list archive

Rev 2790: MWL#66 Subquery cache (IN & single rsult) in file:///Users/bell/maria/bzr/work-maria-5.3-subqueries-cache/

 

At file:///Users/bell/maria/bzr/work-maria-5.3-subqueries-cache/

------------------------------------------------------------
revno: 2790
revision-id: sanja@xxxxxxxxxxxx-20100414203404-8bw8wicortsfmga5
parent: timour@xxxxxxxxxxxx-20100405211515-istsgehaz7zafg0l
committer: sanja@xxxxxxxxxxxx
branch nick: work-maria-5.3-subqueries-cache
timestamp: Wed 2010-04-14 23:34:04 +0300
message:
  MWL#66 Subquery cache (IN & single rsult)
=== modified file 'libmysqld/Makefile.am'
--- a/libmysqld/Makefile.am	2010-03-20 12:01:47 +0000
+++ b/libmysqld/Makefile.am	2010-04-14 20:34:04 +0000
@@ -80,7 +80,8 @@ sqlsources = derror.cc field.cc field_co
         sql_tablespace.cc \
         rpl_injector.cc my_user.c partition_info.cc \
         sql_servers.cc event_parse_data.cc opt_table_elimination.cc \
-        multi_range_read.cc opt_index_cond_pushdown.cc
+        multi_range_read.cc opt_index_cond_pushdown.cc \
+        sql_subquery_cache.cc
 
 libmysqld_int_a_SOURCES= $(libmysqld_sources)
 nodist_libmysqld_int_a_SOURCES= $(libmysqlsources) $(sqlsources)

=== added file 'mysql-test/r/subquery_cache.result'
--- a/mysql-test/r/subquery_cache.result	1970-01-01 00:00:00 +0000
+++ b/mysql-test/r/subquery_cache.result	2010-04-14 20:34:04 +0000
@@ -0,0 +1,369 @@
+set optimizer_switch='subquery_cache=on';
+create table t1 (a int, b int);
+insert into t1 values (1,2),(3,4),(1,2),(3,4),(3,4),(4,5),(4,5),(5,6),(5,6),(4,5);
+create table t2 (c int, d int);
+insert into t2 values (2,3),(3,4),(5,6);
+#single value subquery test
+select a, (select d from t2 where b=c) + 1 from t1;
+a       (select d from t2 where b=c) + 1
+1       4
+3       NULL
+1       4
+3       NULL
+3       NULL
+4       7
+4       7
+5       NULL
+5       NULL
+4       7
+show status like "subquery_cache%";
+Variable_name   Value
+Subquery_cache_hit      6
+Subquery_cache_miss     4
+#single value subquery test (PS)
+prepare stmt1 from 'select a, (select d from t2 where b=c) + 1 from t1';
+execute stmt1;
+a       (select d from t2 where b=c) + 1
+1       4
+3       NULL
+1       4
+3       NULL
+3       NULL
+4       7
+4       7
+5       NULL
+5       NULL
+4       7
+show status like "subquery_cache%";
+Variable_name   Value
+Subquery_cache_hit      12
+Subquery_cache_miss     8
+execute stmt1;
+a       (select d from t2 where b=c) + 1
+1       4
+3       NULL
+1       4
+3       NULL
+3       NULL
+4       7
+4       7
+5       NULL
+5       NULL
+4       7
+show status like "subquery_cache%";
+Variable_name   Value
+Subquery_cache_hit      18
+Subquery_cache_miss     12
+deallocate prepare stmt1;
+#single value subquery test (SP)
+CREATE PROCEDURE p1() select a, (select d from t2 where b=c) + 1 from t1;
+call p1;
+a       (select d from t2 where b=c) + 1
+1       4
+3       NULL
+1       4
+3       NULL
+3       NULL
+4       7
+4       7
+5       NULL
+5       NULL
+4       7
+call p1;
+a       (select d from t2 where b=c) + 1
+1       4
+3       NULL
+1       4
+3       NULL
+3       NULL
+4       7
+4       7
+5       NULL
+5       NULL
+4       7
+drop procedure p1;
+#IN subquery test
+flush status;
+show status like "subquery_cache%";
+Variable_name   Value
+Subquery_cache_hit      0
+Subquery_cache_miss     0
+select a, b , b in (select d from t2) as SUBS from t1;
+a       b       SUBS
+1       2       0
+3       4       1
+1       2       0
+3       4       1
+3       4       1
+4       5       0
+4       5       0
+5       6       1
+5       6       1
+4       5       0
+show status like "subquery_cache%";
+Variable_name   Value
+Subquery_cache_hit      6
+Subquery_cache_miss     4
+insert into t1 values (7,8),(9,NULL);
+select a, b , b in (select d from t2) as SUBS from t1;
+a       b       SUBS
+1       2       0
+3       4       1
+1       2       0
+3       4       1
+3       4       1
+4       5       0
+4       5       0
+5       6       1
+5       6       1
+4       5       0
+7       8       0
+9       NULL    NULL
+show status like "subquery_cache%";
+Variable_name   Value
+Subquery_cache_hit      12
+Subquery_cache_miss     10
+insert into t2 values (8,NULL);
+select a, b , b in (select d from t2) as SUBS from t1;
+a       b       SUBS
+1       2       NULL
+3       4       1
+1       2       NULL
+3       4       1
+3       4       1
+4       5       NULL
+4       5       NULL
+5       6       1
+5       6       1
+4       5       NULL
+7       8       NULL
+9       NULL    NULL
+show status like "subquery_cache%";
+Variable_name   Value
+Subquery_cache_hit      18
+Subquery_cache_miss     16
+#IN subquery tesy (PS)
+delete from t1 where a > 6;
+delete from t2 where c > 6;
+prepare stmt1 from 'select a, b , b in (select d from t2) as SUBS from t1';
+execute stmt1;
+a       b       SUBS
+1       2       0
+3       4       1
+1       2       0
+3       4       1
+3       4       1
+4       5       0
+4       5       0
+5       6       1
+5       6       1
+4       5       0
+show status like "subquery_cache%";
+Variable_name   Value
+Subquery_cache_hit      24
+Subquery_cache_miss     20
+execute stmt1;
+a       b       SUBS
+1       2       0
+3       4       1
+1       2       0
+3       4       1
+3       4       1
+4       5       0
+4       5       0
+5       6       1
+5       6       1
+4       5       0
+show status like "subquery_cache%";
+Variable_name   Value
+Subquery_cache_hit      30
+Subquery_cache_miss     24
+insert into t1 values (7,8),(9,NULL);
+execute stmt1;
+a       b       SUBS
+1       2       0
+3       4       1
+1       2       0
+3       4       1
+3       4       1
+4       5       0
+4       5       0
+5       6       1
+5       6       1
+4       5       0
+9       NULL    NULL
+7       8       0
+show status like "subquery_cache%";
+Variable_name   Value
+Subquery_cache_hit      36
+Subquery_cache_miss     30
+execute stmt1;
+a       b       SUBS
+1       2       0
+3       4       1
+1       2       0
+3       4       1
+3       4       1
+4       5       0
+4       5       0
+5       6       1
+5       6       1
+4       5       0
+9       NULL    NULL
+7       8       0
+show status like "subquery_cache%";
+Variable_name   Value
+Subquery_cache_hit      42
+Subquery_cache_miss     36
+insert into t2 values (8,NULL);
+execute stmt1;
+a       b       SUBS
+1       2       NULL
+3       4       1
+1       2       NULL
+3       4       1
+3       4       1
+4       5       NULL
+4       5       NULL
+5       6       1
+5       6       1
+4       5       NULL
+9       NULL    NULL
+7       8       NULL
+show status like "subquery_cache%";
+Variable_name   Value
+Subquery_cache_hit      48
+Subquery_cache_miss     42
+execute stmt1;
+a       b       SUBS
+1       2       NULL
+3       4       1
+1       2       NULL
+3       4       1
+3       4       1
+4       5       NULL
+4       5       NULL
+5       6       1
+5       6       1
+4       5       NULL
+9       NULL    NULL
+7       8       NULL
+show status like "subquery_cache%";
+Variable_name   Value
+Subquery_cache_hit      54
+Subquery_cache_miss     48
+deallocate prepare stmt1;
+#IN subquery tesy (SP)
+delete from t1 where a > 6;
+delete from t2 where c > 6;
+CREATE PROCEDURE p1() select a, b , b in (select d from t2) as SUBS from t1;
+call p1();
+a       b       SUBS
+1       2       0
+3       4       1
+1       2       0
+3       4       1
+3       4       1
+4       5       0
+4       5       0
+5       6       1
+5       6       1
+4       5       0
+show status like "subquery_cache%";
+Variable_name   Value
+Subquery_cache_hit      60
+Subquery_cache_miss     52
+call p1();
+a       b       SUBS
+1       2       0
+3       4       1
+1       2       0
+3       4       1
+3       4       1
+4       5       0
+4       5       0
+5       6       1
+5       6       1
+4       5       0
+show status like "subquery_cache%";
+Variable_name   Value
+Subquery_cache_hit      66
+Subquery_cache_miss     56
+insert into t1 values (7,8),(9,NULL);
+call p1();
+a       b       SUBS
+1       2       0
+3       4       1
+1       2       0
+3       4       1
+3       4       1
+4       5       0
+4       5       0
+5       6       1
+5       6       1
+4       5       0
+9       NULL    NULL
+7       8       0
+show status like "subquery_cache%";
+Variable_name   Value
+Subquery_cache_hit      72
+Subquery_cache_miss     62
+call p1();
+a       b       SUBS
+1       2       0
+3       4       1
+1       2       0
+3       4       1
+3       4       1
+4       5       0
+4       5       0
+5       6       1
+5       6       1
+4       5       0
+9       NULL    NULL
+7       8       0
+show status like "subquery_cache%";
+Variable_name   Value
+Subquery_cache_hit      78
+Subquery_cache_miss     68
+insert into t2 values (8,NULL);
+call p1();
+a       b       SUBS
+1       2       NULL
+3       4       1
+1       2       NULL
+3       4       1
+3       4       1
+4       5       NULL
+4       5       NULL
+5       6       1
+5       6       1
+4       5       NULL
+9       NULL    NULL
+7       8       NULL
+show status like "subquery_cache%";
+Variable_name   Value
+Subquery_cache_hit      84
+Subquery_cache_miss     74
+call p1();
+a       b       SUBS
+1       2       NULL
+3       4       1
+1       2       NULL
+3       4       1
+3       4       1
+4       5       NULL
+4       5       NULL
+5       6       1
+5       6       1
+4       5       NULL
+9       NULL    NULL
+7       8       NULL
+show status like "subquery_cache%";
+Variable_name   Value
+Subquery_cache_hit      90
+Subquery_cache_miss     80
+drop procedure p1;
+#clean up
+drop table t1,t2;
+set optimizer_switch='subquery_cache=default';

=== added file 'mysql-test/t/subquery_cache.test'
--- a/mysql-test/t/subquery_cache.test	1970-01-01 00:00:00 +0000
+++ b/mysql-test/t/subquery_cache.test	2010-04-14 20:34:04 +0000
@@ -0,0 +1,98 @@
+
+set optimizer_switch='subquery_cache=on';
+
+create table t1 (a int, b int);
+insert into t1 values (1,2),(3,4),(1,2),(3,4),(3,4),(4,5),(4,5),(5,6),(5,6),(4,5);
+create table t2 (c int, d int);
+insert into t2 values (2,3),(3,4),(5,6);
+
+--echo #single value subquery test
+select a, (select d from t2 where b=c) + 1 from t1;
+
+show status like "subquery_cache%";
+
+--echo #single value subquery test (PS)
+prepare stmt1 from 'select a, (select d from t2 where b=c) + 1 from t1';
+execute stmt1;
+show status like "subquery_cache%";
+execute stmt1;
+show status like "subquery_cache%";
+deallocate prepare stmt1;
+
+--echo #single value subquery test (SP)
+CREATE PROCEDURE p1() select a, (select d from t2 where b=c) + 1 from t1;
+
+call p1;
+call p1;
+
+drop procedure p1;
+
+--echo #IN subquery test
+flush status;
+
+show status like "subquery_cache%";
+select a, b , b in (select d from t2) as SUBS from t1;
+show status like "subquery_cache%";
+
+insert into t1 values (7,8),(9,NULL);
+select a, b , b in (select d from t2) as SUBS from t1;
+show status like "subquery_cache%";
+
+insert into t2 values (8,NULL);
+select a, b , b in (select d from t2) as SUBS from t1;
+show status like "subquery_cache%";
+
+--echo #IN subquery tesy (PS)
+delete from t1 where a > 6;
+delete from t2 where c > 6;
+
+prepare stmt1 from 'select a, b , b in (select d from t2) as SUBS from t1';
+execute stmt1;
+show status like "subquery_cache%";
+execute stmt1;
+show status like "subquery_cache%";
+
+insert into t1 values (7,8),(9,NULL);
+execute stmt1;
+show status like "subquery_cache%";
+execute stmt1;
+show status like "subquery_cache%";
+
+insert into t2 values (8,NULL);
+execute stmt1;
+show status like "subquery_cache%";
+execute stmt1;
+show status like "subquery_cache%";
+
+deallocate prepare stmt1;
+
+
+--echo #IN subquery tesy (SP)
+delete from t1 where a > 6;
+delete from t2 where c > 6;
+
+CREATE PROCEDURE p1() select a, b , b in (select d from t2) as SUBS from t1;
+
+call p1();
+show status like "subquery_cache%";
+call p1();
+show status like "subquery_cache%";
+
+insert into t1 values (7,8),(9,NULL);
+call p1();
+show status like "subquery_cache%";
+call p1();
+show status like "subquery_cache%";
+
+insert into t2 values (8,NULL);
+call p1();
+show status like "subquery_cache%";
+call p1();
+show status like "subquery_cache%";
+
+drop procedure p1;
+
+--echo #clean up
+drop table t1,t2;
+
+set optimizer_switch='subquery_cache=default';

=== modified file 'sql/CMakeLists.txt'
--- a/sql/CMakeLists.txt	2010-03-20 12:01:47 +0000
+++ b/sql/CMakeLists.txt	2010-04-14 20:34:04 +0000
@@ -78,7 +78,7 @@ SET (SQL_SOURCE
                rpl_rli.cc rpl_mi.cc sql_servers.cc
                sql_connect.cc scheduler.cc 
                sql_profile.cc event_parse_data.cc opt_table_elimination.cc
-               ds_mrr.cc
+               ds_mrr.cc sql_subquery_cache.cc
                ${PROJECT_SOURCE_DIR}/sql/sql_yacc.cc
                ${PROJECT_SOURCE_DIR}/sql/sql_yacc.h
                ${PROJECT_SOURCE_DIR}/include/mysqld_error.h

=== modified file 'sql/Makefile.am'
--- a/sql/Makefile.am	2010-03-20 12:01:47 +0000
+++ b/sql/Makefile.am	2010-04-14 20:34:04 +0000
@@ -80,7 +80,7 @@ noinst_HEADERS =	item.h item_func.h item
                         event_data_objects.h event_scheduler.h \
                         sql_partition.h partition_info.h partition_element.h \
                         contributors.h sql_servers.h \
-                        multi_range_read.h
+                        multi_range_read.h sql_subquery_cache.h
 
 mysqld_SOURCES =        sql_lex.cc sql_handler.cc sql_partition.cc \
                         item.cc item_sum.cc item_buff.cc item_func.cc \
@@ -130,7 +130,7 @@ mysqld_SOURCES =	sql_lex.cc sql_handler.
                         sql_servers.cc event_parse_data.cc \
                         opt_table_elimination.cc \
                         multi_range_read.cc \
-                        opt_index_cond_pushdown.cc
+                        opt_index_cond_pushdown.cc sql_subquery_cache.cc
 
 nodist_mysqld_SOURCES = mini_client_errors.c pack.c client.c my_time.c my_user.c 
 

=== modified file 'sql/item.cc'
--- a/sql/item.cc	2010-03-20 12:01:47 +0000
+++ b/sql/item.cc	2010-04-14 20:34:04 +0000
@@ -2273,6 +2273,13 @@ void Item_int::print(String *str, enum_q
   str->append(str_value);
 }
 
+void Item_bool_cache::print(String *str, enum_query_type query_type)
+{
+  if (null_value)
+    str->append("NULL", 4);
+  else
+    Item_int::print(str, query_type);
+}
 
 Item_uint::Item_uint(const char *str_arg, uint length):
   Item_int(str_arg, length)
@@ -3646,12 +3653,17 @@ static bool mark_as_dependent(THD *thd, 
                         resolved_item->db_name : "");
   const char *table_name= (resolved_item->table_name ?
                            resolved_item->table_name : "");
+  DBUG_ENTER("mark_as_dependent");
+  DBUG_PRINT("enter", ("Field '%s.%s.%s in select %d resolved in %d",
+                       db_name, table_name,
+                       resolved_item->field_name, current->select_number,
+                       last->select_number));
   /* store pointer on SELECT_LEX from which item is dependent */
   if (mark_item)
     mark_item->depended_from= last;
   if (current->mark_as_dependent(thd, last, /** resolved_item psergey-thu
     **/mark_item))
-    return TRUE;
+    DBUG_RETURN(TRUE);
   if (thd->lex->describe & DESCRIBE_EXTENDED)
   {
     push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
@@ -3661,7 +3673,7 @@ static bool mark_as_dependent(THD *thd, 
                  resolved_item->field_name,
                  current->select_number, last->select_number);
   }
-  return FALSE;
+  DBUG_RETURN(FALSE);
 }
 
 
@@ -3726,6 +3739,7 @@ void mark_select_range_as_dependent(THD 
     mark_as_dependent(thd, last_select, current_sel, resolved_item,
                       dependent);
   }
+  return;
 }
 
 

=== modified file 'sql/item.h'
--- a/sql/item.h	2010-03-20 12:01:47 +0000
+++ b/sql/item.h	2010-04-14 20:34:04 +0000
@@ -1922,8 +1922,31 @@ public:
   virtual void print(String *str, enum_query_type query_type);
   Item_num *neg ();
   uint decimal_precision() const { return max_length; }
-  bool check_partition_func_processor(uchar *bool_arg) { return FALSE;}
-  bool check_vcol_func_processor(uchar *arg) { return FALSE;}
+};
+
+
+/**
+  Item represent TRUE/FALSE/NULL for subquery values
+*/
+
+class Item_bool_cache: public Item_int
+{
+public:
+   Item_bool_cache(): Item_int(0, 1)
+   {
+     unsigned_flag= maybe_null= null_value= TRUE;
+     name= (char *)"bool chache";
+   }
+   Item_bool_cache(my_bool val, my_bool null): Item_int(val, 1)
+   {
+     unsigned_flag= maybe_null= TRUE;
+     null_value= null;
+     name= (char *)"bool chache";
+   }
+  Item *clone_item() { return new Item_bool_cache(value, null_value); }
+  uint decimal_precision() const { return 1; }
+  virtual void print(String *str, enum_query_type query_type);
+  void set(my_bool val, my_bool null) {value= test(val); null_value= null;}
 };
 
 
@@ -3146,7 +3169,8 @@ public:
     example(0), used_table_map(0), cached_field(0), cached_field_type(MYSQL_TYPE_STRING),
     value_cached(0)
   {
-    fixed= 1; 
+    fixed= 1;
+    maybe_null= 1;
     null_value= 1;
   }
   Item_cache(enum_field_types field_type_arg):
@@ -3154,6 +3178,7 @@ public:
     value_cached(0)
   {
     fixed= 1;
+    maybe_null= 1;
     null_value= 1;
   }
 

=== modified file 'sql/item_cmpfunc.cc'
--- a/sql/item_cmpfunc.cc	2010-03-20 12:01:47 +0000
+++ b/sql/item_cmpfunc.cc	2010-04-14 20:34:04 +0000
@@ -1736,6 +1736,12 @@ bool Item_in_optimizer::fix_fields(THD *
   used_tables_cache|= args[1]->used_tables();
   not_null_tables_cache|= args[1]->not_null_tables();
   const_item_cache&= args[1]->const_item();
+  DBUG_ASSERT(scache == NULL);
+  if (thd->variables.optimizer_switch & OPTIMIZER_SWITCH_SUBQUERY_CACHE)
+  {
+    sub->depends_on.push_front(cache);
+    scache= new Subquery_cache_tmptable(thd, sub->depends_on, &result);
+  }
   fixed= 1;
   return FALSE;
 }
@@ -1744,10 +1750,26 @@ bool Item_in_optimizer::fix_fields(THD *
 longlong Item_in_optimizer::val_int()
 {
   bool tmp;
+  DBUG_ENTER("Item_in_optimizer::val_int");
+
   DBUG_ASSERT(fixed == 1);
   cache->store(args[0]);
   cache->cache_value();
-  
+
+  /* check if result is in the cache */
+  if (scache)
+  {
+    Subquery_cache_tmptable::result res;
+    Item *cached_value;
+    res= scache->check_value(&cached_value);
+    if (res == Subquery_cache_tmptable::HIT)
+    {
+      tmp= cached_value->val_int();
+      null_value= cached_value->null_value;
+      DBUG_RETURN(tmp);
+    }
+  }
+
   if (cache->null_value)
   {
     /*
@@ -1818,11 +1840,18 @@ longlong Item_in_optimizer::val_int()
       for (uint i= 0; i < ncols; i++)
         item_subs->set_cond_guard_var(i, TRUE);
     }
-    return 0;
+    DBUG_RETURN(0);
   }
   tmp= args[1]->val_bool_result();
   null_value= args[1]->null_value;
-  return tmp;
+
+  /* put result in the cache */
+  if (scache)
+  {
+    result.set(tmp, null_value);
+    scache->put_value(&result);
+  }
+  DBUG_RETURN(tmp);
 }
 
 
@@ -1839,6 +1868,11 @@ void Item_in_optimizer::cleanup()
   Item_bool_func::cleanup();
   if (!save_cache)
     cache= 0;
+  if (scache)
+  {
+    delete scache;
+    scache= 0;
+  }
   DBUG_VOID_RETURN;
 }
 

=== modified file 'sql/item_cmpfunc.h'
--- a/sql/item_cmpfunc.h	2010-03-20 12:01:47 +0000
+++ b/sql/item_cmpfunc.h	2010-04-14 20:34:04 +0000
@@ -215,6 +215,7 @@ public:
 
 
 class Item_cache;
+class Subquery_cache;
 #define UNKNOWN ((my_bool)-1)
 
 
@@ -237,6 +238,10 @@ class Item_in_optimizer: public Item_boo
 {
 protected:
   Item_cache *cache;
+  /* Subquery cache */
+  Subquery_cache *scache;
+  /* result representation for the subquery cache */
+  Item_bool_cache result;
   bool save_cache;
   /* 
     Stores the value of "NULL IN (SELECT ...)" for uncorrelated subqueries:
@@ -247,7 +252,7 @@ protected:
   my_bool result_for_null_param;
 public:
   Item_in_optimizer(Item *a, Item_in_subselect *b):
-    Item_bool_func(a, my_reinterpret_cast(Item *)(b)), cache(0),
+    Item_bool_func(a, my_reinterpret_cast(Item *)(b)), cache(0), scache(NULL),
     save_cache(0), result_for_null_param(UNKNOWN)
   {}
   bool fix_fields(THD *, Item **);

=== modified file 'sql/item_subselect.cc'
--- a/sql/item_subselect.cc	2010-04-02 14:27:06 +0000
+++ b/sql/item_subselect.cc	2010-04-14 20:34:04 +0000
@@ -1,4 +1,4 @@
-/* Copyright (C) 2000 MySQL AB
+/* Copyrigh (C) 2000 MySQL AB
 
    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
@@ -34,11 +34,10 @@
 
 Item_subselect::Item_subselect():
   Item_result_field(), value_assigned(0), thd(0), substitution(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)
+  engine(0), old_engine(0), scache(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)
 {
   with_subselect= 1;
   reset();
@@ -116,6 +115,12 @@ void Item_subselect::cleanup()
   }
   if (engine)
     engine->cleanup();
+  depends_on.empty();
+  if (scache)
+  {
+    delete scache;
+    scache= 0;
+  }
   reset();
   value_assigned= 0;
   DBUG_VOID_RETURN;
@@ -148,6 +153,8 @@ void Item_in_subselect::cleanup()
 Item_subselect::~Item_subselect()
 {
   delete engine;
+  if (scache)
+    delete scache;
 }
 
 Item_subselect::trans_res
@@ -746,9 +753,19 @@ enum_field_types Item_singlerow_subselec
 
 void Item_singlerow_subselect::fix_length_and_dec()
 {
+  DBUG_ENTER("Item_singlerow_subselect::fix_length_and_dec");
   if ((max_columns= engine->cols()) == 1)
   {
+    DBUG_PRINT("info", ("one, elements: %u  flag %u",
+                        (uint)depends_on.elements,
+                        (uint)test(thd->variables.optimizer_switch & OPTIMIZER_SWITCH_SUBQUERY_CACHE)));
     engine->fix_length_and_dec(row= &value);
+    if (depends_on.elements && optimizer_flag(thd, OPTIMIZER_SWITCH_SUBQUERY_CACHE))
+    {
+      DBUG_ASSERT(scache == NULL);
+      scache= new Subquery_cache_tmptable(thd, depends_on, value);
+      DBUG_PRINT("info", ("cache: 0x%lx", (ulong) scache));
+    }
   }
   else
   {
@@ -765,6 +782,7 @@ void Item_singlerow_subselect::fix_lengt
   */
   if (engine->no_tables())
     maybe_null= engine->may_be_null();
+  DBUG_VOID_RETURN;
 }
 
 uint Item_singlerow_subselect::cols()
@@ -797,46 +815,117 @@ void Item_singlerow_subselect::bring_val
   exec();
 }
 
+
+Item *Item_singlerow_subselect::check_cache()
+{
+  DBUG_ENTER("Item_singlerow_subselect::check_cache");
+  if (scache)
+  {
+    Subquery_cache_tmptable::result res;
+    Item *cached_value;
+    res= scache->check_value(&cached_value);
+    if (res == Subquery_cache_tmptable::HIT)
+      DBUG_RETURN(cached_value);
+  }
+  DBUG_RETURN(NULL);
+}
+
 double Item_singlerow_subselect::val_real()
 {
+  Item *cached_value;
   DBUG_ASSERT(fixed == 1);
+
+  if ((cached_value = check_cache()))
+  {
+    double res= cached_value->val_real();
+    if ((null_value= cached_value->null_value))
+    {
+      reset();
+      return 0;
+    }
+    else
+      return res;
+  }
+
   if (!exec() && !value->null_value)
   {
     null_value= 0;
+    if (scache)
+      scache->put_value(value);
     return value->val_real();
   }
   else
   {
     reset();
+    if (scache)
+      scache->put_value(&const_null_value);
     return 0;
   }
 }
 
 longlong Item_singlerow_subselect::val_int()
 {
+  Item *cached_value;
+  DBUG_ENTER("Item_singlerow_subselect::val_int");
   DBUG_ASSERT(fixed == 1);
+
+  if ((cached_value = check_cache()))
+  {
+    longlong res= cached_value->val_int();
+    if ((null_value= cached_value->null_value))
+    {
+      reset();
+      DBUG_RETURN(0);
+    }
+    else
+      DBUG_RETURN(res);
+  }
+
   if (!exec() && !value->null_value)
   {
     null_value= 0;
-    return value->val_int();
+    if (scache)
+      scache->put_value(value);
+    DBUG_RETURN(value->val_int());
   }
   else
   {
     reset();
-    return 0;
+    if (scache)
+      scache->put_value(&const_null_value);
+    DBUG_RETURN(0);
   }
 }
 
 String *Item_singlerow_subselect::val_str(String *str)
 {
+  Item *cached_value;
+  DBUG_ASSERT(fixed == 1);
+
+  if ((cached_value = check_cache()))
+  {
+    String *res= cached_value->val_str(str);
+    if ((null_value= cached_value->null_value))
+    {
+      reset();
+      return 0;
+    }
+    else
+      return res;
+  }
+
   if (!exec() && !value->null_value)
   {
     null_value= 0;
+    if (scache)
+      scache->put_value(value);
     return value->val_str(str);
   }
   else
   {
     reset();
+    if (scache)
+      scache->put_value(&const_null_value);
     return 0;
   }
 }
@@ -844,14 +933,33 @@ String *Item_singlerow_subselect::val_st
 
 my_decimal *Item_singlerow_subselect::val_decimal(my_decimal *decimal_value)
 {
+  Item *cached_value;
+  DBUG_ASSERT(fixed == 1);
+
+  if ((cached_value = check_cache()))
+  {
+    my_decimal *res= cached_value->val_decimal(decimal_value);
+    if ((null_value= cached_value->null_value))
+    {
+      reset();
+      return 0;
+    }
+    else
+      return res;
+  }
+
   if (!exec() && !value->null_value)
   {
     null_value= 0;
+    if (scache)
+      scache->put_value(value);
     return value->val_decimal(decimal_value);
   }
   else
   {
     reset();
+    if (scache)
+      scache->put_value(&const_null_value);
     return 0;
   }
 }
@@ -859,14 +967,33 @@ my_decimal *Item_singlerow_subselect::va
 
 bool Item_singlerow_subselect::val_bool()
 {
+  Item *cached_value;
+  DBUG_ASSERT(fixed == 1);
+
+  if ((cached_value = check_cache()))
+  {
+    bool res= cached_value->val_bool();
+    if ((null_value= cached_value->null_value))
+    {
+      reset();
+      return 0;
+    }
+    else
+      return res;
+  }
+
   if (!exec() && !value->null_value)
   {
     null_value= 0;
+    if (scache)
+      scache->put_value(value);
     return value->val_bool();
   }
   else
   {
     reset();
+    if (scache)
+      scache->put_value(&const_null_value);
     return 0;
   }
 }

=== modified file 'sql/item_subselect.h'
--- a/sql/item_subselect.h	2010-03-29 14:04:35 +0000
+++ b/sql/item_subselect.h	2010-04-14 20:34:04 +0000
@@ -27,6 +27,7 @@ class subselect_engine;
 class subselect_hash_sj_engine;
 class Item_bool_func2;
 class Cached_item;
+class Subquery_cache;
 
 /* base class for subselects */
 
@@ -57,6 +58,10 @@ protected:
   subselect_engine *engine;
   /* old engine if engine was changed */
   subselect_engine *old_engine;
+  /* subquery cache */
+  Subquery_cache *scache;
+  /* null consrtant for caching */
+  Item_null const_null_value;
   /* cache of used external tables */
   table_map used_tables_cache;
   /* allowed number of columns (1 for single value subqueries) */
@@ -67,7 +72,7 @@ protected:
   bool have_to_be_excluded;
   /* cache of constant state */
   bool const_item_cache;
-  
+
   bool inside_first_fix_fields;
   bool done_first_fix_fields;
 public:
@@ -88,13 +93,18 @@ public:
   */
   List<Ref_to_outside> upper_refs;
   st_select_lex *parent_select;
-  
- /*
+
+  /**
+     List of items subquery depends on (externally resolved);
+   */
+  List<Item> depends_on;
+
+  /*
    TRUE<=>Table Elimination has made it redundant to evaluate this select
           (and so it is not part of QEP, etc)
- */
+  */
   bool eliminated;
-  
+
   /* changed engine indicator */
   bool engine_changed;
   /* subquery is transformed */
@@ -202,6 +212,8 @@ class Item_singlerow_subselect :public I
 {
 protected:
   Item_cache *value, **row;
+
+  Item *check_cache();
 public:
   Item_singlerow_subselect(st_select_lex *select_lex);
   Item_singlerow_subselect() :Item_subselect(), value(0), row (0) {}

=== modified file 'sql/mysql_priv.h'
--- a/sql/mysql_priv.h	2010-03-20 12:01:47 +0000
+++ b/sql/mysql_priv.h	2010-04-14 20:34:04 +0000
@@ -568,12 +568,13 @@ protected:
 #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)
 
 #ifdef DBUG_OFF
-#  define OPTIMIZER_SWITCH_LAST 2048
+#  define OPTIMIZER_SWITCH_LAST (1<<12)
 #else
-#  define OPTIMIZER_SWITCH_TABLE_ELIMINATION 2048
-#  define OPTIMIZER_SWITCH_LAST 4096
+#  define OPTIMIZER_SWITCH_TABLE_ELIMINATION (1<<12)
+#  define OPTIMIZER_SWITCH_LAST (1<<13)
 #endif
 
 #ifdef DBUG_OFF 
@@ -588,7 +589,8 @@ protected:
                                     OPTIMIZER_SWITCH_MATERIALIZATION | \
                                     OPTIMIZER_SWITCH_SEMIJOIN | \
                                     OPTIMIZER_SWITCH_PARTIAL_MATCH_ROWID_MERGE|\
-                                    OPTIMIZER_SWITCH_PARTIAL_MATCH_TABLE_SCAN)
+                                    OPTIMIZER_SWITCH_PARTIAL_MATCH_TABLE_SCAN|\
+                                    OPTIMIZER_SWITCH_SUBQUERY_CACHE)
 #else
 #  define OPTIMIZER_SWITCH_DEFAULT (OPTIMIZER_SWITCH_INDEX_MERGE | \
                                     OPTIMIZER_SWITCH_INDEX_MERGE_UNION | \
@@ -601,7 +603,8 @@ protected:
                                     OPTIMIZER_SWITCH_MATERIALIZATION | \
                                     OPTIMIZER_SWITCH_SEMIJOIN | \
                                     OPTIMIZER_SWITCH_PARTIAL_MATCH_ROWID_MERGE|\
-                                    OPTIMIZER_SWITCH_PARTIAL_MATCH_TABLE_SCAN)
+                                    OPTIMIZER_SWITCH_PARTIAL_MATCH_TABLE_SCAN|\
+                                    OPTIMIZER_SWITCH_SUBQUERY_CACHE)
 #endif
 
 /*
@@ -936,6 +939,7 @@ bool general_log_write(THD *thd, enum en
 #ifdef MYSQL_SERVER
 #include "sql_servers.h"
 #include "opt_range.h"
+#include "sql_subquery_cache.h"
 
 #ifdef HAVE_QUERY_CACHE
 struct Query_cache_query_flags
@@ -1269,6 +1273,10 @@ bool mysql_select(THD *thd, Item ***rref
                   Item *having, ORDER *proc_param, ulonglong select_type, 
                   select_result *result, SELECT_LEX_UNIT *unit, 
                   SELECT_LEX *select_lex);
+
+struct st_join_table *create_index_lookup_join_tab(TABLE *table);
+int join_read_key2(THD *thd, struct st_join_table *tab, TABLE *table,
+                   struct st_table_ref *table_ref);
 void free_underlaid_joins(THD *thd, SELECT_LEX *select);
 bool mysql_explain_union(THD *thd, SELECT_LEX_UNIT *unit,
                          select_result *result);
@@ -1288,6 +1296,7 @@ Field *create_tmp_field(THD *thd, TABLE 
                         bool table_cant_handle_bit_fields,
                         bool make_copy_field,
                         uint convert_blob_length);
+bool open_tmp_table(TABLE *table);
 void sp_prepare_create_field(THD *thd, Create_field *sql_field);
 int prepare_create_field(Create_field *sql_field, 
                          uint *blob_columns, 

=== modified file 'sql/mysqld.cc'
--- a/sql/mysqld.cc	2010-03-20 12:01:47 +0000
+++ b/sql/mysqld.cc	2010-04-14 20:34:04 +0000
@@ -305,6 +305,7 @@ static const char *optimizer_switch_name
   "firstmatch","loosescan","materialization", "semijoin",
   "partial_match_rowid_merge",
   "partial_match_table_scan",
+  "subquery_cache",
 #ifndef DBUG_OFF
   "table_elimination",
 #endif
@@ -325,6 +326,7 @@ static const unsigned int optimizer_swit
   sizeof("semijoin") - 1,
   sizeof("partial_match_rowid_merge") - 1,
   sizeof("partial_match_table_scan") - 1,
+  sizeof("subquery_cache") - 1,
 #ifndef DBUG_OFF
   sizeof("table_elimination") - 1,
 #endif
@@ -404,8 +406,9 @@ static const char *sql_mode_str= "OFF";
 static const char *optimizer_switch_str="index_merge=on,index_merge_union=on,"
                                         "index_merge_sort_union=on,"
                                         "index_merge_intersection=on,"
-                                        "index_condition_pushdown=on"
-#ifndef DBUG_OFF                                        
+                                        "index_condition_pushdown=on,"
+                                        "subquery_cache=on"
+#ifndef DBUG_OFF
                                         ",table_elimination=on";
 #else
                                         ;
@@ -5872,7 +5875,9 @@ enum options_mysqld
   OPT_RECORD_RND_BUFFER, OPT_DIV_PRECINCREMENT, OPT_RELAY_LOG_SPACE_LIMIT,
   OPT_RELAY_LOG_PURGE,
   OPT_SLAVE_NET_TIMEOUT, OPT_SLAVE_COMPRESSED_PROTOCOL, OPT_SLOW_LAUNCH_TIME,
-  OPT_SLAVE_TRANS_RETRIES, OPT_READONLY, OPT_ROWID_MERGE_BUFF_SIZE,
+  OPT_SLAVE_TRANS_RETRIES,
+  OPT_SUBQUERY_CACHE,
+  OPT_READONLY, OPT_ROWID_MERGE_BUFF_SIZE,
   OPT_DEBUGGING, OPT_DEBUG_FLUSH,
   OPT_SORT_BUFFER, OPT_TABLE_OPEN_CACHE, OPT_TABLE_DEF_CACHE,
   OPT_THREAD_CONCURRENCY, OPT_THREAD_CACHE_SIZE,
@@ -7164,7 +7169,7 @@ The minimum value for this variable is 4
   {"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"
+   "index_condition_pushdown, subquery_cache"
 #ifndef DBUG_OFF
    ", table_elimination"
 #endif 
@@ -7868,6 +7873,8 @@ SHOW_VAR status_vars[]= {
   {"Ssl_version",              (char*) &show_ssl_get_version, SHOW_FUNC},
 #endif /* HAVE_OPENSSL */
   {"Syncs",                    (char*) &my_sync_count,          SHOW_LONG_NOFLUSH},
+  {"Subquery_cache_hit",       (char*) &subquery_cache_hit, SHOW_LONG},
+  {"Subquery_cache_miss",      (char*) &subquery_cache_miss, SHOW_LONG},
   {"Table_locks_immediate",    (char*) &locks_immediate,        SHOW_LONG},
   {"Table_locks_waited",       (char*) &locks_waited,           SHOW_LONG},
 #ifdef HAVE_MMAP
@@ -8006,6 +8013,7 @@ static int mysql_init_variables(void)
   abort_loop= select_thread_in_use= signal_thread_in_use= 0;
   ready_to_exit= shutdown_in_progress= grant_option= 0;
   aborted_threads= aborted_connects= 0;
+  subquery_cache_miss= subquery_cache_hit= 0;
   delayed_insert_threads= delayed_insert_writes= delayed_rows_in_use= 0;
   delayed_insert_errors= thread_created= 0;
   specialflag= 0;

=== modified file 'sql/sql_base.cc'
--- a/sql/sql_base.cc	2010-03-20 12:01:47 +0000
+++ b/sql/sql_base.cc	2010-04-14 20:34:04 +0000
@@ -8062,6 +8062,10 @@ int setup_conds(THD *thd, TABLE_LIST *ta
   if (*conds)
   {
     thd->where="where clause";
+    DBUG_EXECUTE("where",
+                 print_where(*conds,
+                             "WHERE in setup_conds",
+                             QT_ORDINARY););
     if ((!(*conds)->fixed && (*conds)->fix_fields(thd, conds)) ||
         (*conds)->check_cols(1))
       goto err_no_arena;

=== modified file 'sql/sql_class.cc'
--- a/sql/sql_class.cc	2010-04-05 21:15:15 +0000
+++ b/sql/sql_class.cc	2010-04-14 20:34:04 +0000
@@ -3034,6 +3034,7 @@ void TMP_TABLE_PARAM::init()
   table_charset= 0;
   precomputed_group_by= 0;
   bit_fields_as_long= 0;
+  skip_create_table= 0;
   DBUG_VOID_RETURN;
 }
 

=== modified file 'sql/sql_class.h'
--- a/sql/sql_class.h	2010-04-05 21:15:15 +0000
+++ b/sql/sql_class.h	2010-04-14 20:34:04 +0000
@@ -2786,6 +2786,11 @@ public:
     that MEMORY tables cannot index BIT columns.
   */
   bool bit_fields_as_long;
+  /*
+    Whether to create or postpone actual creation of this temporary table.
+    TRUE <=> create_tmp_table will create only the TABLE structure.
+  */
+  bool skip_create_table;
 
   TMP_TABLE_PARAM()
     :copy_field(0), group_parts(0),

=== modified file 'sql/sql_lex.cc'
--- a/sql/sql_lex.cc	2010-03-20 12:01:47 +0000
+++ b/sql/sql_lex.cc	2010-04-14 20:34:04 +0000
@@ -1843,7 +1843,7 @@ void st_select_lex_unit::exclude_tree()
 
 bool st_select_lex::mark_as_dependent(THD *thd, st_select_lex *last, Item *dependency)
 {
-
+  DBUG_ENTER("st_select_lex::mark_as_dependent");
   DBUG_ASSERT(this != last);
 
   /*
@@ -1868,15 +1868,22 @@ bool st_select_lex::mark_as_dependent(TH
           sl->uncacheable|= UNCACHEABLE_UNITED;
       }
     }
+    if (dependency && dependency->fixed)
+    {
+      s->master_unit()->item->depends_on.push_back(dependency);
+      DBUG_PRINT("info", ("depends_on: Select: %d  added: %s",
+                          s->select_number,
+                          (dependency->name ? dependency->name : "<no name>")));
+    }
 
     Item_subselect *subquery_expr= s->master_unit()->item;
     if (subquery_expr && subquery_expr->mark_as_dependent(thd, last, 
                                                           dependency))
-      return TRUE;
+      DBUG_RETURN(TRUE);
   } while ((s= s->outer_select()) != last && s != 0);
   is_correlated= TRUE;
   this->master_unit()->item->is_correlated= TRUE;
-  return FALSE;
+  DBUG_RETURN(FALSE);
 }
 
 bool st_select_lex_node::set_braces(bool value)      { return 1; }

=== modified file 'sql/sql_select.cc'
--- a/sql/sql_select.cc	2010-03-29 20:09:40 +0000
+++ b/sql/sql_select.cc	2010-04-14 20:34:04 +0000
@@ -156,7 +156,6 @@ static int join_read_const_table(JOIN_TA
 static int join_read_system(JOIN_TAB *tab);
 static int join_read_const(JOIN_TAB *tab);
 static int join_read_key(JOIN_TAB *tab);
-static int join_read_key2(JOIN_TAB *tab, TABLE *table, TABLE_REF *table_ref);
 static void join_read_key_unlock_row(st_join_table *tab);
 static int join_read_always_key(JOIN_TAB *tab);
 static int join_read_last_key(JOIN_TAB *tab);
@@ -7630,6 +7629,40 @@ make_join_readinfo(JOIN *join, ulonglong
 
 
 /**
+  Creates and fills JOIN_TAB for index look up in temporary table
+
+  @param table           The table where to look up
+
+  @return JOIN_TAB object or NULL in case of error
+*/
+
+JOIN_TAB *create_index_lookup_join_tab(TABLE *table)
+{
+  JOIN_TAB *tab;
+  DBUG_ENTER("create_index_lookup_join_tab");
+
+  if (!((tab= new JOIN_TAB)))
+    DBUG_RETURN(NULL);
+  tab->read_record.table= table;
+  tab->read_record.file=table->file;
+  /*tab->read_record.unlock_row= rr_unlock_row;*/
+  tab->next_select=0;
+  tab->sorted= 1;
+
+  table->status= STATUS_NO_RECORD;
+  tab->read_first_record= join_read_key;
+  /*tab->read_record.unlock_row= join_read_key_unlock_row;*/
+  tab->read_record.read_record= join_no_more_records;
+  if (table->covering_keys.is_set(tab->ref.key) &&
+      !table->no_keyread)
+  {
+    table->key_read=1;
+    table->file->extra(HA_EXTRA_KEYREAD);
+  }
+  DBUG_RETURN(tab);
+}
+
+/**
   Give error if we some tables are done with a full join.
 
   This is used by multi_table_update and multi_table_delete when running
@@ -10783,6 +10816,7 @@ Field *create_tmp_field(THD *thd, TABLE 
   case Item::REF_ITEM:
   case Item::NULL_ITEM:
   case Item::VARBIN_ITEM:
+  case Item::CACHE_ITEM:
     if (make_copy_field)
     {
       DBUG_ASSERT(((Item_result_field*)item)->result_field);
@@ -11557,7 +11591,8 @@ create_tmp_table(THD *thd,TMP_TABLE_PARA
                                   &param->recinfo, select_options))
       goto err;
   }
-  if (open_tmp_table(table))
+  DBUG_PRINT("info", ("skip_create_table: %d", (int)param->skip_create_table));
+  if (!param->skip_create_table && open_tmp_table(table))
     goto err;
 
   thd->mem_root= mem_root_save;
@@ -11705,16 +11740,17 @@ error:
 bool open_tmp_table(TABLE *table)
 {
   int error;
+  DBUG_ENTER("open_tmp_table");
   if ((error= table->file->ha_open(table, table->s->table_name.str, O_RDWR,
                                    HA_OPEN_TMP_TABLE |
                                    HA_OPEN_INTERNAL_TABLE)))
   {
     table->file->print_error(error,MYF(0)); /* purecov: inspected */
     table->db_stat=0;
-    return(1);
+    DBUG_RETURN(1);
   }
   (void) table->file->extra(HA_EXTRA_QUICK);            /* Faster */
-  return(0);
+  DBUG_RETURN(0);
 }
 
 
@@ -12543,7 +12579,8 @@ sub_select_sjm(JOIN *join, JOIN_TAB *joi
   else
   {
     /* Do index lookup in the materialized table */
-    if ((res= join_read_key2(join_tab, sjm->table, sjm->tab_ref)) == 1)
+    if ((res= join_read_key2(join_tab->join->thd, join_tab,
+                             sjm->table, sjm->tab_ref)) == 1)
       DBUG_RETURN(NESTED_LOOP_ERROR); /* purecov: inspected */
     if (res || !sjm->in_equality->val_int())
       DBUG_RETURN(NESTED_LOOP_NO_MORE_ROWS);
@@ -13326,61 +13363,61 @@ join_read_const(JOIN_TAB *tab)
 static int
 join_read_key(JOIN_TAB *tab)
 {
-  return join_read_key2(tab, tab->table, &tab->ref);
+  return join_read_key2(tab->join->thd, tab, tab->table, &tab->ref);
 }
 
 
-/* 
+/*
   eq_ref access handler but generalized a bit to support TABLE and TABLE_REF
   not from the join_tab. See join_read_key for detailed synopsis.
 */
-static int
-join_read_key2(JOIN_TAB *tab, TABLE *table, TABLE_REF *table_ref)
+int join_read_key2(THD *thd, JOIN_TAB *tab, TABLE *table, TABLE_REF *table_ref)
 {
   int error;
+  DBUG_ENTER("join_read_key2");
   if (!table->file->inited)
   {
     table->file->ha_index_init(table_ref->key, tab->sorted);
   }
 
   /* TODO: Why don't we do "Late NULLs Filtering" here? */
-  if (cmp_buffer_with_ref(tab->join->thd, table, table_ref) ||
+  if (cmp_buffer_with_ref(thd, table, table_ref) ||
       (table->status & (STATUS_GARBAGE | STATUS_NO_PARENT | STATUS_NULL_ROW)))
   {
     if (table_ref->key_err)
     {
       table->status=STATUS_NOT_FOUND;
-      return -1;
+      DBUG_RETURN(-1);
     }
     /*
       Moving away from the current record. Unlock the row
       in the handler if it did not match the partial WHERE.
     */
-    if (tab->ref.has_record && tab->ref.use_count == 0)
+    if (table_ref->has_record && table_ref->use_count == 0)
     {
       tab->read_record.file->unlock_row();
-      tab->ref.has_record= FALSE;
+      table_ref->has_record= FALSE;
     }
     error=table->file->ha_index_read_map(table->record[0],
                                   table_ref->key_buff,
                                   make_prev_keypart_map(table_ref->key_parts),
                                   HA_READ_KEY_EXACT);
     if (error && error != HA_ERR_KEY_NOT_FOUND && error != HA_ERR_END_OF_FILE)
-      return report_error(table, error);
+      DBUG_RETURN(report_error(table, error));
 
     if (! error)
     {
-      tab->ref.has_record= TRUE;
-      tab->ref.use_count= 1;
+      table_ref->has_record= TRUE;
+      table_ref->use_count= 1;
     }
   }
   else if (table->status == 0)
   {
-    DBUG_ASSERT(tab->ref.has_record);
-    tab->ref.use_count++;
+    DBUG_ASSERT(table_ref->has_record);
+    table_ref->use_count++;
   }
   table->null_row=0;
-  return table->status ? -1 : 0;
+  DBUG_RETURN(table->status ? -1 : 0);
 }
 
 

=== added file 'sql/sql_subquery_cache.cc'
--- a/sql/sql_subquery_cache.cc	1970-01-01 00:00:00 +0000
+++ b/sql/sql_subquery_cache.cc	2010-04-14 20:34:04 +0000
@@ -0,0 +1,236 @@
+
+#include "mysql_priv.h"
+#include "sql_select.h"
+
+ulonglong subquery_cache_miss, subquery_cache_hit;
+
+/**
+  Creates structures which we need for index look up
+
+  @retval FALSE OK
+  @retval TRUE  Error
+*/
+
+static my_bool createtmp_table_search_structures(THD *thd,
+                                                 TABLE *table,
+                                                 List_iterator_fast<Item> &li,
+                                                 TABLE_REF **ref)
+{
+  /*
+    Create/initialize everything we will need to index lookups into the
+    temptable.
+  */
+  TABLE_REF *tab_ref;
+  KEY       *tmp_key; /* The only index on the temporary table. */
+  Item      *item;
+  uint      tmp_key_parts; /* Number of keyparts in tmp_key. */
+  uint      i;
+
+  DBUG_ENTER("createtmp_table_search_structures");
+
+  tmp_key= table->key_info;
+  tmp_key_parts= tmp_key->key_parts;
+
+  if (!(tab_ref= (TABLE_REF*) thd->alloc(sizeof(TABLE_REF))))
+    DBUG_RETURN(TRUE);
+
+  tab_ref->key= 0; /* The only temp table index. */
+  tab_ref->key_length= tmp_key->key_length;
+  if (!(tab_ref->key_buff=
+        (uchar*) thd->calloc(ALIGN_SIZE(tmp_key->key_length) * 2)) ||
+      !(tab_ref->key_copy=
+        (store_key**) thd->alloc((sizeof(store_key*) *
+                                  (tmp_key_parts + 1)))) ||
+      !(tab_ref->items=
+        (Item**) thd->alloc(sizeof(Item*) * tmp_key_parts)))
+    DBUG_RETURN(TRUE); /* purecov: inspected */
+
+  tab_ref->key_buff2=tab_ref->key_buff+ALIGN_SIZE(tmp_key->key_length);
+  tab_ref->key_err=1;
+  tab_ref->null_rejecting= 1;
+  tab_ref->disable_cache= FALSE;
+  tab_ref->has_record= 0;
+
+  KEY_PART_INFO *cur_key_part= tmp_key->key_part;
+  store_key **ref_key= tab_ref->key_copy;
+  uchar *cur_ref_buff= tab_ref->key_buff;
+
+  for (i= 0; i < tmp_key_parts; i++, cur_key_part++, ref_key++)
+  {
+    item= li++;
+    DBUG_ASSERT(item);
+    tab_ref->items[i]= item;
+    int null_count= test(cur_key_part->field->real_maybe_null());
+    *ref_key= new store_key_item(thd, cur_key_part->field,
+                                 /* TODO:
+                                   the NULL byte is taken into account in
+                                   cur_key_part->store_length, so instead of
+                                   cur_ref_buff + test(maybe_null), we could
+                                   use that information instead.
+                                 */
+                                 cur_ref_buff + null_count,
+                                 null_count ? tab_ref->key_buff : 0,
+                                 cur_key_part->length, tab_ref->items[i]);
+    cur_ref_buff+= cur_key_part->store_length;
+  }
+  *ref_key= NULL; /* End marker. */
+  tab_ref->key_err= 1;
+  tab_ref->key_parts= tmp_key_parts;
+  *ref= tab_ref;
+
+  DBUG_RETURN(FALSE);
+}
+
+
+Subquery_cache_tmptable::Subquery_cache_tmptable(THD *thd,
+                                                 List<Item> &dependance,
+                                                 Item *value)
+  :cache_table(NULL), table_thd(thd)
+{
+  ulonglong keymap;
+  List_iterator_fast<Item> li(dependance);
+  DBUG_ENTER("Subquery_cache_tmptable::Subquery_cache_tmptable");
+
+  if (!(ULONGLONG_MAX >> (dependance.elements + 1)))
+  {
+    DBUG_PRINT("info", ("Too many dependencies"));
+    DBUG_VOID_RETURN;
+  }
+
+  cache_table= NULL;
+  list= &dependance;
+
+  cache_table_param.init();
+  /* dependance items and result */
+  cache_table_param.field_count= dependance.elements + 1;
+  /* postpone table creation to index description */
+  cache_table_param.skip_create_table= 1;
+
+
+  dependance.push_front(value);
+  if (!(cache_table= create_tmp_table(thd, &cache_table_param,
+                                      dependance, (ORDER*) NULL,
+                                      FALSE, FALSE,
+                                      thd->options | TMP_TABLE_ALL_COLUMNS,
+                                      HA_POS_ERROR,
+                                      (char *)"subquery-cache-table")))
+  {
+    DBUG_PRINT("error", ("create_tmp_table failed, caching switched off"));
+    DBUG_VOID_RETURN;
+  }
+
+  /* makes all bits set for keys */
+  keymap= 1 << (dependance.elements); /* + 1 - 1 */
+  if (!keymap)
+    keymap= ULONGLONG_MAX;
+  else
+    keymap--;
+  keymap&=~1;
+
+  li++;
+  if (cache_table->alloc_keys(1) ||
+      (cache_table->add_tmp_key(keymap, "cache-table-key") < 0) ||
+      createtmp_table_search_structures(thd, cache_table, li, &tab_ref) ||
+      !(tab= create_index_lookup_join_tab(cache_table)))
+  {
+    DBUG_PRINT("error", ("creating index failed"));
+    goto error;
+  }
+  cache_table->s->keys= 1;
+  cache_table->s->uniques= 1;
+
+  if (open_tmp_table(cache_table))
+  {
+    DBUG_PRINT("error", ("Opening (creating) temporary table failed"));
+    goto error;
+  }
+
+  if (!(chached_result= new Item_field(cache_table->field[0])))
+  {
+    DBUG_PRINT("error", ("Creating Item_field failed"));
+    goto error;
+  }
+
+  DBUG_VOID_RETURN;
+
+error:
+  /* switch off cache */
+  free_tmp_table(thd, cache_table);
+  cache_table= NULL;
+  DBUG_VOID_RETURN;
+}
+
+
+Subquery_cache_tmptable::~Subquery_cache_tmptable()
+{
+  if (cache_table)
+    free_tmp_table(table_thd, cache_table);
+}
+
+
+Subquery_cache::result Subquery_cache_tmptable::check_value(Item **value)
+{
+  int res;
+  DBUG_ENTER("Subquery_cache_tmptable::check_value");
+  if (cache_table)
+  {
+    DBUG_PRINT("info", ("status: %u  has_record %u",
+                        (uint)cache_table->status, (uint)tab_ref->has_record));
+    if ((res= join_read_key2(table_thd, tab, cache_table, tab_ref)) == 1)
+      DBUG_RETURN(ERROR); /* purecov: inspected */
+    if (res)
+    {
+      subquery_cache_miss++;
+      DBUG_RETURN(MISS);
+    }
+
+    subquery_cache_hit++;
+    *value= chached_result;
+    DBUG_RETURN(Subquery_cache::HIT);
+  }
+  DBUG_RETURN(Subquery_cache::MISS);
+}
+
+
+my_bool Subquery_cache_tmptable::put_value(Item *value)
+{
+  int error;
+  DBUG_ENTER("Subquery_cache_tmptable::put_value");
+  if (!cache_table)
+  {
+    DBUG_PRINT("info", ("No table so behave as we successfully put value"));
+    DBUG_RETURN(FALSE);
+  }
+
+  *(list->head_ref())= value;
+  fill_record(table_thd, cache_table->field, *list, 1);
+  if (table_thd->is_error())
+    goto err;;
+
+  if ((error= cache_table->file->ha_write_row(cache_table->record[0])))
+  {
+    /* create_myisam_from_heap will generate error if needed */
+    if (cache_table->file->is_fatal_error(error, HA_CHECK_DUP) &&
+        create_internal_tmp_table_from_heap(table_thd, cache_table,
+                                            cache_table_param.start_recinfo,
+                                            &cache_table_param.recinfo,
+                                            error, 1))
+      goto err;
+  }
+  cache_table->status= 0; /* cache_table->record contains an existed record */
+  tab_ref->has_record= TRUE; /* the same as above */
+  DBUG_PRINT("info", ("has_record: TRUE  status: 0"));
+
+  DBUG_RETURN(FALSE);
+
+err:
+  free_tmp_table(table_thd, cache_table);
+  cache_table= NULL;
+  DBUG_RETURN(TRUE);
+}
+
+
+void Subquery_cache_tmptable::cleanup()
+{
+  cache_table->file->ha_delete_all_rows();
+}

=== added file 'sql/sql_subquery_cache.h'
--- a/sql/sql_subquery_cache.h	1970-01-01 00:00:00 +0000
+++ b/sql/sql_subquery_cache.h	2010-04-14 20:34:04 +0000
@@ -0,0 +1,68 @@
+#ifndef _SQL_SUBQUERY_CACHE_H_
+#define _SQL_SUBQUERY_CACHE_H_
+
+/**
+  Interface for subquery cache
+*/
+
+extern ulonglong subquery_cache_miss, subquery_cache_hit;
+
+class Subquery_cache :public Sql_alloc
+{
+public:
+  enum result {ERROR, HIT, MISS};
+
+  Subquery_cache(){};
+  virtual ~Subquery_cache() {};
+  /**
+    Checks presence of the key (taken from cache owner) and if found return
+    it via value parameter
+  */
+  virtual result check_value(Item **value)= 0;
+  /**
+    Puts value into this cache (key should be taken from cache owner)
+  */
+  virtual my_bool put_value(Item *value)= 0;
+  /**
+    Cleans up and reset cache before reusing
+  */
+  virtual void cleanup()= 0;
+};
+
+struct st_table_ref;
+struct st_join_table;
+//class Item_cache;
+class Item_field;
+
+/**
+  Implementation of subquery cache over temporary table
+*/
+
+class Subquery_cache_tmptable :public Subquery_cache
+{
+public:
+  Subquery_cache_tmptable(THD *thd, List<Item> &dependance, Item *value);
+  virtual ~Subquery_cache_tmptable();
+  virtual result check_value(Item **value);
+  virtual my_bool put_value(Item *value);
+  virtual void cleanup();
+
+private:
+  /* tmp table parameters */
+  TMP_TABLE_PARAM cache_table_param;
+  /* temporary table to store this cache */
+  TABLE *cache_table;
+  /* Thread handler for the temporary table */
+  THD *table_thd;
+  /* tab_ref for index search */
+  struct st_table_ref *tab_ref;
+  /* cache of subquery value to avoid evaluating it twice */
+  //Item_cache *value_cache;
+  /* JOIN_TAB for index lookup */
+  st_join_table *tab;
+  /* Chached result */
+  Item_field *chached_result;
+  /* List of items */
+  List<Item> *list;
+};
+#endif

=== modified file 'sql/table.cc'
--- a/sql/table.cc	2010-03-20 12:01:47 +0000
+++ b/sql/table.cc	2010-04-14 20:34:04 +0000
@@ -20,6 +20,7 @@
 #include "sql_trigger.h"
 #include <m_ctype.h>
 #include "my_md5.h"
+#include "my_bit.h"
 
 /* INFORMATION_SCHEMA name */
 LEX_STRING INFORMATION_SCHEMA_NAME= {C_STRING_WITH_LEN("information_schema")};
@@ -5096,6 +5097,115 @@ void st_table::mark_virtual_columns_for_
     file->column_bitmaps_signal();
 }
 
+
+/**
+  @brief
+  Allocate space for keys
+
+  @param key_count  number of keys to allocate.
+
+  @details
+  Allocate space enough to fit 'key_count' keys for this table.
+
+  @return FALSE space was successfully allocated.
+  @return TRUE an error occur.
+*/
+
+bool TABLE::alloc_keys(uint key_count)
+{
+  DBUG_ASSERT(!s->keys);
+  key_info= s->key_info= (KEY*) my_malloc(sizeof(KEY)*key_count, MYF(0));
+  max_keys= key_count;
+  return !(key_info);
+}
+
+
+/**
+  @brief Adds one key to a temporary table.
+
+  @param key_parts      bitmap of fields that take a part in the key.
+  @param key_name       name of the key
+
+  @details
+  Creates a key for this table from fields which corresponds the bits set to 1
+  in the 'key_parts' bitmap. The 'key_name' name is given to the newly created
+  key.
+
+  @return <0 an error occur.
+  @return >=0 number of newly added key.
+*/
+
+int TABLE::add_tmp_key(ulonglong key_parts, const char *key_name)
+{
+  DBUG_ASSERT(s->keys< max_keys);
+
+  KEY* keyinfo;
+  Field **reg_field;
+  uint i;
+  bool key_start= TRUE;
+  uint key_part_count= my_count_bits(key_parts);
+  KEY_PART_INFO* key_part_info=
+      (KEY_PART_INFO*) my_malloc(sizeof(KEY_PART_INFO)* key_part_count, MYF(0));
+  if (!key_part_info)
+    return -1;
+  keyinfo= key_info + s->keys;
+  keyinfo->key_part=key_part_info;
+  keyinfo->usable_key_parts=keyinfo->key_parts= key_part_count;
+  keyinfo->key_length=0;
+  keyinfo->algorithm= HA_KEY_ALG_UNDEF;
+  keyinfo->name= (char *)key_name;
+  keyinfo->flags= HA_GENERATED_KEY;
+  keyinfo->rec_per_key= (ulong*)my_malloc(sizeof(ulong)*key_part_count, MYF(0));
+  if (!keyinfo->rec_per_key)
+    return -1;
+  bzero(keyinfo->rec_per_key, sizeof(ulong)*key_part_count);
+  for (i= 0, reg_field=field ;
+       *reg_field;
+       i++, reg_field++)
+  {
+    if (!(key_parts & (1 << i)))
+      continue;
+    if (key_start)
+      (*reg_field)->key_start.set_bit(s->keys);
+    key_start= FALSE;
+    (*reg_field)->part_of_key.set_bit(s->keys);
+    (*reg_field)->flags|= PART_KEY_FLAG;
+    key_part_info->null_bit= (*reg_field)->null_bit;
+    key_part_info->null_offset= (uint) ((*reg_field)->null_ptr -
+                                          (uchar*) record[0]);
+    key_part_info->field=    *reg_field;
+    key_part_info->offset=   (*reg_field)->offset(record[0]);
+    key_part_info->length=   (uint16) (*reg_field)->pack_length();
+    keyinfo->key_length+= key_part_info->length;
+    /* TODO:
+      The below method of computing the key format length of the
+      key part is a copy/paste from opt_range.cc, and table.cc.
+      This should be factored out, e.g. as a method of Field.
+      In addition it is not clear if any of the Field::*_length
+      methods is supposed to compute the same length. If so, it
+      might be reused.
+    */
+    key_part_info->store_length= key_part_info->length;
+
+    if ((*reg_field)->real_maybe_null())
+      key_part_info->store_length+= HA_KEY_NULL_LENGTH;
+    if ((*reg_field)->type() == MYSQL_TYPE_BLOB || 
+        (*reg_field)->real_type() == MYSQL_TYPE_VARCHAR)
+      key_part_info->store_length+= HA_KEY_BLOB_LENGTH;
+
+    key_part_info->type=     (uint8) (*reg_field)->key_type();
+    key_part_info->key_type =
+      ((ha_base_keytype) key_part_info->type == HA_KEYTYPE_TEXT ||
+       (ha_base_keytype) key_part_info->type == HA_KEYTYPE_VARTEXT1 ||
+       (ha_base_keytype) key_part_info->type == HA_KEYTYPE_VARTEXT2) ?
+      0 : FIELDFLAG_BINARY;
+    key_part_info++;
+  }
+  set_if_bigger(s->max_key_length, keyinfo->key_length);
+  return ++s->keys - 1;
+}
+
+
 /**
   @brief Check if this is part of a MERGE table with attached children.
 

=== modified file 'sql/table.h'
--- a/sql/table.h	2010-03-20 12:01:47 +0000
+++ b/sql/table.h	2010-04-14 20:34:04 +0000
@@ -781,6 +781,7 @@ struct st_table {
   uint          temp_pool_slot;         /* Used by intern temp tables */
   uint          status;                 /* What's in record[0] */
   uint          db_stat;                /* mode of file as in handler.h */
+  uint          max_keys;               /* Size of allocated key_info array. */
   /* number of select if it is derived table */
   uint          derived_select_number;
   int           current_lock;           /* Type of lock on table */
@@ -914,6 +915,8 @@ struct st_table {
   inline bool needs_reopen_or_name_lock()
   { return s->version != refresh_version; }
   bool is_children_attached(void);
+  bool alloc_keys(uint key_count);
+  int add_tmp_key(ulonglong key_parts, const char *key_name);
 };
 
 enum enum_schema_table_state