diff --git a/.bzrignore b/.bzrignore
index 396ff534680faa5e1b40b8292682880aeb959576..c3d20201819ab217fd9c3e838ad24c7aede6dc54 100644
--- a/.bzrignore
+++ b/.bzrignore
@@ -508,6 +508,7 @@ mysql-test/install_test_db
 mysql-test/mysql-test-run
 mysql-test/ndb/ndbcluster
 mysql-test/r/*.reject
+mysql-test/r/index_merge_load.result
 mysql-test/r/rpl000001.eval
 mysql-test/r/rpl000002.eval
 mysql-test/r/rpl000014.eval
@@ -518,6 +519,7 @@ mysql-test/r/slave-running.eval
 mysql-test/r/slave-stopped.eval
 mysql-test/share/mysql
 mysql-test/std_data/*.pem
+mysql-test/t/index_merge.load
 mysql-test/var/*
 mysql.kdevprj
 mysql.proj
diff --git a/mysql-test/r/func_group.result b/mysql-test/r/func_group.result
index 9af8fa8c8cc4ecf34d2b9cb77b412364741b0538..acc3205a63ad8fbdfdc8a2c9658f8147522f0a5a 100644
--- a/mysql-test/r/func_group.result
+++ b/mysql-test/r/func_group.result
@@ -476,7 +476,7 @@ CHI	Los Angeles
 explain
 select max(a3) from t1 where a2 is null and a2 = 2;
 id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
-1	SIMPLE	NULL	NULL	NULL	NULL	NULL	NULL	NULL	Impossible WHERE noticed after reading const tables
+1	SIMPLE	NULL	NULL	NULL	NULL	NULL	NULL	NULL	Impossible WHERE
 select max(a3) from t1 where a2 is null and a2 = 2;
 max(a3)
 NULL
diff --git a/mysql-test/r/func_test.result b/mysql-test/r/func_test.result
index 6f8bc702a802c9e5ed16a1a9a8057b6ed42b6f19..4d3a570d1c4624871a0775d15afc534615e8328f 100644
--- a/mysql-test/r/func_test.result
+++ b/mysql-test/r/func_test.result
@@ -77,9 +77,9 @@ select * from t1 where 1 xor 1;
 a
 explain extended select * from t1 where 1 xor 1;
 id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
-1	SIMPLE	NULL	NULL	NULL	NULL	NULL	NULL	NULL	Impossible WHERE noticed after reading const tables
+1	SIMPLE	NULL	NULL	NULL	NULL	NULL	NULL	NULL	Impossible WHERE
 Warnings:
-Note	1003	select `test`.`t1`.`a` AS `a` from `test`.`t1` where (1 xor 1)
+Note	1003	select `test`.`t1`.`a` AS `a` from `test`.`t1`
 select - a from t1;
 - a
 -1
diff --git a/mysql-test/r/index_merge.result b/mysql-test/r/index_merge.result
index cb4b285430c0b856fbb0d0d019b1d5b7330805a3..6b41992a7338d52f2476cfde5f47618576219f3c 100644
--- a/mysql-test/r/index_merge.result
+++ b/mysql-test/r/index_merge.result
@@ -76,13 +76,13 @@ id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
 1	SIMPLE	t0	ALL	i1,i2	NULL	NULL	NULL	1024	Using where
 explain select * from t0 where key2 = 45 or key1 is null;
 id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
-1	SIMPLE	t0	range	i1,i2	i2	4	NULL	1	Using where
+1	SIMPLE	t0	ref	i2	i2	4	const	1	
 explain select * from t0 where key2=10 or key3=3 or key4 <=> null;
 id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
 1	SIMPLE	t0	index_merge	i2,i3,i4	i2,i3	4,4	NULL	2	Using union(i2,i3); Using where
 explain select * from t0 where key2=10 or key3=3 or key4 is null;
 id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
-1	SIMPLE	t0	index_merge	i2,i3,i4	i2,i3	4,4	NULL	2	Using union(i2,i3); Using where
+1	SIMPLE	t0	index_merge	i2,i3	i2,i3	4,4	NULL	2	Using union(i2,i3); Using where
 explain select key1 from t0 where (key1 <=> null) or (key2 < 5) or 
 (key3=10) or (key4 <=> null);
 id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
@@ -257,8 +257,8 @@ explain
 select * from t0,t1 where (t0.key1=t1.key1) and 
 (t0.key1=3 or t0.key2=4) and t1.key1<200;
 id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
-1	SIMPLE	t0	index_merge	i1,i2	i1,i2	4,4	NULL	2	Using union(i1,i2); Using where
-1	SIMPLE	t1	ref	i1	i1	4	test.t0.key1	1	Using where
+1	SIMPLE	t0	range	i1,i2	i1	4	NULL	179	Using where
+1	SIMPLE	t1	ref	i1	i1	4	test.t0.key1	1	
 explain 
 select * from t0,t1 where (t0.key1=t1.key1) and 
 (t0.key1=3 or t0.key2<4) and t1.key1=2;
diff --git a/mysql-test/r/join_nested.result b/mysql-test/r/join_nested.result
index 09c3e67ae70a449f415dc3addadabfdac1a8d55d..6eff8676d9d0ae10b8e77bdd621b81bfd1c32034 100644
--- a/mysql-test/r/join_nested.result
+++ b/mysql-test/r/join_nested.result
@@ -76,7 +76,7 @@ id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
 1	SIMPLE	t3	ALL	NULL	NULL	NULL	NULL	2	Using where
 1	SIMPLE	t4	ALL	NULL	NULL	NULL	NULL	2	
 Warnings:
-Note	1003	select `test`.`t2`.`a` AS `a`,`test`.`t2`.`b` AS `b`,`test`.`t3`.`a` AS `a`,`test`.`t3`.`b` AS `b`,`test`.`t4`.`a` AS `a`,`test`.`t4`.`b` AS `b` from `test`.`t2` left join (`test`.`t3` join `test`.`t4`) on((`test`.`t2`.`b` = `test`.`t4`.`b`)) where ((`test`.`t3`.`a` = 1) or isnull(`test`.`t3`.`c`))
+Note	1003	select `test`.`t2`.`a` AS `a`,`test`.`t2`.`b` AS `b`,`test`.`t3`.`a` AS `a`,`test`.`t3`.`b` AS `b`,`test`.`t4`.`a` AS `a`,`test`.`t4`.`b` AS `b` from `test`.`t2` left join (`test`.`t3` join `test`.`t4`) on((`test`.`t4`.`b` = `test`.`t2`.`b`)) where ((`test`.`t3`.`a` = 1) or isnull(`test`.`t3`.`c`))
 SELECT t2.a,t2.b,t3.a,t3.b,t4.a,t4.b
 FROM t2
 LEFT JOIN              
@@ -153,7 +153,7 @@ id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
 1	SIMPLE	t4	ALL	NULL	NULL	NULL	NULL	2	
 1	SIMPLE	t5	ALL	NULL	NULL	NULL	NULL	3	
 Warnings:
-Note	1003	select `test`.`t2`.`a` AS `a`,`test`.`t2`.`b` AS `b`,`test`.`t3`.`a` AS `a`,`test`.`t3`.`b` AS `b`,`test`.`t4`.`a` AS `a`,`test`.`t4`.`b` AS `b`,`test`.`t5`.`a` AS `a`,`test`.`t5`.`b` AS `b` from `test`.`t2` left join (`test`.`t3` join `test`.`t4` join `test`.`t5`) on((`test`.`t2`.`b` = `test`.`t4`.`b`)) where ((`test`.`t3`.`a` > 1) or isnull(`test`.`t3`.`c`))
+Note	1003	select `test`.`t2`.`a` AS `a`,`test`.`t2`.`b` AS `b`,`test`.`t3`.`a` AS `a`,`test`.`t3`.`b` AS `b`,`test`.`t4`.`a` AS `a`,`test`.`t4`.`b` AS `b`,`test`.`t5`.`a` AS `a`,`test`.`t5`.`b` AS `b` from `test`.`t2` left join (`test`.`t3` join `test`.`t4` join `test`.`t5`) on((`test`.`t4`.`b` = `test`.`t2`.`b`)) where ((`test`.`t3`.`a` > 1) or isnull(`test`.`t3`.`c`))
 SELECT t2.a,t2.b,t3.a,t3.b,t4.a,t4.b,t5.a,t5.b
 FROM t2
 LEFT JOIN              
@@ -183,7 +183,7 @@ id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
 1	SIMPLE	t4	ALL	NULL	NULL	NULL	NULL	2	
 1	SIMPLE	t5	ALL	NULL	NULL	NULL	NULL	3	Using where
 Warnings:
-Note	1003	select `test`.`t2`.`a` AS `a`,`test`.`t2`.`b` AS `b`,`test`.`t3`.`a` AS `a`,`test`.`t3`.`b` AS `b`,`test`.`t4`.`a` AS `a`,`test`.`t4`.`b` AS `b`,`test`.`t5`.`a` AS `a`,`test`.`t5`.`b` AS `b` from `test`.`t2` left join (`test`.`t3` join `test`.`t4` join `test`.`t5`) on((`test`.`t2`.`b` = `test`.`t4`.`b`)) where (((`test`.`t3`.`a` > 1) or isnull(`test`.`t3`.`c`)) and ((`test`.`t5`.`a` < 3) or isnull(`test`.`t5`.`c`)))
+Note	1003	select `test`.`t2`.`a` AS `a`,`test`.`t2`.`b` AS `b`,`test`.`t3`.`a` AS `a`,`test`.`t3`.`b` AS `b`,`test`.`t4`.`a` AS `a`,`test`.`t4`.`b` AS `b`,`test`.`t5`.`a` AS `a`,`test`.`t5`.`b` AS `b` from `test`.`t2` left join (`test`.`t3` join `test`.`t4` join `test`.`t5`) on((`test`.`t4`.`b` = `test`.`t2`.`b`)) where (((`test`.`t3`.`a` > 1) or isnull(`test`.`t3`.`c`)) and ((`test`.`t5`.`a` < 3) or isnull(`test`.`t5`.`c`)))
 SELECT t2.a,t2.b,t3.a,t3.b,t4.a,t4.b,t5.a,t5.b
 FROM t2
 LEFT JOIN              
@@ -233,7 +233,7 @@ id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
 1	SIMPLE	t6	ALL	NULL	NULL	NULL	NULL	3	
 1	SIMPLE	t8	ALL	NULL	NULL	NULL	NULL	2	
 Warnings:
-Note	1003	select `test`.`t6`.`a` AS `a`,`test`.`t6`.`b` AS `b`,`test`.`t7`.`a` AS `a`,`test`.`t7`.`b` AS `b`,`test`.`t8`.`a` AS `a`,`test`.`t8`.`b` AS `b` from `test`.`t6` join `test`.`t7` left join `test`.`t8` on(((`test`.`t7`.`b` = `test`.`t8`.`b`) and (`test`.`t6`.`b` < 10))) where 1
+Note	1003	select `test`.`t6`.`a` AS `a`,`test`.`t6`.`b` AS `b`,`test`.`t7`.`a` AS `a`,`test`.`t7`.`b` AS `b`,`test`.`t8`.`a` AS `a`,`test`.`t8`.`b` AS `b` from `test`.`t6` join `test`.`t7` left join `test`.`t8` on(((`test`.`t8`.`b` = `test`.`t7`.`b`) and (`test`.`t6`.`b` < 10))) where 1
 SELECT t6.a,t6.b,t7.a,t7.b,t8.a,t8.b
 FROM t6,
 t7 
@@ -562,7 +562,7 @@ id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
 1	SIMPLE	t6	ALL	NULL	NULL	NULL	NULL	3	
 1	SIMPLE	t8	ALL	NULL	NULL	NULL	NULL	2	
 Warnings:
-Note	1003	select `test`.`t0`.`a` AS `a`,`test`.`t0`.`b` AS `b`,`test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t2`.`a` AS `a`,`test`.`t2`.`b` AS `b`,`test`.`t3`.`a` AS `a`,`test`.`t3`.`b` AS `b`,`test`.`t4`.`a` AS `a`,`test`.`t4`.`b` AS `b`,`test`.`t5`.`a` AS `a`,`test`.`t5`.`b` AS `b`,`test`.`t6`.`a` AS `a`,`test`.`t6`.`b` AS `b`,`test`.`t7`.`a` AS `a`,`test`.`t7`.`b` AS `b`,`test`.`t8`.`a` AS `a`,`test`.`t8`.`b` AS `b` from `test`.`t0` join `test`.`t1` left join (`test`.`t2` left join (`test`.`t3` join `test`.`t4`) on(((`test`.`t3`.`a` = 1) and (`test`.`t2`.`b` = `test`.`t4`.`b`))) join `test`.`t5` left join (`test`.`t6` join `test`.`t7` left join `test`.`t8` on(((`test`.`t7`.`b` = `test`.`t8`.`b`) and (`test`.`t6`.`b` < 10)))) on(((`test`.`t6`.`b` >= 2) and (`test`.`t5`.`b` = `test`.`t7`.`b`)))) on((((`test`.`t3`.`b` = 2) or isnull(`test`.`t3`.`c`)) and ((`test`.`t6`.`b` = 2) or isnull(`test`.`t6`.`c`)) and ((`test`.`t1`.`b` = `test`.`t5`.`b`) or isnull(`test`.`t3`.`c`) or isnull(`test`.`t6`.`c`) or isnull(`test`.`t8`.`c`)) and (`test`.`t1`.`a` <> 2))) where ((`test`.`t0`.`a` = 1) and (`test`.`t0`.`b` = `test`.`t1`.`b`) and ((`test`.`t2`.`a` >= 4) or isnull(`test`.`t2`.`c`)))
+Note	1003	select `test`.`t0`.`a` AS `a`,`test`.`t0`.`b` AS `b`,`test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t2`.`a` AS `a`,`test`.`t2`.`b` AS `b`,`test`.`t3`.`a` AS `a`,`test`.`t3`.`b` AS `b`,`test`.`t4`.`a` AS `a`,`test`.`t4`.`b` AS `b`,`test`.`t5`.`a` AS `a`,`test`.`t5`.`b` AS `b`,`test`.`t6`.`a` AS `a`,`test`.`t6`.`b` AS `b`,`test`.`t7`.`a` AS `a`,`test`.`t7`.`b` AS `b`,`test`.`t8`.`a` AS `a`,`test`.`t8`.`b` AS `b` from `test`.`t0` join `test`.`t1` left join (`test`.`t2` left join (`test`.`t3` join `test`.`t4`) on(((`test`.`t4`.`b` = `test`.`t2`.`b`) and (`test`.`t3`.`a` = 1))) join `test`.`t5` left join (`test`.`t6` join `test`.`t7` left join `test`.`t8` on(((`test`.`t8`.`b` = `test`.`t5`.`b`) and (`test`.`t6`.`b` < 10)))) on(((`test`.`t7`.`b` = `test`.`t5`.`b`) and (`test`.`t6`.`b` >= 2)))) on((((`test`.`t3`.`b` = 2) or isnull(`test`.`t3`.`c`)) and ((`test`.`t6`.`b` = 2) or isnull(`test`.`t6`.`c`)) and ((`test`.`t5`.`b` = `test`.`t0`.`b`) or isnull(`test`.`t3`.`c`) or isnull(`test`.`t6`.`c`) or isnull(`test`.`t8`.`c`)) and (`test`.`t1`.`a` <> 2))) where ((`test`.`t1`.`b` = `test`.`t0`.`b`) and (`test`.`t0`.`a` = 1) and ((`test`.`t2`.`a` >= 4) or isnull(`test`.`t2`.`c`)))
 SELECT t0.a,t0.b,t1.a,t1.b,t2.a,t2.b,t3.a,t3.b,t4.a,t4.b,
 t5.a,t5.b,t6.a,t6.b,t7.a,t7.b,t8.a,t8.b
 FROM t0,t1
@@ -660,7 +660,7 @@ id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
 1	SIMPLE	t8	ALL	NULL	NULL	NULL	NULL	2	Using where
 1	SIMPLE	t9	ALL	NULL	NULL	NULL	NULL	3	Using where
 Warnings:
-Note	1003	select `test`.`t0`.`a` AS `a`,`test`.`t0`.`b` AS `b`,`test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t2`.`a` AS `a`,`test`.`t2`.`b` AS `b`,`test`.`t3`.`a` AS `a`,`test`.`t3`.`b` AS `b`,`test`.`t4`.`a` AS `a`,`test`.`t4`.`b` AS `b`,`test`.`t5`.`a` AS `a`,`test`.`t5`.`b` AS `b`,`test`.`t6`.`a` AS `a`,`test`.`t6`.`b` AS `b`,`test`.`t7`.`a` AS `a`,`test`.`t7`.`b` AS `b`,`test`.`t8`.`a` AS `a`,`test`.`t8`.`b` AS `b`,`test`.`t9`.`a` AS `a`,`test`.`t9`.`b` AS `b` from `test`.`t0` join `test`.`t1` left join (`test`.`t2` left join (`test`.`t3` join `test`.`t4`) on(((`test`.`t3`.`a` = 1) and (`test`.`t2`.`b` = `test`.`t4`.`b`))) join `test`.`t5` left join (`test`.`t6` join `test`.`t7` left join `test`.`t8` on(((`test`.`t7`.`b` = `test`.`t8`.`b`) and (`test`.`t6`.`b` < 10)))) on(((`test`.`t6`.`b` >= 2) and (`test`.`t5`.`b` = `test`.`t7`.`b`)))) on((((`test`.`t3`.`b` = 2) or isnull(`test`.`t3`.`c`)) and ((`test`.`t6`.`b` = 2) or isnull(`test`.`t6`.`c`)) and ((`test`.`t1`.`b` = `test`.`t5`.`b`) or isnull(`test`.`t3`.`c`) or isnull(`test`.`t6`.`c`) or isnull(`test`.`t8`.`c`)) and (`test`.`t1`.`a` <> 2))) join `test`.`t9` where ((`test`.`t0`.`a` = 1) and (`test`.`t0`.`b` = `test`.`t1`.`b`) and ((`test`.`t2`.`a` >= 4) or isnull(`test`.`t2`.`c`)) and ((`test`.`t3`.`a` < 5) or isnull(`test`.`t3`.`c`)) and ((`test`.`t3`.`b` = `test`.`t4`.`b`) or isnull(`test`.`t3`.`c`) or isnull(`test`.`t4`.`c`)) and ((`test`.`t5`.`a` >= 2) or isnull(`test`.`t5`.`c`)) and ((`test`.`t6`.`a` >= 4) or isnull(`test`.`t6`.`c`)) and ((`test`.`t7`.`a` <= 2) or isnull(`test`.`t7`.`c`)) and ((`test`.`t8`.`a` < 1) or isnull(`test`.`t8`.`c`)) and ((`test`.`t8`.`b` = `test`.`t9`.`b`) or isnull(`test`.`t8`.`c`)) and (`test`.`t9`.`a` = 1))
+Note	1003	select `test`.`t0`.`a` AS `a`,`test`.`t0`.`b` AS `b`,`test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t2`.`a` AS `a`,`test`.`t2`.`b` AS `b`,`test`.`t3`.`a` AS `a`,`test`.`t3`.`b` AS `b`,`test`.`t4`.`a` AS `a`,`test`.`t4`.`b` AS `b`,`test`.`t5`.`a` AS `a`,`test`.`t5`.`b` AS `b`,`test`.`t6`.`a` AS `a`,`test`.`t6`.`b` AS `b`,`test`.`t7`.`a` AS `a`,`test`.`t7`.`b` AS `b`,`test`.`t8`.`a` AS `a`,`test`.`t8`.`b` AS `b`,`test`.`t9`.`a` AS `a`,`test`.`t9`.`b` AS `b` from `test`.`t0` join `test`.`t1` left join (`test`.`t2` left join (`test`.`t3` join `test`.`t4`) on(((`test`.`t4`.`b` = `test`.`t2`.`b`) and (`test`.`t3`.`a` = 1))) join `test`.`t5` left join (`test`.`t6` join `test`.`t7` left join `test`.`t8` on(((`test`.`t8`.`b` = `test`.`t5`.`b`) and (`test`.`t6`.`b` < 10)))) on(((`test`.`t7`.`b` = `test`.`t5`.`b`) and (`test`.`t6`.`b` >= 2)))) on((((`test`.`t3`.`b` = 2) or isnull(`test`.`t3`.`c`)) and ((`test`.`t6`.`b` = 2) or isnull(`test`.`t6`.`c`)) and ((`test`.`t5`.`b` = `test`.`t0`.`b`) or isnull(`test`.`t3`.`c`) or isnull(`test`.`t6`.`c`) or isnull(`test`.`t8`.`c`)) and (`test`.`t1`.`a` <> 2))) join `test`.`t9` where ((`test`.`t9`.`a` = 1) and (`test`.`t1`.`b` = `test`.`t0`.`b`) and (`test`.`t0`.`a` = 1) and ((`test`.`t2`.`a` >= 4) or isnull(`test`.`t2`.`c`)) and ((`test`.`t3`.`a` < 5) or isnull(`test`.`t3`.`c`)) and ((`test`.`t4`.`b` = `test`.`t3`.`b`) or isnull(`test`.`t3`.`c`) or isnull(`test`.`t4`.`c`)) and ((`test`.`t5`.`a` >= 2) or isnull(`test`.`t5`.`c`)) and ((`test`.`t6`.`a` >= 4) or isnull(`test`.`t6`.`c`)) and ((`test`.`t7`.`a` <= 2) or isnull(`test`.`t7`.`c`)) and ((`test`.`t8`.`a` < 1) or isnull(`test`.`t8`.`c`)) and ((`test`.`t9`.`b` = `test`.`t8`.`b`) or isnull(`test`.`t8`.`c`)))
 SELECT t9.a,t9.b
 FROM t9;
 a	b
@@ -858,7 +858,7 @@ id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
 1	SIMPLE	t2	ALL	NULL	NULL	NULL	NULL	3	
 1	SIMPLE	t4	ALL	NULL	NULL	NULL	NULL	2	
 Warnings:
-Note	1003	select `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t2`.`a` AS `a`,`test`.`t2`.`b` AS `b`,`test`.`t3`.`a` AS `a`,`test`.`t3`.`b` AS `b`,`test`.`t4`.`a` AS `a`,`test`.`t4`.`b` AS `b` from `test`.`t1` join `test`.`t3` join `test`.`t2` left join `test`.`t4` on(((`test`.`t3`.`a` = 1) and (`test`.`t2`.`b` = `test`.`t4`.`b`))) where (`test`.`t1`.`a` <= 2)
+Note	1003	select `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t2`.`a` AS `a`,`test`.`t2`.`b` AS `b`,`test`.`t3`.`a` AS `a`,`test`.`t3`.`b` AS `b`,`test`.`t4`.`a` AS `a`,`test`.`t4`.`b` AS `b` from `test`.`t1` join `test`.`t3` join `test`.`t2` left join `test`.`t4` on(((`test`.`t4`.`b` = `test`.`t2`.`b`) and (`test`.`t3`.`a` = 1))) where (`test`.`t1`.`a` <= 2)
 CREATE INDEX idx_b ON t2(b);
 EXPLAIN EXTENDED
 SELECT t2.a,t2.b,t3.a,t3.b,t4.a,t4.b
@@ -872,7 +872,7 @@ id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
 1	SIMPLE	t2	ref	idx_b	idx_b	5	test.t3.b	2	
 1	SIMPLE	t1	ALL	NULL	NULL	NULL	NULL	3	
 Warnings:
-Note	1003	select `test`.`t2`.`a` AS `a`,`test`.`t2`.`b` AS `b`,`test`.`t3`.`a` AS `a`,`test`.`t3`.`b` AS `b`,`test`.`t4`.`a` AS `a`,`test`.`t4`.`b` AS `b` from `test`.`t3` join `test`.`t4` left join (`test`.`t1` join `test`.`t2`) on(((`test`.`t3`.`a` = 1) and (`test`.`t3`.`b` = `test`.`t2`.`b`) and (`test`.`t2`.`b` = `test`.`t4`.`b`))) where 1
+Note	1003	select `test`.`t2`.`a` AS `a`,`test`.`t2`.`b` AS `b`,`test`.`t3`.`a` AS `a`,`test`.`t3`.`b` AS `b`,`test`.`t4`.`a` AS `a`,`test`.`t4`.`b` AS `b` from `test`.`t3` join `test`.`t4` left join (`test`.`t1` join `test`.`t2`) on(((`test`.`t4`.`b` = `test`.`t3`.`b`) and (`test`.`t2`.`b` = `test`.`t3`.`b`) and (`test`.`t3`.`a` = 1))) where 1
 SELECT t2.a,t2.b,t3.a,t3.b,t4.a,t4.b
 FROM t3,t4 
 LEFT JOIN              
@@ -935,7 +935,7 @@ id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
 1	SIMPLE	t8	ALL	NULL	NULL	NULL	NULL	2	Using where
 1	SIMPLE	t9	ALL	NULL	NULL	NULL	NULL	3	Using where
 Warnings:
-Note	1003	select `test`.`t0`.`a` AS `a`,`test`.`t0`.`b` AS `b`,`test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t2`.`a` AS `a`,`test`.`t2`.`b` AS `b`,`test`.`t3`.`a` AS `a`,`test`.`t3`.`b` AS `b`,`test`.`t4`.`a` AS `a`,`test`.`t4`.`b` AS `b`,`test`.`t5`.`a` AS `a`,`test`.`t5`.`b` AS `b`,`test`.`t6`.`a` AS `a`,`test`.`t6`.`b` AS `b`,`test`.`t7`.`a` AS `a`,`test`.`t7`.`b` AS `b`,`test`.`t8`.`a` AS `a`,`test`.`t8`.`b` AS `b`,`test`.`t9`.`a` AS `a`,`test`.`t9`.`b` AS `b` from `test`.`t0` join `test`.`t1` left join (`test`.`t2` left join (`test`.`t3` join `test`.`t4`) on(((`test`.`t3`.`a` = 1) and (`test`.`t2`.`b` = `test`.`t4`.`b`))) join `test`.`t5` left join (`test`.`t6` join `test`.`t7` left join `test`.`t8` on(((`test`.`t7`.`b` = `test`.`t8`.`b`) and (`test`.`t6`.`b` < 10)))) on(((`test`.`t6`.`b` >= 2) and (`test`.`t5`.`b` = `test`.`t7`.`b`)))) on((((`test`.`t3`.`b` = 2) or isnull(`test`.`t3`.`c`)) and ((`test`.`t6`.`b` = 2) or isnull(`test`.`t6`.`c`)) and ((`test`.`t1`.`b` = `test`.`t5`.`b`) or isnull(`test`.`t3`.`c`) or isnull(`test`.`t6`.`c`) or isnull(`test`.`t8`.`c`)) and (`test`.`t1`.`a` <> 2))) join `test`.`t9` where ((`test`.`t0`.`a` = 1) and (`test`.`t0`.`b` = `test`.`t1`.`b`) and ((`test`.`t2`.`a` >= 4) or isnull(`test`.`t2`.`c`)) and ((`test`.`t3`.`a` < 5) or isnull(`test`.`t3`.`c`)) and ((`test`.`t3`.`b` = `test`.`t4`.`b`) or isnull(`test`.`t3`.`c`) or isnull(`test`.`t4`.`c`)) and ((`test`.`t5`.`a` >= 2) or isnull(`test`.`t5`.`c`)) and ((`test`.`t6`.`a` >= 4) or isnull(`test`.`t6`.`c`)) and ((`test`.`t7`.`a` <= 2) or isnull(`test`.`t7`.`c`)) and ((`test`.`t8`.`a` < 1) or isnull(`test`.`t8`.`c`)) and ((`test`.`t8`.`b` = `test`.`t9`.`b`) or isnull(`test`.`t8`.`c`)) and (`test`.`t9`.`a` = 1))
+Note	1003	select `test`.`t0`.`a` AS `a`,`test`.`t0`.`b` AS `b`,`test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t2`.`a` AS `a`,`test`.`t2`.`b` AS `b`,`test`.`t3`.`a` AS `a`,`test`.`t3`.`b` AS `b`,`test`.`t4`.`a` AS `a`,`test`.`t4`.`b` AS `b`,`test`.`t5`.`a` AS `a`,`test`.`t5`.`b` AS `b`,`test`.`t6`.`a` AS `a`,`test`.`t6`.`b` AS `b`,`test`.`t7`.`a` AS `a`,`test`.`t7`.`b` AS `b`,`test`.`t8`.`a` AS `a`,`test`.`t8`.`b` AS `b`,`test`.`t9`.`a` AS `a`,`test`.`t9`.`b` AS `b` from `test`.`t0` join `test`.`t1` left join (`test`.`t2` left join (`test`.`t3` join `test`.`t4`) on(((`test`.`t4`.`b` = `test`.`t2`.`b`) and (`test`.`t3`.`a` = 1))) join `test`.`t5` left join (`test`.`t6` join `test`.`t7` left join `test`.`t8` on(((`test`.`t8`.`b` = `test`.`t5`.`b`) and (`test`.`t6`.`b` < 10)))) on(((`test`.`t7`.`b` = `test`.`t5`.`b`) and (`test`.`t6`.`b` >= 2)))) on((((`test`.`t3`.`b` = 2) or isnull(`test`.`t3`.`c`)) and ((`test`.`t6`.`b` = 2) or isnull(`test`.`t6`.`c`)) and ((`test`.`t5`.`b` = `test`.`t0`.`b`) or isnull(`test`.`t3`.`c`) or isnull(`test`.`t6`.`c`) or isnull(`test`.`t8`.`c`)) and (`test`.`t1`.`a` <> 2))) join `test`.`t9` where ((`test`.`t9`.`a` = 1) and (`test`.`t1`.`b` = `test`.`t0`.`b`) and (`test`.`t0`.`a` = 1) and ((`test`.`t2`.`a` >= 4) or isnull(`test`.`t2`.`c`)) and ((`test`.`t3`.`a` < 5) or isnull(`test`.`t3`.`c`)) and ((`test`.`t4`.`b` = `test`.`t3`.`b`) or isnull(`test`.`t3`.`c`) or isnull(`test`.`t4`.`c`)) and ((`test`.`t5`.`a` >= 2) or isnull(`test`.`t5`.`c`)) and ((`test`.`t6`.`a` >= 4) or isnull(`test`.`t6`.`c`)) and ((`test`.`t7`.`a` <= 2) or isnull(`test`.`t7`.`c`)) and ((`test`.`t8`.`a` < 1) or isnull(`test`.`t8`.`c`)) and ((`test`.`t9`.`b` = `test`.`t8`.`b`) or isnull(`test`.`t8`.`c`)))
 CREATE INDEX idx_b ON t4(b);
 CREATE INDEX idx_b ON t5(b);
 EXPLAIN EXTENDED
@@ -986,7 +986,7 @@ id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
 1	SIMPLE	t8	ALL	NULL	NULL	NULL	NULL	2	Using where
 1	SIMPLE	t9	ALL	NULL	NULL	NULL	NULL	3	Using where
 Warnings:
-Note	1003	select `test`.`t0`.`a` AS `a`,`test`.`t0`.`b` AS `b`,`test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t2`.`a` AS `a`,`test`.`t2`.`b` AS `b`,`test`.`t3`.`a` AS `a`,`test`.`t3`.`b` AS `b`,`test`.`t4`.`a` AS `a`,`test`.`t4`.`b` AS `b`,`test`.`t5`.`a` AS `a`,`test`.`t5`.`b` AS `b`,`test`.`t6`.`a` AS `a`,`test`.`t6`.`b` AS `b`,`test`.`t7`.`a` AS `a`,`test`.`t7`.`b` AS `b`,`test`.`t8`.`a` AS `a`,`test`.`t8`.`b` AS `b`,`test`.`t9`.`a` AS `a`,`test`.`t9`.`b` AS `b` from `test`.`t0` join `test`.`t1` left join (`test`.`t2` left join (`test`.`t3` join `test`.`t4`) on(((`test`.`t3`.`a` = 1) and (`test`.`t2`.`b` = `test`.`t4`.`b`))) join `test`.`t5` left join (`test`.`t6` join `test`.`t7` left join `test`.`t8` on(((`test`.`t7`.`b` = `test`.`t8`.`b`) and (`test`.`t6`.`b` < 10)))) on(((`test`.`t6`.`b` >= 2) and (`test`.`t5`.`b` = `test`.`t7`.`b`)))) on((((`test`.`t3`.`b` = 2) or isnull(`test`.`t3`.`c`)) and ((`test`.`t6`.`b` = 2) or isnull(`test`.`t6`.`c`)) and ((`test`.`t1`.`b` = `test`.`t5`.`b`) or isnull(`test`.`t3`.`c`) or isnull(`test`.`t6`.`c`) or isnull(`test`.`t8`.`c`)) and (`test`.`t1`.`a` <> 2))) join `test`.`t9` where ((`test`.`t0`.`a` = 1) and (`test`.`t0`.`b` = `test`.`t1`.`b`) and ((`test`.`t2`.`a` >= 4) or isnull(`test`.`t2`.`c`)) and ((`test`.`t3`.`a` < 5) or isnull(`test`.`t3`.`c`)) and ((`test`.`t3`.`b` = `test`.`t4`.`b`) or isnull(`test`.`t3`.`c`) or isnull(`test`.`t4`.`c`)) and ((`test`.`t5`.`a` >= 2) or isnull(`test`.`t5`.`c`)) and ((`test`.`t6`.`a` >= 4) or isnull(`test`.`t6`.`c`)) and ((`test`.`t7`.`a` <= 2) or isnull(`test`.`t7`.`c`)) and ((`test`.`t8`.`a` < 1) or isnull(`test`.`t8`.`c`)) and ((`test`.`t8`.`b` = `test`.`t9`.`b`) or isnull(`test`.`t8`.`c`)) and (`test`.`t9`.`a` = 1))
+Note	1003	select `test`.`t0`.`a` AS `a`,`test`.`t0`.`b` AS `b`,`test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t2`.`a` AS `a`,`test`.`t2`.`b` AS `b`,`test`.`t3`.`a` AS `a`,`test`.`t3`.`b` AS `b`,`test`.`t4`.`a` AS `a`,`test`.`t4`.`b` AS `b`,`test`.`t5`.`a` AS `a`,`test`.`t5`.`b` AS `b`,`test`.`t6`.`a` AS `a`,`test`.`t6`.`b` AS `b`,`test`.`t7`.`a` AS `a`,`test`.`t7`.`b` AS `b`,`test`.`t8`.`a` AS `a`,`test`.`t8`.`b` AS `b`,`test`.`t9`.`a` AS `a`,`test`.`t9`.`b` AS `b` from `test`.`t0` join `test`.`t1` left join (`test`.`t2` left join (`test`.`t3` join `test`.`t4`) on(((`test`.`t4`.`b` = `test`.`t2`.`b`) and (`test`.`t3`.`a` = 1))) join `test`.`t5` left join (`test`.`t6` join `test`.`t7` left join `test`.`t8` on(((`test`.`t8`.`b` = `test`.`t5`.`b`) and (`test`.`t6`.`b` < 10)))) on(((`test`.`t7`.`b` = `test`.`t5`.`b`) and (`test`.`t6`.`b` >= 2)))) on((((`test`.`t3`.`b` = 2) or isnull(`test`.`t3`.`c`)) and ((`test`.`t6`.`b` = 2) or isnull(`test`.`t6`.`c`)) and ((`test`.`t5`.`b` = `test`.`t0`.`b`) or isnull(`test`.`t3`.`c`) or isnull(`test`.`t6`.`c`) or isnull(`test`.`t8`.`c`)) and (`test`.`t1`.`a` <> 2))) join `test`.`t9` where ((`test`.`t9`.`a` = 1) and (`test`.`t1`.`b` = `test`.`t0`.`b`) and (`test`.`t0`.`a` = 1) and ((`test`.`t2`.`a` >= 4) or isnull(`test`.`t2`.`c`)) and ((`test`.`t3`.`a` < 5) or isnull(`test`.`t3`.`c`)) and ((`test`.`t4`.`b` = `test`.`t3`.`b`) or isnull(`test`.`t3`.`c`) or isnull(`test`.`t4`.`c`)) and ((`test`.`t5`.`a` >= 2) or isnull(`test`.`t5`.`c`)) and ((`test`.`t6`.`a` >= 4) or isnull(`test`.`t6`.`c`)) and ((`test`.`t7`.`a` <= 2) or isnull(`test`.`t7`.`c`)) and ((`test`.`t8`.`a` < 1) or isnull(`test`.`t8`.`c`)) and ((`test`.`t9`.`b` = `test`.`t8`.`b`) or isnull(`test`.`t8`.`c`)))
 CREATE INDEX idx_b ON t8(b);
 EXPLAIN EXTENDED
 SELECT t0.a,t0.b,t1.a,t1.b,t2.a,t2.b,t3.a,t3.b,t4.a,t4.b,
@@ -1033,10 +1033,10 @@ id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
 1	SIMPLE	t5	ALL	NULL	NULL	NULL	NULL	3	Using where
 1	SIMPLE	t7	ALL	NULL	NULL	NULL	NULL	2	Using where
 1	SIMPLE	t6	ALL	NULL	NULL	NULL	NULL	3	Using where
-1	SIMPLE	t8	ref	idx_b	idx_b	5	test.t7.b	2	Using where
+1	SIMPLE	t8	ref	idx_b	idx_b	5	test.t5.b	2	Using where
 1	SIMPLE	t9	ALL	NULL	NULL	NULL	NULL	3	Using where
 Warnings:
-Note	1003	select `test`.`t0`.`a` AS `a`,`test`.`t0`.`b` AS `b`,`test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t2`.`a` AS `a`,`test`.`t2`.`b` AS `b`,`test`.`t3`.`a` AS `a`,`test`.`t3`.`b` AS `b`,`test`.`t4`.`a` AS `a`,`test`.`t4`.`b` AS `b`,`test`.`t5`.`a` AS `a`,`test`.`t5`.`b` AS `b`,`test`.`t6`.`a` AS `a`,`test`.`t6`.`b` AS `b`,`test`.`t7`.`a` AS `a`,`test`.`t7`.`b` AS `b`,`test`.`t8`.`a` AS `a`,`test`.`t8`.`b` AS `b`,`test`.`t9`.`a` AS `a`,`test`.`t9`.`b` AS `b` from `test`.`t0` join `test`.`t1` left join (`test`.`t2` left join (`test`.`t3` join `test`.`t4`) on(((`test`.`t3`.`a` = 1) and (`test`.`t2`.`b` = `test`.`t4`.`b`))) join `test`.`t5` left join (`test`.`t6` join `test`.`t7` left join `test`.`t8` on(((`test`.`t7`.`b` = `test`.`t8`.`b`) and (`test`.`t6`.`b` < 10)))) on(((`test`.`t6`.`b` >= 2) and (`test`.`t5`.`b` = `test`.`t7`.`b`)))) on((((`test`.`t3`.`b` = 2) or isnull(`test`.`t3`.`c`)) and ((`test`.`t6`.`b` = 2) or isnull(`test`.`t6`.`c`)) and ((`test`.`t1`.`b` = `test`.`t5`.`b`) or isnull(`test`.`t3`.`c`) or isnull(`test`.`t6`.`c`) or isnull(`test`.`t8`.`c`)) and (`test`.`t1`.`a` <> 2))) join `test`.`t9` where ((`test`.`t0`.`a` = 1) and (`test`.`t0`.`b` = `test`.`t1`.`b`) and ((`test`.`t2`.`a` >= 4) or isnull(`test`.`t2`.`c`)) and ((`test`.`t3`.`a` < 5) or isnull(`test`.`t3`.`c`)) and ((`test`.`t3`.`b` = `test`.`t4`.`b`) or isnull(`test`.`t3`.`c`) or isnull(`test`.`t4`.`c`)) and ((`test`.`t5`.`a` >= 2) or isnull(`test`.`t5`.`c`)) and ((`test`.`t6`.`a` >= 4) or isnull(`test`.`t6`.`c`)) and ((`test`.`t7`.`a` <= 2) or isnull(`test`.`t7`.`c`)) and ((`test`.`t8`.`a` < 1) or isnull(`test`.`t8`.`c`)) and ((`test`.`t8`.`b` = `test`.`t9`.`b`) or isnull(`test`.`t8`.`c`)) and (`test`.`t9`.`a` = 1))
+Note	1003	select `test`.`t0`.`a` AS `a`,`test`.`t0`.`b` AS `b`,`test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t2`.`a` AS `a`,`test`.`t2`.`b` AS `b`,`test`.`t3`.`a` AS `a`,`test`.`t3`.`b` AS `b`,`test`.`t4`.`a` AS `a`,`test`.`t4`.`b` AS `b`,`test`.`t5`.`a` AS `a`,`test`.`t5`.`b` AS `b`,`test`.`t6`.`a` AS `a`,`test`.`t6`.`b` AS `b`,`test`.`t7`.`a` AS `a`,`test`.`t7`.`b` AS `b`,`test`.`t8`.`a` AS `a`,`test`.`t8`.`b` AS `b`,`test`.`t9`.`a` AS `a`,`test`.`t9`.`b` AS `b` from `test`.`t0` join `test`.`t1` left join (`test`.`t2` left join (`test`.`t3` join `test`.`t4`) on(((`test`.`t4`.`b` = `test`.`t2`.`b`) and (`test`.`t3`.`a` = 1))) join `test`.`t5` left join (`test`.`t6` join `test`.`t7` left join `test`.`t8` on(((`test`.`t8`.`b` = `test`.`t5`.`b`) and (`test`.`t6`.`b` < 10)))) on(((`test`.`t7`.`b` = `test`.`t5`.`b`) and (`test`.`t6`.`b` >= 2)))) on((((`test`.`t3`.`b` = 2) or isnull(`test`.`t3`.`c`)) and ((`test`.`t6`.`b` = 2) or isnull(`test`.`t6`.`c`)) and ((`test`.`t5`.`b` = `test`.`t0`.`b`) or isnull(`test`.`t3`.`c`) or isnull(`test`.`t6`.`c`) or isnull(`test`.`t8`.`c`)) and (`test`.`t1`.`a` <> 2))) join `test`.`t9` where ((`test`.`t9`.`a` = 1) and (`test`.`t1`.`b` = `test`.`t0`.`b`) and (`test`.`t0`.`a` = 1) and ((`test`.`t2`.`a` >= 4) or isnull(`test`.`t2`.`c`)) and ((`test`.`t3`.`a` < 5) or isnull(`test`.`t3`.`c`)) and ((`test`.`t4`.`b` = `test`.`t3`.`b`) or isnull(`test`.`t3`.`c`) or isnull(`test`.`t4`.`c`)) and ((`test`.`t5`.`a` >= 2) or isnull(`test`.`t5`.`c`)) and ((`test`.`t6`.`a` >= 4) or isnull(`test`.`t6`.`c`)) and ((`test`.`t7`.`a` <= 2) or isnull(`test`.`t7`.`c`)) and ((`test`.`t8`.`a` < 1) or isnull(`test`.`t8`.`c`)) and ((`test`.`t9`.`b` = `test`.`t8`.`b`) or isnull(`test`.`t8`.`c`)))
 CREATE INDEX idx_b ON t1(b);
 CREATE INDEX idx_a ON t0(a);
 EXPLAIN EXTENDED
@@ -1084,10 +1084,10 @@ id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
 1	SIMPLE	t5	ALL	NULL	NULL	NULL	NULL	3	Using where
 1	SIMPLE	t7	ALL	NULL	NULL	NULL	NULL	2	Using where
 1	SIMPLE	t6	ALL	NULL	NULL	NULL	NULL	3	Using where
-1	SIMPLE	t8	ref	idx_b	idx_b	5	test.t7.b	2	Using where
+1	SIMPLE	t8	ref	idx_b	idx_b	5	test.t5.b	2	Using where
 1	SIMPLE	t9	ALL	NULL	NULL	NULL	NULL	3	Using where
 Warnings:
-Note	1003	select `test`.`t0`.`a` AS `a`,`test`.`t0`.`b` AS `b`,`test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t2`.`a` AS `a`,`test`.`t2`.`b` AS `b`,`test`.`t3`.`a` AS `a`,`test`.`t3`.`b` AS `b`,`test`.`t4`.`a` AS `a`,`test`.`t4`.`b` AS `b`,`test`.`t5`.`a` AS `a`,`test`.`t5`.`b` AS `b`,`test`.`t6`.`a` AS `a`,`test`.`t6`.`b` AS `b`,`test`.`t7`.`a` AS `a`,`test`.`t7`.`b` AS `b`,`test`.`t8`.`a` AS `a`,`test`.`t8`.`b` AS `b`,`test`.`t9`.`a` AS `a`,`test`.`t9`.`b` AS `b` from `test`.`t0` join `test`.`t1` left join (`test`.`t2` left join (`test`.`t3` join `test`.`t4`) on(((`test`.`t3`.`a` = 1) and (`test`.`t2`.`b` = `test`.`t4`.`b`))) join `test`.`t5` left join (`test`.`t6` join `test`.`t7` left join `test`.`t8` on(((`test`.`t7`.`b` = `test`.`t8`.`b`) and (`test`.`t6`.`b` < 10)))) on(((`test`.`t6`.`b` >= 2) and (`test`.`t5`.`b` = `test`.`t7`.`b`)))) on((((`test`.`t3`.`b` = 2) or isnull(`test`.`t3`.`c`)) and ((`test`.`t6`.`b` = 2) or isnull(`test`.`t6`.`c`)) and ((`test`.`t1`.`b` = `test`.`t5`.`b`) or isnull(`test`.`t3`.`c`) or isnull(`test`.`t6`.`c`) or isnull(`test`.`t8`.`c`)) and (`test`.`t1`.`a` <> 2))) join `test`.`t9` where ((`test`.`t0`.`a` = 1) and (`test`.`t0`.`b` = `test`.`t1`.`b`) and ((`test`.`t2`.`a` >= 4) or isnull(`test`.`t2`.`c`)) and ((`test`.`t3`.`a` < 5) or isnull(`test`.`t3`.`c`)) and ((`test`.`t3`.`b` = `test`.`t4`.`b`) or isnull(`test`.`t3`.`c`) or isnull(`test`.`t4`.`c`)) and ((`test`.`t5`.`a` >= 2) or isnull(`test`.`t5`.`c`)) and ((`test`.`t6`.`a` >= 4) or isnull(`test`.`t6`.`c`)) and ((`test`.`t7`.`a` <= 2) or isnull(`test`.`t7`.`c`)) and ((`test`.`t8`.`a` < 1) or isnull(`test`.`t8`.`c`)) and ((`test`.`t8`.`b` = `test`.`t9`.`b`) or isnull(`test`.`t8`.`c`)) and (`test`.`t9`.`a` = 1))
+Note	1003	select `test`.`t0`.`a` AS `a`,`test`.`t0`.`b` AS `b`,`test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t2`.`a` AS `a`,`test`.`t2`.`b` AS `b`,`test`.`t3`.`a` AS `a`,`test`.`t3`.`b` AS `b`,`test`.`t4`.`a` AS `a`,`test`.`t4`.`b` AS `b`,`test`.`t5`.`a` AS `a`,`test`.`t5`.`b` AS `b`,`test`.`t6`.`a` AS `a`,`test`.`t6`.`b` AS `b`,`test`.`t7`.`a` AS `a`,`test`.`t7`.`b` AS `b`,`test`.`t8`.`a` AS `a`,`test`.`t8`.`b` AS `b`,`test`.`t9`.`a` AS `a`,`test`.`t9`.`b` AS `b` from `test`.`t0` join `test`.`t1` left join (`test`.`t2` left join (`test`.`t3` join `test`.`t4`) on(((`test`.`t4`.`b` = `test`.`t2`.`b`) and (`test`.`t3`.`a` = 1))) join `test`.`t5` left join (`test`.`t6` join `test`.`t7` left join `test`.`t8` on(((`test`.`t8`.`b` = `test`.`t5`.`b`) and (`test`.`t6`.`b` < 10)))) on(((`test`.`t7`.`b` = `test`.`t5`.`b`) and (`test`.`t6`.`b` >= 2)))) on((((`test`.`t3`.`b` = 2) or isnull(`test`.`t3`.`c`)) and ((`test`.`t6`.`b` = 2) or isnull(`test`.`t6`.`c`)) and ((`test`.`t5`.`b` = `test`.`t0`.`b`) or isnull(`test`.`t3`.`c`) or isnull(`test`.`t6`.`c`) or isnull(`test`.`t8`.`c`)) and (`test`.`t1`.`a` <> 2))) join `test`.`t9` where ((`test`.`t9`.`a` = 1) and (`test`.`t1`.`b` = `test`.`t0`.`b`) and (`test`.`t0`.`a` = 1) and ((`test`.`t2`.`a` >= 4) or isnull(`test`.`t2`.`c`)) and ((`test`.`t3`.`a` < 5) or isnull(`test`.`t3`.`c`)) and ((`test`.`t4`.`b` = `test`.`t3`.`b`) or isnull(`test`.`t3`.`c`) or isnull(`test`.`t4`.`c`)) and ((`test`.`t5`.`a` >= 2) or isnull(`test`.`t5`.`c`)) and ((`test`.`t6`.`a` >= 4) or isnull(`test`.`t6`.`c`)) and ((`test`.`t7`.`a` <= 2) or isnull(`test`.`t7`.`c`)) and ((`test`.`t8`.`a` < 1) or isnull(`test`.`t8`.`c`)) and ((`test`.`t9`.`b` = `test`.`t8`.`b`) or isnull(`test`.`t8`.`c`)))
 SELECT t0.a,t0.b,t1.a,t1.b,t2.a,t2.b,t3.a,t3.b,t4.a,t4.b,
 t5.a,t5.b,t6.a,t6.b,t7.a,t7.b,t8.a,t8.b,t9.a,t9.b
 FROM t0,t1
diff --git a/mysql-test/r/odbc.result b/mysql-test/r/odbc.result
index c0b2ada00531e2927c5ff43323f9af8bfcfbe817..2d9d39393b15f86b6014b9ebc9d4e760a3a152d3 100644
--- a/mysql-test/r/odbc.result
+++ b/mysql-test/r/odbc.result
@@ -12,5 +12,5 @@ select * from t1 where a is null;
 a	b
 explain select * from t1 where b is null;
 id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
-1	SIMPLE	NULL	NULL	NULL	NULL	NULL	NULL	NULL	Impossible WHERE noticed after reading const tables
+1	SIMPLE	NULL	NULL	NULL	NULL	NULL	NULL	NULL	Impossible WHERE
 drop table t1;
diff --git a/mysql-test/r/range.result b/mysql-test/r/range.result
index 4c6caf2d23bed2b1712375eae2ba6b251d6229c5..5369932c13eae058f84cae6375d56058fde1eee8 100644
--- a/mysql-test/r/range.result
+++ b/mysql-test/r/range.result
@@ -220,24 +220,22 @@ insert into t1 (x) values (1),(2),(3),(4),(5),(6),(7),(8),(9);
 update t1 set y=x;
 explain select * from t1, t1 t2 where t1.y = 2 and t2.x between 7 and t1.y+0;
 id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
-1	SIMPLE	t1	ref	y	y	5	const	1	Using where
-1	SIMPLE	t2	range	x	x	5	NULL	4	Using where
+1	SIMPLE	NULL	NULL	NULL	NULL	NULL	NULL	NULL	Impossible WHERE noticed after reading const tables
 explain select * from t1, t1 t2 where t1.y = 2 and t2.x >= 7 and t2.x <= t1.y+0;
 id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
-1	SIMPLE	t1	ref	y	y	5	const	1	Using where
-1	SIMPLE	t2	range	x	x	5	NULL	4	Using where
+1	SIMPLE	NULL	NULL	NULL	NULL	NULL	NULL	NULL	Impossible WHERE noticed after reading const tables
 explain select * from t1, t1 t2 where t1.y = 2 and t2.x between t1.y-1 and t1.y+1;
 id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
 1	SIMPLE	t1	ref	y	y	5	const	1	Using where
-1	SIMPLE	t2	ALL	x	NULL	NULL	NULL	9	Range checked for each record (index map: 0x1)
+1	SIMPLE	t2	range	x	x	5	NULL	3	Using where
 explain select * from t1, t1 t2 where t1.y = 2 and t2.x >= t1.y-1 and t2.x <= t1.y+1;
 id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
 1	SIMPLE	t1	ref	y	y	5	const	1	Using where
-1	SIMPLE	t2	ALL	x	NULL	NULL	NULL	9	Range checked for each record (index map: 0x1)
+1	SIMPLE	t2	range	x	x	5	NULL	3	Using where
 explain select * from t1, t1 t2 where t1.y = 2 and t2.x between 0 and t1.y;
 id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
 1	SIMPLE	t1	ref	y	y	5	const	1	Using where
-1	SIMPLE	t2	ALL	x	NULL	NULL	NULL	9	Using where
+1	SIMPLE	t2	range	x	x	5	NULL	2	Using where
 explain select * from t1, t1 t2 where t1.y = 2 and t2.x >= 0 and t2.x <= t1.y;
 id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
 1	SIMPLE	t1	ref	y	y	5	const	1	Using where
@@ -256,12 +254,12 @@ INSERT INTO t2 VALUES (0),(0),(1),(1),(2),(2);
 explain select * from t1, t2 where (t1.key1 <t2.keya + 1) and t2.keya=3;
 id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
 1	SIMPLE	t2	ref	j1	j1	4	const	1	Using index
-1	SIMPLE	t1	ALL	i1	NULL	NULL	NULL	4	Range checked for each record (index map: 0x1)
+1	SIMPLE	t1	index	i1	i1	4	NULL	4	Using where; Using index
 explain select * from t1 force index(i1), t2 force index(j1) where 
 (t1.key1 <t2.keya + 1) and t2.keya=3;
 id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
 1	SIMPLE	t2	ref	j1	j1	4	const	1	Using index
-1	SIMPLE	t1	ALL	i1	NULL	NULL	NULL	4	Range checked for each record (index map: 0x1)
+1	SIMPLE	t1	index	i1	i1	4	NULL	4	Using where; Using index
 DROP TABLE t1,t2;
 CREATE TABLE t1 (
 a int(11) default NULL,
@@ -415,14 +413,26 @@ count(*)
 select count(*) from t2;
 count(*)
 1026
+analyze table t1,t2;
+Table	Op	Msg_type	Msg_text
+test.t1	analyze	status	OK
+test.t2	analyze	status	Table is already up to date
 explain select * from t1, t2  where t1.uid=t2.uid AND t1.uid > 0;
 id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
 1	SIMPLE	t1	range	uid_index	uid_index	4	NULL	128	Using where
 1	SIMPLE	t2	ref	uid_index	uid_index	4	test.t1.uid	38	
+explain select * from t1, t2  where t1.uid=t2.uid AND t2.uid > 0;
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	SIMPLE	t1	range	uid_index	uid_index	4	NULL	128	Using where
+1	SIMPLE	t2	ref	uid_index	uid_index	4	test.t1.uid	38	
 explain select * from t1, t2  where t1.uid=t2.uid AND t1.uid != 0;
 id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
 1	SIMPLE	t1	range	uid_index	uid_index	4	NULL	129	Using where
 1	SIMPLE	t2	ref	uid_index	uid_index	4	test.t1.uid	38	
+explain select * from t1, t2  where t1.uid=t2.uid AND t2.uid != 0;
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	SIMPLE	t1	range	uid_index	uid_index	4	NULL	129	Using where
+1	SIMPLE	t2	ref	uid_index	uid_index	4	test.t1.uid	38	
 select * from t1, t2  where t1.uid=t2.uid AND t1.uid > 0;
 id	name	uid	id	name	uid
 1001	A	1	1001	A	1
diff --git a/mysql-test/r/select.result b/mysql-test/r/select.result
index eaafc3b72980795bb56272c3481294c3849343d3..d3c312b40fdb56274bad69de4431482e89f75e6b 100644
--- a/mysql-test/r/select.result
+++ b/mysql-test/r/select.result
@@ -1375,7 +1375,7 @@ id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
 explain select t2.companynr,companyname from t4 left join t2 using (companynr) where t2.companynr > 0 and t4.companynr > 0;
 id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
 1	SIMPLE	t2	ALL	NULL	NULL	NULL	NULL	1199	Using where
-1	SIMPLE	t4	eq_ref	PRIMARY	PRIMARY	1	test.t2.companynr	1	Using where
+1	SIMPLE	t4	eq_ref	PRIMARY	PRIMARY	1	test.t2.companynr	1	
 explain select t2.companynr,companyname from t4 left join t2 using (companynr) where t2.companynr > 0 or t2.companynr is null;
 id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
 1	SIMPLE	t4	ALL	NULL	NULL	NULL	NULL	12	
diff --git a/mysql-test/r/subselect.result b/mysql-test/r/subselect.result
index 4dab9082d2e2c39299ffaadbffaf0ad4e3963c21..56df7576fb55f067f7d9c40c5608571cf3e02118 100644
--- a/mysql-test/r/subselect.result
+++ b/mysql-test/r/subselect.result
@@ -331,7 +331,7 @@ id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
 2	DEPENDENT SUBQUERY	t7	eq_ref	PRIMARY	PRIMARY	4	test.t6.clinic_uq	1	Using index
 Warnings:
 Note	1276	Field or reference 'clinic_uq' of SELECT #2 was resolved in SELECT #1
-Note	1003	select `test`.`t6`.`patient_uq` AS `patient_uq`,`test`.`t6`.`clinic_uq` AS `clinic_uq` from `test`.`t6` where exists(select 1 AS `Not_used` from `test`.`t7` where (`test`.`t7`.`uq` = `test`.`t6`.`clinic_uq`))
+Note	1003	select `test`.`t6`.`patient_uq` AS `patient_uq`,`test`.`t6`.`clinic_uq` AS `clinic_uq` from `test`.`t6` where exists(select 1 AS `Not_used` from `test`.`t7` where (`test`.`t6`.`clinic_uq` = `test`.`t7`.`uq`))
 select * from t1 where a= (select a from t2,t4 where t2.b=t4.b);
 ERROR 23000: Column 'a' in field list is ambiguous
 drop table t1,t2,t3;
@@ -545,7 +545,7 @@ id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
 1	PRIMARY	t1	const	PRIMARY,numreponse	PRIMARY	7	const,const	1	Using index
 2	SUBQUERY	NULL	NULL	NULL	NULL	NULL	NULL	NULL	Select tables optimized away
 Warnings:
-Note	1003	select `test`.`t1`.`numreponse` AS `numreponse` from `test`.`t1` where ((`test`.`t1`.`numeropost` = _latin1'1') and (`test`.`t1`.`numreponse` = 3))
+Note	1003	select `test`.`t1`.`numreponse` AS `numreponse` from `test`.`t1` where ((`test`.`t1`.`numreponse` = (select max(`test`.`t1`.`numreponse`) AS `MAX(numreponse)` from `test`.`t1` where (`test`.`t1`.`numeropost` = _latin1'1'))) and (`test`.`t1`.`numeropost` = _latin1'1'))
 drop table t1;
 CREATE TABLE t1 (a int(1));
 INSERT INTO t1 VALUES (1);
@@ -1313,7 +1313,7 @@ id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
 2	DEPENDENT SUBQUERY	t1	eq_ref	PRIMARY	PRIMARY	4	func	1	Using where
 2	DEPENDENT SUBQUERY	t3	eq_ref	PRIMARY	PRIMARY	4	test.t1.b	1	Using where; Using index
 Warnings:
-Note	1003	select `test`.`t2`.`a` AS `a` from `test`.`t2` where <in_optimizer>(`test`.`t2`.`a`,<exists>(select 1 AS `Not_used` from `test`.`t1` join `test`.`t3` where ((`test`.`t1`.`b` = `test`.`t3`.`a`) and (<cache>(`test`.`t2`.`a`) = `test`.`t1`.`a`))))
+Note	1003	select `test`.`t2`.`a` AS `a` from `test`.`t2` where <in_optimizer>(`test`.`t2`.`a`,<exists>(select 1 AS `Not_used` from `test`.`t1` join `test`.`t3` where ((`test`.`t3`.`a` = `test`.`t1`.`b`) and (<cache>(`test`.`t2`.`a`) = `test`.`t1`.`a`))))
 drop table t1, t2, t3;
 create table t1 (a int, b int, index a (a,b));
 create table t2 (a int, index a (a));
@@ -1675,10 +1675,10 @@ Note	1003	select `test`.`t1`.`id` AS `id`,`test`.`t1`.`text` AS `text` from `tes
 explain extended select * from t1 as tt where not exists (select id from t1 where id < 8 and (id = tt.id or id is null) having id is not null);
 id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
 1	PRIMARY	tt	ALL	NULL	NULL	NULL	NULL	12	Using where
-2	DEPENDENT SUBQUERY	t1	eq_ref	PRIMARY	PRIMARY	4	test.tt.id	7	Using where; Using index
+2	DEPENDENT SUBQUERY	t1	eq_ref	PRIMARY	PRIMARY	4	test.tt.id	1	Using where; Using index
 Warnings:
 Note	1276	Field or reference 'tt.id' of SELECT #2 was resolved in SELECT #1
-Note	1003	select `test`.`tt`.`id` AS `id`,`test`.`tt`.`text` AS `text` from `test`.`t1` `tt` where not(exists(select `test`.`t1`.`id` AS `id` from `test`.`t1` where ((`test`.`t1`.`id` < 8) and ((`test`.`t1`.`id` = `test`.`tt`.`id`) or isnull(`test`.`t1`.`id`))) having (`test`.`t1`.`id` is not null)))
+Note	1003	select `test`.`tt`.`id` AS `id`,`test`.`tt`.`text` AS `text` from `test`.`t1` `tt` where not(exists(select `test`.`t1`.`id` AS `id` from `test`.`t1` where ((`test`.`t1`.`id` < 8) and (`test`.`tt`.`id` = `test`.`t1`.`id`)) having (`test`.`t1`.`id` is not null)))
 insert into t1 (id, text) values (1000, 'text1000'), (1001, 'text1001');
 create table t2 (id int not null, text varchar(20) not null default '', primary key (id));
 insert into t2 (id, text) values (1, 'text1'), (2, 'text2'), (3, 'text3'), (4, 'text4'), (5, 'text5'), (6, 'text6'), (7, 'text7'), (8, 'text8'), (9, 'text9'), (10, 'text10'), (11, 'text1'), (12, 'text2'), (13, 'text3'), (14, 'text4'), (15, 'text5'), (16, 'text6'), (17, 'text7'), (18, 'text8'), (19, 'text9'), (20, 'text10'),(21, 'text1'), (22, 'text2'), (23, 'text3'), (24, 'text4'), (25, 'text5'), (26, 'text6'), (27, 'text7'), (28, 'text8'), (29, 'text9'), (30, 'text10'), (31, 'text1'), (32, 'text2'), (33, 'text3'), (34, 'text4'), (35, 'text5'), (36, 'text6'), (37, 'text7'), (38, 'text8'), (39, 'text9'), (40, 'text10'), (41, 'text1'), (42, 'text2'), (43, 'text3'), (44, 'text4'), (45, 'text5'), (46, 'text6'), (47, 'text7'), (48, 'text8'), (49, 'text9'), (50, 'text10');
@@ -1704,7 +1704,7 @@ id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
 1	SIMPLE	b	eq_ref	PRIMARY	PRIMARY	4	test.a.id	2	
 1	SIMPLE	c	eq_ref	PRIMARY	PRIMARY	4	func	1	Using where
 Warnings:
-Note	1003	select `test`.`a`.`id` AS `id`,`test`.`a`.`text` AS `text`,`test`.`b`.`id` AS `id`,`test`.`b`.`text` AS `text`,`test`.`c`.`id` AS `id`,`test`.`c`.`text` AS `text` from `test`.`t1` `a` left join `test`.`t2` `b` on(((`test`.`a`.`id` = `test`.`b`.`id`) or isnull(`test`.`b`.`id`))) join `test`.`t1` `c` where (if(isnull(`test`.`b`.`id`),1000,`test`.`b`.`id`) = `test`.`c`.`id`)
+Note	1003	select `test`.`a`.`id` AS `id`,`test`.`a`.`text` AS `text`,`test`.`b`.`id` AS `id`,`test`.`b`.`text` AS `text`,`test`.`c`.`id` AS `id`,`test`.`c`.`text` AS `text` from `test`.`t1` `a` left join `test`.`t2` `b` on(((`test`.`b`.`id` = `test`.`a`.`id`) or isnull(`test`.`b`.`id`))) join `test`.`t1` `c` where (if(isnull(`test`.`b`.`id`),1000,`test`.`b`.`id`) = `test`.`c`.`id`)
 drop table t1,t2;
 create table t1 (a int);
 insert into t1 values (1);
@@ -1832,7 +1832,7 @@ id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
 2	DEPENDENT SUBQUERY	t1	ALL	NULL	NULL	NULL	NULL	2	Using where
 Warnings:
 Note	1276	Field or reference 'up.a' of SELECT #2 was resolved in SELECT #1
-Note	1003	select `test`.`up`.`a` AS `a`,`test`.`up`.`b` AS `b` from `test`.`t1` `up` where exists(select 1 AS `Not_used` from `test`.`t1` where (`test`.`t1`.`a` = `test`.`up`.`a`))
+Note	1003	select `test`.`up`.`a` AS `a`,`test`.`up`.`b` AS `b` from `test`.`t1` `up` where exists(select 1 AS `Not_used` from `test`.`t1` where (`test`.`up`.`a` = `test`.`t1`.`a`))
 drop table t1;
 CREATE TABLE t1 (t1_a int);
 INSERT INTO t1 VALUES (1);
diff --git a/mysql-test/t/range.test b/mysql-test/t/range.test
index 7a86c0cef1d27583baad54dc914fde412caa28f3..e1427cdf260d0a62ce46a24c517e62d2abb5c95b 100644
--- a/mysql-test/t/range.test
+++ b/mysql-test/t/range.test
@@ -378,8 +378,12 @@ insert into t2(id, uid, name) select id, uid, name from t1;
 select count(*) from t1;  
 select count(*) from t2;
 
+analyze table t1,t2;
+
 explain select * from t1, t2  where t1.uid=t2.uid AND t1.uid > 0;
+explain select * from t1, t2  where t1.uid=t2.uid AND t2.uid > 0;
 explain select * from t1, t2  where t1.uid=t2.uid AND t1.uid != 0;
+explain select * from t1, t2  where t1.uid=t2.uid AND t2.uid != 0;
 
 select * from t1, t2  where t1.uid=t2.uid AND t1.uid > 0;
 select * from t1, t2  where t1.uid=t2.uid AND t1.uid != 0;
diff --git a/sql/item.cc b/sql/item.cc
index 1aa07fb2b72607c753efa822bab9c22926004acc..134b04d55406c001b8b355e837a0aeaa26246260 100644
--- a/sql/item.cc
+++ b/sql/item.cc
@@ -447,6 +447,7 @@ bool DTCollation::aggregate(DTCollation &dt, bool superset_conversion)
 
 Item_field::Item_field(Field *f)
   :Item_ident(NullS, f->table_name, f->field_name),
+  item_equal(0), no_const_subst(0),
    have_privileges(0), any_privileges(0)
 {
   set_field(f);
@@ -457,6 +458,7 @@ Item_field::Item_field(Field *f)
 Item_field::Item_field(THD *thd, Field *f)
   :Item_ident(NullS, thd->strdup(f->table_name), 
               thd->strdup(f->field_name)),
+   item_equal(0), no_const_subst(0),
    have_privileges(0), any_privileges(0)
 {
   set_field(f);
@@ -469,6 +471,8 @@ Item_field::Item_field(THD *thd, Item_field *item)
   :Item_ident(thd, item),
    field(item->field),
    result_field(item->result_field),
+   item_equal(item->item_equal),
+   no_const_subst(item->no_const_subst),
    have_privileges(item->have_privileges),
    any_privileges(item->any_privileges)
 {
@@ -1621,7 +1625,123 @@ void Item_field::cleanup()
     I.e. we can drop 'field'.
    */
   field= result_field= 0;
-  DBUG_VOID_RETURN;
+}
+
+/*
+  Find a field among specified multiple equalities 
+
+  SYNOPSIS
+    find_item_equal()
+    cond_equal   reference to list of multiple equalities where
+                 the field (this object) is to be looked for
+  
+  DESCRIPTION
+    The function first searches the field among multiple equalities
+    of the current level (in the cond_equal->current_level list).
+    If it fails, it continues searching in upper levels accessed
+    through a pointer cond_equal->upper_levels.
+    The search terminates as soon as a multiple equality containing 
+    the field is found. 
+
+  RETURN VALUES
+    First Item_equal containing the field, if success
+    0, otherwise
+*/
+Item_equal *Item_field::find_item_equal(COND_EQUAL *cond_equal)
+{
+  Item_equal *item= 0;
+  while (cond_equal)
+  {
+    List_iterator_fast<Item_equal> li(cond_equal->current_level);
+    while ((item= li++))
+    {
+      if (item->contains(field))
+        return item;
+    }
+    /* 
+      The field is not found in any of the multiple equalities
+      of the current level. Look for it in upper levels
+    */
+    cond_equal= cond_equal->upper_levels;
+  }
+  return 0;
+}
+
+
+/*
+  Set a pointer to the multiple equality the field reference belongs to (if any)
+   
+  SYNOPSIS
+    equal_fields_propagator()
+    arg - reference to list of multiple equalities where
+          the field (this object) is to be looked for
+  
+  DESCRIPTION
+    The function looks for a multiple equality containing the field item
+    among those referenced by arg.
+    In the case such equality exists the function does the following.
+    If the found multiple equality contains a constant, then the field
+    reference is substituted for this constant, otherwise it sets a pointer
+    to the multiple equality in the field item.
+
+  NOTES
+    This function is supposed to be called as a callback parameter in calls
+    of the transform method.  
+
+  RETURN VALUES
+    pointer to the replacing constant item, if the field item was substituted 
+    pointer to the field item, otherwise.
+*/
+
+Item *Item_field::equal_fields_propagator(byte *arg)
+{
+  if (no_const_subst)
+    return this;
+  item_equal= find_item_equal((COND_EQUAL *) arg);
+  Item *item= 0;
+  if (item_equal)
+    item= item_equal->get_const();
+  if (!item)
+    item= this;
+  return item;
+}
+
+
+/*
+  Set a pointer to the multiple equality the field reference belongs to (if any)
+   
+  SYNOPSIS
+    replace_equal_field_processor()
+    arg - a dummy parameter, is not used here
+  
+  DESCRIPTION
+    The function replaces a pointer to a field in the Item_field object
+    by a pointer to another field.
+    The replacement field is taken from the very beginning of
+    the item_equal list which the Item_field object refers to (belongs to)  
+    If the Item_field object does not refer any Item_equal object,
+    nothing is done.
+
+  NOTES
+    This function is supposed to be called as a callback parameter in calls
+    of the walk method.  
+
+  RETURN VALUES
+    0 
+*/
+
+bool Item_field::replace_equal_field_processor(byte *arg)
+{
+  if (item_equal)
+  {
+    Item_field *subst= item_equal->get_first();
+    if (!field->eq(subst->field))
+    {
+      field= subst->field;
+      return 0;
+    }
+  }
+  return 0;
 }
 
 void Item::init_make_field(Send_field *tmp_field,
diff --git a/sql/item.h b/sql/item.h
index 57322146d277612bd78a3d6800aeb34bd8387cf2..f8d04b6c8519ff8d302104ed55648124f51e1a5b 100644
--- a/sql/item.h
+++ b/sql/item.h
@@ -90,6 +90,7 @@ class DTCollation {
 };
 
 typedef bool (Item::*Item_processor)(byte *arg);
+typedef Item* (Item::*Item_transformer) (byte *arg);
 
 class Item {
   Item(const Item &);			/* Prevent use of these */
@@ -261,9 +262,16 @@ class Item {
     return (this->*processor)(arg);
   }
 
+   virtual Item* transform(Item_transformer transformer, byte *arg)
+  {
+    return (this->*transformer)(arg);
+  }
+ 
   virtual bool remove_dependence_processor(byte * arg) { return 0; }
   virtual bool remove_fixed(byte * arg) { fixed= 0; return 0; }
   virtual bool collect_item_field_processor(byte * arg) { return 0; }
+  virtual Item *equal_fields_propagator(byte * arg) { return this; }
+  virtual bool replace_equal_field_processor(byte * arg) { return 0; }
   
   virtual Item *this_item() { return this; } /* For SPs mostly. */
   virtual Item *this_const_item() const { return const_cast<Item*>(this); } /* For SPs mostly. */
@@ -441,6 +449,8 @@ class Item_ident :public Item
                             bool any_privileges, bool allocate_view_names);
 };
 
+class Item_equal;
+class COND_EQUAL;
 
 class Item_field :public Item_ident
 {
@@ -448,6 +458,8 @@ class Item_field :public Item_ident
   void set_field(Field *field);
 public:
   Field *field,*result_field;
+  Item_equal *item_equal;
+  bool no_const_subst;
   /*
     if any_privileges set to TRUE then here real effective privileges will
     be stored
@@ -459,7 +471,8 @@ class Item_field :public Item_ident
   Item_field(const char *db_par,const char *table_name_par,
 	     const char *field_name_par)
     :Item_ident(db_par,table_name_par,field_name_par),
-     field(0), result_field(0), have_privileges(0), any_privileges(0)
+    field(0), result_field(0), item_equal(0), no_const_subst(0),
+     have_privileges(0), any_privileges(0)
   { collation.set(DERIVATION_IMPLICIT); }
   // Constructor need to process subselect with temporary tables (see Item)
   Item_field(THD *thd, Item_field *item);
@@ -500,6 +513,9 @@ class Item_field :public Item_ident
   Item *get_tmp_table_item(THD *thd);
   bool collect_item_field_processor(byte * arg);
   void cleanup();
+  Item_equal *find_item_equal(COND_EQUAL *cond_equal);
+  Item *equal_fields_propagator(byte *arg);
+  bool replace_equal_field_processor(byte *arg);
   inline uint32 max_disp_length() { return field->max_length(); }
   Item_field *filed_for_view_update() { return this; }
   friend class Item_default_value;
@@ -1177,6 +1193,19 @@ class Item_default_value : public Item_field
     return arg->walk(processor, args) ||
       (this->*processor)(args);
   }
+
+  /* 
+     This method like the walk method traverses the item tree, but
+     at the same time it can replace some nodes in the tree
+  */ 
+  Item *transform(Item_transformer transformer, byte *args)
+  {
+    Item *new_item= arg->transform(transformer, args);
+    if (!new_item)
+      return 0;
+    arg= new_item;
+    return (this->*transformer)(args);
+  }
 };
 
 class Item_insert_value : public Item_field
diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc
index 582277cd142a4f2075151dba55307e1b4ade6327..66af96f671f63ac6f5a58e3b4e7977f34f95ddb3 100644
--- a/sql/item_cmpfunc.cc
+++ b/sql/item_cmpfunc.cc
@@ -231,6 +231,8 @@ void Item_bool_func2::fix_length_and_dec()
         conv->collation.set(args[weak]->collation.derivation);
         conv->fix_fields(thd, 0, &conv);
       }
+      if (args[weak]->type() == FIELD_ITEM)
+        ((Item_field *)args[weak])->no_const_subst= 1;
       args[weak]= conv ? conv : args[weak];
     }
   }
@@ -256,7 +258,7 @@ void Item_bool_func2::fix_length_and_dec()
       }
     }
   }
-  if (args[1]->type() == FIELD_ITEM)
+  if (args[1]->type() == FIELD_ITEM /* && !args[1]->const_item() */)
   {
     Field *field=((Item_field*) args[1])->field;
     if (field->store_for_compare())
@@ -1956,7 +1958,7 @@ Item_cond::fix_fields(THD *thd, TABLE_LIST *tables, Item **ref)
   char buff[sizeof(char*)];			// Max local vars in function
 #endif
   not_null_tables_cache= used_tables_cache= 0;
-  const_item_cache= 0;
+  const_item_cache= 1;
   /*
     and_table_cache is the value that Item_cond_or() returns for
     not_null_tables()
@@ -1987,7 +1989,7 @@ Item_cond::fix_fields(THD *thd, TABLE_LIST *tables, Item **ref)
     tmp_table_map=	    item->not_null_tables();
     not_null_tables_cache|= tmp_table_map;
     and_tables_cache&=      tmp_table_map;
-    const_item_cache&=	    item->const_item();
+    const_item_cache&=      item->const_item();
     with_sum_func=	    with_sum_func || item->with_sum_func;
     if (item->maybe_null)
       maybe_null=1;
@@ -2008,12 +2010,50 @@ bool Item_cond::walk(Item_processor processor, byte *arg)
   return Item_func::walk(processor, arg);
 }
 
+
+/*
+  Transform an Item_cond object with a transformer callback function
+   
+  SYNOPSIS
+    transform()
+    transformer   the transformer callback function to be applied to the nodes
+                  of the tree of the object
+    arg           parameter to be passed to the transformer
+  
+  DESCRIPTION
+    The function recursively applies the transform method with the
+    same transformer to each member item of the codition list.
+    If the call of the method for a member item returns a new item
+    the old item is substituted for a new one.
+    After this the transform method is applied to the root node
+    of the Item_cond object. 
+     
+  RETURN VALUES
+    Item returned as the result of transformation of the root node 
+*/
+
+Item *Item_cond::transform(Item_transformer transformer, byte *arg)
+{
+  List_iterator<Item> li(list);
+  Item *item;
+  while ((item= li++))
+  {
+    Item *new_item= item->transform(transformer, arg);
+    if (!new_item)
+      return 0;
+    if (new_item != item)
+      li.replace(new_item);
+  }
+  return Item_func::transform(transformer, arg);
+}
+
+
 void Item_cond::split_sum_func(Item **ref_pointer_array, List<Item> &fields)
 {
   List_iterator<Item> li(list);
   Item *item;
   used_tables_cache=0;
-  const_item_cache=0;
+  const_item_cache=1;
   while ((item=li++))
   {
     if (item->with_sum_func && item->type() != SUM_FUNC_ITEM)
@@ -2050,7 +2090,7 @@ void Item_cond::update_used_tables()
   {
     item->update_used_tables();
     used_tables_cache|= item->used_tables();
-    const_item_cache&=  item->const_item();
+    const_item_cache&= item->const_item();
   }
 }
 
@@ -2854,3 +2894,290 @@ Item *Item_bool_rowready_func2::negated_item()
   DBUG_ASSERT(0);
   return 0;
 }
+
+Item_equal::Item_equal(Item_field *f1, Item_field *f2)
+  : Item_bool_func(), const_item(0), eval_item(0), cond_false(0)
+{
+  const_item_cache= 0;
+  fields.push_back(f1);
+  fields.push_back(f2);
+}
+
+Item_equal::Item_equal(Item *c, Item_field *f)
+  : Item_bool_func(), eval_item(0), cond_false(0)
+{
+  const_item_cache= 0;
+  fields.push_back(f);
+  const_item= c;
+}
+
+Item_equal::Item_equal(Item_equal *item_equal)
+  : Item_bool_func(), eval_item(0), cond_false(0)
+{
+  const_item_cache= 0;
+  List_iterator_fast<Item_field> li(item_equal->fields);
+  Item_field *item;
+  while ((item= li++))
+  {
+    fields.push_back(item);
+  }
+  const_item= item_equal->const_item;
+  cond_false= item_equal->cond_false;
+}
+
+void Item_equal::add(Item *c)
+{
+  if (cond_false)
+    return;
+  if (!const_item)
+  {
+    const_item= c;
+    return;
+  }
+  Item_func_eq *func= new Item_func_eq(c, const_item);
+  func->set_cmp_func();
+  func->quick_fix_field();
+  cond_false =  !(func->val_int());
+}
+
+void Item_equal::add(Item_field *f)
+{
+  fields.push_back(f);
+}
+
+uint Item_equal::members()
+{
+  uint count= 0;
+  List_iterator_fast<Item_field> li(fields);
+  Item_field *item;
+  while ((item= li++))
+    count++;
+  return count;
+}
+
+
+/*
+  Check whether a field is referred in the multiple equality 
+
+  SYNOPSIS
+    contains()
+    field   field whose occurence is to be checked
+  
+  DESCRIPTION
+    The function checks whether field is occured in the Item_equal object 
+    
+  RETURN VALUES
+    1       if nultiple equality contains a reference to field
+    0       otherwise    
+*/
+
+bool Item_equal::contains(Field *field)
+{
+  List_iterator_fast<Item_field> it(fields);
+  Item_field *item;
+  while ((item= it++))
+  {
+    if (field->eq(item->field))
+        return 1;
+  }
+  return 0;
+}
+
+
+/*
+  Join members of another Item_equal object  
+
+  SYNOPSIS
+    merge()
+    item    multiple equality whose members are to be joined
+  
+  DESCRIPTION
+    The function actually merges two multiple equalitis.
+    After this operation the Item_equal object additionally contains
+    the field items of another item of the type Item_equal.
+    If the optional constant items are not equal the cond_false flag is
+    set to 1.  
+       
+  RETURN VALUES
+    none    
+*/
+
+void Item_equal::merge(Item_equal *item)
+{
+  fields.concat(&item->fields);
+  Item *c= item->const_item;
+  if (c)
+  {
+    /* 
+      The flag cond_false will be set to 1 after this, if 
+      the multiple equality already contains a constant and its 
+      value is  not equal to the value of c.
+    */
+    add(const_item);
+  }
+  cond_false|= item->cond_false;
+} 
+
+
+/*
+  Order field items in multiple equality according to a sorting criteria 
+
+  SYNOPSIS
+    sort()
+    cmp          function to compare field item 
+    arg          context extra parameter for the cmp function
+  
+  DESCRIPTION
+    The function perform ordering of the field items in the Item_equal
+    object according to the criteria determined by the cmp callback parameter.
+    If cmp(item_field1,item_field2,arg)<0 than item_field1 must be
+    placed after item_fiel2.
+
+  IMPLEMENTATION
+    The function sorts field items by the exchange sort algorithm.
+    The list of field items is looked through and whenever two neighboring
+    members follow in a wrong order they are swapped. This is performed
+    again and again until we get all members in a right order.
+         
+  RETURN VALUES
+    None    
+*/
+
+void Item_equal::sort(Item_field_cmpfunc cmp, void *arg)
+{
+  bool swap;
+  List_iterator<Item_field> it(fields);
+  do
+  {
+    Item_field *item1= it++;
+    Item_field **ref1= it.ref();
+    Item_field *item2;
+
+    swap= FALSE;
+    while ((item2= it++))
+    {
+      Item_field **ref2= it.ref();
+      if (cmp(item1, item2, arg) < 0)
+      {
+        Item_field *item= *ref1;
+        *ref1= *ref2;
+        *ref2= item;
+        swap= TRUE;
+      }
+      else
+      {
+        item1= item2;
+        ref1= ref2;
+      }
+    }
+    it.rewind();
+  } while (swap);
+}
+
+bool Item_equal::fix_fields(THD *thd, TABLE_LIST *tables, Item **ref)
+{
+  List_iterator_fast<Item_field> li(fields);
+  Item *item;
+  not_null_tables_cache= used_tables_cache= 0;
+  const_item_cache= 0;
+  while ((item=li++))
+  {
+    table_map tmp_table_map;
+    used_tables_cache|= item->used_tables();
+    tmp_table_map= item->not_null_tables();
+    not_null_tables_cache|= tmp_table_map;
+    if (item->maybe_null)
+      maybe_null=1;
+  }
+  fix_length_and_dec();
+  fixed= 1;
+  return 0;
+}
+
+void Item_equal::update_used_tables()
+{
+  List_iterator_fast<Item_field> li(fields);
+  Item *item;
+  not_null_tables_cache= used_tables_cache= 0;
+  if ((const_item_cache= cond_false))
+    return;
+  while ((item=li++))
+  {
+    item->update_used_tables();
+    used_tables_cache|= item->used_tables();
+    const_item_cache&= item->const_item();
+  }
+}
+
+longlong Item_equal::val_int()
+{
+  if (cond_false)
+    return 0;
+  List_iterator_fast<Item_field> it(fields);
+  Item *item= const_item ? const_item : it++;
+  if ((null_value= item->null_value))
+    return 0;
+  eval_item->store_value(item);
+  while ((item= it++))
+  {
+    if ((null_value= item->null_value) || eval_item->cmp(item))
+      return 0;
+  }
+  return 1;
+}
+
+void Item_equal::fix_length_and_dec()
+{
+  Item *item= const_item ? const_item : get_first();
+  eval_item= cmp_item::get_comparator(item);
+  if (item->result_type() == STRING_RESULT)
+    eval_item->cmp_charset= cmp_collation.collation;
+}
+
+bool Item_equal::walk(Item_processor processor, byte *arg)
+{
+  List_iterator_fast<Item_field> it(fields);
+  Item *item;
+  while ((item= it++))
+    if (item->walk(processor, arg))
+      return 1;
+  return Item_func::walk(processor, arg);
+}
+
+Item *Item_equal::transform(Item_transformer transformer, byte *arg)
+{
+  List_iterator<Item_field> it(fields);
+  Item *item;
+  while ((item= it++))
+  {
+    Item *new_item= item->transform(transformer, arg);
+    if (!new_item)
+      return 0;
+    if (new_item != item)
+      it.replace((Item_field *) new_item);
+  }
+  return Item_func::transform(transformer, arg);
+}
+
+void Item_equal::print(String *str)
+{
+  str->append(func_name());
+  str->append('(');
+  List_iterator_fast<Item_field> it(fields);
+  Item *item;
+  if (const_item)
+    const_item->print(str);
+  else
+  {
+    item= it++;
+    item->print(str);
+  }
+  while ((item= it++))
+  {
+    str->append(',');
+    str->append(' ');
+    item->print(str);
+  }
+  str->append(')');
+}
+
diff --git a/sql/item_cmpfunc.h b/sql/item_cmpfunc.h
index e7bef18e6290f2a70112e56e0947f68b4b751b89..a8382df20f50110208e6b44290d601fb7064fc7b 100644
--- a/sql/item_cmpfunc.h
+++ b/sql/item_cmpfunc.h
@@ -20,6 +20,9 @@
 #ifdef __GNUC__
 #pragma interface			/* gcc class implementation */
 #endif
+#ifdef __GNUC__
+template class List_iterator_fast<Item_field>;
+#endif
 
 extern Item_result item_cmp_type(Item_result a,Item_result b);
 class Item_bool_func2;
@@ -27,6 +30,8 @@ class Arg_comparator;
 
 typedef int (Arg_comparator::*arg_cmp_func)();
 
+typedef int (*Item_field_cmpfunc)(Item_field *f1, Item_field *f2, void *arg); 
+
 class Arg_comparator: public Sql_alloc
 {
   Item **a, **b;
@@ -957,6 +962,7 @@ class Item_cond :public Item_bool_func
   Item_cond(List<Item> &nlist)
     :Item_bool_func(), list(nlist), abort_on_null(0) {}
   bool add(Item *item) { return list.push_back(item); }
+  void add_at_head(List<Item> *nlist) { list.prepand(nlist); }
   bool fix_fields(THD *, struct st_table_list *, Item **ref);
 
   enum Type type() const { return COND_ITEM; }
@@ -969,13 +975,151 @@ class Item_cond :public Item_bool_func
   void top_level_item() { abort_on_null=1; }
   void copy_andor_arguments(THD *thd, Item_cond *item);
   bool walk(Item_processor processor, byte *arg);
+  Item *transform(Item_transformer transformer, byte *arg);
   void neg_arguments(THD *thd);
 };
 
 
+/*
+  The class Item_equal is used to represent conjuctions of equality
+  predicates of the form field1 = field2, and field=const in where
+  conditions and on expressions.
+ 
+  All equality predicates of the form field1=field2 contained in a 
+  conjuction are substituted for a sequence of items of this class.
+  An item of this class Item_equal(f1,f2,...fk) respresents a
+  multiple equality f1=f2=...=fk.
+
+  If a conjuction contains predicates f1=f2 and f2=f3, a new item of
+  this class is created Item_equal(f1,f2,f3) representing the multiple
+  equality f1=f2=f3 that substitutes the above equality predicates in
+  the conjuction.
+  A conjuction of the predicates f2=f1 and f3=f1 and f3=f2 will be
+  substituted for the item representing the same multiple equality
+  f1=f2=f3.
+  An item Item_equal(f1,f2) can appear instead of a conjuction of 
+  f2=f1 and f1=f2, or instead of just the predicate f1=f2.
+
+  An item of the class Item_equal inherites equalities from outer 
+  conjunctive levels.
+
+  Suppose we have a where condition of the following form:
+  WHERE f1=f2 AND f3=f4 AND f3=f5 AND ... AND (...OR (f1=f3 AND ...)).
+  In this case:
+    f1=f2 will be substituted for Item_equal(f1,f2);
+    f3=f4 and f3=f5  will be substituted for Item_equal(f3,f4,f5);
+    f1=f3 will be substituted for Item_equal(f1,f2,f3,f4,f5);
+
+  An object of the class Item_equal can contain an optional constant
+  item c. Thenit represents a multiple equality of the form 
+  c=f1=...=fk.
+
+  Objects of the class Item_equal are used for the following:
+
+  1. An object Item_equal(t1.f1,...,tk.fk) allows us to consider any
+  pair of tables ti and tj as joined by an equi-condition.
+  Thus it provide us with additional access paths from table to table.
+
+  2. An object Item_equal(t1.f1,...,tk.fk) is applied to deduce new
+  SARGable predicates:
+    f1=...=fk AND P(fi) => f1=...=fk AND P(fi) AND P(fj).
+  It also can give us additional index scans and can allow us to
+  improve selectivity estimates.
+
+  3. An object Item_equal(t1.f1,...,tk.fk) is used to optimize the 
+  selected execution plan for the query: if table ti is accessed 
+  before the table tj then in any predicate P in the where condition
+  the occurence of tj.fj is substituted for ti.fi. This can allow
+  an evaluation of the predicate at an earlier step.
+
+  When feature 1 is supported they say that join transitive closure 
+  is employed.
+  When feature 2 is supported they say that search argument transitive
+  closure is employed.
+  Both features are usually supported by preprocessing original query and
+  adding additional predicates.
+  We do not just add predicates, we rather dynamically replace some
+  predicates that can not be used to access tables in the investigated
+  plan for those, obtained by substitution of some fields for equal fields,
+  that can be used.     
+*/
+
+class Item_equal: public Item_bool_func
+{
+  List<Item_field> fields; /* list of equal field items                    */
+  Item *const_item;        /* optional constant item equal to fields items */
+  cmp_item *eval_item;
+  bool cond_false;
+  DTCollation cmp_collation;
+public:
+  inline Item_equal()
+    : Item_bool_func(), const_item(0), eval_item(0), cond_false(0)
+  { const_item_cache=0 ;}
+  Item_equal(Item_field *f1, Item_field *f2);
+  Item_equal(Item *c, Item_field *f);
+  Item_equal(Item_equal *item_equal);
+  inline Item* get_const() { return const_item; }
+  void add(Item *c);
+  void add(Item_field *f);
+  uint members();
+  bool contains(Field *field);
+  Item_field* get_first() { return fields.head(); }
+  void merge(Item_equal *item);
+  enum Functype functype() const { return MULT_EQUAL_FUNC; }
+  longlong val_int(); 
+  const char *func_name() const { return "multiple equal"; }
+  optimize_type select_optimize() const { return OPTIMIZE_EQUAL; }
+  void sort(Item_field_cmpfunc cmp, void *arg);
+  friend class Item_equal_iterator;
+  void fix_length_and_dec();
+  bool fix_fields(THD *thd, TABLE_LIST *tables, Item **ref);
+  void update_used_tables();
+  bool walk(Item_processor processor, byte *arg);
+  Item *transform(Item_transformer transformer, byte *arg);
+  void print(String *str);
+  CHARSET_INFO *compare_collation() 
+  { return fields.head()->collation.collation; }
+}; 
+
+class COND_EQUAL
+{
+public:
+  uint max_members;               /* max number of members the current level
+                                     list and all lower level lists */ 
+  COND_EQUAL *upper_levels;       /* multiple equalities of upper and levels */
+  List<Item_equal> current_level; /* list of multiple equalities of 
+                                     the current and level           */
+  COND_EQUAL()
+  { 
+    max_members= 0;
+    upper_levels= 0;
+  }
+};
+
+
+class Item_equal_iterator :List_iterator_fast<Item_field>
+{
+public:
+  inline Item_equal_iterator(Item_equal &item_equal) 
+    :List_iterator_fast<Item_field> (item_equal.fields)
+  {}
+  inline Item_field* operator++(int)
+  { 
+    Item_field *item= (*(List_iterator_fast<Item_field> *) this)++;
+    return  item;
+  }
+  inline void rewind(void) 
+  { 
+    List_iterator_fast<Item_field>::rewind();
+  }
+};
+
 class Item_cond_and :public Item_cond
 {
 public:
+  COND_EQUAL cond_equal;  /* contains list of Item_equal objects for 
+                             the current and level and reference
+                             to multiple equalities of upper and levels */  
   Item_cond_and() :Item_cond() {}
   Item_cond_and(Item *i1,Item *i2) :Item_cond(i1,i2) {}
   Item_cond_and(THD *thd, Item_cond_and *item) :Item_cond(thd, item) {}
diff --git a/sql/item_func.cc b/sql/item_func.cc
index 4aec7fea2d487959e749c9916832812f63afd484..fe5112cd75b4b685b844986f72a46b821c9a5433 100644
--- a/sql/item_func.cc
+++ b/sql/item_func.cc
@@ -260,6 +260,45 @@ bool Item_func::walk (Item_processor processor, byte *argument)
   return (this->*processor)(argument);
 }
 
+
+/*
+  Transform an Item_func object with a transformer callback function
+   
+  SYNOPSIS
+    transform()
+    transformer   the transformer callback function to be applied to the nodes
+                  of the tree of the object
+    argument      parameter to be passed to the transformer
+  
+  DESCRIPTION
+    The function recursively applies the transform method with the
+    same transformer to each argument the function.
+    If the call of the method for a member item returns a new item
+    the old item is substituted for a new one.
+    After this the transform method is applied to the root node
+    of the Item_func object. 
+     
+  RETURN VALUES
+    Item returned as the result of transformation of the root node 
+*/
+
+Item *Item_func::transform(Item_transformer transformer, byte *argument)
+{
+  if (arg_count)
+  {
+    Item **arg,**arg_end;
+    for (arg= args, arg_end= args+arg_count; arg != arg_end; arg++)
+    {
+      Item *new_item= (*arg)->transform(transformer, argument);
+      if (!new_item)
+	return 0;
+      *arg= new_item;
+    }
+  }
+  return (this->*transformer)(argument);
+}
+
+
 void Item_func::split_sum_func(Item **ref_pointer_array, List<Item> &fields)
 {
   Item **arg, **arg_end;
diff --git a/sql/item_func.h b/sql/item_func.h
index f1a74bdf3abe03e13e3aa96de4d25517fc746cd7..a5448f546938f4e0ac70fce7f908f8ae6c88cf7c 100644
--- a/sql/item_func.h
+++ b/sql/item_func.h
@@ -40,7 +40,8 @@ class Item_func :public Item_result_field
   enum Functype { UNKNOWN_FUNC,EQ_FUNC,EQUAL_FUNC,NE_FUNC,LT_FUNC,LE_FUNC,
 		  GE_FUNC,GT_FUNC,FT_FUNC,
 		  LIKE_FUNC,NOTLIKE_FUNC,ISNULL_FUNC,ISNOTNULL_FUNC,
-		  COND_AND_FUNC, COND_OR_FUNC, COND_XOR_FUNC, BETWEEN, IN_FUNC,
+		  COND_AND_FUNC, COND_OR_FUNC, COND_XOR_FUNC,
+                  BETWEEN, IN_FUNC, MULT_EQUAL_FUNC,
 		  INTERVAL_FUNC, ISNOTNULLTEST_FUNC,
 		  SP_EQUALS_FUNC, SP_DISJOINT_FUNC,SP_INTERSECTS_FUNC,
 		  SP_TOUCHES_FUNC,SP_CROSSES_FUNC,SP_WITHIN_FUNC,
@@ -50,7 +51,8 @@ class Item_func :public Item_result_field
                   NOT_FUNC, NOT_ALL_FUNC,
                   NOW_FUNC, TRIG_COND_FUNC,
                   GUSERVAR_FUNC};
-  enum optimize_type { OPTIMIZE_NONE,OPTIMIZE_KEY,OPTIMIZE_OP, OPTIMIZE_NULL };
+  enum optimize_type { OPTIMIZE_NONE,OPTIMIZE_KEY,OPTIMIZE_OP, OPTIMIZE_NULL,
+                       OPTIMIZE_EQUAL };
   enum Type type() const { return FUNC_ITEM; }
   virtual enum Functype functype() const   { return UNKNOWN_FUNC; }
   Item_func(void):
@@ -150,6 +152,7 @@ class Item_func :public Item_result_field
                                          bool allow_superset_comversion= FALSE);
 
   bool walk(Item_processor processor, byte *arg);
+  Item *transform(Item_transformer transformer, byte *arg);
 };
 
 
diff --git a/sql/item_row.cc b/sql/item_row.cc
index f6623e80734f665a7857a59afca02fa53a655033..8bf0d5061a7d282e597fc15a9e8f00264b1ad193 100644
--- a/sql/item_row.cc
+++ b/sql/item_row.cc
@@ -145,6 +145,18 @@ bool Item_row::walk(Item_processor processor, byte *arg)
   return (this->*processor)(arg);
 }
 
+Item *Item_row::transform(Item_transformer transformer, byte *arg)
+{
+  for (uint i= 0; i < arg_count; i++)
+  {
+    Item *new_item= items[i]->transform(transformer, arg);
+    if (!new_item)
+      return 0;
+    items[i]= new_item;
+  }
+  return (this->*transformer)(arg);
+}
+
 void Item_row::bring_value()
 {
   for (uint i= 0; i < arg_count; i++)
diff --git a/sql/item_row.h b/sql/item_row.h
index f87b4f66e806980982f4b0ac84630428bd07103f..ec5f0f1fc95ddf23764c8524fb90754b764fa2d3 100644
--- a/sql/item_row.h
+++ b/sql/item_row.h
@@ -65,6 +65,7 @@ class Item_row: public Item
   void print(String *str);
 
   bool walk(Item_processor processor, byte *arg);
+  Item *transform(Item_transformer transformer, byte *arg);
 
   uint cols() { return arg_count; }
   Item* el(uint i) { return items[i]; }
diff --git a/sql/item_strfunc.h b/sql/item_strfunc.h
index 111f0e206985db96ebdfb578b97dc74d2ef9d3f8..357ce0af45aef47b3e60856efb57500c4a83c221 100644
--- a/sql/item_strfunc.h
+++ b/sql/item_strfunc.h
@@ -428,6 +428,14 @@ class Item_func_make_set :public Item_str_func
     return item->walk(processor, arg) ||
       Item_str_func::walk(processor, arg);
   }
+  Item *transform(Item_transformer transformer, byte *arg)
+  {
+    Item *new_item= item->transform(transformer, arg);
+    if (!new_item)
+      return 0;
+    item= new_item;
+    return Item_str_func::transform(transformer, arg);
+  }
   void print(String *str);
 };
 
diff --git a/sql/opt_range.cc b/sql/opt_range.cc
index 387537e76ba115d5238b24306b25907ce4315621..1757ef7f86447f4cfb7aec65ed37ae07fe4b9fe6 100644
--- a/sql/opt_range.cc
+++ b/sql/opt_range.cc
@@ -3233,12 +3233,98 @@ QUICK_SELECT_I *TRP_ROR_UNION::make_quick(PARAM *param,
   DBUG_RETURN(quick_roru);
 }
 
-/****************************************************************************/
+
+/*
+  Build a SEL_TREE for a simple predicate
+ 
+  SYNOPSIS
+    get_func_mm_tree()
+      param       PARAM from SQL_SELECT::test_quick_select
+      cond_func   item for the predicate
+      field       field in the predicate
+      value       constant in the predicate
+      cmp_type    compare type for the field
+
+  RETURN 
+    Pointer to thre built tree
+*/
+
+static SEL_TREE *get_func_mm_tree(PARAM *param, Item_func *cond_func, 
+                                  Field *field, Item *value,
+                                  Item_result cmp_type)
+{
+  SEL_TREE *tree= 0;
+  DBUG_ENTER("get_func_mm_tree");
+
+  switch (cond_func->functype()) {
+  case Item_func::NE_FUNC:
+    tree= get_mm_parts(param, cond_func, field, Item_func::LT_FUNC,
+		       value, cmp_type);
+    if (tree)
+    {
+      tree= tree_or(param, tree, get_mm_parts(param, cond_func, field,
+					      Item_func::GT_FUNC,
+					      value, cmp_type));
+    }
+    break;
+  case Item_func::BETWEEN:
+    tree= get_mm_parts(param, cond_func, field, Item_func::GE_FUNC,
+		       cond_func->arguments()[1],cmp_type);
+    if (tree)
+    {
+      tree= tree_and(param, tree, get_mm_parts(param, cond_func, field,
+					       Item_func::LE_FUNC,
+					       cond_func->arguments()[2],
+                                               cmp_type));
+    }
+    break;
+  case Item_func::IN_FUNC:
+  {
+    Item_func_in *func=(Item_func_in*) cond_func;
+    tree= get_mm_parts(param, cond_func, field, Item_func::EQ_FUNC,
+                       func->arguments()[1], cmp_type);
+    if (tree)
+    {
+      Item **arg, **end;
+      for (arg= func->arguments()+2, end= arg+func->argument_count()-2;
+           arg < end ; arg++)
+      {
+        tree=  tree_or(param, tree, get_mm_parts(param, cond_func, field, 
+                                                 Item_func::EQ_FUNC,
+                                                 *arg,
+                                                 cmp_type));
+      }
+    }
+    break;
+  }
+  default: 
+  {
+    /* 
+       Here the function for the following predicates are processed:
+       <, <=, =, >=, >, LIKE, IS NULL, IS NOT NULL.
+       If the predicate is of the form (value op field) it is handled
+       as the equivalent predicate (field rev_op value), e.g.
+       2 <= a is handled as a >= 2.
+    */
+    Item_func::Functype func_type=
+      (value != cond_func->arguments()[0]) ? cond_func->functype() :
+        ((Item_bool_func2*) cond_func)->rev_functype();
+    tree= get_mm_parts(param, cond_func, field, func_type, value, cmp_type);
+  }
+  }
+
+  DBUG_RETURN(tree);
+
+}
+
 	/* make a select tree of all keys in condition */
 
 static SEL_TREE *get_mm_tree(PARAM *param,COND *cond)
 {
   SEL_TREE *tree=0;
+  SEL_TREE *ftree= 0;
+  Item_field *field_item= 0;
+  Item *value;
   DBUG_ENTER("get_mm_tree");
 
   if (cond->type() == Item::COND_ITEM)
@@ -3286,9 +3372,12 @@ static SEL_TREE *get_mm_tree(PARAM *param,COND *cond)
     DBUG_RETURN(new SEL_TREE(SEL_TREE::IMPOSSIBLE));
   }
 
-  table_map ref_tables=cond->used_tables();
+  table_map ref_tables= 0;
+  table_map param_comp= ~(param->prev_tables | param->read_tables |
+		          param->current_table);
   if (cond->type() != Item::FUNC_ITEM)
   {						// Should be a field
+    ref_tables= cond->used_tables();
     if ((ref_tables & param->current_table) ||
 	(ref_tables & ~(param->prev_tables | param->read_tables)))
       DBUG_RETURN(0);
@@ -3299,80 +3388,99 @@ static SEL_TREE *get_mm_tree(PARAM *param,COND *cond)
   if (cond_func->select_optimize() == Item_func::OPTIMIZE_NONE)
     DBUG_RETURN(0);				// Can't be calculated
 
-  param->cond= cond;
-
-  if (cond_func->functype() == Item_func::BETWEEN)
+  switch (cond_func->functype()) {
+  case Item_func::BETWEEN:
+    if (cond_func->arguments()[0]->type() != Item::FIELD_ITEM)
+      DBUG_RETURN(0);
+    field_item= (Item_field*) (cond_func->arguments()[0]);
+    value= NULL;
+    break;
+  case Item_func::IN_FUNC:
   {
+    Item_func_in *func=(Item_func_in*) cond_func;
+    if (func->key_item()->type() != Item::FIELD_ITEM)
+      DBUG_RETURN(0);
+    field_item= (Item_field*) (func->key_item());
+    value= NULL;
+    break;
+  }
+  case Item_func::MULT_EQUAL_FUNC:
+  {
+    Item_equal *item_equal= (Item_equal *) cond;    
+    if (!(value= item_equal->get_const()))
+      DBUG_RETURN(0);
+    Item_equal_iterator it(*item_equal);
+    ref_tables= value->used_tables();
+    while ((field_item= it++))
+    {
+      Field *field= field_item->field;
+      Item_result cmp_type= field->cmp_type();
+      if (!((ref_tables | field->table->map) & param_comp))
+      {
+        tree= get_mm_parts(param, cond, field, Item_func::EQ_FUNC,
+		           value,cmp_type);
+        ftree= !ftree ? tree : tree_and(param, ftree, tree);
+      }
+    }
+    
+    DBUG_RETURN(ftree);
+  }
+  default:
     if (cond_func->arguments()[0]->type() == Item::FIELD_ITEM)
     {
-      Field *field=((Item_field*) (cond_func->arguments()[0]))->field;
-      Item_result cmp_type=field->cmp_type();
-      DBUG_RETURN(tree_and(param,
-			   get_mm_parts(param, cond_func, field,
-					Item_func::GE_FUNC,
-					cond_func->arguments()[1], cmp_type),
-			   get_mm_parts(param, cond_func, field,
-					Item_func::LE_FUNC,
-					cond_func->arguments()[2], cmp_type)));
+      field_item= (Item_field*) (cond_func->arguments()[0]);
+      value= cond_func->arg_count > 1 ? cond_func->arguments()[1] : 0;
     }
-    DBUG_RETURN(0);
+    else if (cond_func->have_rev_func() &&
+             cond_func->arguments()[1]->type() == Item::FIELD_ITEM)
+    {
+      field_item= (Item_field*) (cond_func->arguments()[1]);
+      value= cond_func->arguments()[0];
+    }
+    else
+      DBUG_RETURN(0);
   }
-  if (cond_func->functype() == Item_func::IN_FUNC)
-  {						// COND OR
-    Item_func_in *func=(Item_func_in*) cond_func;
-    if (func->key_item()->type() == Item::FIELD_ITEM)
-    {
-      Field *field=((Item_field*) (func->key_item()))->field;
-      Item_result cmp_type=field->cmp_type();
-      tree= get_mm_parts(param,cond_func,field,Item_func::EQ_FUNC,
-			 func->arguments()[1],cmp_type);
-      if (!tree)
-	DBUG_RETURN(tree);			// Not key field
-      for (uint i=2 ; i < func->argument_count(); i++)
+
+  /* 
+     If the where condition contains a predicate (ti.field op const),
+     then not only SELL_TREE for this predicate is built, but
+     the trees for the results of substitution of ti.field for
+     each tj.field belonging to the same multiple equality as ti.field
+     are built as well.
+     E.g. for WHERE t1.a=t2.a AND t2.a > 10 
+     a SEL_TREE for t2.a > 10 will be built for quick select from t2
+     and   
+     a SEL_TREE for t1.a > 10 will be built for quick select from t1.
+  */
+     
+  for (uint i= 0; i < cond_func->arg_count; i++)
+  {
+    Item *arg= cond_func->arguments()[i];
+    if (arg != field_item)
+      ref_tables|= arg->used_tables();
+  }
+  Field *field= field_item->field;
+  Item_result cmp_type= field->cmp_type();
+  if (!((ref_tables | field->table->map) & param_comp))
+    ftree= get_func_mm_tree(param, cond_func, field, value, cmp_type);
+  Item_equal *item_equal= field_item->item_equal;
+  if (item_equal)
+  {
+    Item_equal_iterator it(*item_equal);
+    Item_field *item;
+    while ((item= it++))
+    {
+      Field *f= item->field;
+      if (field->eq(f))
+        continue;
+      if (!((ref_tables | f->table->map) & param_comp))
       {
-	SEL_TREE *new_tree=get_mm_parts(param,cond_func,field,
-					Item_func::EQ_FUNC,
-					func->arguments()[i],cmp_type);
-	tree=tree_or(param,tree,new_tree);
+        tree= get_func_mm_tree(param, cond_func, f, value, cmp_type);
+        ftree= !ftree ? tree : tree_and(param, ftree, tree);
       }
-      DBUG_RETURN(tree);
-    }
-    DBUG_RETURN(0);				// Can't optimize this IN
-  }
-
-  if (ref_tables & ~(param->prev_tables | param->read_tables |
-		     param->current_table))
-    DBUG_RETURN(0);				// Can't be calculated yet
-  if (!(ref_tables & param->current_table))
-    DBUG_RETURN(new SEL_TREE(SEL_TREE::MAYBE)); // This may be FALSE or TRUE
-
-  /* check field op const */
-  /* btw, ft_func's arguments()[0] isn't FIELD_ITEM.  SerG*/
-  if (cond_func->arguments()[0]->type() == Item::FIELD_ITEM)
-  {
-    tree= get_mm_parts(param, cond_func,
-		       ((Item_field*) (cond_func->arguments()[0]))->field,
-		       cond_func->functype(),
-		       cond_func->arg_count > 1 ? cond_func->arguments()[1] :
-		       0,
-		       ((Item_field*) (cond_func->arguments()[0]))->field->
-		       cmp_type());
-  }
-  /* check const op field */
-  if (!tree &&
-      cond_func->have_rev_func() &&
-      cond_func->arguments()[1]->type() == Item::FIELD_ITEM)
-  {
-    DBUG_RETURN(get_mm_parts(param, cond_func,
-			     ((Item_field*)
-			      (cond_func->arguments()[1]))->field,
-			     ((Item_bool_func2*) cond_func)->rev_functype(),
-			     cond_func->arguments()[0],
-			     ((Item_field*)
-			      (cond_func->arguments()[1]))->field->cmp_type()
-			     ));
+    }
   }
-  DBUG_RETURN(tree);
+  DBUG_RETURN(ftree);
 }
 
 
@@ -3381,17 +3489,10 @@ get_mm_parts(PARAM *param, COND *cond_func, Field *field,
 	     Item_func::Functype type,
 	     Item *value, Item_result cmp_type)
 {
-  bool ne_func= FALSE;
   DBUG_ENTER("get_mm_parts");
   if (field->table != param->table)
     DBUG_RETURN(0);
 
-  if (type == Item_func::NE_FUNC)
-  {
-    ne_func= TRUE;
-    type= Item_func::LT_FUNC;
-  }
-
   KEY_PART *key_part = param->key_parts;
   KEY_PART *end = param->key_parts_end;
   SEL_TREE *tree=0;
@@ -3429,15 +3530,6 @@ get_mm_parts(PARAM *param, COND *cond_func, Field *field,
     }
   }
 
-  if (ne_func)
-  {
-    SEL_TREE *tree2= get_mm_parts(param, cond_func,
-				  field, Item_func::GT_FUNC,
-                                  value, cmp_type);
-    if (tree2)
-      tree= tree_or(param,tree,tree2);
-  }
-
   DBUG_RETURN(tree);
 }
 
diff --git a/sql/opt_sum.cc b/sql/opt_sum.cc
index 2507b65111bf220085ea024a1ae64fd7958be6a6..6b966c28b1a1218f2f4c8e17b694f4433ff61524 100644
--- a/sql/opt_sum.cc
+++ b/sql/opt_sum.cc
@@ -341,6 +341,18 @@ bool simple_pred(Item_func *func_item, Item **args, bool *inv_order)
   Item *item;
   *inv_order= 0;
   switch (func_item->argument_count()) {
+  case 0:
+    /* MULT_EQUAL_FUNC */
+    {
+      Item_equal *item_equal= (Item_equal *) func_item;
+      Item_equal_iterator it(*item_equal);
+      args[0]= it++;
+      if (it++)
+        return 0;
+      if (!(args[1]= item_equal->get_const()))
+        return 0;
+    }
+    break;
   case 1:
     /* field IS NULL */
     item= func_item->arguments()[0];
@@ -481,6 +493,9 @@ static bool matching_cond(bool max_fl, TABLE_REF *ref, KEY *keyinfo,
   case Item_func::BETWEEN:
     between= 1;
     break;
+  case Item_func::MULT_EQUAL_FUNC:
+    eq_type= 1;
+    break;
   default:
     return 0;                                        // Can't optimize function
   }
diff --git a/sql/sql_list.h b/sql/sql_list.h
index a4379b74c17bde504b391754ff9673b535ec4a47..40530314893ba6ef0e7b21d395da79296097a63f 100644
--- a/sql/sql_list.h
+++ b/sql/sql_list.h
@@ -127,10 +127,12 @@ class base_list :public Sql_alloc
   void remove(list_node **prev)
   {
     list_node *node=(*prev)->next;
-    delete *prev;
-    *prev=node;
     if (!--elements)
       last= &first;
+    else if (last == &(*prev)->next)
+      last= prev;
+    delete *prev;
+    *prev=node;
   }
   inline void *pop(void)
   {
@@ -143,9 +145,36 @@ class base_list :public Sql_alloc
   }
   inline void concat(base_list *list)
   {
-    *last= list->first;
-    last= list->last;
-    elements+= list->elements;
+    if (!list->is_empty())
+    {
+      *last= list->first;
+      last= list->last;
+      elements+= list->elements;
+    }
+  }
+  inline void disjoin(base_list *list)
+  {
+    list_node **prev= &first;
+    list_node *node= first;
+    list_node *list_first= list->first;
+    elements=0;
+    while (node && node != list_first)
+    {
+      prev= &node->next;
+      node= node->next;
+      elements++;
+    }
+    *prev= *last;
+    last= prev;
+  }
+  inline void prepand(base_list *list)
+  {
+    if (!list->is_empty())
+    {
+      *list->last= first;
+      first= list->first;
+      elements+= list->elements;
+    }
   }
   inline list_node* last_node() { return *last; }
   inline list_node* first_node() { return first;}
@@ -257,6 +286,9 @@ template <class T> class List :public base_list
   inline T* head() {return (T*) base_list::head(); }
   inline T** head_ref() {return (T**) base_list::head_ref(); }
   inline T* pop()  {return (T*) base_list::pop(); }
+  inline void concat(List<T> *list) { base_list::concat(list); }
+  inline void disjoin(List<T> *list) { base_list::disjoin(list); }
+  inline void prepand(List<T> *list) { base_list::prepand(list); }
   void delete_elements(void)
   {
     list_node *element,*next;
@@ -267,7 +299,6 @@ template <class T> class List :public base_list
     }
     empty();
   }
-  inline void concat(List<T> *list) { base_list::concat(list); }
 };
 
 
@@ -278,6 +309,8 @@ template <class T> class List_iterator :public base_list_iterator
   inline T* operator++(int) { return (T*) base_list_iterator::next(); }
   inline T *replace(T *a)   { return (T*) base_list_iterator::replace(a); }
   inline T *replace(List<T> &a) { return (T*) base_list_iterator::replace(a); }
+  inline void rewind(void)  { base_list_iterator::rewind(); }
+  inline void remove()      { base_list_iterator::remove(); }
   inline void after(T *a)   { base_list_iterator::after(a); }
   inline T** ref(void)	    { return (T**) base_list_iterator::ref(); }
 };
diff --git a/sql/sql_select.cc b/sql/sql_select.cc
index ed65c8fd8fa9ef415ffb86dd4e79d4d07902f6ec..e1f15bc1c0418b67e1a03aa023f965e730b380dc 100644
--- a/sql/sql_select.cc
+++ b/sql/sql_select.cc
@@ -43,6 +43,7 @@ static bool make_join_statistics(JOIN *join,TABLE_LIST *tables,COND *conds,
 static bool update_ref_and_keys(THD *thd, DYNAMIC_ARRAY *keyuse,
 				JOIN_TAB *join_tab,
                                 uint tables, COND *conds,
+                                COND_EQUAL *cond_equal,
 				table_map table_map, SELECT_LEX *select_lex);
 static int sort_keyuse(KEYUSE *a,KEYUSE *b);
 static void set_position(JOIN *join,uint index,JOIN_TAB *table,KEYUSE *key);
@@ -90,6 +91,13 @@ static int return_zero_rows(JOIN *join, select_result *res,TABLE_LIST *tables,
 			    uint select_options, const char *info,
 			    Item *having, Procedure *proc,
 			    SELECT_LEX_UNIT *unit);
+static COND *build_equal_items(COND *cond,
+                               COND_EQUAL *inherited,
+                               List<TABLE_LIST> *join_list,
+                               COND_EQUAL **cond_equal_ref);
+static COND* substitute_for_best_equal_field(COND *cond,
+                                             COND_EQUAL *cond_equal,
+                                             void *table_join_idx);
 static COND *simplify_joins(JOIN *join, List<TABLE_LIST> *join_list,
                             COND *conds, bool top);
 static COND *optimize_cond(JOIN *join, COND *conds,
@@ -525,6 +533,40 @@ JOIN::optimize()
     }
   }
 #endif
+  SELECT_LEX *sel= thd->lex->current_select;
+  if (sel->first_cond_optimization)
+  {
+    /*
+      The following code will allocate the new items in a permanent
+      MEMROOT for prepared statements and stored procedures.
+    */
+
+    Item_arena *arena= thd->current_arena, backup;
+    if (arena->is_conventional())
+      arena= 0;                                   // For easier test
+    else
+      thd->set_n_backup_item_arena(arena, &backup);
+
+    sel->first_cond_optimization= 0;
+
+    /* Convert all outer joins to inner joins if possible */
+    conds= simplify_joins(this, join_list, conds, TRUE);
+
+    sel->prep_where= conds ? conds->copy_andor_structure(thd) : 0;
+
+    if (arena)
+      thd->restore_backup_item_arena(arena, &backup);
+  }
+
+  /* 
+    Build all multiple equality predicates and eliminate equality
+    predicates that can be inferred from these multiple equalities.
+    For each reference of a field included into a multiple equality
+    that occurs in a function set a pointer to the multiple equality
+    predicate. Substitute a constant instead of this field if the
+    multiple equality contains a constant.
+  */ 
+  conds= build_equal_items(conds, NULL, join_list, &cond_equal);
 
   conds= optimize_cond(this, conds,&cond_value);   
   if (thd->net.report_error)
@@ -618,7 +660,6 @@ JOIN::optimize()
   if (const_tables && !thd->locked_tables &&
       !(select_options & SELECT_NO_UNLOCK))
     mysql_unlock_some_tables(thd, table, const_tables);
-
   if (!conds && outer_join)
   {
     /* Handle the case where we have an OUTER JOIN without a WHERE */
@@ -635,6 +676,32 @@ JOIN::optimize()
 
   make_outerjoin_info(this);
 
+  /*
+    Among the equal fields belonging to the same multiple equality
+    choose the one that is to be retrieved first and substitute
+    all references to these in where condition for a reference for
+    the selected field.
+  */
+  if (conds)
+  {
+    conds= substitute_for_best_equal_field(conds, cond_equal, map2table);
+    conds->update_used_tables();
+  }
+  /*
+    Permorm the the optimization on fields evaluation mentioned above
+    for all on expressions.
+  */ 
+  for (JOIN_TAB *tab= join_tab + const_tables; tab < join_tab + tables ; tab++)
+  {
+    if (*tab->on_expr_ref)
+    {
+      *tab->on_expr_ref= substitute_for_best_equal_field(*tab->on_expr_ref,
+                                                         tab->cond_equal,
+                                                         map2table);
+      (*tab->on_expr_ref)->update_used_tables();
+    }
+  }
+
   if (make_join_select(this, select, conds))
   {
     zero_result_cause=
@@ -1470,8 +1537,6 @@ JOIN::exec()
 	  /*
 	    table->keyuse is set in the case there was an original WHERE clause
 	    on the table that was optimized away.
-	    table->on_expr tells us that it was a LEFT JOIN and there will be
-	    at least one row generated from the table.
 	  */
 	  if (curr_table->select_cond ||
 	      (curr_table->keyuse && !curr_table->first_inner))
@@ -1571,6 +1636,7 @@ JOIN::cleanup()
     tmp_table_param.copy_field=0;
     DBUG_RETURN(tmp_join->cleanup());
   }
+  cond_equal= 0;
 
   lock=0;                                     // It's faster to unlock later
   join_free(1);
@@ -1719,7 +1785,7 @@ Cursor::fetch(ulong num_rows)
 {
   THD *thd= join->thd;
   JOIN_TAB *join_tab= join->join_tab + join->const_tables;;
-  COND *on_expr= join_tab->on_expr;
+  COND *on_expr= *join_tab->on_expr_ref;
   COND *select_cond= join_tab->select_cond;
   READ_RECORD *info= &join_tab->read_record;
 
@@ -2081,7 +2147,8 @@ make_join_statistics(JOIN *join,TABLE_LIST *tables,COND *conds,
     s->dependent= tables->dep_tables;
     s->key_dependent= 0;
 
-    if ((s->on_expr=tables->on_expr))
+    s->on_expr_ref= &tables->on_expr;
+    if (*s->on_expr_ref)
     {
       /* s is the only inner table of an outer join */
       if (!table->file->records)
@@ -2151,7 +2218,8 @@ make_join_statistics(JOIN *join,TABLE_LIST *tables,COND *conds,
 
   if (conds || outer_join)
     if (update_ref_and_keys(join->thd, keyuse_array, stat, join->tables,
-                            conds, ~outer_join, join->select_lex))
+                            conds, join->cond_equal,
+                            ~outer_join, join->select_lex))
       DBUG_RETURN(1);
 
   /* Read tables with 0 or 1 rows (system tables) */
@@ -2308,7 +2376,7 @@ make_join_statistics(JOIN *join,TABLE_LIST *tables,COND *conds,
       SQL_SELECT *select;
       select= make_select(s->table, found_const_table_map,
 			  found_const_table_map,
-			  s->on_expr ? s->on_expr : conds,
+			  *s->on_expr_ref ? *s->on_expr_ref : conds,
 			  &error, true);
       if (!select)
         DBUG_RETURN(1);
@@ -2328,7 +2396,7 @@ make_join_statistics(JOIN *join,TABLE_LIST *tables,COND *conds,
 	join->const_table_map|= s->table->map;
 	set_position(join,const_count++,s,(KEYUSE*) 0);
 	s->type= JT_CONST;
-	if (s->on_expr)
+	if (*s->on_expr_ref)
 	{
 	  /* Generate empty row */
 	  s->info= "Impossible ON condition";
@@ -2492,6 +2560,7 @@ merge_key_fields(KEY_FIELD *start,KEY_FIELD *new_fields,KEY_FIELD *end,
     add_key_field()
     key_fields			Pointer to add key, if usable
     and_level			And level, to be stored in KEY_FIELD
+    cond                        Condition predicate
     field			Field used in comparision
     eq_func			True if we used =, <=> or IS NULL
     value			Value used for comparison with field
@@ -2507,8 +2576,8 @@ merge_key_fields(KEY_FIELD *start,KEY_FIELD *new_fields,KEY_FIELD *end,
 */
 
 static void
-add_key_field(KEY_FIELD **key_fields,uint and_level, COND *cond,
-	      Field *field,bool eq_func,Item **value, uint num_values,
+add_key_field(KEY_FIELD **key_fields, uint and_level, COND *cond,
+	      Field *field, bool eq_func, Item **value, uint num_values,
 	      table_map usable_tables)
 {
   uint exists_optimize= 0;
@@ -2617,6 +2686,57 @@ add_key_field(KEY_FIELD **key_fields,uint and_level, COND *cond,
 }
 
 
+/*
+  Add possible keys to array of possible keys originated from a simple predicate
+
+  SYNPOSIS
+    add_key_equal_fields()
+    key_fields			Pointer to add key, if usable
+    and_level			And level, to be stored in KEY_FIELD
+    cond                        Condition predicate
+    field			Field used in comparision
+    eq_func			True if we used =, <=> or IS NULL
+    value			Value used for comparison with field
+				Is NULL for BETWEEN and IN    
+    usable_tables		Tables which can be used for key optimization
+
+  NOTES
+    If field items f1 and f2 belong to the same multiple equality and
+    a key is added for f1, the the same key is added for f2.
+
+  RETURN
+    *key_fields is incremented if we stored a key in the array
+*/
+
+static void
+add_key_equal_fields(KEY_FIELD **key_fields, uint and_level,
+                     COND *cond, Item_field *field_item,
+                     bool eq_func, Item **val,
+                     uint num_values, table_map usable_tables)
+{
+  Field *field= field_item->field;
+  add_key_field(key_fields, and_level, cond, field,
+                eq_func, val, num_values, usable_tables);
+  Item_equal *item_equal= field_item->item_equal;
+  if (item_equal)
+  { 
+    /*
+      Add to the set of possible key values every substitution of
+      the field for an equal field included into item_equal
+    */
+    Item_equal_iterator it(*item_equal);
+    Item_field *item;
+    while ((item= it++))
+    {
+      if (!field->eq(item->field))
+      {
+        add_key_field(key_fields, and_level, cond, item->field,
+                      eq_func, val, num_values, usable_tables);
+      }
+    }
+  }
+}
+
 static void
 add_key_fields(JOIN_TAB *stat,KEY_FIELD **key_fields,uint *and_level,
 	       COND *cond, table_map usable_tables)
@@ -2659,16 +2779,26 @@ add_key_fields(JOIN_TAB *stat,KEY_FIELD **key_fields,uint *and_level,
   case Item_func::OPTIMIZE_NONE:
     break;
   case Item_func::OPTIMIZE_KEY:
-    // BETWEEN, IN, NOT
+  {
+    // BETWEEN, IN, NE
     if (cond_func->key_item()->real_item()->type() == Item::FIELD_ITEM &&
 	!(cond_func->used_tables() & OUTER_REF_TABLE_BIT))
-      add_key_field(key_fields,*and_level,cond_func,
-		    ((Item_field*)(cond_func->key_item()->real_item()))->field,
-                    cond_func->argument_count() == 2 &&
-                    cond_func->functype() == Item_func::IN_FUNC,
-                    cond_func->arguments()+1, cond_func->argument_count()-1,
-                    usable_tables);
+    {
+      Item **values= cond_func->arguments()+1;
+      if (cond_func->functype() == Item_func::NE_FUNC &&
+        cond_func->arguments()[1]->real_item()->type() == Item::FIELD_ITEM &&
+	     !(cond_func->arguments()[0]->used_tables() & OUTER_REF_TABLE_BIT))
+        values--;
+      add_key_equal_fields(key_fields, *and_level, cond_func,
+                           (Item_field*) (cond_func->key_item()->real_item()),
+                           cond_func->argument_count() == 2 &&
+                           cond_func->functype() == Item_func::IN_FUNC,
+                           values,
+                           cond_func->argument_count()-1,
+                           usable_tables);
+    }
     break;
+  }
   case Item_func::OPTIMIZE_OP:
   {
     bool equal_func=(cond_func->functype() == Item_func::EQ_FUNC ||
@@ -2677,21 +2807,19 @@ add_key_fields(JOIN_TAB *stat,KEY_FIELD **key_fields,uint *and_level,
     if (cond_func->arguments()[0]->real_item()->type() == Item::FIELD_ITEM &&
 	!(cond_func->arguments()[0]->used_tables() & OUTER_REF_TABLE_BIT))
     {
-      add_key_field(key_fields,*and_level,cond_func,
-		    ((Item_field*) (cond_func->arguments()[0])->real_item())
-		    ->field,
-		    equal_func,
-                    cond_func->arguments()+1, 1, usable_tables);
+      add_key_equal_fields(key_fields, *and_level, cond_func,
+	                (Item_field*) (cond_func->arguments()[0])->real_item(),
+		           equal_func,
+		           cond_func->arguments()+1, 1, usable_tables);
     }
     if (cond_func->arguments()[1]->real_item()->type() == Item::FIELD_ITEM &&
 	cond_func->functype() != Item_func::LIKE_FUNC &&
 	!(cond_func->arguments()[1]->used_tables() & OUTER_REF_TABLE_BIT))
     {
-      add_key_field(key_fields,*and_level,cond_func,
-		    ((Item_field*) (cond_func->arguments()[1])->real_item())
-		    ->field,
-		    equal_func,
-		    cond_func->arguments(),1,usable_tables);
+      add_key_equal_fields(key_fields, *and_level, cond_func, 
+                       (Item_field*) (cond_func->arguments()[1])->real_item(),
+		           equal_func,
+		           cond_func->arguments(),1,usable_tables);
     }
     break;
   }
@@ -2703,15 +2831,55 @@ add_key_fields(JOIN_TAB *stat,KEY_FIELD **key_fields,uint *and_level,
       Item *tmp=new Item_null;
       if (unlikely(!tmp))                       // Should never be true
 	return;
-      add_key_field(key_fields,*and_level,cond_func,
-		    ((Item_field*) (cond_func->arguments()[0])->real_item())
-		    ->field,
+      add_key_equal_fields(key_fields, *and_level, cond_func,
+		    (Item_field*) (cond_func->arguments()[0])->real_item(),
 		    cond_func->functype() == Item_func::ISNULL_FUNC,
 		    &tmp, 1, usable_tables);
     }
     break;
+  case Item_func::OPTIMIZE_EQUAL:
+    Item_equal *item_equal= (Item_equal *) cond;
+    Item *const_item= item_equal->get_const();
+    Item_equal_iterator it(*item_equal);
+    Item_field *item;
+    if (const_item)
+    {
+      /*
+        For each field field1 from item_equal consider the equality 
+        field1=const_item as a condition allowing an index access of the table
+        with field1 by the keys value of field1.
+      */   
+      while ((item= it++))
+      {
+        add_key_field(key_fields, *and_level, cond, item->field,
+                      TRUE, &const_item, 1, usable_tables);
+      }
+    }
+    else 
+    {
+      /*
+        Consider all pairs of different fields included into item_equal.
+        For each of them (field1, field1) consider the equality 
+        field1=field2 as a condition allowing an index access of the table
+        with field1 by the keys value of field2.
+      */   
+      Item_equal_iterator fi(*item_equal);
+      while ((item= fi++))
+      {
+        Field *field= item->field;
+        while ((item= it++))
+        {
+          if (!field->eq(item->field))
+          {
+            add_key_field(key_fields, *and_level, cond, field,
+                          TRUE, (Item **) &item, 1, usable_tables);
+          }
+        }
+        it.rewind();
+      }
+    }
+    break;
   }
-  return;
 }
 
 /*
@@ -2854,15 +3022,19 @@ sort_keyuse(KEYUSE *a,KEYUSE *b)
 
 static bool
 update_ref_and_keys(THD *thd, DYNAMIC_ARRAY *keyuse,JOIN_TAB *join_tab,
-		    uint tables, COND *cond, table_map normal_tables,
-		    SELECT_LEX *select_lex)
+		    uint tables, COND *cond, COND_EQUAL *cond_equal,
+                    table_map normal_tables, SELECT_LEX *select_lex)
 {
   uint	and_level,i,found_eq_constant;
   KEY_FIELD *key_fields, *end, *field;
+  uint m= 1;
+  
+  if (cond_equal && cond_equal->max_members)
+    m= cond_equal->max_members;
 
   if (!(key_fields=(KEY_FIELD*)
 	thd->alloc(sizeof(key_fields[0])*
-		   (thd->lex->current_select->cond_count+1)*2)))
+		   (thd->lex->current_select->cond_count+1)*2*m)))
     return TRUE; /* purecov: inspected */
   and_level= 0;
   field= end= key_fields;
@@ -2891,9 +3063,9 @@ update_ref_and_keys(THD *thd, DYNAMIC_ARRAY *keyuse,JOIN_TAB *join_tab,
       for inner tables in outer joins these keys will be taken
       into account as well.
     */ 
-    if (join_tab[i].on_expr)
+    if (*join_tab[i].on_expr_ref)
     {
-      add_key_fields(join_tab,&end,&and_level,join_tab[i].on_expr,
+      add_key_fields(join_tab,&end,&and_level,*join_tab[i].on_expr_ref,
 		     join_tab[i].table->map);
     }
     else 
@@ -4559,7 +4731,7 @@ get_best_combination(JOIN *join)
     form=join->table[tablenr]=j->table;
     used_tables|= form->map;
     form->reginfo.join_tab=j;
-    if (!j->on_expr)
+    if (!*j->on_expr_ref)
       form->reginfo.not_exists_optimize=0;	// Only with LEFT JOIN
     if (j->type == JT_CONST)
       continue;					// Handled in make_join_stat..
@@ -4814,7 +4986,7 @@ make_simple_join(JOIN *join,TABLE *tmp_table)
   join_tab->type= JT_ALL;			/* Map through all records */
   join_tab->keys.init(~0);                      /* test everything in quick */
   join_tab->info=0;
-  join_tab->on_expr=0;
+  join_tab->on_expr_ref=0;
   join_tab->last_inner= 0;
   join_tab->first_unmatched= 0;
   join_tab->ref.key = -1;
@@ -4883,7 +5055,7 @@ add_found_match_trig_cond(JOIN_TAB *tab, COND *cond, JOIN_TAB *root_tab)
     first inner table of the embedding outer join operation, if there is any,
     through the field t0->first_upper.
     The on expression for the outer join operation is attached to the
-    corresponding first inner table through the field t0->on_expr.
+    corresponding first inner table through the field t0->on_expr_ref.
     Here ti are structures of the JOIN_TAB type.
 
   EXAMPLE
@@ -4897,8 +5069,8 @@ add_found_match_trig_cond(JOIN_TAB *tab, COND *cond, JOIN_TAB *root_tab)
     is selected, the following references will be set;
     t4->last_inner=[t4], t4->first_inner=[t4], t4->first_upper=[t2]
     t2->last_inner=[t4], t2->first_inner=t3->first_inner=[t2],
-    on expression (t1.a=t2.a AND t1.b=t3.b) will be attached to t2->on_expr,
-    while t3.a=t4.a will be attached to t4->on_expr.
+    on expression (t1.a=t2.a AND t1.b=t3.b) will be attached to 
+    *t2->on_expr_ref, while t3.a=t4.a will be attached to *t4->on_expr_ref.
             
   NOTES
     The function assumes that the simplification procedure has been
@@ -4925,7 +5097,8 @@ make_outerjoin_info(JOIN *join)
         is in the query above.)
       */
       tab->last_inner= tab->first_inner= tab;
-      tab->on_expr= tbl->on_expr;
+      tab->on_expr_ref= &tbl->on_expr;
+      tab->cond_equal= tbl->cond_equal;
       if (embedding)
         tab->first_upper= embedding->nested_join->first_nested;
     }    
@@ -4939,7 +5112,8 @@ make_outerjoin_info(JOIN *join)
           Save reference to it in the nested join structure.
         */ 
         nested_join->first_nested= tab;
-        tab->on_expr= embedding->on_expr;
+        tab->on_expr_ref= &embedding->on_expr;
+        tab->cond_equal= tbl->cond_equal;
         if (embedding->embedding)
           tab->first_upper= embedding->embedding->nested_join->first_nested;
       }
@@ -4963,25 +5137,26 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond)
     table_map used_tables;
     if (cond)                /* Because of QUICK_GROUP_MIN_MAX_SELECT */
     {                        /* there may be a select without a cond. */
+    
       if (join->tables > 1)
         cond->update_used_tables();		// Tablenr may have changed
       if (join->const_tables == join->tables &&
-          join->thd->lex->current_select->master_unit() ==
-          &join->thd->lex->unit)		// not upper level SELECT
+	  join->thd->lex->current_select->master_unit() ==
+	  &join->thd->lex->unit)		// not upper level SELECT
         join->const_table_map|=RAND_TABLE_BIT;
       {						// Check const tables
         COND *const_cond=
-          make_cond_for_table(cond,join->const_table_map,(table_map) 0);
+	  make_cond_for_table(cond,join->const_table_map,(table_map) 0);
         DBUG_EXECUTE("where",print_where(const_cond,"constants"););
         for (JOIN_TAB *tab= join->join_tab+join->const_tables;
              tab < join->join_tab+join->tables ; tab++)
         {
-          if (tab->on_expr)
+          if (*tab->on_expr_ref)
           {
             JOIN_TAB *cond_tab= tab->first_inner;
-            COND *tmp= make_cond_for_table(tab->on_expr,
+            COND *tmp= make_cond_for_table(*tab->on_expr_ref,
                                            join->const_table_map,
-                                           (table_map) 0);
+                                         (  table_map) 0);
             if (!tmp)
               continue;
             tmp= new Item_func_trig_cond(tmp, &cond_tab->not_null_compl);
@@ -4989,16 +5164,16 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond)
               DBUG_RETURN(1);
             tmp->quick_fix_field();
             cond_tab->select_cond= !cond_tab->select_cond ? tmp :
-                                   new Item_cond_and(cond_tab->select_cond,tmp);
+	                            new Item_cond_and(cond_tab->select_cond,tmp);
             if (!cond_tab->select_cond)
-              DBUG_RETURN(1);
+	      DBUG_RETURN(1);
             cond_tab->select_cond->quick_fix_field();
-          }
+          }       
         }
         if (const_cond && !const_cond->val_int())
         {
-          DBUG_PRINT("info",("Found impossible WHERE condition"));
-          DBUG_RETURN(1);			  // Impossible const condition
+	  DBUG_PRINT("info",("Found impossible WHERE condition"));
+	  DBUG_RETURN(1);				// Impossible const condition
         }
       }
     }
@@ -5129,7 +5304,7 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond)
 	  {
 	    /* Join with outer join condition */
 	    COND *orig_cond=sel->cond;
-	    sel->cond= and_conds(sel->cond, tab->on_expr);
+	    sel->cond= and_conds(sel->cond, *tab->on_expr_ref);
 	    if (sel->cond && !sel->cond->fixed)
 	      sel->cond->fix_fields(join->thd, 0, &sel->cond);
 	    if (sel->test_quick_select(join->thd, tab->keys,
@@ -5144,7 +5319,7 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond)
 		we have to check isn't it only "impossible ON" instead
 	      */
               sel->cond=orig_cond;
-              if (!tab->on_expr ||
+              if (!*tab->on_expr_ref ||
                   sel->test_quick_select(join->thd, tab->keys,
                                          used_tables & ~ current_map,
                                          (join->select_options &
@@ -5210,7 +5385,7 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond)
           Table tab is the last inner table of an outer join.
           An on expression is always attached to it.
 	*/     
-        COND *on_expr= first_inner_tab->on_expr;
+        COND *on_expr= *first_inner_tab->on_expr_ref;
 
         table_map used_tables= join->const_table_map |
 		               OUTER_REF_TABLE_BIT | RAND_TABLE_BIT;
@@ -5724,7 +5899,7 @@ remove_const(JOIN *join,ORDER *first_order, COND *cond, bool *simple_order)
   table_map not_const_tables= ~join->const_table_map;
   table_map ref;
   prev_ptr= &first_order;
-  *simple_order= join->join_tab[join->const_tables].on_expr ? 0 : 1;
+  *simple_order= *join->join_tab[join->const_tables].on_expr_ref ? 0 : 1;
 
   /* NOTE: A variable of not_const_tables ^ first_table; breaks gcc 2.7 */
 
@@ -5848,6 +6023,783 @@ template class List<Item_func_match>;
 template class List_iterator<Item_func_match>;
 #endif
 
+
+/* 
+  Find the multiple equality predicate containing a field
+ 
+  SYNOPSIS
+    find_item_equal()
+    cond_equal          multiple equalities to search in
+    field               field to look for
+    inherited_fl  :out  set up to TRUE iff multiple equality is found
+                        on upper levels (not on current level of cond_equal) 
+
+  DESCRIPTION
+    The function retrieves the multiple equalities accessed through
+    the con_equal structure from current level and up looking for
+    an equality containing field. It stops retrieval as soon as the equality
+    is found and set up inherited_fl to TRUE if it's found on upper levels.
+
+  RETURN
+    Item_equal for the found multiple equality predicate if a success;
+    NULL - otherwise.
+*/
+
+Item_equal *find_item_equal(COND_EQUAL *cond_equal, Field *field,
+                            bool *inherited_fl)
+{
+  Item_equal *item= 0;
+  bool in_upper_level= FALSE;
+  while (cond_equal)
+  {
+    List_iterator_fast<Item_equal> li(cond_equal->current_level);
+    while ((item= li++))
+    {
+      if (item->contains(field))
+        goto finish;
+    }
+    in_upper_level= TRUE;
+    cond_equal= cond_equal->upper_levels;
+  }
+  in_upper_level= FALSE;
+finish:
+  *inherited_fl= in_upper_level;
+  return item;
+}
+
+  
+/* 
+  Check whether an item is a simple equality predicate and if so
+  create/find a multiple equality for this predicate
+
+  SYNOPSIS
+    check_equality()
+    item       item to check
+    cond_equal multiple equalities that must hold together with the predicate
+
+  DESCRIPTION
+    This function first checks whether an item is a simple equality i.e.
+    the one that equates a field with another field or a constant
+    (item=constant_item or item=field_item).
+    If this is the case the function looks a for a multiple equality
+    in the lists referenced directly or indirectly by cond_equal inferring
+    the given simple equality. If it doesn't find any, it builds a multiple
+    equality that covers the predicate, i.e. the predicate can be inferred
+    from it.
+    The built multiple equality could be obtained in such a way:
+    create a binary  multiple equality equivalent to the predicate, then
+    merge it, if possible, with one of old multiple equalities.
+    This guarantees that the set of multiple equalities covering equality
+    predicates will
+    be minimal.
+
+  EXAMPLE
+    For the where condition
+    WHERE a=b AND b=c AND
+          (b=2 OR f=e)
+    the check_equality will be called for the following equality
+    predicates a=b, b=c, b=2 and f=e.
+    For a=b it will be called with *cond_equal=(0,[]) and will transform
+    *cond_equal into (0,[Item_equal(a,b)]). 
+    For b=c it will be called with *cond_equal=(0,[Item_equal(a,b)])
+    and will transform *cond_equal into CE=(0,[Item_equal(a,b,c)]).
+    For b=2 it will be called with *cond_equal=(ptr(CE),[])
+    and will transform *cond_equal into (ptr(CE,[Item_equal(2,a,b,c)]).
+    For f=e it will be called with *cond_equal=(ptr(CE), [])
+    and will transform *cond_equal into (ptr(CE,[Item_equal(f,e)]).
+
+  NOTES
+    Now only fields that have the same type defintions (verified by
+    the Field::eq_def method) are placed to the same multiple equalities.
+    Because of this some equality predicates are not eliminated and
+    can be used in the constant propagation procedure.
+    We could weeken the equlity test as soon as at least one of the 
+    equal fields is to be equal to a constant. It would require a 
+    more complicated implementation: we would have to store, in
+    general case, its own constant for each fields from the multiple
+    equality. But at the same time it would allow us to get rid
+    of constant propagation completely: it would be done by the call
+    to build_equal_items_for_cond.
+    
+  IMPLEMENTATION
+    The implementation does not follow exactly the above rules to
+    build a new multiple equality for the equality predicate.
+    If it processes the equality of the form field1=field2, it
+    looks for multiple equalities me1 containig field1 and me2 containing
+    field2. If only one of them is found the fuction expands it with
+    the lacking field. If multiple equalities for both fields are
+    found they are merged. If both searches fail a new multiple equality
+    containing just field1 and field2 is added to the existing
+    multiple equalities.
+    If the function processes the predicate of the form field1=const,
+    it looks for a multiple equality containing field1. If found, the 
+    function checks the constant of the multiple equality. If the value
+    is unknown, it is setup to const. Otherwise the value is compared with
+    const and the evaluation of the equality predicate is performed.
+    When expanding/merging equality predicates from the upper levels
+    the function first copies them for the current level. It looks
+    acceptable, as this happens rarely. The implementation without
+    copying would be much more complicated.
+
+  RETURN
+    TRUE  - if the predicate is a simple equality predicate
+    FALSE - otherwise
+*/
+
+static bool check_equality(Item *item, COND_EQUAL *cond_equal)
+{
+  if (item->type() == Item::FUNC_ITEM &&
+         ((Item_func*) item)->functype() == Item_func::EQ_FUNC)
+  {
+    Item *left_item= ((Item_func*) item)->arguments()[0];
+    Item *right_item= ((Item_func*) item)->arguments()[1];
+    if (left_item->type() == Item::FIELD_ITEM &&
+        right_item->type() == Item::FIELD_ITEM)
+    {
+      /* The predicate the form field1=field2 is processed */
+
+      Field *left_field= ((Item_field*) left_item)->field;
+      Field *right_field= ((Item_field*) right_item)->field;
+
+      if (!left_field->eq_def(right_field))
+        return FALSE;
+
+      if (left_field->eq(right_field))  /* f = f */
+        return TRUE;
+      
+      /* Search for multiple equalities containing field1 and/or field2 */
+      bool left_copyfl, right_copyfl;
+      Item_equal *left_item_equal=
+                 find_item_equal(cond_equal, left_field, &left_copyfl);
+      Item_equal *right_item_equal= 
+                 find_item_equal(cond_equal, right_field, &right_copyfl);
+
+      if (left_item_equal && left_item_equal == right_item_equal)
+      {
+        /* 
+           The equality predicate is inference of one of the existing
+           multiple equalities, i.e the condition is already covered
+           by upper level equalities
+        */
+          return TRUE;
+      }
+      
+      /* Copy the found multiple equalities at the current level if needed */
+      if (left_copyfl)
+      {
+        /* left_item_equal of an upper level contains left_item */
+        left_item_equal= new Item_equal(left_item_equal);
+        cond_equal->current_level.push_back(left_item_equal);
+      }
+      if (right_copyfl)
+      {
+        /* right_item_equal of an upper level contains right_item */
+        right_item_equal= new Item_equal(right_item_equal);
+        cond_equal->current_level.push_back(right_item_equal);
+      }
+
+      if (left_item_equal)
+      { 
+        /* left item was found in the current or one of the upper levels */
+        if (! right_item_equal)
+          left_item_equal->add((Item_field *) right_item);
+        else
+        {
+          /* Merge two multiple equalities forming a new one */
+          left_item_equal->merge(right_item_equal);
+          /* Remove the merged multiple equality from the list */
+          List_iterator<Item_equal> li(cond_equal->current_level);
+          while ((li++) != right_item_equal);
+          li.remove();
+        }
+      }
+      else
+      { 
+        /* left item was not found neither the current nor in upper levels  */
+         if (right_item_equal)
+           right_item_equal->add((Item_field *) left_item);
+         else 
+         {
+           /* None of the fields was found in multiple equalities */
+           Item_equal *item= new Item_equal((Item_field *) left_item,
+                                            (Item_field *) right_item);
+           cond_equal->current_level.push_back(item);
+         }
+      }
+      return TRUE;
+    }
+
+    {
+      /* The predicate of the form field=const/const=field is processed */
+      Item *const_item= 0;
+      Item_field *field_item= 0;
+      if (left_item->type() == Item::FIELD_ITEM && 
+          right_item->const_item())
+      {
+        field_item= (Item_field*) left_item;
+        const_item= right_item;
+      }
+      else if (right_item->type() == Item::FIELD_ITEM && 
+               left_item->const_item())
+      {
+        field_item= (Item_field*) right_item;
+        const_item= left_item;
+      }
+      if (const_item &&
+          field_item->result_type() == const_item->result_type())
+      {
+        bool copyfl;
+
+        if (field_item->result_type() == STRING_RESULT &&
+              ((Field_str *) field_item->field)->charset() !=
+               ((Item_cond *) item)->compare_collation())
+          return FALSE;
+
+        Item_equal *item_equal = find_item_equal(cond_equal,
+                                                 field_item->field, &copyfl);
+        if (copyfl)
+        {
+          item_equal= new Item_equal(item_equal);
+          cond_equal->current_level.push_back(item_equal);
+        }
+        if (item_equal)
+        {
+          /* 
+            The flag cond_false will be set to 1 after this, if item_equal
+            already contains a constant and its value is  not equal to
+            the value of const_item.
+          */
+          item_equal->add(const_item);
+        }
+        else
+        {
+          item_equal= new Item_equal(const_item, field_item);
+          cond_equal->current_level.push_back(item_equal);
+        }
+        return TRUE;
+      }
+    }
+  }
+  return FALSE;
+}
+
+/* 
+  Replace all equality predicates in a condition by multiple equality items
+
+  SYNOPSIS
+    build_equal_items_for_cond()
+    cond       condition(expression) where to make replacement
+    inherited  path to all inherited multiple equality items
+
+  DESCRIPTION
+    At each 'and' level the function detects items for equality predicates
+    and replaced them by a set of multiple equality items of class Item_equal,
+    taking into account inherited equalities from upper levels. 
+    If an equality predicate is used not in a conjunction it's just
+    replaced by a multiple equality predicate.
+    For each 'and' level the function set a pointer to the inherited
+    multiple equalities in the cond_equal field of the associated
+    object of the type Item_cond_and.   
+    The function also traverses the cond tree and and for each field reference
+    sets a pointer to the multiple equality item containing the field, if there
+    is any. If this multiple equality equates fields to a constant the
+    function replace the field reference by the constant.
+    The function also determines the maximum number of members in 
+    equality lists of each Item_cond_and object assigning it to
+    cond_equal->max_members of this object and updating accordingly
+    the upper levels COND_EQUAL structures.  
+
+  NOTES
+    Multiple equality predicate =(f1,..fn) is equivalent to the conjuction of
+    f1=f2, .., fn-1=fn. It substitutes any inference from these
+    equality predicates that is equivalent to the conjunction.
+    Thus, =(a1,a2,a3) can substitute for ((a1=a3) AND (a2=a3) AND (a2=a1)) as
+    it is equivalent to ((a1=a2) AND (a2=a3)).
+    The function always makes a substitution of all equality predicates occured
+    in a conjuction for a minimal set of multiple equality predicates.
+    This set can be considered as a canonical representation of the
+    sub-conjunction of the equality predicates.
+    E.g. (t1.a=t2.b AND t2.b>5 AND t1.a=t3.c) is replaced by 
+    (=(t1.a,t2.b,t3.c) AND t2.b>5), not by
+    (=(t1.a,t2.b) AND =(t1.a,t3.c) AND t2.b>5);
+    while (t1.a=t2.b AND t2.b>5 AND t3.c=t4.d) is replaced by
+    (=(t1.a,t2.b) AND =(t3.c=t4.d) AND t2.b>5),
+    but if additionally =(t4.d,t2.b) is inherited, it
+    will be replaced by (=(t1.a,t2.b,t3.c,t4.d) AND t2.b>5)
+
+  IMPLEMENTATION
+    The function performs the substitution in a recursive descent by
+    the condtion tree, passing to the next AND level a chain of multiple
+    equality predicates which have been built at the upper levels.
+    The Item_equal items built at the level are attached to other 
+    non-equality conjucts as a sublist. The pointer to the inherited
+    multiple equalities is saved in the and condition object (Item_cond_and).
+    This chain allows us for any field reference occurence easyly to find a 
+    multiple equality that must be held for this occurence.
+    For each AND level we do the following:
+    - scan it for all equality predicate (=) items
+    - join them into disjoint Item_equal() groups
+    - process the included OR conditions recursively to do the same for 
+      lower AND levels. 
+    We need to do things in this order as lower AND levels need to know about
+    all possible Item_equal objects in upper levels.
+
+  RETURN
+    pointer to the transformed condition
+*/
+
+static COND *build_equal_items_for_cond(COND *cond,
+                                        COND_EQUAL *inherited)
+{
+  Item_equal *item_equal;
+  uint members;
+  COND_EQUAL cond_equal;
+  cond_equal.upper_levels= inherited;
+
+  if (cond->type() == Item::COND_ITEM)
+  {
+    bool and_level= ((Item_cond*) cond)->functype() ==
+      Item_func::COND_AND_FUNC;
+    List<Item> *args= ((Item_cond*) cond)->argument_list();
+    
+    List_iterator<Item> li(*args);
+    Item *item;
+
+    if (and_level)
+    {
+      /*
+         Retrieve all conjucts of this level detecting the equality
+         that are subject to substitution by multiple equality items and
+         removing each such predicate from the conjunction after having 
+         found/created a multiple equality whose inference the predicate is.
+     */      
+      while ((item= li++))
+      {
+        if (check_equality(item, &cond_equal))
+          li.remove();
+      }
+
+      List_iterator_fast<Item_equal> it(cond_equal.current_level);
+      while ((item_equal= it++))
+      {
+        item_equal->fix_length_and_dec();
+        item_equal->update_used_tables();
+        members= item_equal->members();
+        if (cond_equal.max_members < members)
+          cond_equal.max_members= members; 
+      }
+      members= cond_equal.max_members;
+      if (inherited && inherited->max_members < members)
+      {
+        do
+        {
+	  inherited->max_members= members;
+          inherited= inherited->upper_levels;
+        }
+        while (inherited);
+      }
+
+      ((Item_cond_and*)cond)->cond_equal= cond_equal;
+      inherited= &(((Item_cond_and*)cond)->cond_equal);
+    }
+    /*
+       Make replacement of equality predicates for lower levels
+       of the condition expression.
+    */
+    li.rewind();
+    while((item= li++))
+    { 
+      Item *new_item;
+      if ((new_item = build_equal_items_for_cond(item, inherited))!= item)
+      {
+        /* This replacement happens only for standalone equalities */
+        li.replace(new_item);
+      }
+    }
+    if (and_level)
+      args->concat((List<Item> *)&cond_equal.current_level);
+  }
+  else if (cond->type() == Item::FUNC_ITEM)
+  {
+    /*
+      If an equality predicate forms the whole and level,
+      we call it standalone equality and it's processed here.
+      E.g. in the following where condition
+      WHERE a=5 AND (b=5 or a=c)
+      (b=5) and (a=c) are standalone equalities.
+      In general we can't leave alone standalone eqalities:
+      for WHERE a=b AND c=d AND (b=c OR d=5)
+      b=c is replaced by =(a,b,c,d).  
+     */
+    if (check_equality(cond, &cond_equal) &&
+        (item_equal= cond_equal.current_level.pop()))
+    {
+      item_equal->fix_length_and_dec();
+      item_equal->update_used_tables();
+      return item_equal;
+    }
+    /* 
+      For each field reference in cond, not from equalitym predicates,
+      set a pointer to the multiple equality if belongs to (if there is any)
+    */ 
+    cond= cond->transform(&Item::equal_fields_propagator,
+                            (byte *) inherited);
+    cond->update_used_tables();
+  }
+  return cond;
+}
+
+/* 
+  Build multiple equalities for a condition and all on expressions that
+  inherit these multiple equalities
+
+  SYNOPSIS
+    build_equal_items()
+    cond                condition to build the multiple equalities for
+    inherited           path to all inherited multiple equality items
+    join_list           list of join tables to which the condition refers to
+    cond_equal_ref :out pointer to the structure to place built equalities in
+
+  DESCRIPTION
+    The function first applies the build_equal_items_for_cond function
+    to build all multiple equalities for condition cond utilizing equalities
+    referred through the parameter inherited. The extended set of
+    equalities is returned in the structure referred by the cond_equal_ref
+    parameter. After this the function calls itself recursively for
+    all on expressions whose direct references can be found in join_list
+    and who inherit directly the multiple equalities just having built.
+
+  NOTES
+    The on expression used in an outer join operation inherits all equalities
+    from the on expression of the embedding join, if there is any, or 
+    otherwise - from the where condition.
+    This fact is not obvious, but presumably can be proved.
+    Consider the following query:
+      SELECT * FROM (t1,t2) LEFT JOIN (t3,t4) ON t1.a=t3.a AND t2.a=t4.a
+        WHERE t1.a=t2.a;
+    If the on expression in the query inherits =(t1.a,t2.a), then we
+    can build the multiple equality =(t1.a,t2.a,t3.a,t4.a) that infers
+    the equality t3.a=t4.a. Although the on expression
+    t1.a=t3.a AND t2.a=t4.a AND t3.a=t4.a is not equivalent to the one
+    in the query the latter can be replaced by the former: the new query
+    will return the same result set as the original one.
+
+    Interesting that multiple equality =(t1.a,t2.a,t3.a,t4.a) allows us
+    to use t1.a=t3.a AND t3.a=t4.a under the on condition:
+      SELECT * FROM (t1,t2) LEFT JOIN (t3,t4) ON t1.a=t3.a AND t3.a=t4.a
+        WHERE t1.a=t2.a
+    This query equivalent to:
+      SELECT * FROM (t1 LEFT JOIN (t3,t4) ON t1.a=t3.a AND t3.a=t4.a),t2
+        WHERE t1.a=t2.a
+    Similarly the original query can be rewritten to the query:
+      SELECT * FROM (t1,t2) LEFT JOIN (t3,t4) ON t2.a=t4.a AND t3.a=t4.a
+        WHERE t1.a=t2.a
+    that is equivalent to:   
+      SELECT * FROM (t2 LEFT JOIN (t3,t4)ON t2.a=t4.a AND t3.a=t4.a), t1
+        WHERE t1.a=t2.a
+    Thus, applying equalities from the where condition we basically
+    can get more freedom in performing join operations.
+    Althogh we don't use this property now, it probably makes sense to use 
+    it in the future.    
+         
+  RETURN
+    pointer to the transformed condition containing multiple equalities
+*/
+   
+static COND *build_equal_items(COND *cond,
+                               COND_EQUAL *inherited,
+                               List<TABLE_LIST> *join_list,
+                               COND_EQUAL **cond_equal_ref)
+{
+  COND_EQUAL *cond_equal= 0;
+
+  if (cond) 
+  {
+    cond= build_equal_items_for_cond(cond, inherited);
+    cond->update_used_tables();
+    if (cond->type() == Item::COND_ITEM &&
+        ((Item_cond*) cond)->functype() == Item_func::COND_AND_FUNC)
+      cond_equal= &((Item_cond_and*) cond)->cond_equal;
+    else if (cond->type() == Item::FUNC_ITEM &&
+             ((Item_cond*) cond)->functype() == Item_func::MULT_EQUAL_FUNC)
+    {
+      cond_equal= new COND_EQUAL;
+      cond_equal->current_level.push_back((Item_equal *) cond);
+    }
+  }
+  if (cond_equal)
+  {
+    cond_equal->upper_levels= inherited;
+    inherited= cond_equal;
+  }
+  *cond_equal_ref= cond_equal;
+
+  if (join_list)
+  {
+    TABLE_LIST *table;
+    List_iterator<TABLE_LIST> li(*join_list);
+
+    while ((table= li++))
+    {
+      if (table->on_expr)
+      {
+        List<TABLE_LIST> *join_list= table->nested_join ?
+	                             &table->nested_join->join_list : NULL;
+        table->on_expr= build_equal_items(table->on_expr,
+                                          inherited,
+                                          join_list,
+                                          &table->cond_equal);
+      }
+    }
+  }
+
+  return cond;
+}    
+
+/* 
+  Compare field items by table order in the execution plan
+ 
+  SYNOPSIS
+    compare_fields_by_table_order()
+    field1          first field item to compare
+    field2          second field item to compare
+    table_join_idx  index to tables determining table order    
+
+  DESCRIPTION
+    field1 considered as better than field2 if the table containing
+    field1 is accessed earlier than the table containing field2.   
+    The function finds out what of two fields is better according
+    this criteria.
+
+  RETURN
+     1, if field1 is better than field2 
+    -1, if field2 is better than field1
+     0, otherwise
+*/
+
+static int compare_fields_by_table_order(Item_field *field1,
+                                  Item_field *field2,
+                                  void *table_join_idx)
+{
+  int cmp= 0;
+  bool outer_ref= 0;
+  if (field2->used_tables() & OUTER_REF_TABLE_BIT)
+  {  
+    outer_ref= 1;
+    cmp= -1;
+  }
+  if (field2->used_tables() & OUTER_REF_TABLE_BIT)
+  {
+    outer_ref= 1;
+    cmp++;
+  }
+  if (outer_ref)
+    return cmp;
+  JOIN_TAB **idx= (JOIN_TAB **) table_join_idx;
+  cmp= idx[field2->field->table->tablenr]-idx[field1->field->table->tablenr];
+  return cmp < 0 ? -1 : (cmp ? 1 : 0);
+}
+
+
+/* 
+  Generate minimal set of simple equalities equivalent to a multiple equality
+ 
+  SYNOPSIS
+    eliminate_item_equal()
+    cond            condition to add the generated equality to
+    upper_levels    structure to access multiple equality of upper levels
+    item_equal      multiple equality to generate simple equality from     
+
+  DESCRIPTION
+    The function retrieves the fields of the multiple equality item
+    item_equal and  for each field f:
+    - if item_equal contains const it generates the equality f=const_item;
+    - otherwise, if f is not the first field, generates the equality
+      f=item_equal->get_first().
+    All generated equality are added to the cond conjunction.
+
+  NOTES
+    Before generating an equality function checks that it has not
+    been generated for multiple equalies of the upper levels.
+    E.g. for the following where condition
+    WHERE a=5 AND ((a=b AND b=c) OR  c>4)
+    the upper level AND condition will contain =(5,a),
+    while the lower level AND condition will contain =(5,a,b,c).
+    When splitting =(5,a,b,c) into a separate equality predicates
+    we should omit 5=a, as we have it already in the upper level.
+    The following where condition gives us a more complicated case:
+    WHERE t1.a=t2.b AND t3.c=t4.d AND (t2.b=t3.c OR t4.e>5 ...) AND ...
+    Given the tables are accessed in the order t1->t2->t3->t4 for
+    the selected query execution plan the lower level multiple
+    equality =(t1.a,t2.b,t3.c,t4.d) formally  should be converted to
+    t1.a=t2.b AND t1.a=t3.c AND t1.a=t4.d. But t1.a=t2.a will be
+    generated for the upper level. Also t3.c=t4.d will be generated there.
+    So only t1.a=t3.c should be left in the lower level.
+    If cond is equal to 0, then not more then one equality is generated
+    and a pointer to it is returned as the result of the function.
+
+  RETURN
+    The condition with generated simple equalities or
+    a pointer to the simple generated equality, if success.
+    0, otherwise.
+*/
+
+static Item *eliminate_item_equal(COND *cond, COND_EQUAL *upper_levels,
+                                  Item_equal *item_equal)
+{
+  List<Item> eq_list;
+  Item_func_eq *eq_item= 0;
+  if (((Item *) item_equal)->const_item() && !item_equal->val_int())
+  {
+    cond= new Item_int((char*) "FALSE",0,1);
+    return cond;
+  } 
+  Item *item_const= item_equal->get_const();
+  Item_equal_iterator it(*item_equal);
+  Item *head;
+  if (item_const)
+    head= item_const;
+  else
+  {
+    head= item_equal->get_first();
+    it++;
+  }
+  Item_field *item_field;
+  while ((item_field= it++))
+  {
+    Item_equal *upper= item_field->find_item_equal(upper_levels);
+    Item_field *item= item_field;
+    if (upper)
+    { 
+      if (item_const && upper->get_const())
+        item= 0;
+      else
+      {
+        Item_equal_iterator li(*item_equal);
+        while ((item= li++) != item_field)
+        {
+          if (item->find_item_equal(upper_levels) == upper)
+            break;
+        }
+      }
+    }
+    if (item == item_field)
+    {
+      if (eq_item)
+        eq_list.push_back(eq_item);
+      eq_item= new Item_func_eq(item_field, head);
+      if (!eq_item)
+        return 0;
+      eq_item->set_cmp_func();
+      eq_item->quick_fix_field();
+   }
+  }
+
+  if (!cond && !eq_list.head())
+    return eq_item;
+
+  eq_list.push_back(eq_item);
+  if (!cond)
+    cond= new Item_cond_and(eq_list);
+  else
+    ((Item_cond *) cond)->add_at_head(&eq_list);
+
+  cond->quick_fix_field();
+  cond->update_used_tables();
+   
+  return cond;
+}
+
+
+/* 
+  Substitute every field reference in a condition by the best equal field 
+  and eliminate all multiplle equality predicates
+ 
+  SYNOPSIS
+    substitute_for_best_equal_field()
+    cond            condition to process
+    cond_equal      multiple equalities to take into consideration
+    table_join_idx  index to tables determining field preference
+
+  DESCRIPTION
+    The function retrieves the cond condition and for each encountered
+    multiple equality predicate it sorts the field references in it
+    according to the order of tables specified by the table_join_idx
+    parameter. Then it eliminates the multiple equality predicate it
+    replacing it by the conjunction of simple equality predicates 
+    equating every field from the multiple equality to the first
+    field in it, or to the constant, if there is any.
+    After this the function retrieves all other conjuncted
+    predicates substitute every field reference by the field reference
+    to the first equal field or equal constant if there are any.
+ 
+  NOTES
+    At the first glance full sort of fields in multiple equality
+    seems to be an overkill. Yet it's not the case due to possible
+    new fields in multiple equality item of lower levels. We want
+    the order in them to comply with the order of upper levels.
+
+  RETURN
+    The transformed condition
+*/
+
+static COND* substitute_for_best_equal_field(COND *cond,
+                                             COND_EQUAL *cond_equal,
+                                             void *table_join_idx)
+{
+  Item_equal *item_equal;
+
+  if (cond->type() == Item::COND_ITEM)
+  {
+    List<Item> *cond_list= ((Item_cond*) cond)->argument_list();
+
+    bool and_level= ((Item_cond*) cond)->functype() ==
+                      Item_func::COND_AND_FUNC;
+    if (and_level)
+    {
+      cond_equal= &((Item_cond_and *) cond)->cond_equal;
+      cond_list->disjoin((List<Item> *) &cond_equal->current_level);
+
+      List_iterator_fast<Item_equal> it(cond_equal->current_level);      
+      while ((item_equal= it++))
+      {
+        item_equal->sort(&compare_fields_by_table_order, table_join_idx);
+      }
+    }
+    
+    List_iterator<Item> li(*cond_list);
+    Item *item;
+    while ((item= li++))
+    {
+      Item *new_item =substitute_for_best_equal_field(item, cond_equal,
+                                                        table_join_idx);
+      if (new_item != item)
+        li.replace(new_item);
+   }
+
+    if (and_level)
+    {
+      List_iterator_fast<Item_equal> it(cond_equal->current_level);
+      while ((item_equal= it++))
+      {
+        eliminate_item_equal(cond, cond_equal->upper_levels, item_equal);
+      }
+    }
+  }
+  else if (cond->type() == Item::FUNC_ITEM && 
+           ((Item_cond*) cond)->functype() == Item_func::MULT_EQUAL_FUNC)
+  {
+    item_equal= (Item_equal *) cond;
+    item_equal->sort(&compare_fields_by_table_order, table_join_idx);
+    if (cond_equal && cond_equal->current_level.head() == item_equal)
+      cond_equal= 0;
+    return eliminate_item_equal(0, cond_equal, item_equal);
+  }
+  else
+    cond->walk(&Item::replace_equal_field_processor, 0);
+  return cond;
+}
+
 /*
   change field = field to field = const for each found field = const in the
   and_level
@@ -6255,41 +7207,16 @@ simplify_joins(JOIN *join, List<TABLE_LIST> *join_list, COND *conds, bool top)
       li.replace(nested_join->join_list);
     }
   }
-  DBUG_RETURN(conds);         
+  DBUG_RETURN(conds); 
 }
-
-     
+        
 static COND *
 optimize_cond(JOIN *join, COND *conds, Item::cond_result *cond_value)
 {
   THD *thd= join->thd;
-  SELECT_LEX *select= thd->lex->current_select;
+  SELECT_LEX *select= thd->lex->current_select;    
   DBUG_ENTER("optimize_cond");
 
-  if (select->first_cond_optimization)
-  {
-    /*
-      The following code will allocate the new items in a permanent
-      MEMROOT for prepared statements and stored procedures.
-    */
-
-    Item_arena *arena= thd->current_arena, backup;
-    if (arena->is_conventional())
-      arena= 0;                                   // For easier test
-    else
-      thd->set_n_backup_item_arena(arena, &backup);
-
-    select->first_cond_optimization= 0;
-
-    /* Convert all outer joins to inner joins if possible */
-    conds= simplify_joins(join, join->join_list, conds, TRUE);
-
-    select->prep_where= conds ? conds->copy_andor_structure(thd) : 0;
-
-    if (arena)
-      thd->restore_backup_item_arena(arena, &backup);
-  }
-
   if (!conds)
   {
     *cond_value= Item::COND_TRUE;
@@ -6431,6 +7358,11 @@ remove_eq_conds(THD *thd, COND *cond, Item::cond_result *cond_value)
 	}
       }
     }
+    if (cond->const_item())
+    {
+      *cond_value= eval_const_cond(cond) ? Item::COND_TRUE : Item::COND_FALSE;
+      return (COND*) 0;
+    }
   }
   else if (cond->const_item())
   {
@@ -8134,9 +9066,9 @@ join_read_const_table(JOIN_TAB *tab, POSITION *pos)
       table->file->extra(HA_EXTRA_NO_KEYREAD);
     }
   }
-  if (tab->on_expr && !table->null_row)
+  if (*tab->on_expr_ref && !table->null_row)
   {
-    if ((table->null_row= test(tab->on_expr->val_int() == 0)))
+    if ((table->null_row= test((*tab->on_expr_ref)->val_int() == 0)))
       mark_as_null_row(table);  
   }
   if (!table->null_row)
@@ -9942,6 +10874,7 @@ join_init_cache(THD *thd,JOIN_TAB *tables,uint table_count)
   }
   if (!(cache->field=(CACHE_FIELD*)
 	sql_alloc(sizeof(CACHE_FIELD)*(cache->fields+table_count*2)+(blobs+1)*
+
 		  sizeof(CACHE_FIELD*))))
   {
     my_free((gptr) cache->buff,MYF(0));		/* purecov: inspected */
diff --git a/sql/sql_select.h b/sql/sql_select.h
index 95b3b5e493e50f6eb22eb083e628cc0c72b72ebd..d489e911363e330bdc6263702a23e986ac774dc3 100644
--- a/sql/sql_select.h
+++ b/sql/sql_select.h
@@ -91,7 +91,8 @@ typedef struct st_join_table {
   SQL_SELECT	*select;
   COND		*select_cond;
   QUICK_SELECT_I *quick;
-  Item		*on_expr;       /* associated on expression                  */
+  Item	       **on_expr_ref;   /* pointer to the associated on expression   */
+  COND_EQUAL    *cond_equal;    /* multiple equalities for the on expression */
   st_join_table *first_inner;   /* first inner table for including outerjoin */
   bool           found;         /* true after all matches or null complement */
   bool           not_null_compl;/* true before null complement is added      */
@@ -222,6 +223,7 @@ class JOIN :public Sql_alloc
   Item *conds_history;                    // store WHERE for explain
   TABLE_LIST *tables_list;           //hold 'tables' parameter of mysql_select
   List<TABLE_LIST> *join_list;       // list of joined tables in reverse order
+  COND_EQUAL *cond_equal;
   SQL_SELECT *select;                //created in optimisation phase
   JOIN_TAB *return_tab;              //used only for outer joins
   Item **ref_pointer_array; //used pointer reference for this select
@@ -284,6 +286,7 @@ class JOIN :public Sql_alloc
     ref_pointer_array_size= 0;
     zero_result_cause= 0;
     optimized= 0;
+    cond_equal= 0;
 
     fields_list= fields_arg;
     bzero((char*) &keyuse,sizeof(keyuse));
diff --git a/sql/table.h b/sql/table.h
index 35f6c6803fbf574b396c24654b7f575ff274e030..605cd516d9cded6ba335831ed6d454b1d881bc9a 100644
--- a/sql/table.h
+++ b/sql/table.h
@@ -21,6 +21,7 @@ class Item;				/* Needed by ORDER */
 class GRANT_TABLE;
 class st_select_lex_unit;
 class st_select_lex;
+class COND_EQUAL;
 
 /* Order clause list element */
 
@@ -209,6 +210,7 @@ typedef struct st_table_list
   char		*db, *alias, *real_name;
   char          *option;                /* Used by cache index  */
   Item		*on_expr;		/* Used with outer join */
+  COND_EQUAL    *cond_equal;            /* Used with outer join */
   struct st_table_list *natural_join;	/* natural join on this table*/
   /* ... join ... USE INDEX ... IGNORE INDEX */
   List<String>	*use_index, *ignore_index;