← Back to team overview

maria-developers team mailing list archive

subquery cache for review

 

Hi!

I have problem with automatic commits sending so sends the diff here (sorry, will fix it tomorrow).

I also have thought about renaming sql/sql_expression_cache.* to sql/item_expression_cache and moving the item also there but I am not sure if it is better.

Also I am not sure that Item_cache_wrapper is the best name but Item_expression_cache_wrapper IMHO is too long.

I re-made 5.3-mwl-66 so it need re-branching (not pulling) if you need to look at it.


=== modified file 'libmysqld/Makefile.am'
--- libmysqld/Makefile.am	2010-03-20 12:01:47 +0000
+++ libmysqld/Makefile.am	2010-06-23 19:01:58 +0000
@@ -80,7 +80,8 @@
 	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_expression_cache.cc
 
 libmysqld_int_a_SOURCES= $(libmysqld_sources)
 nodist_libmysqld_int_a_SOURCES= $(libmysqlsources) $(sqlsources)

=== modified file 'mysql-test/r/index_merge_myisam.result'
--- mysql-test/r/index_merge_myisam.result	2010-03-20 12:01:47 +0000
+++ mysql-test/r/index_merge_myisam.result	2010-06-23 18:44:58 +0000
@@ -1419,19 +1419,19 @@
 #
 select @@optimizer_switch;
 @@optimizer_switch
-index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,index_condition_pushdown=on,firstmatch=on,loosescan=on,materialization=on,semijoin=on,partial_match_rowid_merge=on,partial_match_table_scan=on
+index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,index_condition_pushdown=on,firstmatch=on,loosescan=on,materialization=on,semijoin=on,partial_match_rowid_merge=on,partial_match_table_scan=on,subquery_cache=on
 set optimizer_switch='index_merge=off,index_merge_union=off';
 select @@optimizer_switch;
 @@optimizer_switch
-index_merge=off,index_merge_union=off,index_merge_sort_union=on,index_merge_intersection=on,index_condition_pushdown=on,firstmatch=on,loosescan=on,materialization=on,semijoin=on,partial_match_rowid_merge=on,partial_match_table_scan=on
+index_merge=off,index_merge_union=off,index_merge_sort_union=on,index_merge_intersection=on,index_condition_pushdown=on,firstmatch=on,loosescan=on,materialization=on,semijoin=on,partial_match_rowid_merge=on,partial_match_table_scan=on,subquery_cache=on
 set optimizer_switch='index_merge_union=on';
 select @@optimizer_switch;
 @@optimizer_switch
-index_merge=off,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,index_condition_pushdown=on,firstmatch=on,loosescan=on,materialization=on,semijoin=on,partial_match_rowid_merge=on,partial_match_table_scan=on
+index_merge=off,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,index_condition_pushdown=on,firstmatch=on,loosescan=on,materialization=on,semijoin=on,partial_match_rowid_merge=on,partial_match_table_scan=on,subquery_cache=on
 set optimizer_switch='default,index_merge_sort_union=off';
 select @@optimizer_switch;
 @@optimizer_switch
-index_merge=on,index_merge_union=on,index_merge_sort_union=off,index_merge_intersection=on,index_condition_pushdown=on,firstmatch=on,loosescan=on,materialization=on,semijoin=on,partial_match_rowid_merge=on,partial_match_table_scan=on
+index_merge=on,index_merge_union=on,index_merge_sort_union=off,index_merge_intersection=on,index_condition_pushdown=on,firstmatch=on,loosescan=on,materialization=on,semijoin=on,partial_match_rowid_merge=on,partial_match_table_scan=on,subquery_cache=on
 set optimizer_switch=4;
 ERROR 42000: Variable 'optimizer_switch' can't be set to the value of '4'
 set optimizer_switch=NULL;
@@ -1458,21 +1458,21 @@
 set optimizer_switch='index_merge=off,index_merge_union=off,default';
 select @@optimizer_switch;
 @@optimizer_switch
-index_merge=off,index_merge_union=off,index_merge_sort_union=on,index_merge_intersection=on,index_condition_pushdown=on,firstmatch=on,loosescan=on,materialization=on,semijoin=on,partial_match_rowid_merge=on,partial_match_table_scan=on
+index_merge=off,index_merge_union=off,index_merge_sort_union=on,index_merge_intersection=on,index_condition_pushdown=on,firstmatch=on,loosescan=on,materialization=on,semijoin=on,partial_match_rowid_merge=on,partial_match_table_scan=on,subquery_cache=on
 set optimizer_switch=default;
 select @@global.optimizer_switch;
 @@global.optimizer_switch
-index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,index_condition_pushdown=on,firstmatch=on,loosescan=on,materialization=on,semijoin=on,partial_match_rowid_merge=on,partial_match_table_scan=on
+index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,index_condition_pushdown=on,firstmatch=on,loosescan=on,materialization=on,semijoin=on,partial_match_rowid_merge=on,partial_match_table_scan=on,subquery_cache=on
 set @@global.optimizer_switch=default;
 select @@global.optimizer_switch;
 @@global.optimizer_switch
-index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,index_condition_pushdown=on,firstmatch=on,loosescan=on,materialization=on,semijoin=on,partial_match_rowid_merge=on,partial_match_table_scan=on
+index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,index_condition_pushdown=on,firstmatch=on,loosescan=on,materialization=on,semijoin=on,partial_match_rowid_merge=on,partial_match_table_scan=on,subquery_cache=on
 #
 # Check index_merge's @@optimizer_switch flags
 #
 select @@optimizer_switch;
 @@optimizer_switch
-index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,index_condition_pushdown=on,firstmatch=on,loosescan=on,materialization=on,semijoin=on,partial_match_rowid_merge=on,partial_match_table_scan=on
+index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,index_condition_pushdown=on,firstmatch=on,loosescan=on,materialization=on,semijoin=on,partial_match_rowid_merge=on,partial_match_table_scan=on,subquery_cache=on
 create table t0 (a int);
 insert into t0 values (0),(1),(2),(3),(4),(5),(6),(7),(8),(9);
 create table t1 (a int, b int, c int, filler char(100), 
@@ -1582,5 +1582,5 @@
 set optimizer_switch=default;
 show variables like 'optimizer_switch';
 Variable_name	Value
-optimizer_switch	index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,index_condition_pushdown=on,firstmatch=on,loosescan=on,materialization=on,semijoin=on,partial_match_rowid_merge=on,partial_match_table_scan=on
+optimizer_switch	index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,index_condition_pushdown=on,firstmatch=on,loosescan=on,materialization=on,semijoin=on,partial_match_rowid_merge=on,partial_match_table_scan=on,subquery_cache=on
 drop table t0, t1;

=== modified file 'mysql-test/r/myisam_mrr.result'
--- mysql-test/r/myisam_mrr.result	2010-03-11 21:43:31 +0000
+++ mysql-test/r/myisam_mrr.result	2010-06-23 18:44:58 +0000
@@ -394,7 +394,7 @@
 #   - engine_condition_pushdown does not affect ICP
 select @@optimizer_switch;
 @@optimizer_switch
-index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,index_condition_pushdown=on,firstmatch=on,loosescan=on,materialization=on,semijoin=on,partial_match_rowid_merge=on,partial_match_table_scan=on
+index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,index_condition_pushdown=on,firstmatch=on,loosescan=on,materialization=on,semijoin=on,partial_match_rowid_merge=on,partial_match_table_scan=on,subquery_cache=on
 create table t0 (a int);
 insert into t0 values (0),(1),(2),(3),(4),(5),(6),(7),(8),(9);
 create table t1 (a int, b int, key(a));

=== modified file 'mysql-test/r/subselect3.result'
--- mysql-test/r/subselect3.result	2010-03-29 14:04:35 +0000
+++ mysql-test/r/subselect3.result	2010-06-23 18:44:58 +0000
@@ -105,6 +105,7 @@
 Handler_read_rnd_next	5
 delete from t2;
 insert into t2 values (NULL, 0),(NULL, 0), (NULL, 0), (NULL, 0);
+set optimizer_switch='subquery_cache=off';
 flush status;
 select oref, a, a in (select a from t1 where oref=t2.oref) Z from t2;
 oref	a	Z
@@ -123,6 +124,7 @@
 select 'No key lookups, seq reads: 29= 5 reads from t2 + 4 * 6 reads from t1.' Z;
 Z
 No key lookups, seq reads: 29= 5 reads from t2 + 4 * 6 reads from t1.
+set @@optimizer_switch=@save_optimizer_switch;
 drop table t1, t2;
 create table t1 (a int, b int, primary key (a));
 insert into t1 values (1,1), (3,1),(100,1);

=== modified file 'mysql-test/r/subselect3_jcl6.result'
--- mysql-test/r/subselect3_jcl6.result	2010-03-29 14:04:35 +0000
+++ mysql-test/r/subselect3_jcl6.result	2010-06-23 18:44:58 +0000
@@ -109,6 +109,7 @@
 Handler_read_rnd_next	5
 delete from t2;
 insert into t2 values (NULL, 0),(NULL, 0), (NULL, 0), (NULL, 0);
+set optimizer_switch='subquery_cache=off';
 flush status;
 select oref, a, a in (select a from t1 where oref=t2.oref) Z from t2;
 oref	a	Z
@@ -127,6 +128,7 @@
 select 'No key lookups, seq reads: 29= 5 reads from t2 + 4 * 6 reads from t1.' Z;
 Z
 No key lookups, seq reads: 29= 5 reads from t2 + 4 * 6 reads from t1.
+set @@optimizer_switch=@save_optimizer_switch;
 drop table t1, t2;
 create table t1 (a int, b int, primary key (a));
 insert into t1 values (1,1), (3,1),(100,1);

=== modified file 'mysql-test/r/subselect_no_mat.result'
--- mysql-test/r/subselect_no_mat.result	2010-03-20 12:01:47 +0000
+++ mysql-test/r/subselect_no_mat.result	2010-06-23 18:44:58 +0000
@@ -1,6 +1,6 @@
 show variables like 'optimizer_switch';
 Variable_name	Value
-optimizer_switch	index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,index_condition_pushdown=on,firstmatch=on,loosescan=on,materialization=on,semijoin=on,partial_match_rowid_merge=on,partial_match_table_scan=on
+optimizer_switch	index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,index_condition_pushdown=on,firstmatch=on,loosescan=on,materialization=on,semijoin=on,partial_match_rowid_merge=on,partial_match_table_scan=on,subquery_cache=on
 set optimizer_switch='materialization=off';
 drop table if exists t1,t2,t3,t4,t5,t6,t7,t8,t11,t12;
 set @save_optimizer_switch=@@optimizer_switch;
@@ -4826,4 +4826,4 @@
 set optimizer_switch=default;
 show variables like 'optimizer_switch';
 Variable_name	Value
-optimizer_switch	index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,index_condition_pushdown=on,firstmatch=on,loosescan=on,materialization=on,semijoin=on,partial_match_rowid_merge=on,partial_match_table_scan=on
+optimizer_switch	index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,index_condition_pushdown=on,firstmatch=on,loosescan=on,materialization=on,semijoin=on,partial_match_rowid_merge=on,partial_match_table_scan=on,subquery_cache=on

=== modified file 'mysql-test/r/subselect_no_opts.result'
--- mysql-test/r/subselect_no_opts.result	2010-03-20 12:01:47 +0000
+++ mysql-test/r/subselect_no_opts.result	2010-06-23 18:44:58 +0000
@@ -1,6 +1,6 @@
 show variables like 'optimizer_switch';
 Variable_name	Value
-optimizer_switch	index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,index_condition_pushdown=on,firstmatch=on,loosescan=on,materialization=on,semijoin=on,partial_match_rowid_merge=on,partial_match_table_scan=on
+optimizer_switch	index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,index_condition_pushdown=on,firstmatch=on,loosescan=on,materialization=on,semijoin=on,partial_match_rowid_merge=on,partial_match_table_scan=on,subquery_cache=on
 set optimizer_switch='materialization=off,semijoin=off';
 drop table if exists t1,t2,t3,t4,t5,t6,t7,t8,t11,t12;
 set @save_optimizer_switch=@@optimizer_switch;
@@ -4826,4 +4826,4 @@
 set optimizer_switch=default;
 show variables like 'optimizer_switch';
 Variable_name	Value
-optimizer_switch	index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,index_condition_pushdown=on,firstmatch=on,loosescan=on,materialization=on,semijoin=on,partial_match_rowid_merge=on,partial_match_table_scan=on
+optimizer_switch	index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,index_condition_pushdown=on,firstmatch=on,loosescan=on,materialization=on,semijoin=on,partial_match_rowid_merge=on,partial_match_table_scan=on,subquery_cache=on

=== modified file 'mysql-test/r/subselect_no_semijoin.result'
--- mysql-test/r/subselect_no_semijoin.result	2010-03-20 12:01:47 +0000
+++ mysql-test/r/subselect_no_semijoin.result	2010-06-23 18:44:58 +0000
@@ -1,6 +1,6 @@
 show variables like 'optimizer_switch';
 Variable_name	Value
-optimizer_switch	index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,index_condition_pushdown=on,firstmatch=on,loosescan=on,materialization=on,semijoin=on,partial_match_rowid_merge=on,partial_match_table_scan=on
+optimizer_switch	index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,index_condition_pushdown=on,firstmatch=on,loosescan=on,materialization=on,semijoin=on,partial_match_rowid_merge=on,partial_match_table_scan=on,subquery_cache=on
 set optimizer_switch='semijoin=off';
 drop table if exists t1,t2,t3,t4,t5,t6,t7,t8,t11,t12;
 set @save_optimizer_switch=@@optimizer_switch;
@@ -4826,4 +4826,4 @@
 set optimizer_switch=default;
 show variables like 'optimizer_switch';
 Variable_name	Value
-optimizer_switch	index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,index_condition_pushdown=on,firstmatch=on,loosescan=on,materialization=on,semijoin=on,partial_match_rowid_merge=on,partial_match_table_scan=on
+optimizer_switch	index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,index_condition_pushdown=on,firstmatch=on,loosescan=on,materialization=on,semijoin=on,partial_match_rowid_merge=on,partial_match_table_scan=on,subquery_cache=on

=== modified file 'mysql-test/r/subselect_sj.result'
--- mysql-test/r/subselect_sj.result	2010-03-29 14:04:35 +0000
+++ mysql-test/r/subselect_sj.result	2010-06-23 18:44:58 +0000
@@ -202,39 +202,39 @@
 
 select @@optimizer_switch;
 @@optimizer_switch
-index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,index_condition_pushdown=on,firstmatch=on,loosescan=on,materialization=on,semijoin=on,partial_match_rowid_merge=on,partial_match_table_scan=on
+index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,index_condition_pushdown=on,firstmatch=on,loosescan=on,materialization=on,semijoin=on,partial_match_rowid_merge=on,partial_match_table_scan=on,subquery_cache=on
 set optimizer_switch='default,materialization=off';
 select @@optimizer_switch;
 @@optimizer_switch
-index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,index_condition_pushdown=on,firstmatch=on,loosescan=on,materialization=off,semijoin=on,partial_match_rowid_merge=on,partial_match_table_scan=on
+index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,index_condition_pushdown=on,firstmatch=on,loosescan=on,materialization=off,semijoin=on,partial_match_rowid_merge=on,partial_match_table_scan=on,subquery_cache=on
 set optimizer_switch='default,semijoin=off';
 select @@optimizer_switch;
 @@optimizer_switch
-index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,index_condition_pushdown=on,firstmatch=on,loosescan=on,materialization=on,semijoin=off,partial_match_rowid_merge=on,partial_match_table_scan=on
+index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,index_condition_pushdown=on,firstmatch=on,loosescan=on,materialization=on,semijoin=off,partial_match_rowid_merge=on,partial_match_table_scan=on,subquery_cache=on
 set optimizer_switch='default,loosescan=off';
 select @@optimizer_switch;
 @@optimizer_switch
-index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,index_condition_pushdown=on,firstmatch=on,loosescan=off,materialization=on,semijoin=on,partial_match_rowid_merge=on,partial_match_table_scan=on
+index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,index_condition_pushdown=on,firstmatch=on,loosescan=off,materialization=on,semijoin=on,partial_match_rowid_merge=on,partial_match_table_scan=on,subquery_cache=on
 set optimizer_switch='default,semijoin=off,materialization=off';
 select @@optimizer_switch;
 @@optimizer_switch
-index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,index_condition_pushdown=on,firstmatch=on,loosescan=on,materialization=off,semijoin=off,partial_match_rowid_merge=on,partial_match_table_scan=on
+index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,index_condition_pushdown=on,firstmatch=on,loosescan=on,materialization=off,semijoin=off,partial_match_rowid_merge=on,partial_match_table_scan=on,subquery_cache=on
 set optimizer_switch='default,materialization=off,semijoin=off';
 select @@optimizer_switch;
 @@optimizer_switch
-index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,index_condition_pushdown=on,firstmatch=on,loosescan=on,materialization=off,semijoin=off,partial_match_rowid_merge=on,partial_match_table_scan=on
+index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,index_condition_pushdown=on,firstmatch=on,loosescan=on,materialization=off,semijoin=off,partial_match_rowid_merge=on,partial_match_table_scan=on,subquery_cache=on
 set optimizer_switch='default,semijoin=off,materialization=off,loosescan=off';
 select @@optimizer_switch;
 @@optimizer_switch
-index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,index_condition_pushdown=on,firstmatch=on,loosescan=off,materialization=off,semijoin=off,partial_match_rowid_merge=on,partial_match_table_scan=on
+index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,index_condition_pushdown=on,firstmatch=on,loosescan=off,materialization=off,semijoin=off,partial_match_rowid_merge=on,partial_match_table_scan=on,subquery_cache=on
 set optimizer_switch='default,semijoin=off,loosescan=off';
 select @@optimizer_switch;
 @@optimizer_switch
-index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,index_condition_pushdown=on,firstmatch=on,loosescan=off,materialization=on,semijoin=off,partial_match_rowid_merge=on,partial_match_table_scan=on
+index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,index_condition_pushdown=on,firstmatch=on,loosescan=off,materialization=on,semijoin=off,partial_match_rowid_merge=on,partial_match_table_scan=on,subquery_cache=on
 set optimizer_switch='default,materialization=off,loosescan=off';
 select @@optimizer_switch;
 @@optimizer_switch
-index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,index_condition_pushdown=on,firstmatch=on,loosescan=off,materialization=off,semijoin=on,partial_match_rowid_merge=on,partial_match_table_scan=on
+index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,index_condition_pushdown=on,firstmatch=on,loosescan=off,materialization=off,semijoin=on,partial_match_rowid_merge=on,partial_match_table_scan=on,subquery_cache=on
 set optimizer_switch=default;
 drop table t0, t1, t2;
 drop table t10, t11, t12;

=== modified file 'mysql-test/r/subselect_sj_jcl6.result'
--- mysql-test/r/subselect_sj_jcl6.result	2010-03-29 14:04:35 +0000
+++ mysql-test/r/subselect_sj_jcl6.result	2010-06-23 18:44:58 +0000
@@ -206,39 +206,39 @@
 
 select @@optimizer_switch;
 @@optimizer_switch
-index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,index_condition_pushdown=on,firstmatch=on,loosescan=on,materialization=on,semijoin=on,partial_match_rowid_merge=on,partial_match_table_scan=on
+index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,index_condition_pushdown=on,firstmatch=on,loosescan=on,materialization=on,semijoin=on,partial_match_rowid_merge=on,partial_match_table_scan=on,subquery_cache=on
 set optimizer_switch='default,materialization=off';
 select @@optimizer_switch;
 @@optimizer_switch
-index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,index_condition_pushdown=on,firstmatch=on,loosescan=on,materialization=off,semijoin=on,partial_match_rowid_merge=on,partial_match_table_scan=on
+index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,index_condition_pushdown=on,firstmatch=on,loosescan=on,materialization=off,semijoin=on,partial_match_rowid_merge=on,partial_match_table_scan=on,subquery_cache=on
 set optimizer_switch='default,semijoin=off';
 select @@optimizer_switch;
 @@optimizer_switch
-index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,index_condition_pushdown=on,firstmatch=on,loosescan=on,materialization=on,semijoin=off,partial_match_rowid_merge=on,partial_match_table_scan=on
+index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,index_condition_pushdown=on,firstmatch=on,loosescan=on,materialization=on,semijoin=off,partial_match_rowid_merge=on,partial_match_table_scan=on,subquery_cache=on
 set optimizer_switch='default,loosescan=off';
 select @@optimizer_switch;
 @@optimizer_switch
-index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,index_condition_pushdown=on,firstmatch=on,loosescan=off,materialization=on,semijoin=on,partial_match_rowid_merge=on,partial_match_table_scan=on
+index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,index_condition_pushdown=on,firstmatch=on,loosescan=off,materialization=on,semijoin=on,partial_match_rowid_merge=on,partial_match_table_scan=on,subquery_cache=on
 set optimizer_switch='default,semijoin=off,materialization=off';
 select @@optimizer_switch;
 @@optimizer_switch
-index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,index_condition_pushdown=on,firstmatch=on,loosescan=on,materialization=off,semijoin=off,partial_match_rowid_merge=on,partial_match_table_scan=on
+index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,index_condition_pushdown=on,firstmatch=on,loosescan=on,materialization=off,semijoin=off,partial_match_rowid_merge=on,partial_match_table_scan=on,subquery_cache=on
 set optimizer_switch='default,materialization=off,semijoin=off';
 select @@optimizer_switch;
 @@optimizer_switch
-index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,index_condition_pushdown=on,firstmatch=on,loosescan=on,materialization=off,semijoin=off,partial_match_rowid_merge=on,partial_match_table_scan=on
+index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,index_condition_pushdown=on,firstmatch=on,loosescan=on,materialization=off,semijoin=off,partial_match_rowid_merge=on,partial_match_table_scan=on,subquery_cache=on
 set optimizer_switch='default,semijoin=off,materialization=off,loosescan=off';
 select @@optimizer_switch;
 @@optimizer_switch
-index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,index_condition_pushdown=on,firstmatch=on,loosescan=off,materialization=off,semijoin=off,partial_match_rowid_merge=on,partial_match_table_scan=on
+index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,index_condition_pushdown=on,firstmatch=on,loosescan=off,materialization=off,semijoin=off,partial_match_rowid_merge=on,partial_match_table_scan=on,subquery_cache=on
 set optimizer_switch='default,semijoin=off,loosescan=off';
 select @@optimizer_switch;
 @@optimizer_switch
-index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,index_condition_pushdown=on,firstmatch=on,loosescan=off,materialization=on,semijoin=off,partial_match_rowid_merge=on,partial_match_table_scan=on
+index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,index_condition_pushdown=on,firstmatch=on,loosescan=off,materialization=on,semijoin=off,partial_match_rowid_merge=on,partial_match_table_scan=on,subquery_cache=on
 set optimizer_switch='default,materialization=off,loosescan=off';
 select @@optimizer_switch;
 @@optimizer_switch
-index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,index_condition_pushdown=on,firstmatch=on,loosescan=off,materialization=off,semijoin=on,partial_match_rowid_merge=on,partial_match_table_scan=on
+index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,index_condition_pushdown=on,firstmatch=on,loosescan=off,materialization=off,semijoin=on,partial_match_rowid_merge=on,partial_match_table_scan=on,subquery_cache=on
 set optimizer_switch=default;
 drop table t0, t1, t2;
 drop table t10, t11, t12;

=== modified file 'mysql-test/t/subselect3.test'
--- mysql-test/t/subselect3.test	2010-03-20 12:01:47 +0000
+++ mysql-test/t/subselect3.test	2010-06-23 18:44:58 +0000
@@ -98,10 +98,12 @@
 delete from t2;
 insert into t2 values (NULL, 0),(NULL, 0), (NULL, 0), (NULL, 0);
 
+set optimizer_switch='subquery_cache=off';
 flush status;
 select oref, a, a in (select a from t1 where oref=t2.oref) Z from t2;
 show status like '%Handler_read%';
 select 'No key lookups, seq reads: 29= 5 reads from t2 + 4 * 6 reads from t1.' Z;
+set @@optimizer_switch=@save_optimizer_switch;
 
 drop table t1, t2;
 

=== modified file 'sql/CMakeLists.txt'
--- sql/CMakeLists.txt	2010-03-20 12:01:47 +0000
+++ sql/CMakeLists.txt	2010-06-23 18:47:59 +0000
@@ -78,7 +78,7 @@
                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_expression_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'
--- sql/Makefile.am	2010-03-20 12:01:47 +0000
+++ sql/Makefile.am	2010-06-23 18:47:59 +0000
@@ -80,7 +80,7 @@
 			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_expression_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 @@
 			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_expression_cache.cc
 
 nodist_mysqld_SOURCES =	mini_client_errors.c pack.c client.c my_time.c my_user.c 
 

=== modified file 'sql/item.cc'
--- sql/item.cc	2010-03-20 12:01:47 +0000
+++ sql/item.cc	2010-06-23 21:34:31 +0000
@@ -28,6 +28,9 @@
 
 const String my_null_string("NULL", 4, default_charset_info);
 
+static int save_field_in_field(Field *from,my_bool * null_value,
+                               Field *to, bool no_conversions);
+
 /****************************************************************************/
 
 /* Hybrid_type_traits {_real} */
@@ -540,6 +543,35 @@
   return (this->*transformer)(arg);
 }
 
+/**
+  Creates and sets expression cache for this item
+
+  @param thd             Thread handler
+  @param depends_on      List of external dependences
+
+  @details Creates expression cache item, prepare it and set expression
+  cache in it for this item with parameters in 'depends_on' list. If all
+  above finished with success reference to the new cache will be returned
+  otherwise - reference to this item.
+
+  @note it is common part for all items which creates and set expression
+  cache
+*/
+
+Item* Item::set_ecache(THD *thd, List<Item *> &depends_on)
+{
+  DBUG_ENTER("Item::set_ecache");
+  Item_cache_wrapper *wrapper;
+  if ((wrapper= new Item_cache_wrapper(this)) &&
+      !wrapper->fix_fields(thd, (Item**)&wrapper))
+  {
+    if (wrapper->set_cache(thd, depends_on))
+      DBUG_RETURN(this);
+    DBUG_RETURN(wrapper);
+  }
+  DBUG_RETURN(this);
+}
+
 
 Item_ident::Item_ident(Name_resolution_context *context_arg,
                        const char *db_name_arg,const char *table_name_arg,
@@ -2273,7 +2305,6 @@
   str->append(str_value);
 }
 
-
 Item_uint::Item_uint(const char *str_arg, uint length):
   Item_int(str_arg, length)
 {
@@ -3646,12 +3677,20 @@
                         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)
+  {
+    DBUG_PRINT("info", ("depended_from assigned: 0x%lx", (ulong) last));
     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 +3700,7 @@
                  resolved_item->field_name,
                  current->select_number, last->select_number);
   }
-  return FALSE;
+  DBUG_RETURN(FALSE);
 }
 
 
@@ -4098,6 +4137,9 @@
                               ((ref_type == REF_ITEM ||
                                 ref_type == FIELD_ITEM) ?
                                (Item_ident*) (*reference) : 0));
+            context->select_lex->
+              register_dependency_item(last_checked_context->select_lex,
+                                       reference);
             return 0;
           }
         }
@@ -4113,7 +4155,9 @@
                             ((ref_type == REF_ITEM || ref_type == FIELD_ITEM) ?
                              (Item_ident*) (*reference) :
                              0));
-                            
+          context->select_lex->
+              register_dependency_item(last_checked_context->select_lex,
+                                       reference);
           /*
             A reference to a view field had been found and we
             substituted it instead of this Item (find_field_in_tables
@@ -4215,6 +4259,10 @@
     mark_as_dependent(thd, last_checked_context->select_lex,
                       context->select_lex, rf,
                       rf);
+    context->select_lex->
+              register_dependency_item(last_checked_context->select_lex,
+                                       reference);
+
     return 0;
   }
   else
@@ -4222,6 +4270,9 @@
     mark_as_dependent(thd, last_checked_context->select_lex,
                       context->select_lex,
                       this, (Item_ident*)*reference);
+    context->select_lex->
+              register_dependency_item(last_checked_context->select_lex,
+                                       reference);
     if (last_checked_context->select_lex->having_fix_field)
     {
       Item_ref *rf;
@@ -5082,39 +5133,69 @@
 
 
 /**
+  Saves one Fields (field or result_field) of an Item_ref or Item_field
+  in given Field
+
+  @param from            Field to copy value from
+  @param null_value      reference on item null_value to set it if it is needed
+  @param to              Field to cope value to
+  @param no_conversions  how to deal with NULL value (see
+                         set_field_to_null_with_conversions())
+
+  @details Saves 'from' field value in 'to' field and set null_value
+  reference in TRUE if the value is NULL. 'no_conversions' passed to
+  set_field_to_null_with_conversions to determinate what to do with NULL
+  value if it can't be accepted by 'to' field.
+
+  @note Used by Item_field::save_in_field, Item_field::save_org_in_field and
+  Item_ref::save_in_field
+
+  @retval FALSE OK
+  @retval TRUE  Error
+*/
+
+static int save_field_in_field(Field *from, my_bool *null_value,
+                               Field *to, bool no_conversions)
+{
+  int res;
+  DBUG_ENTER("save_field_in_field");
+  if (from->is_null())
+  {
+    (*null_value)= 1;
+    res= set_field_to_null_with_conversions(to, no_conversions);
+  }
+  else
+  {
+    to->set_notnull();
+    res= field_conv(to, from);
+    (*null_value)= 0;
+  }
+  DBUG_RETURN(res);
+}
+
+/**
   Set a field's value from a item.
 */
 
 void Item_field::save_org_in_field(Field *to)
 {
-  if (field->is_null())
-  {
-    null_value=1;
-    set_field_to_null_with_conversions(to, 1);
-  }
-  else
-  {
-    to->set_notnull();
-    field_conv(to,field);
-    null_value=0;
-  }
+  save_field_in_field(field, &null_value, to, TRUE);
 }
 
 int Item_field::save_in_field(Field *to, bool no_conversions)
 {
-  int res;
-  if (result_field->is_null())
-  {
-    null_value=1;
-    res= set_field_to_null_with_conversions(to, no_conversions);
-  }
-  else
-  {
-    to->set_notnull();
-    res= field_conv(to,result_field);
-    null_value=0;
-  }
-  return res;
+  DBUG_ENTER("Item_field::save_in_field");
+  /* if it is external field */
+  if (unlikely(depended_from))
+  {
+    DBUG_PRINT("info", ("field used, depended_from: 0x%lx",
+                        (ulong) depended_from));
+    DBUG_RETURN(save_field_in_field(field, &null_value, to, no_conversions));
+  }
+
+  DBUG_PRINT("info", ("result_field used"));
+  DBUG_RETURN(save_field_in_field(result_field, &null_value, to,
+                                  no_conversions));
 }
 
 
@@ -5973,6 +6054,9 @@
                                 refer_type == FIELD_ITEM) ?
                                (Item_ident*) (*reference) :
                                0));
+            context->select_lex->
+              register_dependency_item(last_checked_context->select_lex,
+                                       reference);
             /*
               view reference found, we substituted it instead of this
               Item, so can quit
@@ -6023,6 +6107,9 @@
         thd->change_item_tree(reference, fld);
         mark_as_dependent(thd, last_checked_context->select_lex,
                           thd->lex->current_select, fld, fld);
+        context->select_lex->
+              register_dependency_item(last_checked_context->select_lex,
+                                       reference);
         /*
           A reference is resolved to a nest level that's outer or the same as
           the nest level of the enclosing set function : adjust the value of
@@ -6046,6 +6133,9 @@
       DBUG_ASSERT(*ref && (*ref)->fixed);
       mark_as_dependent(thd, last_checked_context->select_lex,
                         context->select_lex, this, this);
+      context->select_lex->
+              register_dependency_item(last_checked_context->select_lex,
+                                       ref);
       /*
         A reference is resolved to a nest level that's outer or the same as
         the nest level of the enclosing set function : adjust the value of
@@ -6312,7 +6402,8 @@
 int Item_ref::save_in_field(Field *to, bool no_conversions)
 {
   int res;
-  DBUG_ASSERT(!result_field);
+  if (result_field && !depended_from)
+    return save_field_in_field(result_field, &null_value, to, no_conversions);
   res= (*ref)->save_in_field(to, no_conversions);
   null_value= (*ref)->null_value;
   return res;
@@ -6421,6 +6512,297 @@
 
 
 /**
+  Item_cache_wrapper destructor also destroys expression cache
+*/
+
+Item_cache_wrapper::~Item_cache_wrapper()
+{
+  delete ecache;
+  // expression_value is Item so it will be destroyed from list of Items
+}
+
+/**
+  Prepare Item_cache_wrapper
+
+  @details Creates Item_cache of the same type as result of Item we are
+  caching for. This cache will be used for value of the expression to avoid
+  its double evaluation
+
+  @retval FALSE OK
+  @retval TRUE  Error
+*/
+bool Item_cache_wrapper::fix_fields(THD *thd  __attribute__((unused)),
+                                    Item **it __attribute__((unused)))
+{
+  DBUG_ENTER("Item_cache_wrapper::fix_fields");
+  DBUG_ASSERT(item->fixed);
+  if ((expression_value= Item_cache::get_cache(item)))
+    expression_value->setup(item);
+  fixed= 1;
+  DBUG_RETURN(expression_value == NULL);
+}
+
+/**
+  Cleanup the cache before reusing or destruction by Query_arena.
+*/
+
+void Item_cache_wrapper::cleanup()
+{
+  delete ecache;
+  ecache= 0;
+  // expression_value is Item so it will be destroyed from list of Items
+  expression_value= 0;
+}
+
+
+/**
+  Set expression cache for the arguments list
+
+  @details Creates Expression_cache_tmptable for given list of dependencies
+  ('depends_on' and result of the expression this item caching.
+
+  @retval FALSE OK
+  @retval TRUE  Error
+*/
+
+bool Item_cache_wrapper::set_cache(THD *thd, List<Item*> &depends_on)
+{
+  DBUG_ENTER("Item_cache_wrapper::set_cache");
+  ecache= new Expression_cache_tmptable(thd, depends_on, expression_value);
+  DBUG_RETURN(ecache == NULL);
+}
+
+/**
+  Checks if current set of parameters (stored in cache during its creation)
+  represented in the cache and return result as Item if it is true.
+
+  @retval NULL   nothing was found in the cache
+  @retval <item*> result found
+*/
+
+Item *Item_cache_wrapper::check_cache()
+{
+  DBUG_ENTER("Item_cache_wrapper::check_cache");
+  if (ecache)
+  {
+    Expression_cache_tmptable::result res;
+    Item *cached_value;
+    res= ecache->check_value(&cached_value);
+    if (res == Expression_cache_tmptable::HIT)
+      DBUG_RETURN(cached_value);
+  }
+  DBUG_RETURN(NULL);
+}
+
+
+/**
+  Evaluates integer value of this item via the cache or the cached item
+*/
+
+longlong Item_cache_wrapper::val_int()
+{
+  DBUG_ENTER("Item_cache_wrapper::val_int");
+  if (!ecache)
+  {
+    longlong tmp= item->val_int();
+    null_value= item->null_value;
+    DBUG_RETURN(tmp);
+  }
+  {
+    Item *cached_value;
+    if ((cached_value= check_cache()))
+    {
+      longlong tmp= cached_value->val_int();
+      null_value= cached_value->null_value;
+      DBUG_RETURN(tmp);
+    }
+    // Get value in cache to avoid double evaluation
+    expression_value->store(item);
+    expression_value->cache_value();
+    ecache->put_value(expression_value); // put in ecache
+    null_value= expression_value->null_value;
+    DBUG_RETURN(expression_value->val_int());
+  }
+}
+
+/**
+  Evaluates real value of this item via the cache or the cached item
+*/
+
+double Item_cache_wrapper::val_real()
+{
+  DBUG_ENTER("Item_cache_wrapper::val_real");
+  if (!ecache)
+  {
+    double tmp= item->val_real();
+    null_value= item->null_value;
+    DBUG_RETURN(tmp);
+  }
+  {
+    Item *cached_value;
+    if ((cached_value= check_cache()))
+    {
+      double tmp= cached_value->val_real();
+      null_value= cached_value->null_value;
+      DBUG_RETURN(tmp);
+    }
+    // Get value in cache to avoid double evaluation
+    expression_value->store(item);
+    expression_value->cache_value();
+    ecache->put_value(expression_value); // put in ecache
+    null_value= expression_value->null_value;
+    DBUG_RETURN(expression_value->val_real());
+  }
+}
+
+
+/**
+  Evaluates string value of this item via the cache or the cached item
+*/
+
+String *Item_cache_wrapper::val_str(String* str)
+{
+  DBUG_ENTER("Item_cache_wrapper::val_str");
+  if (!ecache)
+  {
+    String *tmp= item->val_str(str);
+    null_value= item->null_value;
+    DBUG_RETURN(tmp);
+  }
+  {
+    Item *cached_value;
+    if ((cached_value= check_cache()))
+    {
+      String *tmp= cached_value->val_str(str);
+      null_value= cached_value->null_value;
+      DBUG_RETURN(tmp);
+    }
+    // Get value in cache to avoid double evaluation
+    expression_value->store(item);
+    expression_value->cache_value();
+    ecache->put_value(expression_value); // put in ecache
+    if ((null_value= expression_value->null_value))
+      DBUG_RETURN(NULL);
+    DBUG_RETURN(expression_value->val_str(str));
+  }
+}
+
+
+/**
+  Evaluates decimal value of this item via the cache or the cached item
+*/
+
+my_decimal *Item_cache_wrapper::val_decimal(my_decimal* decimal_value)
+{
+  DBUG_ENTER("Item_cache_wrapper::val_decimal");
+  if (!ecache)
+  {
+    my_decimal *tmp= item->val_decimal(decimal_value);
+    null_value= item->null_value;
+    DBUG_RETURN(tmp);
+  }
+  {
+    Item *cached_value;
+    if ((cached_value= check_cache()))
+    {
+      my_decimal *tmp= cached_value->val_decimal(decimal_value);
+      null_value= cached_value->null_value;
+      DBUG_RETURN(tmp);
+    }
+    // Get value in cache to avoid double evaluation
+    expression_value->store(item);
+    expression_value->cache_value();
+    ecache->put_value(expression_value); // put in ecache
+    if ((null_value= expression_value->null_value))
+      DBUG_RETURN(NULL);
+    DBUG_RETURN(expression_value->val_decimal(decimal_value));
+  }
+}
+
+
+/**
+  Evaluates boolean value of this item via the cache or the cached item
+*/
+
+bool Item_cache_wrapper::val_bool()
+{
+  DBUG_ENTER("Item_cache_wrapper::val_bool");
+  if (!ecache)
+  {
+    bool tmp= item->val_bool();
+    null_value= item->null_value;
+    DBUG_RETURN(tmp);
+  }
+  {
+    Item *cached_value;
+    if ((cached_value= check_cache()))
+    {
+      bool tmp= cached_value->val_bool();
+      null_value= cached_value->null_value;
+      DBUG_RETURN(tmp);
+    }
+    // Get value in cache to avoid double evaluation
+    expression_value->store(item);
+    expression_value->cache_value();
+    ecache->put_value(expression_value); // put in ecache
+    null_value= expression_value->null_value;
+    DBUG_RETURN(expression_value->val_bool());
+  }
+}
+
+/**
+  Checks value of this item for NULL via the cache or the cached item
+*/
+
+bool Item_cache_wrapper::is_null()
+{
+  DBUG_ENTER("Item_cache_wrapper::is_null");
+  if (!ecache)
+  {
+    bool tmp= item->is_null();
+    null_value= item->null_value;
+    DBUG_RETURN(tmp);
+  }
+  {
+    Item *cached_value;
+    if ((cached_value= check_cache()))
+    {
+      bool tmp= cached_value->is_null();
+      null_value= cached_value->null_value;
+      DBUG_RETURN(tmp);
+    }
+    expression_value->store(item);
+    expression_value->cache_value();
+    ecache->put_value(expression_value); // put in ecache
+    DBUG_RETURN((null_value= expression_value->null_value));
+  }
+}
+
+
+/**
+  Evaluates date value of this item via the cache or the cached item
+*/
+
+bool Item_cache_wrapper::get_date(MYSQL_TIME *ltime, uint fuzzydate)
+{
+  DBUG_ENTER("Item_cache_wrapper::get_date");
+  if (!ecache)
+    DBUG_RETURN((null_value= item->get_date(ltime, fuzzydate)));
+
+  {
+    Item *cached_value;
+    if ((cached_value= check_cache()))
+      DBUG_RETURN((null_value= cached_value->get_date(ltime, fuzzydate)));
+
+    // Get value in cache to avoid double evaluation
+    expression_value->store(item);
+    expression_value->cache_value();
+    ecache->put_value(expression_value); // put in ecache
+    DBUG_RETURN((null_value= expression_value->get_date(ltime, fuzzydate)));
+  }
+}
+
+/**
   Prepare referenced field then call usual Item_direct_ref::fix_fields .
 
   @param thd         thread handler

=== modified file 'sql/item.h'
--- sql/item.h	2010-03-20 12:01:47 +0000
+++ sql/item.h	2010-06-23 18:44:58 +0000
@@ -1090,6 +1090,7 @@
 
   virtual Item *neg_transformer(THD *thd) { return NULL; }
   virtual Item *update_value_transformer(uchar *select_arg) { return this; }
+  virtual Item *cache_insert_transformer(uchar *thd_arg) { return this; }
   virtual Item *safe_charset_converter(CHARSET_INFO *tocs);
   void delete_self()
   {
@@ -1143,6 +1144,9 @@
     { return Field::GEOM_GEOMETRY; };
   String *check_well_formed_result(String *str, bool send_error= 0);
   bool eq_by_collation(Item *item, bool binary_cmp, CHARSET_INFO *cs); 
+
+  /* Sets expression cache for this Item */
+  Item* set_ecache(THD *thd, List<Item*> &depends_on);
 };
 
 
@@ -1922,8 +1926,6 @@
   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;}
 };
 
 
@@ -2346,7 +2348,8 @@
 protected:
   void set_properties();
 public:
-  enum Ref_Type { REF, DIRECT_REF, VIEW_REF, OUTER_REF, AGGREGATE_REF };
+  enum Ref_Type { REF, DIRECT_REF, VIEW_REF, OUTER_REF, AGGREGATE_REF,
+                  CACHE_REF };
   Field *result_field;			 /* Save result here */
   Item **ref;
   Item_ref(Name_resolution_context *context_arg,
@@ -2509,6 +2512,73 @@
   virtual Ref_Type ref_type() { return DIRECT_REF; }
 };
 
+class Expression_cache;
+class Item_cache;
+/*
+  The same as Item_ref, but get value from val_* family of method to get
+  value of item on which it referred instead of result* family. Also it
+  uses Expression_cache for result cacheing if it is set
+*/
+class Item_cache_wrapper :public Item_ref
+{
+private:
+  Expression_cache *ecache;
+  Item_cache *expression_value;
+  Item *item;
+  Name_resolution_context cn; // Fake name resolution context
+
+
+  Item *check_cache();
+
+public:
+  Item_cache_wrapper(Item *item_arg)
+    :Item_ref(&cn, 0, "<ecache>", "<Item>", FALSE),
+    ecache(NULL), expression_value(NULL), item(item_arg)
+  {
+    cn.init(); // Fake name resolution context initialization;
+    ref= &item;
+    set_properties();
+    name= item_arg->name;
+    name_length= item_arg->name_length;
+  }
+
+  ~Item_cache_wrapper();
+
+  bool set_cache(THD *thd, List<Item*> &depends_on);
+
+  bool fix_fields(THD *thd, Item **it);
+  void cleanup();
+
+  /* Methods of getting value which should be cached */
+  double val_real();
+  longlong val_int();
+  String *val_str(String* tmp);
+  my_decimal *val_decimal(my_decimal *);
+  bool val_bool();
+  bool is_null();
+  bool get_date(MYSQL_TIME *ltime,uint fuzzydate);
+  bool send(Protocol *protocol, String *buffer)
+  { return Item::send(protocol, buffer); }
+
+  /*
+    Methods of getting value which should be cached in general case
+  int save_in_field(Field *field, bool no_conversions);
+  void save_org_in_field(Field *field);
+  */
+
+  // Should be invisible
+  virtual void print(String *str, enum_query_type query_type)
+  {
+    // TODO: maybe print something for EXPLAIN EXTENDED
+    return item->print(str, query_type);
+  }
+  virtual const char *full_name() const { return item->full_name(); }
+  virtual void make_field(Send_field *field) { item->make_field(field); }
+
+  virtual Ref_Type ref_type() { return CACHE_REF; }
+};
+
+
 /*
   Class for view fields, the same as Item_direct_ref, but call fix_fields
   of reference if it is not called yet
@@ -3146,7 +3216,8 @@
     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 +3225,7 @@
     value_cached(0)
   {
     fixed= 1;
+    maybe_null= 1;
     null_value= 1;
   }
 

=== modified file 'sql/item_cmpfunc.cc'
--- sql/item_cmpfunc.cc	2010-03-20 12:01:47 +0000
+++ sql/item_cmpfunc.cc	2010-06-23 21:39:51 +0000
@@ -1716,19 +1716,20 @@
 
 bool Item_in_optimizer::fix_fields(THD *thd, Item **ref)
 {
+  DBUG_ENTER("Item_in_optimizer::fix_fields");
   DBUG_ASSERT(fixed == 0);
   if (fix_left(thd, ref))
-    return TRUE;
+    DBUG_RETURN(TRUE);
   if (args[0]->maybe_null)
     maybe_null=1;
 
   if (!args[1]->fixed && args[1]->fix_fields(thd, args+1))
-    return TRUE;
+    DBUG_RETURN(TRUE);
   Item_in_subselect * sub= (Item_in_subselect *)args[1];
   if (args[0]->cols() != sub->engine->cols())
   {
     my_error(ER_OPERAND_COLUMNS, MYF(0), args[0]->cols());
-    return TRUE;
+    DBUG_RETURN(TRUE);
   }
   if (args[1]->maybe_null)
     maybe_null=1;
@@ -1737,17 +1738,54 @@
   not_null_tables_cache|= args[1]->not_null_tables();
   const_item_cache&= args[1]->const_item();
   fixed= 1;
-  return FALSE;
+  DBUG_RETURN(FALSE);
+}
+
+
+/**
+  Adds expression cache for the subquery controlled by this item
+  if it is needed
+
+  @param thd_arg         Thread handler
+
+  @details Adds left expression to the list of dependencies, checks if it is
+  possible to cache this IN subquery and add expression cache if it is
+  possible (scalar expression, cacheable subquery, not prohibited by switch).
+
+  @note used from Item::transform()
+
+  @return new cache of pointer to this item
+*/
+
+Item *Item_in_optimizer::cache_insert_transformer(uchar *thd_arg)
+{
+  THD *thd= (THD*) thd_arg;
+  DBUG_ENTER("Item_in_optimizer::cache_insert_transformer");
+  List<Item*> &depends_on= ((Item_subselect *)args[1])->depends_on;
+
+  // We depends from left expression so adds it to the list
+  depends_on.push_front((Item**)args);
+
+  if (args[0]->cols() == 1 &&
+      thd->variables.optimizer_switch & OPTIMIZER_SWITCH_SUBQUERY_CACHE &&
+      !(((Item_subselect *)args[1])->engine->uncacheable() &
+        (UNCACHEABLE_RAND | UNCACHEABLE_SIDEEFFECT)))
+  {
+    DBUG_RETURN(set_ecache(thd, depends_on));
+  }
+  DBUG_RETURN(this);
 }
 
 
 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();
-  
+
   if (cache->null_value)
   {
     /*
@@ -1818,11 +1856,11 @@
       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;
+  DBUG_RETURN(tmp);
 }
 
 

=== modified file 'sql/item_cmpfunc.h'
--- sql/item_cmpfunc.h	2010-03-20 12:01:47 +0000
+++ sql/item_cmpfunc.h	2010-06-23 18:44:58 +0000
@@ -215,6 +215,7 @@
 
 
 class Item_cache;
+class Expression_cache;
 #define UNKNOWN ((my_bool)-1)
 
 
@@ -259,6 +260,7 @@
   Item_cache **get_cache() { return &cache; }
   void keep_top_level_cache();
   Item *transform(Item_transformer transformer, uchar *arg);
+  virtual Item *cache_insert_transformer(uchar *thd_arg);
 };
 
 class Comp_creator

=== modified file 'sql/item_subselect.cc'
--- sql/item_subselect.cc	2010-03-29 14:04:35 +0000
+++ sql/item_subselect.cc	2010-06-23 21:45:48 +0000
@@ -35,9 +35,9 @@
 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), 
+  const_item_cache(1),
   inside_first_fix_fields(0), done_first_fix_fields(FALSE),
-  eliminated(FALSE), 
+  eliminated(FALSE),
   engine_changed(0), changed(0), is_correlated(FALSE)
 {
   with_subselect= 1;
@@ -93,6 +93,8 @@
     SELECT_LEX *upper= unit->outer_select();
     if (upper->parsing_place == IN_HAVING)
       upper->subquery_in_having= 1;
+    /* Subquery is expression cache candidate */
+    upper->has_ecache_candidates[upper->parsing_place]= TRUE;
   }
   DBUG_VOID_RETURN;
 }
@@ -116,6 +118,7 @@
   }
   if (engine)
     engine->cleanup();
+  depends_on.empty();
   reset();
   value_assigned= 0;
   DBUG_VOID_RETURN;
@@ -746,8 +749,12 @@
 
 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);
   }
   else
@@ -765,7 +772,40 @@
   */
   if (engine->no_tables())
     maybe_null= engine->may_be_null();
-}
+  DBUG_VOID_RETURN;
+}
+
+
+/**
+  Adds expression cache for this subquery if it is needed
+
+  @param thd_arg         Thread handler
+
+  @details Checks if it is possible to cache this single row subquery and
+  add expression cache if it is possible (the subquery is dependent, scalar,
+  cacheable and caching is not prohibited by switch).
+
+  @note used from Item::transform()
+
+  @return new cache of pointer to this item
+*/
+
+Item* Item_singlerow_subselect::cache_insert_transformer(uchar *thd_arg)
+{
+  THD *thd= (THD*) thd_arg;
+  DBUG_ENTER("Item_singlerow_subselect::cache_insert_transformer");
+
+  if (depends_on.elements &&
+      engine->cols() == 1 &&
+      optimizer_flag(thd, OPTIMIZER_SWITCH_SUBQUERY_CACHE) &&
+      !(engine->uncacheable() & (UNCACHEABLE_RAND |
+                                 UNCACHEABLE_SIDEEFFECT)))
+  {
+    DBUG_RETURN(set_ecache(thd, depends_on));
+  }
+  DBUG_RETURN(this);
+}
+
 
 uint Item_singlerow_subselect::cols()
 {
@@ -952,12 +992,44 @@
 
 void Item_exists_subselect::fix_length_and_dec()
 {
+  DBUG_ENTER("Item_exists_subselect::fix_length_and_dec");
    decimals= 0;
    max_length= 1;
    max_columns= engine->cols();
   /* We need only 1 row to determine existence */
   unit->global_parameters->select_limit= new Item_int((int32) 1);
-}
+
+  DBUG_VOID_RETURN;
+}
+
+
+/**
+  Adds expression cache for this subquery if it is needed
+
+  @param thd_arg         Thread handler
+
+  @details Checks if it is possible to cache this EXISTS subquery and add
+  expression cache if it is possible (the subquery is really EXISTS,
+  dependent, scalar, cacheable and caching is not prohibited by switch).
+  @note used from Item::transform()
+
+  @return new cache of pointer to this item
+*/
+
+Item* Item_exists_subselect::cache_insert_transformer(uchar *thd_arg)
+{
+  THD *thd= (THD*) thd_arg;
+  DBUG_ENTER("Item_exists_subselect::cache_insert_transformer");
+  if (substype() == EXISTS_SUBS && depends_on.elements &&
+      optimizer_flag(thd, OPTIMIZER_SWITCH_SUBQUERY_CACHE) &&
+      !(engine->uncacheable() & (UNCACHEABLE_RAND |
+                                 UNCACHEABLE_SIDEEFFECT)))
+  {
+    DBUG_RETURN(set_ecache(thd, depends_on));
+  }
+  DBUG_RETURN(this);
+}
+
 
 double Item_exists_subselect::val_real()
 {

=== modified file 'sql/item_subselect.h'
--- sql/item_subselect.h	2010-03-29 14:04:35 +0000
+++ sql/item_subselect.h	2010-06-23 18:44:59 +0000
@@ -88,11 +88,19 @@
   */
   List<Ref_to_outside> upper_refs;
   st_select_lex *parent_select;
-  
- /*
+
+  /**
+     List of references on items subquery depends on (externally resolved);
+
+     @note We can't store direct links on Items because it could be
+           substituted with other item (for example for grouping).
+   */
+  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 */
@@ -202,6 +210,7 @@
 {
 protected:
   Item_cache *value, **row;
+
 public:
   Item_singlerow_subselect(st_select_lex *select_lex);
   Item_singlerow_subselect() :Item_subselect(), value(0), row (0) {}
@@ -242,6 +251,8 @@
   */
   st_select_lex* invalidate_and_restore_select_lex();
 
+  Item* cache_insert_transformer(uchar *thd_arg);
+
   friend class select_singlerow_subselect;
 };
 
@@ -289,6 +300,8 @@
   void fix_length_and_dec();
   virtual void print(String *str, enum_query_type query_type);
 
+  Item* cache_insert_transformer(uchar *thd_arg);
+
   friend class select_exists_subselect;
   friend class subselect_uniquesubquery_engine;
   friend class subselect_indexsubquery_engine;
@@ -812,7 +825,7 @@
   {
     return materialize_engine->cols();
   }
-  uint8 uncacheable() { return UNCACHEABLE_DEPENDENT; }
+  uint8 uncacheable() { return materialize_engine->uncacheable(); }
   table_map upper_select_const_tables() { return 0; }
   bool no_rows() { return !tmp_table->file->stats.records; }
   virtual enum_engine_type engine_type() { return HASH_SJ_ENGINE; }

=== modified file 'sql/item_sum.cc'
--- sql/item_sum.cc	2010-03-20 12:01:47 +0000
+++ sql/item_sum.cc	2010-06-23 18:44:59 +0000
@@ -319,6 +319,7 @@
   if (aggr_level >= 0)
   {
     ref_by= ref;
+    thd->lex->current_select->register_dependency_item(aggr_sel, ref);
     /* Add the object to the list of registered objects assigned to aggr_sel */
     if (!aggr_sel->inner_sum_func_list)
       next= this;

=== modified file 'sql/mysql_priv.h'
--- sql/mysql_priv.h	2010-03-20 12:01:47 +0000
+++ sql/mysql_priv.h	2010-06-23 18:47:59 +0000
@@ -568,12 +568,13 @@
 #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 @@
                                     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 @@
                                     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
 
 /*
@@ -694,7 +697,8 @@
   IN_HAVING,
   SELECT_LIST,
   IN_WHERE,
-  IN_ON
+  IN_ON,
+  PARSING_PLACE_SIZE /* always should be the last */
 };
 
 struct st_table;
@@ -936,6 +940,7 @@
 #ifdef MYSQL_SERVER
 #include "sql_servers.h"
 #include "opt_range.h"
+#include "sql_expression_cache.h"
 
 #ifdef HAVE_QUERY_CACHE
 struct Query_cache_query_flags
@@ -1269,6 +1274,10 @@
                   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 key_no);
+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 +1297,7 @@
 			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'
--- sql/mysqld.cc	2010-03-20 12:01:47 +0000
+++ sql/mysqld.cc	2010-06-23 19:41:34 +0000
@@ -305,6 +305,7 @@
   "firstmatch","loosescan","materialization", "semijoin",
   "partial_match_rowid_merge",
   "partial_match_table_scan",
+  "subquery_cache",
 #ifndef DBUG_OFF
   "table_elimination",
 #endif
@@ -325,6 +326,7 @@
   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 *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 @@
   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 @@
   {"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,14 @@
   {"Ssl_version",              (char*) &show_ssl_get_version, SHOW_FUNC},
 #endif /* HAVE_OPENSSL */
   {"Syncs",                    (char*) &my_sync_count,          SHOW_LONG_NOFLUSH},
+
+  /*
+    As long as subquery is the only thing which cached by the expression cache
+    we will call statistcs variables subquery_cache* to make meaning clear for
+    users.
+  */
+  {"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 +8019,7 @@
   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'
--- sql/sql_base.cc	2010-03-20 12:01:47 +0000
+++ sql/sql_base.cc	2010-06-23 18:44:59 +0000
@@ -8062,6 +8062,10 @@
   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'
--- sql/sql_class.cc	2010-03-20 12:01:47 +0000
+++ sql/sql_class.cc	2010-06-23 18:44:59 +0000
@@ -3020,6 +3020,7 @@
   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'
--- sql/sql_class.h	2010-03-20 12:01:47 +0000
+++ sql/sql_class.h	2010-06-23 18:44:59 +0000
@@ -2786,12 +2786,17 @@
     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),
      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)
+     bit_fields_as_long(0), skip_create_table(0)
   {}
   ~TMP_TABLE_PARAM()
   {

=== added file 'sql/sql_expression_cache.cc'
--- sql/sql_expression_cache.cc	1970-01-01 00:00:00 +0000
+++ sql/sql_expression_cache.cc	2010-06-23 21:49:48 +0000
@@ -0,0 +1,300 @@
+
+#include "mysql_priv.h"
+#include "sql_select.h"
+
+/*
+  As long as subquery is the only thing which cached by the expression cache
+  we will call statistc variables subquery_cache* to make meaning clear for
+  users.
+*/
+ulonglong subquery_cache_miss, subquery_cache_hit;
+
+Expression_cache_tmptable::Expression_cache_tmptable(THD *thd,
+                                                 List<Item*> &dependance,
+                                                 Item *value)
+  :cache_table(NULL), table_thd(thd), list(&dependance), val(value),
+   equalities(NULL), inited (0)
+{
+  DBUG_ENTER("Expression_cache_tmptable::Expression_cache_tmptable");
+  DBUG_VOID_RETURN;
+};
+
+
+/**
+  Creates equalities expression.
+
+  @details For some type of fields index lookup do not return failure but set
+  pointer on the next record. To check exact match we use expression like:
+  field1=value1 and field2=value2 ...
+
+  @retval FALSE OK
+  @retval TRUE  Error
+*/
+
+bool Expression_cache_tmptable::make_equalities()
+{
+  List<Item> args;
+  List_iterator_fast<Item*> li(*list);
+  Item **ref;
+  Name_resolution_context *cn= NULL;
+  DBUG_ENTER("Expression_cache_tmptable::make_equalities");
+
+  for (uint i= 1 /* skip result filed */; (ref= li++); i++)
+  {
+    Field *fld= cache_table->field[i];
+    /* Only some field types should be checked after lookup */
+    if (fld->type() == MYSQL_TYPE_VARCHAR ||
+        fld->type() == MYSQL_TYPE_TINY_BLOB ||
+        fld->type() == MYSQL_TYPE_MEDIUM_BLOB ||
+        fld->type() == MYSQL_TYPE_LONG_BLOB ||
+        fld->type() == MYSQL_TYPE_BLOB ||
+        fld->type() == MYSQL_TYPE_VAR_STRING ||
+        fld->type() == MYSQL_TYPE_STRING ||
+        fld->type() == MYSQL_TYPE_NEWDECIMAL ||
+        fld->type() == MYSQL_TYPE_DECIMAL)
+    {
+      if (!cn)
+      {
+        // dummy resolution context
+        cn= new Name_resolution_context();
+        cn->init();
+      }
+      args.push_front(new Item_func_eq(new Item_ref(cn, ref, "", "", FALSE),
+                                       new Item_field(fld)));
+    }
+  }
+  if (args.elements == 1)
+    equalities= args.head();
+  else
+    equalities= new Item_cond_and(args);
+
+  DBUG_RETURN(equalities->fix_fields(table_thd, &equalities));
+}
+
+
+/**
+  Enumerates all fields in field number order.
+
+  @param arg             reference on current field number
+
+  @note Used for temporary table key creation
+
+  @return field number
+*/
+
+static uint field_enumerator(uchar *arg)
+{
+  return ((uint*)arg)[0]++;
+}
+
+
+/**
+  Initializes temporary table and index for this cache
+
+  @details Creates temporary table, index and search structures. If any of
+  steps fails it just disable the cache by removing its temporary table.
+*/
+
+void Expression_cache_tmptable::init()
+{
+  List_iterator_fast<Item*> li(*list);
+  List_iterator_fast<Item> li_items(items);
+  Item **item;
+  uint field_counter;
+  DBUG_ENTER("Expression_cache_tmptable::init");
+  DBUG_ASSERT(!inited);
+  inited= TRUE;
+
+  if (!(ULONGLONG_MAX >> (list->elements + 1)))
+  {
+    DBUG_PRINT("info", ("Too many dependencies"));
+    DBUG_VOID_RETURN;
+  }
+
+  cache_table= NULL;
+  while ((item= li++))
+  {
+    DBUG_ASSERT(item);
+    DBUG_ASSERT(*item);
+    DBUG_ASSERT((*item)->fixed);
+    items.push_back((*item));
+  }
+
+  cache_table_param.init();
+  /* dependance items and result */
+  cache_table_param.field_count= list->elements + 1;
+  /* postpone table creation to index description */
+  cache_table_param.skip_create_table= 1;
+
+
+  items.push_front(val);
+  if (!(cache_table= create_tmp_table(table_thd, &cache_table_param,
+                                      items, (ORDER*) NULL,
+                                      FALSE, FALSE,
+                                      ((table_thd->options |
+                                        TMP_TABLE_ALL_COLUMNS) &
+                                       ~(OPTION_BIG_TABLES |
+                                         TMP_TABLE_FORCE_MYISAM)),
+                                      HA_POS_ERROR,
+                                      (char *)"subquery-cache-table")))
+  {
+    DBUG_PRINT("error", ("create_tmp_table failed, caching switched off"));
+    DBUG_VOID_RETURN;
+  }
+
+  if (cache_table->s->db_type() != heap_hton)
+  {
+    DBUG_PRINT("error", ("we need only heap table"));
+    goto error;
+  }
+
+  /* first field in the table is result value, so we skip it */
+  li_items++;
+  field_counter=1;
+
+  if (cache_table->alloc_keys(1) ||
+      (cache_table->add_tmp_key(0, items.elements - 1,
+                                &field_enumerator,
+                                (uchar*)&field_counter) < 0) ||
+      cache_table->createtmp_table_search_structures(table_thd, li_items,
+						     &tab_ref) ||
+      !(tab= create_index_lookup_join_tab(cache_table, 0)))
+  {
+    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 (!(cached_result= new Item_field(cache_table->field[0])))
+  {
+    DBUG_PRINT("error", ("Creating Item_field failed"));
+    goto error;
+  }
+
+  if (make_equalities())
+  {
+    DBUG_PRINT("error", ("Creating equalities failed"));
+    goto error;
+  }
+
+  DBUG_VOID_RETURN;
+
+error:
+  /* switch off cache */
+  free_tmp_table(table_thd, cache_table);
+  cache_table= NULL;
+  DBUG_VOID_RETURN;
+}
+
+/**
+  Destroys temporary table if it exists
+*/
+
+Expression_cache_tmptable::~Expression_cache_tmptable()
+{
+  if (cache_table)
+    free_tmp_table(table_thd, cache_table);
+}
+
+
+/**
+  Checks if current key present in the cache and returns value if it is true
+
+  @param value           assigned Item with value from the cache if key
+                         is found
+
+  @return result of the key lookup
+*/
+
+Expression_cache::result Expression_cache_tmptable::check_value(Item **value)
+{
+  int res;
+  DBUG_ENTER("Expression_cache_tmptable::check_value");
+
+  /*
+    We delay cache initialization to get item references which should be
+    used at the moment of query execution. I.e. we store reference on item
+    reference at the moment of class creation but for table creation and
+    index supply structures (join_tab) we need real Items which used at the
+    moment of execution so we can resolve reference only at this point.
+  */
+  if (!inited)
+    init();
+
+  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);
+    if (res || (equalities && !equalities->val_int()))
+    {
+      subquery_cache_miss++;
+      DBUG_RETURN(MISS);
+    }
+
+    subquery_cache_hit++;
+    *value= cached_result;
+    DBUG_RETURN(Expression_cache::HIT);
+  }
+  DBUG_RETURN(Expression_cache::MISS);
+}
+
+
+/**
+  Puts given value in the cache
+
+  @param value           Value to put in the cache
+
+  @note As value of the key of the given value used values of parameter
+  items stored during initialization.
+
+  @retval FALSE OK
+  @retval TRUE  Error
+*/
+
+my_bool Expression_cache_tmptable::put_value(Item *value)
+{
+  int error;
+  DBUG_ENTER("Expression_cache_tmptable::put_value");
+  DBUG_ASSERT(inited);
+
+  if (!cache_table)
+  {
+    DBUG_PRINT("info", ("No table so behave as we successfully put value"));
+    DBUG_RETURN(FALSE);
+  }
+
+  *(items.head_ref())= value;
+  fill_record(table_thd, cache_table->field, items, 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);
+}

=== added file 'sql/sql_expression_cache.h'
--- sql/sql_expression_cache.h	1970-01-01 00:00:00 +0000
+++ sql/sql_expression_cache.h	2010-06-23 20:28:44 +0000
@@ -0,0 +1,74 @@
+#ifndef _SQL_SUBQUERY_CACHE_H_
+#define _SQL_SUBQUERY_CACHE_H_
+
+/**
+  Interface for expression cache works with Item_cache_wrapper
+
+  @note Parameters of the cache passed on its creation and are not subject
+  of this interface.
+*/
+
+extern ulonglong subquery_cache_miss, subquery_cache_hit;
+
+class Expression_cache :public Sql_alloc
+{
+public:
+  enum result {ERROR, HIT, MISS};
+
+  Expression_cache(){};
+  virtual ~Expression_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;
+};
+
+struct st_table_ref;
+struct st_join_table;
+class Item_field;
+
+/**
+  Implementation of expression cache over temporary table
+*/
+
+class Expression_cache_tmptable :public Expression_cache
+{
+public:
+  Expression_cache_tmptable(THD *thd, List<Item*> &dependance, Item *value);
+  virtual ~Expression_cache_tmptable();
+  virtual result check_value(Item **value);
+  virtual my_bool put_value(Item *value);
+
+private:
+  void init();
+  bool make_equalities();
+
+  /* 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;
+  /* JOIN_TAB for index lookup */
+  st_join_table *tab;
+  /* Chached result */
+  Item_field *cached_result;
+  /* List of references to items */
+  List<Item*> *list;
+  /* List of items */
+  List<Item> items;
+  /* Value Item example */
+  Item *val;
+  /* Expression to check after index lookup */
+  Item *equalities;
+  /* set if structures are inited */
+  bool inited;
+};
+#endif

=== modified file 'sql/sql_lex.cc'
--- sql/sql_lex.cc	2010-03-20 12:01:47 +0000
+++ sql/sql_lex.cc	2010-06-23 21:54:52 +0000
@@ -1632,6 +1632,8 @@
   nest_level= 0;
   link_next= 0;
   lock_option= TL_READ_DEFAULT;
+
+  bzero((char*) has_ecache_candidates, sizeof(has_ecache_candidates));
 }
 
 void st_select_lex::init_select()
@@ -1829,6 +1831,59 @@
 }
 
 
+/**
+  Registers reference on items on which the subqueries depends
+
+  @param last            pointer to last st_select_lex struct, before
+                         which all st_select_lex have to be marked as
+                         dependent
+  @param dependency      reference on the item on which all this
+                         subqueries depends
+
+  @details Goes from this SELECT_LEX upper to 'last' and put 'dependency'
+  reference in their depends_on list if it is not present there already.
+
+  TODO: check all entries of mark_as_dependant used without call of this
+  method, probably they do not need or even lead to incorrect marking during
+  prepared statement execution
+
+*/
+
+void st_select_lex::register_dependency_item(st_select_lex *last,
+                                             Item **dependency)
+{
+  SELECT_LEX *s= this;
+  DBUG_ENTER("st_select_lex::register_dependency_item");
+  DBUG_ASSERT(this != last);
+  DBUG_ASSERT(*dependency);
+  do
+  {
+    /* check duplicates */
+    List_iterator_fast<Item*> li(s->master_unit()->item->depends_on);
+    Item **dep;
+    while ((dep= li++))
+    {
+      if ((*dep)->eq(*dependency, FALSE))
+      {
+         DBUG_PRINT("info", ("dependency %s already present",
+                             ((*dependency)->name ?
+                              (*dependency)->name :
+                              "<no name>")));
+         DBUG_VOID_RETURN;
+      }
+    }
+
+    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>")));
+  } while ((s= s->outer_select()) != last && s != 0);
+  DBUG_VOID_RETURN;
+}
+
+
 /*
   st_select_lex_node::mark_as_dependent mark all st_select_lex struct from 
   this to 'last' as dependent
@@ -1843,7 +1898,7 @@
 
 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);
 
   /*
@@ -1872,11 +1927,11 @@
     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_lex.h'
--- sql/sql_lex.h	2010-03-20 12:01:47 +0000
+++ sql/sql_lex.h	2010-06-23 18:44:59 +0000
@@ -660,6 +660,9 @@
 
   /* explicit LIMIT clause was used */
   bool explicit_limit;
+
+  /* where we have candidates for expression caching, see enum_parsing_place */
+  bool has_ecache_candidates[PARSING_PLACE_SIZE];
   /*
     there are subquery in HAVING clause => we can't close tables before
     query processing end even if we use temporary table
@@ -748,6 +751,7 @@
   }
 
   bool mark_as_dependent(THD *thd, st_select_lex *last, Item *dependency);
+  void register_dependency_item(st_select_lex *last, Item **dependency);
 
   bool set_braces(bool value);
   bool inc_in_sum_expr();

=== modified file 'sql/sql_select.cc'
--- sql/sql_select.cc	2010-05-10 13:46:08 +0000
+++ sql/sql_select.cc	2010-06-23 22:06:13 +0000
@@ -151,7 +151,6 @@
 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);
@@ -1479,6 +1478,9 @@
       DBUG_RETURN(-1);                         /* purecov: inspected */
   }
 
+  if (setup_subquery_caches(thd))
+    DBUG_RETURN(-1);
+
   error= 0;
   DBUG_RETURN(0);
 
@@ -1495,6 +1497,62 @@
   DBUG_RETURN(0);
 }
 
+/**
+  Setup expression caches if there is subqueries
+
+  @param thd             Thread handler
+
+  @details Calls transformer which puts expression cache before subqueries
+  for expressions of this SELECT JOIN if subqueries was detected in them
+
+  @note We call this method on late optimization phase so should check
+  WHERE, HAVING and ON together, because optimization moves expression
+  between them.
+
+  @retval FALSE OK
+  @retval TRUE  Error
+*/
+bool JOIN::setup_subquery_caches(THD *thd)
+{
+  DBUG_ENTER("JOIN::setup_subquery_caches");
+  if (select_lex->has_ecache_candidates[IN_WHERE] ||
+      select_lex->has_ecache_candidates[IN_HAVING] ||
+      select_lex->has_ecache_candidates[IN_ON])
+  {
+    if (conds)
+      conds= conds->transform(&Item::cache_insert_transformer,
+                              (uchar*) thd);
+    if (having)
+      having= having->transform(&Item::cache_insert_transformer,
+                                (uchar*) thd);
+    for (TABLE_LIST *table= tables_list; table; table= table->next_local)
+    {
+      if (table->on_expr)
+      {
+        Item *new_on=
+          table->on_expr->transform(&Item::cache_insert_transformer,
+                                    (uchar*) thd);
+        if (new_on != table->on_expr)
+          thd->change_item_tree(&table->on_expr, new_on);
+      }
+    }
+  }
+  if (select_lex->has_ecache_candidates[SELECT_LIST])
+  {
+    List_iterator<Item> li(all_fields);
+    Item *item;
+    while ((item= li++))
+    {
+      Item *new_item=
+        item->transform(&Item::cache_insert_transformer, (uchar*) thd);
+      if (new_item != item)
+      {
+        thd->change_item_tree(li.ref(), new_item);
+      }
+    }
+  }
+  DBUG_RETURN(FALSE);
+}
 
 /**
   Restore values in temporary join.
@@ -1556,6 +1614,7 @@
   DBUG_RETURN(0);
 }
 
+
 /**
    @brief Save the original join layout
       
@@ -5209,7 +5268,7 @@
         'join->best_positions' contains a complete optimal extension of the
         current partial QEP.
       */
-      DBUG_EXECUTE("opt", print_plan(join, join->tables,
+      DBUG_EXECUTE("opt", print_plan(join, n_tables,
                                      record_count, read_time, read_time,
                                      "optimal"););
       DBUG_RETURN(FALSE);
@@ -7625,6 +7684,43 @@
 
 
 /**
+  Creates and fills JOIN_TAB for index look up in temporary table
+
+  @param table           The table where to look up
+  @param key_no          Number of key
+
+  @details Prepares this JOIN_TAB be used with 'key_no' key in the 'table'.
+
+  @return JOIN_TAB object or NULL in case of error
+*/
+
+JOIN_TAB *create_index_lookup_join_tab(TABLE *table, int key_no)
+{
+  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->next_select=0;
+  tab->sorted= 1;
+  tab->ref.key= key_no;
+
+  table->status= STATUS_NO_RECORD;
+  tab->read_first_record= join_read_key;
+  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
@@ -10778,6 +10874,7 @@
   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);
@@ -11552,7 +11649,8 @@
                                   &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;
@@ -11700,16 +11798,17 @@
 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);
 }
 
 
@@ -12540,7 +12639,8 @@
   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);
@@ -13323,61 +13423,62 @@
 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 )
+    if (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);
 }
 
 

=== modified file 'sql/sql_select.h'
--- sql/sql_select.h	2010-06-14 11:17:54 +0000
+++ sql/sql_select.h	2010-06-23 18:44:59 +0000
@@ -1711,6 +1711,7 @@
             ((group || tmp_table_param.sum_func_count) && !group_list)) ?
               NULL : join_tab+const_tables;
   }
+  bool setup_subquery_caches(THD *thd);
 private:
   /**
     TRUE if the query contains an aggregate function but has no GROUP

=== modified file 'sql/table.cc'
--- sql/table.cc	2010-03-20 12:01:47 +0000
+++ sql/table.cc	2010-06-23 22:06:13 +0000
@@ -20,6 +20,8 @@
 #include "sql_trigger.h"
 #include <m_ctype.h>
 #include "my_md5.h"
+#include "my_bit.h"
+#include "sql_select.h"
 
 /* INFORMATION_SCHEMA name */
 LEX_STRING INFORMATION_SCHEMA_NAME= {C_STRING_WITH_LEN("information_schema")};
@@ -5096,6 +5098,210 @@
     file->column_bitmaps_signal();
 }
 
+
+/**
+  Allocates space for keys
+
+  @param key_count  number of keys to allocate.
+
+  @details
+  Allocates 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*) alloc_root(&mem_root, sizeof(KEY)*key_count);
+  max_keys= key_count;
+  return !(key_info);
+}
+
+
+/**
+  Adds one key to a temporary table.
+
+  @param key            key number.
+  @param key_parts      number of fields in the key
+  @param next_field_no  function which returns field numbers which
+                        should be included in the key
+  @param arg            above function argement
+
+  @return <0 an error occur.
+  @return >=0 number of newly added key.
+*/
+
+bool TABLE::add_tmp_key(uint key, uint key_parts,
+                        uint (*next_field_no) (uchar *), uchar *arg)
+{
+  DBUG_ASSERT(key < max_keys);
+
+  char buf[NAME_CHAR_LEN];
+  KEY* keyinfo;
+  Field **reg_field;
+  uint i;
+  bool key_start= TRUE;
+  KEY_PART_INFO* key_part_info=
+      (KEY_PART_INFO*) alloc_root(&mem_root, sizeof(KEY_PART_INFO)*key_parts);
+  if (!key_part_info)
+    return TRUE;
+  keyinfo= key_info + key;
+  keyinfo->key_part= key_part_info;
+  keyinfo->usable_key_parts= keyinfo->key_parts = key_parts;
+  keyinfo->key_length=0;
+  keyinfo->algorithm= HA_KEY_ALG_UNDEF;
+  keyinfo->flags= HA_GENERATED_KEY;
+  sprintf(buf, "key%i", key);
+  if (!(keyinfo->name= strdup_root(&mem_root, buf)))
+    return TRUE;
+  keyinfo->rec_per_key= (ulong*) alloc_root(&mem_root,
+                                            sizeof(ulong)*key_parts);
+  if (!keyinfo->rec_per_key)
+    return TRUE;
+  bzero(keyinfo->rec_per_key, sizeof(ulong)*key_parts);
+  for (i= 0; i < key_parts; i++)
+  {
+    reg_field= field + next_field_no(arg);
+    if (key_start)
+      (*reg_field)->key_start.set_bit(key);
+    key_start= FALSE;
+      (*reg_field)->part_of_key.set_bit(key);
+    (*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;
+    key_part_info->key_part_flag= 0;
+    /* 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;
+      keyinfo->key_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;
+      keyinfo->key_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);
+  s->keys++;
+  return FALSE;
+}
+
+
+/**
+  Creates structures which we need for index look up
+
+  @param thd             Thread handler
+  @param li              list of items representing the key
+  @param ref             reference to write created TABLE_REF
+
+  @details Creates TABLE_REF object for this (temporary) table and fill it
+  according with 'li' list  to allow to search in the temporary table index
+  using items from the list 'li'. If everything OK created object will be
+  assigned to 'ref' and FALSE returned.
+
+  @note should be carefully merged with code ported from 6.0, because mostly
+  taken from there
+
+  @retval FALSE OK
+  @retval TRUE  Error
+*/
+
+bool TABLE::createtmp_table_search_structures(THD *thd,
+					      List_iterator_fast<Item> &li,
+					      st_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;
+  uint      i;
+
+  DBUG_ENTER("createtmp_table_search_structures");
+
+  tmp_key= this->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;
+  tab_ref->use_count= 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_parts= tmp_key_parts;
+  *ref= tab_ref;
+
+  DBUG_RETURN(FALSE);
+}
+
+
 /**
   @brief Check if this is part of a MERGE table with attached children.
 

=== modified file 'sql/table.h'
--- sql/table.h	2010-03-20 12:01:47 +0000
+++ sql/table.h	2010-06-23 19:51:11 +0000
@@ -25,6 +25,7 @@
 class partition_info;
 class COND_EQUAL;
 class Security_context;
+struct st_table_ref;
 
 /*************************************************************************/
 
@@ -781,6 +782,7 @@
   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 */
@@ -913,6 +915,12 @@
   */
   inline bool needs_reopen_or_name_lock()
   { return s->version != refresh_version; }
+  bool alloc_keys(uint key_count);
+  bool add_tmp_key(uint key, uint key_parts,
+                   uint (*next_field_no) (uchar *), uchar *arg);
+  bool createtmp_table_search_structures(THD *thd,
+					 List_iterator_fast<Item> &li,
+					 st_table_ref **ref);
   bool is_children_attached(void);
 };
 

=== modified file 'storage/maria/ha_maria.cc'
--- storage/maria/ha_maria.cc	2010-03-20 12:01:47 +0000
+++ storage/maria/ha_maria.cc	2010-06-23 18:44:59 +0000
@@ -995,6 +995,8 @@
 {
   MARIA_HA *tmp= file;
   file= 0;
+  if (!tmp)
+    return 0;
   return maria_close(tmp);
 }