← Back to team overview

maria-developers team mailing list archive

PLEASE REVIEWA: pre-requisite patch for the data type plugins, also fixing a number of optimizer bugs

 

  Hello,

While working on pluggable data types, I noticed a few problems
related to optimizer:

MDEV-6950 Bad results with joins comparing DATE/DATETIME and INT/DECIMAL/DOUBLE/ENUM/VARCHAR columns
MDEV-6971 Bad results with joins comparing TIME and DOUBLE/DECIMAL columns
MDEV-6978 Bad results with joins comparing case insensitive VARCHAR/ENUM/SET expression to a _bin ENUM column
MDEV-6982 LEFT JOIN table elimination is not always used when it could
MDEV-6989 BINARY and COLLATE xxx_bin comparisions are not used for optimization in some cases
MDEV-6990 GROUP_MIN_MAX optimization is not applied in some cases when it could
MDEV-6991 GROUP_MIN_MAX optimization is erroneously applied in some cases

In some cases wrong result sets are returned.
So it would be nice to have this fixed in 10.0.

I made these changes as a standalone patch.
Please review.

I also noticed some other optimizer bugs but not sure how to fix them properly:
MDEV-6986 Bad results with join comparing INT and VARCHAR columns
MDEV-6969 Bad results with joins comparing DOUBLE to BIGINT/DECIMAL columns
MDEV-6993 Bad results with join comparing DECIMAL and ENUM/SET columns
Igor or SergeyP can possibly fix them on top of my changes.


Also, I found some other problems (not related to optimizer):
MDEV-6973 XOR aggregates argument collations
MDEV-7005 NULLIF does not work as documented
Not sure which version to fix them in.
Please suggest.


Thanks.
=== modified file 'mysql-test/r/group_min_max.result'
--- mysql-test/r/group_min_max.result	2014-08-07 16:06:56 +0000
+++ mysql-test/r/group_min_max.result	2014-11-01 21:20:24 +0000
@@ -3686,3 +3686,43 @@ a	b
 3	2
 3	3
 drop table t1;
+#
+# Start of 10.0 tests
+#
+#
+# MDEV-6991 GROUP_MIN_MAX optimization is erroneously applied in some cases
+#
+CREATE TABLE t1 (id INT NOT NULL, a VARCHAR(20)) ENGINE=MyISAM;
+INSERT INTO t1 VALUES (1,'2001-01-01');
+INSERT INTO t1 VALUES (1,'2001-01-02');
+INSERT INTO t1 VALUES (1,'2001-01-03');
+INSERT INTO t1 VALUES (1,' 2001-01-04');
+INSERT INTO t1 VALUES (2,'2001-01-01');
+INSERT INTO t1 VALUES (2,'2001-01-02');
+INSERT INTO t1 VALUES (2,'2001-01-03');
+INSERT INTO t1 VALUES (2,' 2001-01-04');
+INSERT INTO t1 VALUES (3,'2001-01-01');
+INSERT INTO t1 VALUES (3,'2001-01-02');
+INSERT INTO t1 VALUES (3,'2001-01-03');
+INSERT INTO t1 VALUES (3,' 2001-01-04');
+INSERT INTO t1 VALUES (4,'2001-01-01');
+INSERT INTO t1 VALUES (4,'2001-01-02');
+INSERT INTO t1 VALUES (4,'2001-01-03');
+INSERT INTO t1 VALUES (4,' 2001-01-04');
+SELECT id,MIN(a),MAX(a) FROM t1 WHERE a>=DATE'2001-01-04' GROUP BY id;
+id	MIN(a)	MAX(a)
+1	 2001-01-04	 2001-01-04
+2	 2001-01-04	 2001-01-04
+3	 2001-01-04	 2001-01-04
+4	 2001-01-04	 2001-01-04
+ALTER TABLE t1 ADD KEY(id,a);
+SELECT id,MIN(a),MAX(a) FROM t1 WHERE a>=DATE'2001-01-04' GROUP BY id;
+id	MIN(a)	MAX(a)
+1	 2001-01-04	 2001-01-04
+2	 2001-01-04	 2001-01-04
+3	 2001-01-04	 2001-01-04
+4	 2001-01-04	 2001-01-04
+DROP TABLE t1;
+#
+# End of 10.0 tests
+#

=== modified file 'mysql-test/r/range.result'
--- mysql-test/r/range.result	2014-06-05 22:07:27 +0000
+++ mysql-test/r/range.result	2014-11-01 21:20:19 +0000
@@ -618,10 +618,10 @@ id	select_type	table	type	possible_keys
 1	SIMPLE	t1	ref	a	a	11	const	2	Using index condition
 explain select * from t1 where a=binary 'aaa';
 id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
-1	SIMPLE	t1	range	a	a	11	NULL	2	Using index condition
+1	SIMPLE	t1	ref	a	a	11	const	2	Using index condition
 explain select * from t1 where a='aaa' collate latin1_bin;
 id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
-1	SIMPLE	t1	range	a	a	11	NULL	2	Using index condition
+1	SIMPLE	t1	ref	a	a	11	const	2	Using index condition
 explain select * from t1 where a='aaa' collate latin1_german1_ci;
 id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
 1	SIMPLE	t1	ALL	a	NULL	NULL	NULL	9	Using where

=== modified file 'mysql-test/r/range_mrr_icp.result'
--- mysql-test/r/range_mrr_icp.result	2014-06-05 22:07:27 +0000
+++ mysql-test/r/range_mrr_icp.result	2014-11-01 21:20:19 +0000
@@ -620,10 +620,10 @@ id	select_type	table	type	possible_keys
 1	SIMPLE	t1	ref	a	a	11	const	2	Using index condition
 explain select * from t1 where a=binary 'aaa';
 id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
-1	SIMPLE	t1	range	a	a	11	NULL	2	Using index condition; Rowid-ordered scan
+1	SIMPLE	t1	ref	a	a	11	const	2	Using index condition
 explain select * from t1 where a='aaa' collate latin1_bin;
 id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
-1	SIMPLE	t1	range	a	a	11	NULL	2	Using index condition; Rowid-ordered scan
+1	SIMPLE	t1	ref	a	a	11	const	2	Using index condition
 explain select * from t1 where a='aaa' collate latin1_german1_ci;
 id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
 1	SIMPLE	t1	ALL	a	NULL	NULL	NULL	9	Using where

=== modified file 'mysql-test/r/type_date.result'
--- mysql-test/r/type_date.result	2014-05-09 10:35:11 +0000
+++ mysql-test/r/type_date.result	2014-11-01 21:20:23 +0000
@@ -438,3 +438,281 @@ id	select_type	table	type	possible_keys
 select 1 from t1 as t1_0 inner join t1 as t2 on (t1_0.a <=> now()) join t1 on 1;
 1
 drop table t1;
+#
+# Start of 10.0 tests
+#
+#
+# MDEV-6982 LEFT JOIN table elimination is not always used when it could
+#
+CREATE TABLE t1 (c1 INT PRIMARY KEY);
+INSERT INTO t1 VALUES (20010101),(20010102);
+CREATE TABLE t2 (c1 DATE);
+INSERT INTO t2 VALUES ('2001-01-01'),('2001-01-02');
+SELECT t1.* FROM t1 JOIN t2 USING(c1);
+c1
+20010101
+20010102
+SELECT t1.* FROM t1 LEFT JOIN t2 USING(c1);
+c1
+20010101
+20010102
+ALTER TABLE t2 ADD PRIMARY KEY(c1);
+SELECT t1.* FROM t1 JOIN t2 USING(c1);
+c1
+20010101
+20010102
+SELECT t1.* FROM t1 LEFT JOIN t2 USING(c1);
+c1
+20010101
+20010102
+# Ok to eliminate t2
+EXPLAIN SELECT t1.* FROM t1 LEFT JOIN t2 USING(c1);
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	SIMPLE	t1	index	NULL	PRIMARY	4	NULL	2	Using index
+DROP TABLE t1,t2;
+CREATE TABLE t1 (c1 DOUBLE PRIMARY KEY);
+INSERT INTO t1 VALUES (20010101),(20010102);
+CREATE TABLE t2 (c1 DATE);
+INSERT INTO t2 VALUES ('2001-01-01'),('2001-01-02');
+SELECT t1.* FROM t1 JOIN t2 USING(c1);
+c1
+20010101
+20010102
+SELECT t1.* FROM t1 LEFT JOIN t2 USING(c1);
+c1
+20010101
+20010102
+ALTER TABLE t2 ADD PRIMARY KEY(c1);
+SELECT t1.* FROM t1 JOIN t2 USING(c1);
+c1
+20010101
+20010102
+SELECT t1.* FROM t1 LEFT JOIN t2 USING(c1);
+c1
+20010101
+20010102
+# Ok to eliminate t2
+EXPLAIN SELECT t1.* FROM t1 LEFT JOIN t2 USING(c1);
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	SIMPLE	t1	index	NULL	PRIMARY	8	NULL	2	Using index
+DROP TABLE t1,t2;
+CREATE TABLE t1 (c1 DECIMAL(20,0) PRIMARY KEY);
+INSERT INTO t1 VALUES (20010101),(20010102);
+CREATE TABLE t2 (c1 DATE);
+INSERT INTO t2 VALUES ('2001-01-01'),('2001-01-02');
+SELECT t1.* FROM t1 JOIN t2 USING(c1);
+c1
+20010101
+20010102
+SELECT t1.* FROM t1 LEFT JOIN t2 USING(c1);
+c1
+20010101
+20010102
+ALTER TABLE t2 ADD PRIMARY KEY(c1);
+SELECT t1.* FROM t1 JOIN t2 USING(c1);
+c1
+20010101
+20010102
+SELECT t1.* FROM t1 LEFT JOIN t2 USING(c1);
+c1
+20010101
+20010102
+# Ok to eliminate t2
+EXPLAIN SELECT t1.* FROM t1 LEFT JOIN t2 USING(c1);
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	SIMPLE	t1	index	NULL	PRIMARY	9	NULL	2	Using index
+DROP TABLE t1,t2;
+CREATE TABLE t1 (c1 CHAR(20) PRIMARY KEY);
+INSERT INTO t1 VALUES ('2001-01-01'),('2001/01/02');
+CREATE TABLE t2 (c1 DATE);
+INSERT INTO t2 VALUES ('2001-01-01'),('2001-01-02');
+SELECT t1.* FROM t1 JOIN t2 USING(c1);
+c1
+2001-01-01
+2001/01/02
+SELECT t1.* FROM t1 LEFT JOIN t2 USING(c1);
+c1
+2001-01-01
+2001/01/02
+ALTER TABLE t2 ADD PRIMARY KEY(c1);
+SELECT t1.* FROM t1 JOIN t2 USING(c1);
+c1
+2001-01-01
+2001/01/02
+SELECT t1.* FROM t1 LEFT JOIN t2 USING(c1);
+c1
+2001-01-01
+2001/01/02
+# Ok to eliminate t2
+EXPLAIN SELECT t1.* FROM t1 LEFT JOIN t2 USING(c1);
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	SIMPLE	t1	index	NULL	PRIMARY	20	NULL	2	Using index
+DROP TABLE t1,t2;
+CREATE TABLE t1 (c1 ENUM('2001-01-01','2001/01/02') PRIMARY KEY);
+INSERT INTO t1 VALUES ('2001-01-01'),('2001/01/02');
+CREATE TABLE t2 (c1 DATE);
+INSERT INTO t2 VALUES ('2001-01-01'),('2001-01-02');
+SELECT t1.* FROM t1 JOIN t2 USING(c1);
+c1
+2001-01-01
+2001/01/02
+SELECT t1.* FROM t1 LEFT JOIN t2 USING(c1);
+c1
+2001-01-01
+2001/01/02
+ALTER TABLE t2 ADD PRIMARY KEY(c1);
+SELECT t1.* FROM t1 JOIN t2 USING(c1);
+c1
+2001-01-01
+2001/01/02
+SELECT t1.* FROM t1 LEFT JOIN t2 USING(c1);
+c1
+2001-01-01
+2001/01/02
+# Ok to eliminate t2
+EXPLAIN SELECT t1.* FROM t1 LEFT JOIN t2 USING(c1);
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	SIMPLE	t1	index	NULL	PRIMARY	1	NULL	2	Using index
+DROP TABLE t1,t2;
+CREATE TABLE t1 (c1 SET('2001-01-01','2001/01/02') PRIMARY KEY);
+INSERT INTO t1 VALUES ('2001-01-01'),('2001/01/02');
+CREATE TABLE t2 (c1 DATE);
+INSERT INTO t2 VALUES ('2001-01-01'),('2001-01-02');
+SELECT t1.* FROM t1 JOIN t2 USING(c1);
+c1
+2001-01-01
+2001/01/02
+SELECT t1.* FROM t1 LEFT JOIN t2 USING(c1);
+c1
+2001-01-01
+2001/01/02
+ALTER TABLE t2 ADD PRIMARY KEY(c1);
+SELECT t1.* FROM t1 JOIN t2 USING(c1);
+c1
+2001-01-01
+2001/01/02
+SELECT t1.* FROM t1 LEFT JOIN t2 USING(c1);
+c1
+2001-01-01
+2001/01/02
+# Ok to eliminate t2
+EXPLAIN SELECT t1.* FROM t1 LEFT JOIN t2 USING(c1);
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	SIMPLE	t1	index	NULL	PRIMARY	1	NULL	2	Using index
+DROP TABLE t1,t2;
+CREATE TABLE t1 (c1 DATE PRIMARY KEY);
+INSERT INTO t1 VALUES ('2001-01-01'),('2001-01-02');
+CREATE TABLE t2 (c1 DATE);
+INSERT INTO t2 VALUES ('2001-01-01'),('2001-01-02');
+SELECT t1.* FROM t1 JOIN t2 USING(c1);
+c1
+2001-01-01
+2001-01-02
+SELECT t1.* FROM t1 LEFT JOIN t2 USING(c1);
+c1
+2001-01-01
+2001-01-02
+ALTER TABLE t2 ADD PRIMARY KEY(c1);
+SELECT t1.* FROM t1 JOIN t2 USING(c1);
+c1
+2001-01-01
+2001-01-02
+SELECT t1.* FROM t1 LEFT JOIN t2 USING(c1);
+c1
+2001-01-01
+2001-01-02
+# Ok to eliminate t2
+EXPLAIN SELECT t1.* FROM t1 LEFT JOIN t2 USING(c1);
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	SIMPLE	t1	index	NULL	PRIMARY	3	NULL	2	Using index
+DROP TABLE t1,t2;
+CREATE TABLE t1 (c1 DATETIME PRIMARY KEY);
+INSERT INTO t1 VALUES ('2001-01-01 00:00:00'),('2001-01-02 00:00:00');
+CREATE TABLE t2 (c1 DATE);
+INSERT INTO t2 VALUES ('2001-01-01'),('2001-01-02');
+SELECT t1.* FROM t1 JOIN t2 USING(c1);
+c1
+2001-01-01 00:00:00
+2001-01-02 00:00:00
+SELECT t1.* FROM t1 LEFT JOIN t2 USING(c1);
+c1
+2001-01-01 00:00:00
+2001-01-02 00:00:00
+ALTER TABLE t2 ADD PRIMARY KEY(c1);
+SELECT t1.* FROM t1 JOIN t2 USING(c1);
+c1
+2001-01-01 00:00:00
+2001-01-02 00:00:00
+SELECT t1.* FROM t1 LEFT JOIN t2 USING(c1);
+c1
+2001-01-01 00:00:00
+2001-01-02 00:00:00
+# Ok to eliminate t2
+EXPLAIN SELECT t1.* FROM t1 LEFT JOIN t2 USING(c1);
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	SIMPLE	t1	index	NULL	PRIMARY	8	NULL	2	Using index
+DROP TABLE t1,t2;
+SET timestamp=UNIX_TIMESTAMP('2001-01-01 00:00:00');
+CREATE TABLE t1 (c1 TIME PRIMARY KEY);
+INSERT INTO t1 VALUES ('00:00:00'),('24:00:00');
+CREATE TABLE t2 (c1 DATE);
+INSERT INTO t2 VALUES ('2001-01-01'),('2001-01-02');
+SELECT t1.* FROM t1 JOIN t2 USING(c1);
+c1
+00:00:00
+24:00:00
+SELECT t1.* FROM t1 LEFT JOIN t2 USING(c1);
+c1
+00:00:00
+24:00:00
+ALTER TABLE t2 ADD PRIMARY KEY(c1);
+SELECT t1.* FROM t1 JOIN t2 USING(c1);
+c1
+00:00:00
+24:00:00
+SELECT t1.* FROM t1 LEFT JOIN t2 USING(c1);
+c1
+00:00:00
+24:00:00
+# Ok to eliminate t2
+EXPLAIN SELECT t1.* FROM t1 LEFT JOIN t2 USING(c1);
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	SIMPLE	t1	index	NULL	PRIMARY	3	NULL	2	Using index
+DROP TABLE t1,t2;
+SET timestamp=DEFAULT;
+#
+# MDEV-6990 GROUP_MIN_MAX optimization is not applied in some cases when it could
+#
+CREATE TABLE t1 (id INT NOT NULL, a DATE, KEY(id,a)) ENGINE=MyISAM;
+INSERT INTO t1 VALUES (1,'2001-01-01');
+INSERT INTO t1 VALUES (1,'2001-01-02');
+INSERT INTO t1 VALUES (1,'2001-01-03');
+INSERT INTO t1 VALUES (1,'2001-01-04');
+INSERT INTO t1 VALUES (2,'2001-01-01');
+INSERT INTO t1 VALUES (2,'2001-01-02');
+INSERT INTO t1 VALUES (2,'2001-01-03');
+INSERT INTO t1 VALUES (2,'2001-01-04');
+INSERT INTO t1 VALUES (3,'2001-01-01');
+INSERT INTO t1 VALUES (3,'2001-01-02');
+INSERT INTO t1 VALUES (3,'2001-01-03');
+INSERT INTO t1 VALUES (3,'2001-01-04');
+INSERT INTO t1 VALUES (4,'2001-01-01');
+INSERT INTO t1 VALUES (4,'2001-01-02');
+INSERT INTO t1 VALUES (4,'2001-01-03');
+INSERT INTO t1 VALUES (4,'2001-01-04');
+EXPLAIN SELECT id,MIN(a),MAX(a) FROM t1 WHERE a>='2001-01-04' GROUP BY id;
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	SIMPLE	t1	range	NULL	id	8	NULL	9	Using where; Using index for group-by
+EXPLAIN SELECT id,MIN(a),MAX(a) FROM t1 WHERE a>=20010104 GROUP BY id;
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	SIMPLE	t1	range	NULL	id	8	NULL	9	Using where; Using index for group-by
+EXPLAIN SELECT id,MIN(a),MAX(a) FROM t1 WHERE a>=20010104.0 GROUP BY id;
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	SIMPLE	t1	range	NULL	id	8	NULL	9	Using where; Using index for group-by
+EXPLAIN SELECT id,MIN(a),MAX(a) FROM t1 WHERE a>=20010104e0 GROUP BY id;
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	SIMPLE	t1	range	NULL	id	8	NULL	9	Using where; Using index for group-by
+DROP TABLE t1;
+#
+# End of 10.0 tests
+#

=== modified file 'mysql-test/r/type_enum.result'
--- mysql-test/r/type_enum.result	2013-09-14 01:09:36 +0000
+++ mysql-test/r/type_enum.result	2014-11-03 09:00:12 +0000
@@ -1869,3 +1869,259 @@ AVG(f1)
 1.5000
 drop table t1;
 End of 5.3 tests
+#
+# Start of 10.0 tests
+#
+#
+# MDEV-6950 Bad results with joins comparing DATE/DATETIME and INT/ENUM/VARCHAR columns
+#
+CREATE TABLE t1 (c1 DATE PRIMARY KEY);
+INSERT INTO t1 VALUES ('2001-01-01');
+CREATE TABLE t2 (c1 ENUM('2001-01-01','2001/01/01'));
+INSERT INTO t2 VALUES ('2001-01-01');
+INSERT INTO t2 VALUES ('2001/01/01');
+SELECT t1.* FROM t1,t2 WHERE t1.c1=t2.c1;
+c1
+2001-01-01
+2001-01-01
+SELECT t1.* FROM t1 LEFT JOIN t2 ON t1.c1=t2.c1;
+c1
+2001-01-01
+2001-01-01
+ALTER TABLE t2 ADD PRIMARY KEY(c1);
+SELECT t1.* FROM t1,t2 WHERE t1.c1=t2.c1;
+c1
+2001-01-01
+2001-01-01
+EXPLAIN SELECT t1.* FROM t1,t2 WHERE t1.c1=t2.c1;
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	SIMPLE	t1	system	PRIMARY	NULL	NULL	NULL	1	
+1	SIMPLE	t2	index	PRIMARY	PRIMARY	1	NULL	2	Using where; Using index
+SELECT t1.* FROM t1 LEFT JOIN t2 USING (c1);
+c1
+2001-01-01
+2001-01-01
+# t2 should NOT be eliminated
+EXPLAIN SELECT t1.* FROM t1 LEFT JOIN t2 USING (c1);
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	SIMPLE	t1	system	NULL	NULL	NULL	NULL	1	
+1	SIMPLE	t2	index	PRIMARY	PRIMARY	1	NULL	2	Using where; Using index
+DROP TABLE t1, t2;
+#
+# MDEV-6978 Bad results with join comparing case insensitive VARCHAR/ENUM/SET expression to a _bin ENUM column
+#
+CREATE TABLE t1 (c1 ENUM('a') CHARACTER SET latin1 PRIMARY KEY);
+INSERT INTO t1 VALUES ('a');
+CREATE TABLE t2 (c1 ENUM('a','A') CHARACTER SET latin1 COLLATE latin1_bin);
+INSERT INTO t2 VALUES ('a'),('A');
+SELECT t1.* FROM t1 JOIN t2 ON t1.c1 COLLATE latin1_swedish_ci=t2.c1;
+c1
+a
+a
+SELECT t1.* FROM t1 LEFT JOIN t2 ON t1.c1 COLLATE latin1_swedish_ci=t2.c1;
+c1
+a
+a
+ALTER TABLE t2 ADD PRIMARY KEY(c1);
+SELECT t1.* FROM t1 JOIN t2 ON t1.c1 COLLATE latin1_swedish_ci=t2.c1;
+c1
+a
+a
+SELECT t1.* FROM t1 LEFT JOIN t2 ON t1.c1 COLLATE latin1_swedish_ci=t2.c1;
+c1
+a
+a
+# t2 should NOT be eliminated
+EXPLAIN SELECT t1.* FROM t1 LEFT JOIN t2 ON t1.c1 COLLATE latin1_swedish_ci=t2.c1;
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	SIMPLE	t1	system	NULL	NULL	NULL	NULL	1	
+1	SIMPLE	t2	index	PRIMARY	PRIMARY	1	NULL	2	Using where; Using index
+DROP TABLE IF EXISTS t1,t2;
+CREATE TABLE t1 (c1 SET('a') CHARACTER SET latin1 PRIMARY KEY);
+INSERT INTO t1 VALUES ('a');
+CREATE TABLE t2 (c1 ENUM('a','A') CHARACTER SET latin1 COLLATE latin1_bin);
+INSERT INTO t2 VALUES ('a'),('A');
+SELECT t1.* FROM t1 JOIN t2 ON t1.c1 COLLATE latin1_swedish_ci=t2.c1;
+c1
+a
+a
+SELECT t1.* FROM t1 LEFT JOIN t2 ON t1.c1 COLLATE latin1_swedish_ci=t2.c1;
+c1
+a
+a
+ALTER TABLE t2 ADD PRIMARY KEY(c1);
+SELECT t1.* FROM t1 JOIN t2 ON t1.c1 COLLATE latin1_swedish_ci=t2.c1;
+c1
+a
+a
+SELECT t1.* FROM t1 LEFT JOIN t2 ON t1.c1 COLLATE latin1_swedish_ci=t2.c1;
+c1
+a
+a
+# t2 should NOT be eliminated
+EXPLAIN SELECT t1.* FROM t1 LEFT JOIN t2 ON t1.c1 COLLATE latin1_swedish_ci=t2.c1;
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	SIMPLE	t1	system	NULL	NULL	NULL	NULL	1	
+1	SIMPLE	t2	index	PRIMARY	PRIMARY	1	NULL	2	Using where; Using index
+DROP TABLE IF EXISTS t1,t2;
+CREATE TABLE t1 (c1 VARCHAR(10) CHARACTER SET latin1 PRIMARY KEY);
+INSERT INTO t1 VALUES ('a');
+CREATE TABLE t2 (c1 ENUM('a','A') CHARACTER SET latin1 COLLATE latin1_bin);
+INSERT INTO t2 VALUES ('a'),('A');
+SELECT t1.* FROM t1 JOIN t2 ON t1.c1 COLLATE latin1_swedish_ci=t2.c1;
+c1
+a
+a
+SELECT t1.* FROM t1 LEFT JOIN t2 ON t1.c1 COLLATE latin1_swedish_ci=t2.c1;
+c1
+a
+a
+ALTER TABLE t2 ADD PRIMARY KEY(c1);
+SELECT t1.* FROM t1 JOIN t2 ON t1.c1 COLLATE latin1_swedish_ci=t2.c1;
+c1
+a
+a
+SELECT t1.* FROM t1 LEFT JOIN t2 ON t1.c1 COLLATE latin1_swedish_ci=t2.c1;
+c1
+a
+a
+# t2 should NOT be eliminated
+EXPLAIN SELECT t1.* FROM t1 LEFT JOIN t2 ON t1.c1 COLLATE latin1_swedish_ci=t2.c1;
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	SIMPLE	t1	system	NULL	NULL	NULL	NULL	1	
+1	SIMPLE	t2	index	PRIMARY	PRIMARY	1	NULL	2	Using where; Using index
+DROP TABLE IF EXISTS t1,t2;
+#
+# MDEV-6982 LEFT JOIN table elimination is not always used when it could
+#
+CREATE TABLE t1 (c1 INT PRIMARY KEY);
+INSERT INTO t1 VALUES (1),(2);
+CREATE TABLE t2 (c1 ENUM('a','b'));
+INSERT INTO t2 VALUES ('a'),('b');
+SELECT t1.* FROM t1 JOIN t2 USING (c1);
+c1
+1
+2
+SELECT t1.* FROM t1 LEFT JOIN t2 USING (c1);
+c1
+1
+2
+ALTER TABLE t2 ADD PRIMARY KEY(c1);
+SELECT t1.* FROM t1 JOIN t2 USING (c1);
+c1
+1
+2
+SELECT t1.* FROM t1 LEFT JOIN t2 USING (c1);
+c1
+1
+2
+# Ok to eliminated t2
+EXPLAIN SELECT t1.* FROM t1 LEFT JOIN t2 USING (c1);
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	SIMPLE	t1	index	NULL	PRIMARY	4	NULL	2	Using index
+DROP TABLE t1,t2;
+CREATE TABLE t1 (c1 DOUBLE PRIMARY KEY);
+INSERT INTO t1 VALUES (1),(2);
+CREATE TABLE t2 (c1 ENUM('a','b'));
+INSERT INTO t2 VALUES ('a'),('b');
+SELECT t1.* FROM t1 JOIN t2 USING (c1);
+c1
+1
+2
+SELECT t1.* FROM t1 LEFT JOIN t2 USING (c1);
+c1
+1
+2
+ALTER TABLE t2 ADD PRIMARY KEY(c1);
+SELECT t1.* FROM t1 JOIN t2 USING (c1);
+c1
+1
+2
+SELECT t1.* FROM t1 LEFT JOIN t2 USING (c1);
+c1
+1
+2
+# Ok to eliminated t2
+EXPLAIN SELECT t1.* FROM t1 LEFT JOIN t2 USING (c1);
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	SIMPLE	t1	index	NULL	PRIMARY	8	NULL	2	Using index
+DROP TABLE t1,t2;
+CREATE TABLE t1 (c1 DECIMAL(10,1) PRIMARY KEY);
+INSERT INTO t1 VALUES (1),(2);
+CREATE TABLE t2 (c1 ENUM('a','b'));
+INSERT INTO t2 VALUES ('a'),('b');
+SELECT t1.* FROM t1 JOIN t2 USING (c1);
+c1
+1.0
+2.0
+SELECT t1.* FROM t1 LEFT JOIN t2 USING (c1);
+c1
+1.0
+2.0
+ALTER TABLE t2 ADD PRIMARY KEY(c1);
+SELECT t1.* FROM t1 LEFT JOIN t2 USING (c1);
+c1
+1.0
+2.0
+# Ok to eliminated t2
+EXPLAIN SELECT t1.* FROM t1 LEFT JOIN t2 USING (c1);
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	SIMPLE	t1	index	NULL	PRIMARY	5	NULL	2	Using index
+DROP TABLE t1,t2;
+#
+# MDEV-6991 GROUP_MIN_MAX optimization is erroneously applied in some cases
+#
+CREATE TABLE t1 (id INT NOT NULL, a ENUM('04','03','02','01')) ENGINE=MyISAM;
+INSERT INTO t1 VALUES (1,'01');
+INSERT INTO t1 VALUES (1,'02');
+INSERT INTO t1 VALUES (1,'03');
+INSERT INTO t1 VALUES (1,'04');
+INSERT INTO t1 VALUES (2,'01');
+INSERT INTO t1 VALUES (2,'02');
+INSERT INTO t1 VALUES (2,'03');
+INSERT INTO t1 VALUES (2,'04');
+INSERT INTO t1 VALUES (3,'01');
+INSERT INTO t1 VALUES (3,'02');
+INSERT INTO t1 VALUES (3,'03');
+INSERT INTO t1 VALUES (3,'04');
+INSERT INTO t1 VALUES (4,'01');
+INSERT INTO t1 VALUES (4,'02');
+INSERT INTO t1 VALUES (4,'03');
+INSERT INTO t1 VALUES (4,'04');
+SELECT id,MIN(a),MAX(a) FROM t1 WHERE a>='02' GROUP BY id;
+id	MIN(a)	MAX(a)
+1	02	04
+2	02	04
+3	02	04
+4	02	04
+SELECT id,MIN(a),MAX(a) FROM t1 WHERE a<=3 GROUP BY id;
+id	MIN(a)	MAX(a)
+1	02	04
+2	02	04
+3	02	04
+4	02	04
+ALTER TABLE t1 ADD KEY(id,a);
+SELECT id,MIN(a),MAX(a) FROM t1 WHERE a>='02' GROUP BY id;
+id	MIN(a)	MAX(a)
+1	02	04
+2	02	04
+3	02	04
+4	02	04
+# Should NOT use group_min_max optimization
+EXPLAIN SELECT id,MIN(a),MAX(a) FROM t1 WHERE a>='02' GROUP BY id;
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	SIMPLE	t1	index	NULL	id	6	NULL	16	Using where; Using index
+SELECT id,MIN(a),MAX(a) FROM t1 WHERE a<=3 GROUP BY id;
+id	MIN(a)	MAX(a)
+1	02	04
+2	02	04
+3	02	04
+4	02	04
+# Should NOT use group_min_max optimization
+EXPLAIN SELECT id,MIN(a),MAX(a) FROM t1 WHERE a<=3 GROUP BY id;
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	SIMPLE	t1	index	NULL	id	6	NULL	16	Using where; Using index
+DROP TABLE t1;
+#
+# End of 10.0 tests
+#

=== modified file 'mysql-test/r/type_float.result'
--- mysql-test/r/type_float.result	2014-03-14 07:31:16 +0000
+++ mysql-test/r/type_float.result	2014-11-03 09:10:53 +0000
@@ -465,3 +465,70 @@ SELECT * FROM t1 ORDER BY f;
 1
 2
 DROP TABLE t1;
+#
+# Start of 10.0 tests
+#
+#
+# MDEV-6950 Bad results with joins comparing DATE/DATETIME and INT/DECIMAL/DOUBLE/ENUM/VARCHAR columns
+#
+CREATE TABLE t1 (a DATETIME PRIMARY KEY);
+INSERT INTO t1 VALUES ('1999-01-01 00:00:00');
+CREATE TABLE t2 (a DOUBLE);
+INSERT INTO t2 VALUES (19990101000000);
+INSERT INTO t2 VALUES (990101000000);
+SELECT t1.* FROM t1,t2 WHERE t1.a=t2.a;
+a
+1999-01-01 00:00:00
+1999-01-01 00:00:00
+SELECT t1.* FROM t1 LEFT JOIN t2 ON t1.a=t2.a;
+a
+1999-01-01 00:00:00
+1999-01-01 00:00:00
+ALTER TABLE t2 ADD PRIMARY KEY(a);
+SELECT t1.* FROM t1,t2 WHERE t1.a=t2.a;
+a
+1999-01-01 00:00:00
+1999-01-01 00:00:00
+SELECT t1.* FROM t1 LEFT JOIN t2 ON t1.a=t2.a;
+a
+1999-01-01 00:00:00
+1999-01-01 00:00:00
+# t2 should NOT be eliminated
+EXPLAIN SELECT t1.* FROM t1 LEFT JOIN t2 ON t1.a=t2.a;
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	SIMPLE	t1	system	NULL	NULL	NULL	NULL	1	
+1	SIMPLE	t2	index	PRIMARY	PRIMARY	8	NULL	2	Using where; Using index
+DROP TABLE t1,t2;
+#
+# MDEV-6971 Bad results with joins comparing TIME and DOUBLE/DECIMAL columns
+#
+CREATE TABLE t1 (a TIME(6) PRIMARY KEY);
+INSERT INTO t1 VALUES ('10:20:30');
+CREATE TABLE t2 (a DOUBLE);
+INSERT INTO t2 VALUES (102030),(102030.000000001);
+SELECT t1.* FROM t1 JOIN t2 USING(a);
+a
+10:20:30.000000
+10:20:30.000000
+SELECT t1.* FROM t1 LEFT JOIN t2 USING(a);
+a
+10:20:30.000000
+10:20:30.000000
+ALTER TABLE t2 ADD PRIMARY KEY(a);
+SELECT t1.* FROM t1 JOIN t2 USING(a);
+a
+10:20:30.000000
+10:20:30.000000
+SELECT t1.* FROM t1 LEFT JOIN t2 USING(a);
+a
+10:20:30.000000
+10:20:30.000000
+# t2 should NOT be elimitated
+EXPLAIN SELECT t1.* FROM t1 LEFT JOIN t2 USING(a);
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	SIMPLE	t1	system	NULL	NULL	NULL	NULL	1	
+1	SIMPLE	t2	index	PRIMARY	PRIMARY	8	NULL	2	Using where; Using index
+DROP TABLE t1,t2;
+#
+# End of 10.0 tests
+#

=== modified file 'mysql-test/r/type_newdecimal.result'
--- mysql-test/r/type_newdecimal.result	2014-10-09 08:30:11 +0000
+++ mysql-test/r/type_newdecimal.result	2014-11-03 09:11:43 +0000
@@ -1997,3 +1997,84 @@ select 0.0000000001 mod 1;
 select 0.01 mod 1;
 0.01 mod 1
 0.01
+#
+# Start of 10.0 tests
+#
+#
+# MDEV-6950 Bad results with joins comparing DATE and INT/ENUM/VARCHAR columns
+#
+CREATE TABLE t1 (a DATETIME PRIMARY KEY);
+INSERT INTO t1 VALUES ('1999-01-01 00:00:00');
+CREATE TABLE t2 (a DECIMAL(30,1));
+INSERT INTO t2 VALUES (19990101000000);
+INSERT INTO t2 VALUES (990101000000);
+SELECT t1.* FROM t1,t2 WHERE t1.a=t2.a;
+a
+1999-01-01 00:00:00
+1999-01-01 00:00:00
+SELECT t1.* FROM t1 LEFT JOIN t2 ON t1.a=t2.a;
+a
+1999-01-01 00:00:00
+1999-01-01 00:00:00
+ALTER TABLE t2 ADD PRIMARY KEY(a);
+SELECT t1.* FROM t1,t2 WHERE t1.a=t2.a;
+a
+1999-01-01 00:00:00
+1999-01-01 00:00:00
+SELECT t1.* FROM t1 LEFT JOIN t2 ON t1.a=t2.a;
+a
+1999-01-01 00:00:00
+1999-01-01 00:00:00
+# t2 should NOT be eliminated
+EXPLAIN SELECT t1.* FROM t1 LEFT JOIN t2 ON t1.a=t2.a;
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	SIMPLE	t1	system	NULL	NULL	NULL	NULL	1	
+1	SIMPLE	t2	index	PRIMARY	PRIMARY	14	NULL	2	Using where; Using index
+DROP TABLE t1,t2;
+#
+# MDEV-6971 Bad results with joins comparing TIME and DOUBLE/DECIMAL columns
+#
+CREATE TABLE t1 (a TIME(6) PRIMARY KEY);
+INSERT INTO t1 VALUES ('10:20:30');
+CREATE TABLE t2 (a DECIMAL(30,10));
+INSERT INTO t2 VALUES (102030),(102030.000000001);
+SELECT t1.* FROM t1 JOIN t2 USING(a);
+a
+10:20:30.000000
+10:20:30.000000
+Warnings:
+Note	1292	Truncated incorrect time value: '102030.0000000000'
+Note	1292	Truncated incorrect time value: '102030.0000000010'
+SELECT t1.* FROM t1 LEFT JOIN t2 USING(a);
+a
+10:20:30.000000
+10:20:30.000000
+Warnings:
+Note	1292	Truncated incorrect time value: '102030.0000000000'
+Note	1292	Truncated incorrect time value: '102030.0000000000'
+Note	1292	Truncated incorrect time value: '102030.0000000010'
+ALTER TABLE t2 ADD PRIMARY KEY(a);
+SELECT t1.* FROM t1 JOIN t2 USING(a);
+a
+10:20:30.000000
+10:20:30.000000
+Warnings:
+Note	1292	Truncated incorrect time value: '102030.0000000000'
+Note	1292	Truncated incorrect time value: '102030.0000000010'
+SELECT t1.* FROM t1 LEFT JOIN t2 USING(a);
+a
+10:20:30.000000
+10:20:30.000000
+Warnings:
+Note	1292	Truncated incorrect time value: '102030.0000000000'
+Note	1292	Truncated incorrect time value: '102030.0000000000'
+Note	1292	Truncated incorrect time value: '102030.0000000010'
+# t2 should NOT be eliminated
+EXPLAIN SELECT t1.* FROM t1 LEFT JOIN t2 USING(a);
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	SIMPLE	t1	system	NULL	NULL	NULL	NULL	1	
+1	SIMPLE	t2	index	PRIMARY	PRIMARY	14	NULL	2	Using where; Using index
+DROP TABLE t1,t2;
+#
+# End of 10.0 tests
+#

=== modified file 'mysql-test/r/type_set.result'
--- mysql-test/r/type_set.result	2008-09-05 15:21:59 +0000
+++ mysql-test/r/type_set.result	2014-11-03 09:03:20 +0000
@@ -104,3 +104,205 @@ INSERT INTO t1 ( set_unique_utf8 ) VALUE
 ERROR 23000: Duplicate entry '' for key 'set_unique_utf8'
 DROP TABLE t1;
 End of 5.0 tests
+#
+# Start of 10.0 tests
+#
+#
+# MDEV-6950 Bad results with joins compating DATE and INT/ENUM/VARCHAR columns
+#
+CREATE TABLE t1 (c1 DATE PRIMARY KEY);
+INSERT INTO t1 VALUES ('2001-01-01');
+CREATE TABLE t2 (c1 SET('2001-01-01','2001/01/01'));
+INSERT INTO t2 VALUES ('2001-01-01');
+INSERT INTO t2 VALUES ('2001/01/01');
+SELECT t1.* FROM t1,t2 WHERE t1.c1=t2.c1;
+c1
+2001-01-01
+2001-01-01
+SELECT t1.* FROM t1 LEFT JOIN t2 ON t1.c1=t2.c1;
+c1
+2001-01-01
+2001-01-01
+ALTER TABLE t2 ADD PRIMARY KEY(c1);
+SELECT t1.* FROM t1,t2 WHERE t1.c1=t2.c1;
+c1
+2001-01-01
+2001-01-01
+EXPLAIN SELECT t1.* FROM t1,t2 WHERE t1.c1=t2.c1;
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	SIMPLE	t1	system	PRIMARY	NULL	NULL	NULL	1	
+1	SIMPLE	t2	index	PRIMARY	PRIMARY	1	NULL	2	Using where; Using index
+SELECT t1.* FROM t1 LEFT JOIN t2 ON t1.c1=t2.c1;
+c1
+2001-01-01
+2001-01-01
+# t2 should NOT be eliminated
+EXPLAIN SELECT t1.* FROM t1 LEFT JOIN t2 ON t1.c1=t2.c1;
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	SIMPLE	t1	system	NULL	NULL	NULL	NULL	1	
+1	SIMPLE	t2	index	PRIMARY	PRIMARY	1	NULL	2	Using where; Using index
+DROP TABLE t1, t2;
+#
+# MDEV-6978 Bad results with join comparing case insensitive VARCHAR/ENUM/SET expression to a _bin ENUM column
+#
+CREATE TABLE t1 (c1 ENUM('a') CHARACTER SET latin1 PRIMARY KEY);
+INSERT INTO t1 VALUES ('a');
+CREATE TABLE t2 (c1 SET('a','A') CHARACTER SET latin1 COLLATE latin1_bin);
+INSERT INTO t2 VALUES ('a'),('A');
+SELECT t1.* FROM t1 JOIN t2 ON t1.c1 COLLATE latin1_swedish_ci=t2.c1;
+c1
+a
+a
+SELECT t1.* FROM t1 LEFT JOIN t2 ON t1.c1 COLLATE latin1_swedish_ci=t2.c1;
+c1
+a
+a
+ALTER TABLE t2 ADD PRIMARY KEY(c1);
+SELECT t1.* FROM t1 JOIN t2 ON t1.c1 COLLATE latin1_swedish_ci=t2.c1;
+c1
+a
+a
+SELECT t1.* FROM t1 LEFT JOIN t2 ON t1.c1 COLLATE latin1_swedish_ci=t2.c1;
+c1
+a
+a
+# t2 should NOT be eliminated
+EXPLAIN SELECT t1.* FROM t1 LEFT JOIN t2 ON t1.c1 COLLATE latin1_swedish_ci=t2.c1;
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	SIMPLE	t1	system	NULL	NULL	NULL	NULL	1	
+1	SIMPLE	t2	index	PRIMARY	PRIMARY	1	NULL	2	Using where; Using index
+DROP TABLE IF EXISTS t1,t2;
+CREATE TABLE t1 (c1 SET('a') CHARACTER SET latin1 PRIMARY KEY);
+INSERT INTO t1 VALUES ('a');
+CREATE TABLE t2 (c1 SET('a','A') CHARACTER SET latin1 COLLATE latin1_bin);
+INSERT INTO t2 VALUES ('a'),('A');
+SELECT t1.* FROM t1 JOIN t2 ON t1.c1 COLLATE latin1_swedish_ci=t2.c1;
+c1
+a
+a
+SELECT t1.* FROM t1 LEFT JOIN t2 ON t1.c1 COLLATE latin1_swedish_ci=t2.c1;
+c1
+a
+a
+ALTER TABLE t2 ADD PRIMARY KEY(c1);
+SELECT t1.* FROM t1 JOIN t2 ON t1.c1 COLLATE latin1_swedish_ci=t2.c1;
+c1
+a
+a
+SELECT t1.* FROM t1 LEFT JOIN t2 ON t1.c1 COLLATE latin1_swedish_ci=t2.c1;
+c1
+a
+a
+# t2 should NOT be eliminated
+EXPLAIN SELECT t1.* FROM t1 LEFT JOIN t2 ON t1.c1 COLLATE latin1_swedish_ci=t2.c1;
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	SIMPLE	t1	system	NULL	NULL	NULL	NULL	1	
+1	SIMPLE	t2	index	PRIMARY	PRIMARY	1	NULL	2	Using where; Using index
+DROP TABLE IF EXISTS t1,t2;
+CREATE TABLE t1 (c1 VARCHAR(10) CHARACTER SET latin1 PRIMARY KEY);
+INSERT INTO t1 VALUES ('a');
+CREATE TABLE t2 (c1 SET('a','A') CHARACTER SET latin1 COLLATE latin1_bin);
+INSERT INTO t2 VALUES ('a'),('A');
+SELECT t1.* FROM t1 JOIN t2 ON t1.c1 COLLATE latin1_swedish_ci=t2.c1;
+c1
+a
+a
+SELECT t1.* FROM t1 LEFT JOIN t2 ON t1.c1 COLLATE latin1_swedish_ci=t2.c1;
+c1
+a
+a
+ALTER TABLE t2 ADD PRIMARY KEY(c1);
+SELECT t1.* FROM t1 JOIN t2 ON t1.c1 COLLATE latin1_swedish_ci=t2.c1;
+c1
+a
+a
+SELECT t1.* FROM t1 LEFT JOIN t2 ON t1.c1 COLLATE latin1_swedish_ci=t2.c1;
+c1
+a
+a
+# t2 should NOT be eliminated
+EXPLAIN SELECT t1.* FROM t1 LEFT JOIN t2 ON t1.c1 COLLATE latin1_swedish_ci=t2.c1;
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	SIMPLE	t1	system	NULL	NULL	NULL	NULL	1	
+1	SIMPLE	t2	index	PRIMARY	PRIMARY	1	NULL	2	Using where; Using index
+DROP TABLE IF EXISTS t1,t2;
+#
+# MDEV-6982 LEFT JOIN table elimination is not always used when it could
+#
+CREATE TABLE t1 (c1 INT PRIMARY KEY);
+INSERT INTO t1 VALUES (1),(2);
+CREATE TABLE t2 (c1 SET('a','b'));
+INSERT INTO t2 VALUES ('a'),('b');
+SELECT t1.* FROM t1 JOIN t2 USING (c1);
+c1
+1
+2
+SELECT t1.* FROM t1 LEFT JOIN t2 USING (c1);
+c1
+1
+2
+ALTER TABLE t2 ADD PRIMARY KEY(c1);
+SELECT t1.* FROM t1 JOIN t2 USING (c1);
+c1
+1
+2
+SELECT t1.* FROM t1 LEFT JOIN t2 USING (c1);
+c1
+1
+2
+# Ok to eliminated t2
+EXPLAIN SELECT t1.* FROM t1 LEFT JOIN t2 USING (c1);
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	SIMPLE	t1	index	NULL	PRIMARY	4	NULL	2	Using index
+DROP TABLE t1,t2;
+CREATE TABLE t1 (c1 DOUBLE PRIMARY KEY);
+INSERT INTO t1 VALUES (1),(2);
+CREATE TABLE t2 (c1 SET('a','b'));
+INSERT INTO t2 VALUES ('a'),('b');
+SELECT t1.* FROM t1 JOIN t2 USING (c1);
+c1
+1
+2
+SELECT t1.* FROM t1 LEFT JOIN t2 USING (c1);
+c1
+1
+2
+ALTER TABLE t2 ADD PRIMARY KEY(c1);
+SELECT t1.* FROM t1 JOIN t2 USING (c1);
+c1
+1
+2
+SELECT t1.* FROM t1 LEFT JOIN t2 USING (c1);
+c1
+1
+2
+# Ok to eliminated t2
+EXPLAIN SELECT t1.* FROM t1 LEFT JOIN t2 USING (c1);
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	SIMPLE	t1	index	NULL	PRIMARY	8	NULL	2	Using index
+DROP TABLE t1,t2;
+CREATE TABLE t1 (c1 DECIMAL(10,1) PRIMARY KEY);
+INSERT INTO t1 VALUES (1),(2);
+CREATE TABLE t2 (c1 SET('a','b'));
+INSERT INTO t2 VALUES ('a'),('b');
+SELECT t1.* FROM t1 JOIN t2 USING (c1);
+c1
+1.0
+2.0
+SELECT t1.* FROM t1 LEFT JOIN t2 USING (c1);
+c1
+1.0
+2.0
+ALTER TABLE t2 ADD PRIMARY KEY(c1);
+SELECT t1.* FROM t1 LEFT JOIN t2 USING (c1);
+c1
+1.0
+2.0
+# Ok to eliminated t2
+EXPLAIN SELECT t1.* FROM t1 LEFT JOIN t2 USING (c1);
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	SIMPLE	t1	index	NULL	PRIMARY	5	NULL	2	Using index
+DROP TABLE t1,t2;
+#
+# End of 10.0 tests
+#

=== modified file 'mysql-test/r/type_uint.result'
--- mysql-test/r/type_uint.result	2005-12-02 11:01:44 +0000
+++ mysql-test/r/type_uint.result	2014-11-03 09:05:25 +0000
@@ -14,3 +14,40 @@ this
 0
 4294967295
 drop table t1;
+#
+# Start of 10.0 tests
+#
+#
+# MDEV-6950 Bad results with joins comparing DATE and INT/ENUM/VARCHAR columns
+#
+CREATE TABLE t1 (a DATE PRIMARY KEY);
+INSERT INTO t1 VALUES ('1999-01-01');
+CREATE TABLE t2 (a INT UNSIGNED);
+INSERT INTO t2 VALUES (19990101);
+INSERT INTO t2 VALUES (990101);
+SELECT t1.* FROM t1,t2 WHERE t1.a=t2.a;
+a
+1999-01-01
+1999-01-01
+SELECT t1.* FROM t1 LEFT JOIN t2 ON t1.a=t2.a;
+a
+1999-01-01
+1999-01-01
+ALTER TABLE t2 ADD PRIMARY KEY(a);
+SELECT t1.* FROM t1,t2 WHERE t1.a=t2.a;
+a
+1999-01-01
+1999-01-01
+SELECT t1.* FROM t1 LEFT JOIN t2 ON t1.a=t2.a;
+a
+1999-01-01
+1999-01-01
+# t2 should NOT be eliminated
+EXPLAIN SELECT t1.* FROM t1 LEFT JOIN t2 ON t1.a=t2.a;
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	SIMPLE	t1	system	NULL	NULL	NULL	NULL	1	
+1	SIMPLE	t2	index	PRIMARY	PRIMARY	4	NULL	2	Using where; Using index
+DROP TABLE t1,t2;
+#
+# End of 10.0 tests
+#

=== modified file 'mysql-test/r/type_varchar.result'
--- mysql-test/r/type_varchar.result	2011-10-19 19:45:18 +0000
+++ mysql-test/r/type_varchar.result	2014-11-03 09:06:59 +0000
@@ -510,3 +510,104 @@ SELECT 5 = a FROM t1;
 Warnings:
 Warning	1292	Truncated incorrect DOUBLE value: 's               '
 DROP TABLE t1;
+#
+# Start of 10.0 tests
+#
+#
+# MDEV-6950 Bad results with joins comparing DATE and INT/ENUM/VARCHAR columns
+#
+CREATE TABLE t1 (c1 DATE PRIMARY KEY);
+INSERT INTO t1 VALUES ('2001-01-01');
+CREATE TABLE t2 (c1 VARCHAR(20));
+INSERT INTO t2 VALUES ('2001-01-01');
+INSERT INTO t2 VALUES ('2001/01/01');
+SELECT t1.* FROM t1,t2 WHERE t1.c1=t2.c1;
+c1
+2001-01-01
+2001-01-01
+SELECT t1.* FROM t1 LEFT JOIN t2 ON t1.c1=t2.c1;
+c1
+2001-01-01
+2001-01-01
+ALTER TABLE t2 ADD PRIMARY KEY(c1);
+SELECT t1.* FROM t1,t2 WHERE t1.c1=t2.c1;
+c1
+2001-01-01
+2001-01-01
+EXPLAIN SELECT t1.* FROM t1,t2 WHERE t1.c1=t2.c1;
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	SIMPLE	t1	system	PRIMARY	NULL	NULL	NULL	1	
+1	SIMPLE	t2	index	PRIMARY	PRIMARY	22	NULL	2	Using where; Using index
+SELECT t1.* FROM t1 LEFT JOIN t2 ON t1.c1=t2.c1;
+c1
+2001-01-01
+2001-01-01
+# t2 should NOT be eliminated
+EXPLAIN SELECT t1.* FROM t1 LEFT JOIN t2 ON t1.c1=t2.c1;
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	SIMPLE	t1	system	NULL	NULL	NULL	NULL	1	
+1	SIMPLE	t2	index	PRIMARY	PRIMARY	22	NULL	2	Using where; Using index
+DROP TABLE IF EXISTS t1,t2;
+#
+# MDEV-6989 BINARY and COLLATE xxx_bin comparisions are not used for optimization in some cases
+#
+CREATE TABLE t1 (c1 VARCHAR(20) CHARACTER SET latin1, PRIMARY KEY(c1));
+INSERT INTO t1 VALUES ('a'),('b'),('c'),('d');
+SELECT * FROM t1 WHERE c1=BINARY 'a';
+c1
+a
+EXPLAIN SELECT * FROM t1 WHERE c1=BINARY 'a';
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	SIMPLE	t1	const	PRIMARY	PRIMARY	22	const	1	Using index
+SELECT * FROM t1 WHERE c1=_latin1'a' COLLATE latin1_bin;
+c1
+a
+EXPLAIN SELECT * FROM t1 WHERE c1=_latin1'a' COLLATE latin1_bin;
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	SIMPLE	t1	const	PRIMARY	PRIMARY	22	const	1	Using index
+DROP TABLE t1;
+CREATE TABLE t1 (c1 VARCHAR(10) CHARACTER SET latin1 COLLATE latin1_bin);
+INSERT INTO t1 VALUES ('a');
+CREATE TABLE t2 (c1 VARCHAR(10) CHARACTER SET latin1, PRIMARY KEY(c1));
+INSERT INTO t2 VALUES ('a'),('b');
+SELECT * FROM t1, t2 WHERE t1.c1=t2.c1;
+c1	c1
+a	a
+EXPLAIN SELECT * FROM t1, t2 WHERE t1.c1=t2.c1;
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	SIMPLE	t1	system	NULL	NULL	NULL	NULL	1	
+1	SIMPLE	t2	const	PRIMARY	PRIMARY	12	const	1	Using index
+ALTER TABLE t1 MODIFY c1 VARBINARY(10);
+SELECT * FROM t1, t2 WHERE t1.c1=t2.c1;
+c1	c1
+a	a
+EXPLAIN SELECT * FROM t1, t2 WHERE t1.c1=t2.c1;
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	SIMPLE	t1	system	NULL	NULL	NULL	NULL	1	
+1	SIMPLE	t2	const	PRIMARY	PRIMARY	12	const	1	Using index
+DROP TABLE t1, t2;
+CREATE TABLE t1 (c1 VARCHAR(10) CHARACTER SET latin1 COLLATE latin1_bin);
+INSERT INTO t1 VALUES ('a'),('c');
+CREATE TABLE t2 (c1 VARCHAR(10) CHARACTER SET latin1, PRIMARY KEY(c1));
+INSERT INTO t2 VALUES ('a'),('b');
+SELECT t1.* FROM t1 LEFT JOIN t2 USING (c1);
+c1
+a
+c
+# t2 should be eliminated
+EXPLAIN SELECT t1.* FROM t1 LEFT JOIN t2 USING (c1);
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	SIMPLE	t1	ALL	NULL	NULL	NULL	NULL	2	
+ALTER TABLE t1 MODIFY c1 VARBINARY(10);
+SELECT t1.* FROM t1 LEFT JOIN t2 USING (c1);
+c1
+a
+c
+# t2 should be eliminated
+EXPLAIN SELECT t1.* FROM t1 LEFT JOIN t2 USING (c1);
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	SIMPLE	t1	ALL	NULL	NULL	NULL	NULL	2	
+DROP TABLE t1,t2;
+#
+# End of 10.0 tests
+#

=== modified file 'mysql-test/t/group_min_max.test'
--- mysql-test/t/group_min_max.test	2014-08-07 16:06:56 +0000
+++ mysql-test/t/group_min_max.test	2014-11-01 21:20:24 +0000
@@ -1488,3 +1488,38 @@ SELECT distinct a, b FROM t1 where a = '
 SELECT distinct a, b FROM t1 where a = '3' ORDER BY b; 
 
 drop table t1;
+
+--echo #
+--echo # Start of 10.0 tests
+--echo #
+
+--echo #
+--echo # MDEV-6991 GROUP_MIN_MAX optimization is erroneously applied in some cases
+--echo #
+CREATE TABLE t1 (id INT NOT NULL, a VARCHAR(20)) ENGINE=MyISAM;
+INSERT INTO t1 VALUES (1,'2001-01-01');
+INSERT INTO t1 VALUES (1,'2001-01-02');
+INSERT INTO t1 VALUES (1,'2001-01-03');
+INSERT INTO t1 VALUES (1,' 2001-01-04');
+INSERT INTO t1 VALUES (2,'2001-01-01');
+INSERT INTO t1 VALUES (2,'2001-01-02');
+INSERT INTO t1 VALUES (2,'2001-01-03');
+INSERT INTO t1 VALUES (2,' 2001-01-04');
+INSERT INTO t1 VALUES (3,'2001-01-01');
+INSERT INTO t1 VALUES (3,'2001-01-02');
+INSERT INTO t1 VALUES (3,'2001-01-03');
+INSERT INTO t1 VALUES (3,' 2001-01-04');
+INSERT INTO t1 VALUES (4,'2001-01-01');
+INSERT INTO t1 VALUES (4,'2001-01-02');
+INSERT INTO t1 VALUES (4,'2001-01-03');
+INSERT INTO t1 VALUES (4,' 2001-01-04');
+SELECT id,MIN(a),MAX(a) FROM t1 WHERE a>=DATE'2001-01-04' GROUP BY id;
+ALTER TABLE t1 ADD KEY(id,a);
+SELECT id,MIN(a),MAX(a) FROM t1 WHERE a>=DATE'2001-01-04' GROUP BY id;
+DROP TABLE t1;
+
+
+--echo #
+--echo # End of 10.0 tests
+--echo #
+

=== modified file 'mysql-test/t/type_date.test'
--- mysql-test/t/type_date.test	2014-05-09 10:35:11 +0000
+++ mysql-test/t/type_date.test	2014-11-01 21:20:23 +0000
@@ -385,3 +385,162 @@ select 1 from t1 as t1_0 inner join t1 a
 select 1 from t1 as t1_0 inner join t1 as t2 on (t1_0.a <=> now()) join t1 on 1;
 drop table t1;
 
+
+--echo #
+--echo # Start of 10.0 tests
+--echo #
+
+--echo #
+--echo # MDEV-6982 LEFT JOIN table elimination is not always used when it could
+--echo #
+
+CREATE TABLE t1 (c1 INT PRIMARY KEY);
+INSERT INTO t1 VALUES (20010101),(20010102);
+CREATE TABLE t2 (c1 DATE);
+INSERT INTO t2 VALUES ('2001-01-01'),('2001-01-02');
+SELECT t1.* FROM t1 JOIN t2 USING(c1);
+SELECT t1.* FROM t1 LEFT JOIN t2 USING(c1);
+ALTER TABLE t2 ADD PRIMARY KEY(c1);
+SELECT t1.* FROM t1 JOIN t2 USING(c1);
+SELECT t1.* FROM t1 LEFT JOIN t2 USING(c1);
+--echo # Ok to eliminate t2
+EXPLAIN SELECT t1.* FROM t1 LEFT JOIN t2 USING(c1);
+DROP TABLE t1,t2;
+
+CREATE TABLE t1 (c1 DOUBLE PRIMARY KEY);
+INSERT INTO t1 VALUES (20010101),(20010102);
+CREATE TABLE t2 (c1 DATE);
+INSERT INTO t2 VALUES ('2001-01-01'),('2001-01-02');
+SELECT t1.* FROM t1 JOIN t2 USING(c1);
+SELECT t1.* FROM t1 LEFT JOIN t2 USING(c1);
+ALTER TABLE t2 ADD PRIMARY KEY(c1);
+SELECT t1.* FROM t1 JOIN t2 USING(c1);
+SELECT t1.* FROM t1 LEFT JOIN t2 USING(c1);
+--echo # Ok to eliminate t2
+EXPLAIN SELECT t1.* FROM t1 LEFT JOIN t2 USING(c1);
+DROP TABLE t1,t2;
+
+CREATE TABLE t1 (c1 DECIMAL(20,0) PRIMARY KEY);
+INSERT INTO t1 VALUES (20010101),(20010102);
+CREATE TABLE t2 (c1 DATE);
+INSERT INTO t2 VALUES ('2001-01-01'),('2001-01-02');
+SELECT t1.* FROM t1 JOIN t2 USING(c1);
+SELECT t1.* FROM t1 LEFT JOIN t2 USING(c1);
+ALTER TABLE t2 ADD PRIMARY KEY(c1);
+SELECT t1.* FROM t1 JOIN t2 USING(c1);
+SELECT t1.* FROM t1 LEFT JOIN t2 USING(c1);
+--echo # Ok to eliminate t2
+EXPLAIN SELECT t1.* FROM t1 LEFT JOIN t2 USING(c1);
+DROP TABLE t1,t2;
+
+CREATE TABLE t1 (c1 CHAR(20) PRIMARY KEY);
+INSERT INTO t1 VALUES ('2001-01-01'),('2001/01/02');
+CREATE TABLE t2 (c1 DATE);
+INSERT INTO t2 VALUES ('2001-01-01'),('2001-01-02');
+SELECT t1.* FROM t1 JOIN t2 USING(c1);
+SELECT t1.* FROM t1 LEFT JOIN t2 USING(c1);
+ALTER TABLE t2 ADD PRIMARY KEY(c1);
+SELECT t1.* FROM t1 JOIN t2 USING(c1);
+SELECT t1.* FROM t1 LEFT JOIN t2 USING(c1);
+--echo # Ok to eliminate t2
+EXPLAIN SELECT t1.* FROM t1 LEFT JOIN t2 USING(c1);
+DROP TABLE t1,t2;
+
+CREATE TABLE t1 (c1 ENUM('2001-01-01','2001/01/02') PRIMARY KEY);
+INSERT INTO t1 VALUES ('2001-01-01'),('2001/01/02');
+CREATE TABLE t2 (c1 DATE);
+INSERT INTO t2 VALUES ('2001-01-01'),('2001-01-02');
+SELECT t1.* FROM t1 JOIN t2 USING(c1);
+SELECT t1.* FROM t1 LEFT JOIN t2 USING(c1);
+ALTER TABLE t2 ADD PRIMARY KEY(c1);
+SELECT t1.* FROM t1 JOIN t2 USING(c1);
+SELECT t1.* FROM t1 LEFT JOIN t2 USING(c1);
+--echo # Ok to eliminate t2
+EXPLAIN SELECT t1.* FROM t1 LEFT JOIN t2 USING(c1);
+DROP TABLE t1,t2;
+
+CREATE TABLE t1 (c1 SET('2001-01-01','2001/01/02') PRIMARY KEY);
+INSERT INTO t1 VALUES ('2001-01-01'),('2001/01/02');
+CREATE TABLE t2 (c1 DATE);
+INSERT INTO t2 VALUES ('2001-01-01'),('2001-01-02');
+SELECT t1.* FROM t1 JOIN t2 USING(c1);
+SELECT t1.* FROM t1 LEFT JOIN t2 USING(c1);
+ALTER TABLE t2 ADD PRIMARY KEY(c1);
+SELECT t1.* FROM t1 JOIN t2 USING(c1);
+SELECT t1.* FROM t1 LEFT JOIN t2 USING(c1);
+--echo # Ok to eliminate t2
+EXPLAIN SELECT t1.* FROM t1 LEFT JOIN t2 USING(c1);
+DROP TABLE t1,t2;
+
+CREATE TABLE t1 (c1 DATE PRIMARY KEY);
+INSERT INTO t1 VALUES ('2001-01-01'),('2001-01-02');
+CREATE TABLE t2 (c1 DATE);
+INSERT INTO t2 VALUES ('2001-01-01'),('2001-01-02');
+SELECT t1.* FROM t1 JOIN t2 USING(c1);
+SELECT t1.* FROM t1 LEFT JOIN t2 USING(c1);
+ALTER TABLE t2 ADD PRIMARY KEY(c1);
+SELECT t1.* FROM t1 JOIN t2 USING(c1);
+SELECT t1.* FROM t1 LEFT JOIN t2 USING(c1);
+--echo # Ok to eliminate t2
+EXPLAIN SELECT t1.* FROM t1 LEFT JOIN t2 USING(c1);
+DROP TABLE t1,t2;
+
+CREATE TABLE t1 (c1 DATETIME PRIMARY KEY);
+INSERT INTO t1 VALUES ('2001-01-01 00:00:00'),('2001-01-02 00:00:00');
+CREATE TABLE t2 (c1 DATE);
+INSERT INTO t2 VALUES ('2001-01-01'),('2001-01-02');
+SELECT t1.* FROM t1 JOIN t2 USING(c1);
+SELECT t1.* FROM t1 LEFT JOIN t2 USING(c1);
+ALTER TABLE t2 ADD PRIMARY KEY(c1);
+SELECT t1.* FROM t1 JOIN t2 USING(c1);
+SELECT t1.* FROM t1 LEFT JOIN t2 USING(c1);
+--echo # Ok to eliminate t2
+EXPLAIN SELECT t1.* FROM t1 LEFT JOIN t2 USING(c1);
+DROP TABLE t1,t2;
+
+SET timestamp=UNIX_TIMESTAMP('2001-01-01 00:00:00');
+CREATE TABLE t1 (c1 TIME PRIMARY KEY);
+INSERT INTO t1 VALUES ('00:00:00'),('24:00:00');
+CREATE TABLE t2 (c1 DATE);
+INSERT INTO t2 VALUES ('2001-01-01'),('2001-01-02');
+SELECT t1.* FROM t1 JOIN t2 USING(c1);
+SELECT t1.* FROM t1 LEFT JOIN t2 USING(c1);
+ALTER TABLE t2 ADD PRIMARY KEY(c1);
+SELECT t1.* FROM t1 JOIN t2 USING(c1);
+SELECT t1.* FROM t1 LEFT JOIN t2 USING(c1);
+--echo # Ok to eliminate t2
+EXPLAIN SELECT t1.* FROM t1 LEFT JOIN t2 USING(c1);
+DROP TABLE t1,t2;
+SET timestamp=DEFAULT;
+
+
+--echo #
+--echo # MDEV-6990 GROUP_MIN_MAX optimization is not applied in some cases when it could
+--echo #
+CREATE TABLE t1 (id INT NOT NULL, a DATE, KEY(id,a)) ENGINE=MyISAM;
+INSERT INTO t1 VALUES (1,'2001-01-01');
+INSERT INTO t1 VALUES (1,'2001-01-02');
+INSERT INTO t1 VALUES (1,'2001-01-03');
+INSERT INTO t1 VALUES (1,'2001-01-04');
+INSERT INTO t1 VALUES (2,'2001-01-01');
+INSERT INTO t1 VALUES (2,'2001-01-02');
+INSERT INTO t1 VALUES (2,'2001-01-03');
+INSERT INTO t1 VALUES (2,'2001-01-04');
+INSERT INTO t1 VALUES (3,'2001-01-01');
+INSERT INTO t1 VALUES (3,'2001-01-02');
+INSERT INTO t1 VALUES (3,'2001-01-03');
+INSERT INTO t1 VALUES (3,'2001-01-04');
+INSERT INTO t1 VALUES (4,'2001-01-01');
+INSERT INTO t1 VALUES (4,'2001-01-02');
+INSERT INTO t1 VALUES (4,'2001-01-03');
+INSERT INTO t1 VALUES (4,'2001-01-04');
+EXPLAIN SELECT id,MIN(a),MAX(a) FROM t1 WHERE a>='2001-01-04' GROUP BY id;
+EXPLAIN SELECT id,MIN(a),MAX(a) FROM t1 WHERE a>=20010104 GROUP BY id;
+EXPLAIN SELECT id,MIN(a),MAX(a) FROM t1 WHERE a>=20010104.0 GROUP BY id;
+EXPLAIN SELECT id,MIN(a),MAX(a) FROM t1 WHERE a>=20010104e0 GROUP BY id;
+DROP TABLE t1;
+
+
+--echo #
+--echo # End of 10.0 tests
+--echo #

=== modified file 'mysql-test/t/type_enum.test'
--- mysql-test/t/type_enum.test	2013-09-14 01:09:36 +0000
+++ mysql-test/t/type_enum.test	2014-11-03 08:58:47 +0000
@@ -236,3 +236,148 @@ SELECT AVG(f1) FROM t1;
 drop table t1;
 
 --echo End of 5.3 tests
+
+--echo #
+--echo # Start of 10.0 tests
+--echo #
+
+--echo #
+--echo # MDEV-6950 Bad results with joins comparing DATE/DATETIME and INT/ENUM/VARCHAR columns
+--echo #
+
+CREATE TABLE t1 (c1 DATE PRIMARY KEY);
+INSERT INTO t1 VALUES ('2001-01-01');
+CREATE TABLE t2 (c1 ENUM('2001-01-01','2001/01/01'));
+INSERT INTO t2 VALUES ('2001-01-01');
+INSERT INTO t2 VALUES ('2001/01/01');
+SELECT t1.* FROM t1,t2 WHERE t1.c1=t2.c1;
+SELECT t1.* FROM t1 LEFT JOIN t2 ON t1.c1=t2.c1;
+ALTER TABLE t2 ADD PRIMARY KEY(c1);
+SELECT t1.* FROM t1,t2 WHERE t1.c1=t2.c1;
+EXPLAIN SELECT t1.* FROM t1,t2 WHERE t1.c1=t2.c1;
+SELECT t1.* FROM t1 LEFT JOIN t2 USING (c1);
+--echo # t2 should NOT be eliminated
+EXPLAIN SELECT t1.* FROM t1 LEFT JOIN t2 USING (c1);
+DROP TABLE t1, t2;
+
+--echo #
+--echo # MDEV-6978 Bad results with join comparing case insensitive VARCHAR/ENUM/SET expression to a _bin ENUM column
+--echo #
+CREATE TABLE t1 (c1 ENUM('a') CHARACTER SET latin1 PRIMARY KEY);
+INSERT INTO t1 VALUES ('a');
+CREATE TABLE t2 (c1 ENUM('a','A') CHARACTER SET latin1 COLLATE latin1_bin);
+INSERT INTO t2 VALUES ('a'),('A');
+SELECT t1.* FROM t1 JOIN t2 ON t1.c1 COLLATE latin1_swedish_ci=t2.c1;
+SELECT t1.* FROM t1 LEFT JOIN t2 ON t1.c1 COLLATE latin1_swedish_ci=t2.c1;
+ALTER TABLE t2 ADD PRIMARY KEY(c1);
+SELECT t1.* FROM t1 JOIN t2 ON t1.c1 COLLATE latin1_swedish_ci=t2.c1;
+SELECT t1.* FROM t1 LEFT JOIN t2 ON t1.c1 COLLATE latin1_swedish_ci=t2.c1;
+--echo # t2 should NOT be eliminated
+EXPLAIN SELECT t1.* FROM t1 LEFT JOIN t2 ON t1.c1 COLLATE latin1_swedish_ci=t2.c1;
+DROP TABLE IF EXISTS t1,t2;
+
+CREATE TABLE t1 (c1 SET('a') CHARACTER SET latin1 PRIMARY KEY);
+INSERT INTO t1 VALUES ('a');
+CREATE TABLE t2 (c1 ENUM('a','A') CHARACTER SET latin1 COLLATE latin1_bin);
+INSERT INTO t2 VALUES ('a'),('A');
+SELECT t1.* FROM t1 JOIN t2 ON t1.c1 COLLATE latin1_swedish_ci=t2.c1;
+SELECT t1.* FROM t1 LEFT JOIN t2 ON t1.c1 COLLATE latin1_swedish_ci=t2.c1;
+ALTER TABLE t2 ADD PRIMARY KEY(c1);
+SELECT t1.* FROM t1 JOIN t2 ON t1.c1 COLLATE latin1_swedish_ci=t2.c1;
+SELECT t1.* FROM t1 LEFT JOIN t2 ON t1.c1 COLLATE latin1_swedish_ci=t2.c1;
+--echo # t2 should NOT be eliminated
+EXPLAIN SELECT t1.* FROM t1 LEFT JOIN t2 ON t1.c1 COLLATE latin1_swedish_ci=t2.c1;
+DROP TABLE IF EXISTS t1,t2;
+
+CREATE TABLE t1 (c1 VARCHAR(10) CHARACTER SET latin1 PRIMARY KEY);
+INSERT INTO t1 VALUES ('a');
+CREATE TABLE t2 (c1 ENUM('a','A') CHARACTER SET latin1 COLLATE latin1_bin);
+INSERT INTO t2 VALUES ('a'),('A');
+SELECT t1.* FROM t1 JOIN t2 ON t1.c1 COLLATE latin1_swedish_ci=t2.c1;
+SELECT t1.* FROM t1 LEFT JOIN t2 ON t1.c1 COLLATE latin1_swedish_ci=t2.c1;
+ALTER TABLE t2 ADD PRIMARY KEY(c1);
+SELECT t1.* FROM t1 JOIN t2 ON t1.c1 COLLATE latin1_swedish_ci=t2.c1;
+SELECT t1.* FROM t1 LEFT JOIN t2 ON t1.c1 COLLATE latin1_swedish_ci=t2.c1;
+--echo # t2 should NOT be eliminated
+EXPLAIN SELECT t1.* FROM t1 LEFT JOIN t2 ON t1.c1 COLLATE latin1_swedish_ci=t2.c1;
+DROP TABLE IF EXISTS t1,t2;
+
+--echo #
+--echo # MDEV-6982 LEFT JOIN table elimination is not always used when it could
+--echo #
+CREATE TABLE t1 (c1 INT PRIMARY KEY);
+INSERT INTO t1 VALUES (1),(2);
+CREATE TABLE t2 (c1 ENUM('a','b'));
+INSERT INTO t2 VALUES ('a'),('b');
+SELECT t1.* FROM t1 JOIN t2 USING (c1);
+SELECT t1.* FROM t1 LEFT JOIN t2 USING (c1);
+ALTER TABLE t2 ADD PRIMARY KEY(c1);
+SELECT t1.* FROM t1 JOIN t2 USING (c1);
+SELECT t1.* FROM t1 LEFT JOIN t2 USING (c1);
+--echo # Ok to eliminated t2
+EXPLAIN SELECT t1.* FROM t1 LEFT JOIN t2 USING (c1);
+DROP TABLE t1,t2;
+
+CREATE TABLE t1 (c1 DOUBLE PRIMARY KEY);
+INSERT INTO t1 VALUES (1),(2);
+CREATE TABLE t2 (c1 ENUM('a','b'));
+INSERT INTO t2 VALUES ('a'),('b');
+SELECT t1.* FROM t1 JOIN t2 USING (c1);
+SELECT t1.* FROM t1 LEFT JOIN t2 USING (c1);
+ALTER TABLE t2 ADD PRIMARY KEY(c1);
+SELECT t1.* FROM t1 JOIN t2 USING (c1);
+SELECT t1.* FROM t1 LEFT JOIN t2 USING (c1);
+--echo # Ok to eliminated t2
+EXPLAIN SELECT t1.* FROM t1 LEFT JOIN t2 USING (c1);
+DROP TABLE t1,t2;
+
+CREATE TABLE t1 (c1 DECIMAL(10,1) PRIMARY KEY);
+INSERT INTO t1 VALUES (1),(2);
+CREATE TABLE t2 (c1 ENUM('a','b'));
+INSERT INTO t2 VALUES ('a'),('b');
+SELECT t1.* FROM t1 JOIN t2 USING (c1);
+SELECT t1.* FROM t1 LEFT JOIN t2 USING (c1);
+ALTER TABLE t2 ADD PRIMARY KEY(c1);
+#
+# Uncomment this after fixing MDEV-6993
+#SELECT t1.* FROM t1 JOIN t2 USING (c1);
+#
+SELECT t1.* FROM t1 LEFT JOIN t2 USING (c1);
+--echo # Ok to eliminated t2
+EXPLAIN SELECT t1.* FROM t1 LEFT JOIN t2 USING (c1);
+DROP TABLE t1,t2;
+
+--echo #
+--echo # MDEV-6991 GROUP_MIN_MAX optimization is erroneously applied in some cases
+--echo #
+CREATE TABLE t1 (id INT NOT NULL, a ENUM('04','03','02','01')) ENGINE=MyISAM;
+INSERT INTO t1 VALUES (1,'01');
+INSERT INTO t1 VALUES (1,'02');
+INSERT INTO t1 VALUES (1,'03');
+INSERT INTO t1 VALUES (1,'04');
+INSERT INTO t1 VALUES (2,'01');
+INSERT INTO t1 VALUES (2,'02');
+INSERT INTO t1 VALUES (2,'03');
+INSERT INTO t1 VALUES (2,'04');
+INSERT INTO t1 VALUES (3,'01');
+INSERT INTO t1 VALUES (3,'02');
+INSERT INTO t1 VALUES (3,'03');
+INSERT INTO t1 VALUES (3,'04');
+INSERT INTO t1 VALUES (4,'01');
+INSERT INTO t1 VALUES (4,'02');
+INSERT INTO t1 VALUES (4,'03');
+INSERT INTO t1 VALUES (4,'04');
+SELECT id,MIN(a),MAX(a) FROM t1 WHERE a>='02' GROUP BY id;
+SELECT id,MIN(a),MAX(a) FROM t1 WHERE a<=3 GROUP BY id;
+ALTER TABLE t1 ADD KEY(id,a);
+SELECT id,MIN(a),MAX(a) FROM t1 WHERE a>='02' GROUP BY id;
+--echo # Should NOT use group_min_max optimization
+EXPLAIN SELECT id,MIN(a),MAX(a) FROM t1 WHERE a>='02' GROUP BY id;
+SELECT id,MIN(a),MAX(a) FROM t1 WHERE a<=3 GROUP BY id;
+--echo # Should NOT use group_min_max optimization
+EXPLAIN SELECT id,MIN(a),MAX(a) FROM t1 WHERE a<=3 GROUP BY id;
+DROP TABLE t1;
+
+--echo #
+--echo # End of 10.0 tests
+--echo #

=== modified file 'mysql-test/t/type_float.test'
--- mysql-test/t/type_float.test	2014-03-14 07:31:16 +0000
+++ mysql-test/t/type_float.test	2014-11-03 09:10:24 +0000
@@ -343,3 +343,45 @@ INSERT INTO t1 VALUES ('2.0.');
 INSERT INTO t1 VALUES ('.');
 SELECT * FROM t1 ORDER BY f;
 DROP TABLE t1;
+
+
+--echo #
+--echo # Start of 10.0 tests
+--echo #
+
+--echo #
+--echo # MDEV-6950 Bad results with joins comparing DATE/DATETIME and INT/DECIMAL/DOUBLE/ENUM/VARCHAR columns
+--echo #
+CREATE TABLE t1 (a DATETIME PRIMARY KEY);
+INSERT INTO t1 VALUES ('1999-01-01 00:00:00');
+CREATE TABLE t2 (a DOUBLE);
+INSERT INTO t2 VALUES (19990101000000);
+INSERT INTO t2 VALUES (990101000000);  
+SELECT t1.* FROM t1,t2 WHERE t1.a=t2.a;
+SELECT t1.* FROM t1 LEFT JOIN t2 ON t1.a=t2.a;
+ALTER TABLE t2 ADD PRIMARY KEY(a);
+SELECT t1.* FROM t1,t2 WHERE t1.a=t2.a;
+SELECT t1.* FROM t1 LEFT JOIN t2 ON t1.a=t2.a;
+--echo # t2 should NOT be eliminated
+EXPLAIN SELECT t1.* FROM t1 LEFT JOIN t2 ON t1.a=t2.a;
+DROP TABLE t1,t2; 
+
+--echo #
+--echo # MDEV-6971 Bad results with joins comparing TIME and DOUBLE/DECIMAL columns
+--echo #
+CREATE TABLE t1 (a TIME(6) PRIMARY KEY);
+INSERT INTO t1 VALUES ('10:20:30');
+CREATE TABLE t2 (a DOUBLE);
+INSERT INTO t2 VALUES (102030),(102030.000000001);
+SELECT t1.* FROM t1 JOIN t2 USING(a);
+SELECT t1.* FROM t1 LEFT JOIN t2 USING(a);
+ALTER TABLE t2 ADD PRIMARY KEY(a);
+SELECT t1.* FROM t1 JOIN t2 USING(a);
+SELECT t1.* FROM t1 LEFT JOIN t2 USING(a);
+--echo # t2 should NOT be elimitated
+EXPLAIN SELECT t1.* FROM t1 LEFT JOIN t2 USING(a);
+DROP TABLE t1,t2;
+
+--echo #
+--echo # End of 10.0 tests
+--echo #

=== modified file 'mysql-test/t/type_newdecimal.test'
--- mysql-test/t/type_newdecimal.test	2014-10-07 22:46:10 +0000
+++ mysql-test/t/type_newdecimal.test	2014-11-03 09:11:22 +0000
@@ -1581,3 +1581,44 @@ select 0.0000000000000000000000000000000
 select 0.0000000001 mod 1;
 select 0.01 mod 1;
 
+--echo #
+--echo # Start of 10.0 tests
+--echo #
+
+--echo #
+--echo # MDEV-6950 Bad results with joins comparing DATE and INT/ENUM/VARCHAR columns
+--echo #
+CREATE TABLE t1 (a DATETIME PRIMARY KEY);
+INSERT INTO t1 VALUES ('1999-01-01 00:00:00');
+CREATE TABLE t2 (a DECIMAL(30,1));
+INSERT INTO t2 VALUES (19990101000000);
+INSERT INTO t2 VALUES (990101000000);  
+SELECT t1.* FROM t1,t2 WHERE t1.a=t2.a;
+SELECT t1.* FROM t1 LEFT JOIN t2 ON t1.a=t2.a;
+ALTER TABLE t2 ADD PRIMARY KEY(a);
+SELECT t1.* FROM t1,t2 WHERE t1.a=t2.a;
+SELECT t1.* FROM t1 LEFT JOIN t2 ON t1.a=t2.a;
+--echo # t2 should NOT be eliminated
+EXPLAIN SELECT t1.* FROM t1 LEFT JOIN t2 ON t1.a=t2.a;
+DROP TABLE t1,t2; 
+
+--echo #
+--echo # MDEV-6971 Bad results with joins comparing TIME and DOUBLE/DECIMAL columns
+--echo #
+CREATE TABLE t1 (a TIME(6) PRIMARY KEY);
+INSERT INTO t1 VALUES ('10:20:30');
+CREATE TABLE t2 (a DECIMAL(30,10));
+INSERT INTO t2 VALUES (102030),(102030.000000001);
+SELECT t1.* FROM t1 JOIN t2 USING(a);
+SELECT t1.* FROM t1 LEFT JOIN t2 USING(a);
+ALTER TABLE t2 ADD PRIMARY KEY(a);
+SELECT t1.* FROM t1 JOIN t2 USING(a);
+SELECT t1.* FROM t1 LEFT JOIN t2 USING(a);
+--echo # t2 should NOT be eliminated
+EXPLAIN SELECT t1.* FROM t1 LEFT JOIN t2 USING(a);
+DROP TABLE t1,t2;
+
+
+--echo #
+--echo # End of 10.0 tests
+--echo #

=== modified file 'mysql-test/t/type_set.test'
--- mysql-test/t/type_set.test	2008-09-05 15:21:59 +0000
+++ mysql-test/t/type_set.test	2014-11-03 09:02:45 +0000
@@ -95,3 +95,117 @@ DROP TABLE t1;
 
 
 --echo End of 5.0 tests
+
+--echo #
+--echo # Start of 10.0 tests
+--echo #
+
+--echo #
+--echo # MDEV-6950 Bad results with joins compating DATE and INT/ENUM/VARCHAR columns
+--echo #
+
+CREATE TABLE t1 (c1 DATE PRIMARY KEY);
+INSERT INTO t1 VALUES ('2001-01-01');
+CREATE TABLE t2 (c1 SET('2001-01-01','2001/01/01'));
+INSERT INTO t2 VALUES ('2001-01-01');
+INSERT INTO t2 VALUES ('2001/01/01');
+SELECT t1.* FROM t1,t2 WHERE t1.c1=t2.c1;
+SELECT t1.* FROM t1 LEFT JOIN t2 ON t1.c1=t2.c1;
+ALTER TABLE t2 ADD PRIMARY KEY(c1);
+SELECT t1.* FROM t1,t2 WHERE t1.c1=t2.c1;
+EXPLAIN SELECT t1.* FROM t1,t2 WHERE t1.c1=t2.c1;
+SELECT t1.* FROM t1 LEFT JOIN t2 ON t1.c1=t2.c1;
+--echo # t2 should NOT be eliminated
+EXPLAIN SELECT t1.* FROM t1 LEFT JOIN t2 ON t1.c1=t2.c1;
+DROP TABLE t1, t2;
+
+--echo #
+--echo # MDEV-6978 Bad results with join comparing case insensitive VARCHAR/ENUM/SET expression to a _bin ENUM column
+--echo #
+CREATE TABLE t1 (c1 ENUM('a') CHARACTER SET latin1 PRIMARY KEY);
+INSERT INTO t1 VALUES ('a');
+CREATE TABLE t2 (c1 SET('a','A') CHARACTER SET latin1 COLLATE latin1_bin);
+INSERT INTO t2 VALUES ('a'),('A');
+SELECT t1.* FROM t1 JOIN t2 ON t1.c1 COLLATE latin1_swedish_ci=t2.c1;
+SELECT t1.* FROM t1 LEFT JOIN t2 ON t1.c1 COLLATE latin1_swedish_ci=t2.c1;
+ALTER TABLE t2 ADD PRIMARY KEY(c1);
+SELECT t1.* FROM t1 JOIN t2 ON t1.c1 COLLATE latin1_swedish_ci=t2.c1;
+SELECT t1.* FROM t1 LEFT JOIN t2 ON t1.c1 COLLATE latin1_swedish_ci=t2.c1;
+--echo # t2 should NOT be eliminated
+EXPLAIN SELECT t1.* FROM t1 LEFT JOIN t2 ON t1.c1 COLLATE latin1_swedish_ci=t2.c1;
+DROP TABLE IF EXISTS t1,t2;
+
+CREATE TABLE t1 (c1 SET('a') CHARACTER SET latin1 PRIMARY KEY);
+INSERT INTO t1 VALUES ('a');
+CREATE TABLE t2 (c1 SET('a','A') CHARACTER SET latin1 COLLATE latin1_bin);
+INSERT INTO t2 VALUES ('a'),('A');
+SELECT t1.* FROM t1 JOIN t2 ON t1.c1 COLLATE latin1_swedish_ci=t2.c1;
+SELECT t1.* FROM t1 LEFT JOIN t2 ON t1.c1 COLLATE latin1_swedish_ci=t2.c1;
+ALTER TABLE t2 ADD PRIMARY KEY(c1);
+SELECT t1.* FROM t1 JOIN t2 ON t1.c1 COLLATE latin1_swedish_ci=t2.c1;
+SELECT t1.* FROM t1 LEFT JOIN t2 ON t1.c1 COLLATE latin1_swedish_ci=t2.c1;
+--echo # t2 should NOT be eliminated
+EXPLAIN SELECT t1.* FROM t1 LEFT JOIN t2 ON t1.c1 COLLATE latin1_swedish_ci=t2.c1;
+DROP TABLE IF EXISTS t1,t2;
+
+CREATE TABLE t1 (c1 VARCHAR(10) CHARACTER SET latin1 PRIMARY KEY);
+INSERT INTO t1 VALUES ('a');
+CREATE TABLE t2 (c1 SET('a','A') CHARACTER SET latin1 COLLATE latin1_bin);
+INSERT INTO t2 VALUES ('a'),('A');
+SELECT t1.* FROM t1 JOIN t2 ON t1.c1 COLLATE latin1_swedish_ci=t2.c1;
+SELECT t1.* FROM t1 LEFT JOIN t2 ON t1.c1 COLLATE latin1_swedish_ci=t2.c1;
+ALTER TABLE t2 ADD PRIMARY KEY(c1);
+SELECT t1.* FROM t1 JOIN t2 ON t1.c1 COLLATE latin1_swedish_ci=t2.c1;
+SELECT t1.* FROM t1 LEFT JOIN t2 ON t1.c1 COLLATE latin1_swedish_ci=t2.c1;
+--echo # t2 should NOT be eliminated
+EXPLAIN SELECT t1.* FROM t1 LEFT JOIN t2 ON t1.c1 COLLATE latin1_swedish_ci=t2.c1;
+DROP TABLE IF EXISTS t1,t2;
+
+--echo #
+--echo # MDEV-6982 LEFT JOIN table elimination is not always used when it could
+--echo #
+CREATE TABLE t1 (c1 INT PRIMARY KEY);
+INSERT INTO t1 VALUES (1),(2);
+CREATE TABLE t2 (c1 SET('a','b'));
+INSERT INTO t2 VALUES ('a'),('b');
+SELECT t1.* FROM t1 JOIN t2 USING (c1);
+SELECT t1.* FROM t1 LEFT JOIN t2 USING (c1);
+ALTER TABLE t2 ADD PRIMARY KEY(c1);
+SELECT t1.* FROM t1 JOIN t2 USING (c1);
+SELECT t1.* FROM t1 LEFT JOIN t2 USING (c1);
+--echo # Ok to eliminated t2
+EXPLAIN SELECT t1.* FROM t1 LEFT JOIN t2 USING (c1);
+DROP TABLE t1,t2;
+
+CREATE TABLE t1 (c1 DOUBLE PRIMARY KEY);
+INSERT INTO t1 VALUES (1),(2);
+CREATE TABLE t2 (c1 SET('a','b'));
+INSERT INTO t2 VALUES ('a'),('b');
+SELECT t1.* FROM t1 JOIN t2 USING (c1);
+SELECT t1.* FROM t1 LEFT JOIN t2 USING (c1);
+ALTER TABLE t2 ADD PRIMARY KEY(c1);
+SELECT t1.* FROM t1 JOIN t2 USING (c1);
+SELECT t1.* FROM t1 LEFT JOIN t2 USING (c1);
+--echo # Ok to eliminated t2
+EXPLAIN SELECT t1.* FROM t1 LEFT JOIN t2 USING (c1);
+DROP TABLE t1,t2;
+
+CREATE TABLE t1 (c1 DECIMAL(10,1) PRIMARY KEY);
+INSERT INTO t1 VALUES (1),(2);
+CREATE TABLE t2 (c1 SET('a','b'));
+INSERT INTO t2 VALUES ('a'),('b');
+SELECT t1.* FROM t1 JOIN t2 USING (c1);
+SELECT t1.* FROM t1 LEFT JOIN t2 USING (c1);
+ALTER TABLE t2 ADD PRIMARY KEY(c1);
+#
+# Uncomment this after fixing MDEV-6993
+#SELECT t1.* FROM t1 JOIN t2 USING (c1);
+#
+SELECT t1.* FROM t1 LEFT JOIN t2 USING (c1);
+--echo # Ok to eliminated t2
+EXPLAIN SELECT t1.* FROM t1 LEFT JOIN t2 USING (c1);
+DROP TABLE t1,t2;
+
+--echo #
+--echo # End of 10.0 tests
+--echo #

=== modified file 'mysql-test/t/type_uint.test'
--- mysql-test/t/type_uint.test	2005-07-28 00:22:47 +0000
+++ mysql-test/t/type_uint.test	2014-11-03 09:05:02 +0000
@@ -15,3 +15,29 @@ select * from t1;
 drop table t1;
 
 # End of 4.1 tests
+
+
+--echo #
+--echo # Start of 10.0 tests
+--echo #
+
+--echo #
+--echo # MDEV-6950 Bad results with joins comparing DATE and INT/ENUM/VARCHAR columns
+--echo #
+CREATE TABLE t1 (a DATE PRIMARY KEY);
+INSERT INTO t1 VALUES ('1999-01-01');
+CREATE TABLE t2 (a INT UNSIGNED);
+INSERT INTO t2 VALUES (19990101);
+INSERT INTO t2 VALUES (990101);
+SELECT t1.* FROM t1,t2 WHERE t1.a=t2.a;
+SELECT t1.* FROM t1 LEFT JOIN t2 ON t1.a=t2.a;
+ALTER TABLE t2 ADD PRIMARY KEY(a);
+SELECT t1.* FROM t1,t2 WHERE t1.a=t2.a;
+SELECT t1.* FROM t1 LEFT JOIN t2 ON t1.a=t2.a;
+--echo # t2 should NOT be eliminated
+EXPLAIN SELECT t1.* FROM t1 LEFT JOIN t2 ON t1.a=t2.a;
+DROP TABLE t1,t2;
+
+--echo #
+--echo # End of 10.0 tests
+--echo #

=== modified file 'mysql-test/t/type_varchar.test'
--- mysql-test/t/type_varchar.test	2009-10-09 13:34:07 +0000
+++ mysql-test/t/type_varchar.test	2014-11-03 09:06:21 +0000
@@ -217,3 +217,66 @@ CREATE TABLE t1 (a CHAR(16));
 INSERT INTO t1 VALUES ('5'), ('s'), ('');
 SELECT 5 = a FROM t1;
 DROP TABLE t1;
+
+--echo #
+--echo # Start of 10.0 tests
+--echo #
+
+--echo #
+--echo # MDEV-6950 Bad results with joins comparing DATE and INT/ENUM/VARCHAR columns
+--echo #
+CREATE TABLE t1 (c1 DATE PRIMARY KEY);
+INSERT INTO t1 VALUES ('2001-01-01');
+CREATE TABLE t2 (c1 VARCHAR(20));
+INSERT INTO t2 VALUES ('2001-01-01');
+INSERT INTO t2 VALUES ('2001/01/01');
+SELECT t1.* FROM t1,t2 WHERE t1.c1=t2.c1;
+SELECT t1.* FROM t1 LEFT JOIN t2 ON t1.c1=t2.c1;
+ALTER TABLE t2 ADD PRIMARY KEY(c1);
+SELECT t1.* FROM t1,t2 WHERE t1.c1=t2.c1;
+EXPLAIN SELECT t1.* FROM t1,t2 WHERE t1.c1=t2.c1;
+SELECT t1.* FROM t1 LEFT JOIN t2 ON t1.c1=t2.c1;
+--echo # t2 should NOT be eliminated
+EXPLAIN SELECT t1.* FROM t1 LEFT JOIN t2 ON t1.c1=t2.c1;
+DROP TABLE IF EXISTS t1,t2;
+
+
+--echo #
+--echo # MDEV-6989 BINARY and COLLATE xxx_bin comparisions are not used for optimization in some cases
+--echo #
+CREATE TABLE t1 (c1 VARCHAR(20) CHARACTER SET latin1, PRIMARY KEY(c1));
+INSERT INTO t1 VALUES ('a'),('b'),('c'),('d');
+SELECT * FROM t1 WHERE c1=BINARY 'a';
+EXPLAIN SELECT * FROM t1 WHERE c1=BINARY 'a';
+SELECT * FROM t1 WHERE c1=_latin1'a' COLLATE latin1_bin;
+EXPLAIN SELECT * FROM t1 WHERE c1=_latin1'a' COLLATE latin1_bin;
+DROP TABLE t1;
+
+CREATE TABLE t1 (c1 VARCHAR(10) CHARACTER SET latin1 COLLATE latin1_bin);
+INSERT INTO t1 VALUES ('a');
+CREATE TABLE t2 (c1 VARCHAR(10) CHARACTER SET latin1, PRIMARY KEY(c1));
+INSERT INTO t2 VALUES ('a'),('b');
+SELECT * FROM t1, t2 WHERE t1.c1=t2.c1;
+EXPLAIN SELECT * FROM t1, t2 WHERE t1.c1=t2.c1;
+ALTER TABLE t1 MODIFY c1 VARBINARY(10);
+SELECT * FROM t1, t2 WHERE t1.c1=t2.c1;
+EXPLAIN SELECT * FROM t1, t2 WHERE t1.c1=t2.c1;
+DROP TABLE t1, t2;
+
+CREATE TABLE t1 (c1 VARCHAR(10) CHARACTER SET latin1 COLLATE latin1_bin);
+INSERT INTO t1 VALUES ('a'),('c');
+CREATE TABLE t2 (c1 VARCHAR(10) CHARACTER SET latin1, PRIMARY KEY(c1));
+INSERT INTO t2 VALUES ('a'),('b');
+SELECT t1.* FROM t1 LEFT JOIN t2 USING (c1);
+--echo # t2 should be eliminated
+EXPLAIN SELECT t1.* FROM t1 LEFT JOIN t2 USING (c1);
+ALTER TABLE t1 MODIFY c1 VARBINARY(10);
+SELECT t1.* FROM t1 LEFT JOIN t2 USING (c1);
+--echo # t2 should be eliminated
+EXPLAIN SELECT t1.* FROM t1 LEFT JOIN t2 USING (c1);
+DROP TABLE t1,t2;
+
+--echo #
+--echo # End of 10.0 tests
+--echo #
+

=== modified file 'sql/field.cc'
--- sql/field.cc	2014-10-20 12:42:00 +0000
+++ sql/field.cc	2014-11-03 07:15:23 +0000
@@ -1246,6 +1246,66 @@ double Field::pos_in_interval_val_str(Fi
 }
 
 
+/*
+  This handles all numeric and BIT data types.
+*/
+bool Field::can_optimize(const Tool &tool,
+                         const Item_func *cond, const Item *arg)
+{
+  DBUG_ASSERT((tool.key_flags() & HA_SPATIAL) == 0);
+  DBUG_ASSERT(cmp_type() != STRING_RESULT);
+  /*
+    Can't optimize a date/datetime/timestamp condition on a numeruc column,
+    as mapping from numbers to date/datetime/timestamp is not linear and
+    not consistent:
+    - Different numeric values can map to the same temporal value
+      (990101 and 19990101 map to DATE'1999-01-01')
+    - Bad values like -1 and 99991232 (notice bad day) are equal to zero date:
+         SELECT DATE'0000-00-00'=-1;        -> 1
+         SELECT DATE'0000-00-00'=99991232;  -> 1
+      The same for time:
+         SELECT TIME'00:00:00'=99991231;    -> 1
+    - Numbers and temporal values can be compared differently in different
+      context (e.g. mdev-6983).
+    Also, can't optimize a string condition on a numeric column.
+    Allow only conditions on numeric data types.
+  */
+  if (cond->compare_type() != INT_RESULT &&
+      cond->compare_type() != DECIMAL_RESULT  &&
+      cond->compare_type() != REAL_RESULT )
+    return false;
+
+  switch (tool.type())
+  {
+  case TOOL_KEYPART_RANGE:
+    return tool.optimize_range(table);
+  case TOOL_KEY_GROUP_MIN_MAX:
+  case TOOL_PARTITION_RANGE:
+  case TOOL_SELECTIVITY:
+    return true;
+  case TOOL_EQUALITY_FOR_OUTER_JOIN_TABLE_ELIMINATION:
+  case TOOL_KEYPART_RANGE_TRIVIAL:
+  case TOOL_KEYPART_REF:
+  case TOOL_PARTITION_REF:
+  case TOOL_HASH_JOIN:
+    /*
+      This fixes MDEV-6969 and MDEV-6986 but breaks some existing tests.
+      Needs a discussion with the optimizer team.
+    */
+    /*
+    if (query.cond()->compare_type() == REAL_RESULT)
+    {
+      uint precise_digits= type() == MYSQL_TYPE_FLOAT ? 7 : 15;
+      if (query.item()->max_length > precise_digits)
+        return false;
+    }
+    */
+    return true;
+  }
+  return false;
+}
+
+
 /**
   Numeric fields base class constructor.
 */
@@ -1933,12 +1993,6 @@ int Field::store_time_dec(MYSQL_TIME *lt
 }
 
 
-bool Field::optimize_range(uint idx, uint part)
-{
-  return MY_TEST(table->file->index_flags(idx, part, 1) & HA_READ_RANGE);
-}
-
-
 Field *Field::new_field(MEM_ROOT *root, TABLE *new_table,
                         bool keep_type __attribute__((unused)))
 {
@@ -5256,6 +5310,31 @@ my_decimal *Field_temporal::val_decimal(
   return TIME_to_my_decimal(&ltime, d);
 }
 
+
+bool Field_temporal::can_optimize(const Tool &tool,
+                                  const Item_func *cond, const Item *arg)
+{
+  DBUG_ASSERT((tool.key_flags() & HA_SPATIAL) == 0);
+  if (cond->compare_type() != TIME_RESULT)
+    return false;
+  switch (tool.type())
+  {
+  case TOOL_KEYPART_RANGE:
+    return tool.optimize_range(table);
+  case TOOL_EQUALITY_FOR_OUTER_JOIN_TABLE_ELIMINATION:
+  case TOOL_KEY_GROUP_MIN_MAX:
+  case TOOL_PARTITION_RANGE:
+  case TOOL_KEYPART_RANGE_TRIVIAL:
+  case TOOL_KEYPART_REF:
+  case TOOL_PARTITION_REF:
+  case TOOL_HASH_JOIN:
+  case TOOL_SELECTIVITY:
+    return true;
+  }
+  return false;
+}
+
+
 /****************************************************************************
 ** time type
 ** In string context: HH:MM:SS
@@ -6462,6 +6541,72 @@ uint32 Field_longstr::max_data_length()
 }
 
 
+bool Field_longstr::can_optimize(const Tool &tool,
+                                 const Item_func *cond, const Item *arg)
+
+{
+  DBUG_ASSERT((tool.key_flags() & HA_SPATIAL) == 0);
+  DBUG_ASSERT(arg->result_type() == STRING_RESULT ||
+              arg->cmp_type() != STRING_RESULT);
+
+  if (cond->compare_type() != STRING_RESULT)
+    return false;
+
+  switch (tool.type())
+  {
+  case TOOL_KEYPART_RANGE:
+    if (!tool.optimize_range(table))
+      return false;
+    // Pass through
+  case TOOL_HASH_JOIN:
+  case TOOL_PARTITION_RANGE:
+  case TOOL_KEY_GROUP_MIN_MAX:
+    return charset() == cond->compare_collation();
+
+  case TOOL_EQUALITY_FOR_OUTER_JOIN_TABLE_ELIMINATION:
+    /*
+      A string column cannot be eliminated when compared as a number:
+        CREATE TABLE t1 (a INT PRIMARY KEY);
+        CREATE TABLE t2 (a VARCHAR(10) PRIMARY KEY);
+        SELECT t1.* FROM t1 LEFT JOIN t2 USING (a);
+      because unique string values '10' and '010' produce
+      non-unique val_int(), val_real(), val_decimal() results.
+
+      A string column cannot be eliminated when compared as date/time/datetime,
+      because unique string values can produce non-unique get_date() values:
+        '2001-10-10' and '2001/10/10' -> DATE'2001-10-10'
+        '10:10:10' and ' 10:10:10'    -> TIME'10:10:10'
+
+      A string column can be eliminated when compared as a string, only:
+      - if the collation of the operation is exactly the same with the
+        collation of the column;
+      - or if binary comparison is used.
+        These three queries return exactly the same set of rows:
+          SELECT DISTINCT primary_key_value FROM t2;
+          SELECT DISTINCT BINARY primary_key_value FROM t2;
+          SELECT DISTINCT primary_key_value COLLATE xxx_bin FROM t2;
+        So it should be safe to eliminate t2 from a LEFT JOIN in case
+        of binary comparison.
+    */
+  case TOOL_KEYPART_REF:
+  case TOOL_KEYPART_RANGE_TRIVIAL:
+  case TOOL_PARTITION_REF:
+  case TOOL_SELECTIVITY:
+    /*
+      Usually we can't use an index if the column collation
+      differ from the operation collation, with the exception that it's okey
+      to use any index (including case or accent insensitive indexes)
+      for binary equality:
+        WHERE latin1_swedish_ci_column = 'a' COLLATE lati1_bin
+        WHERE latin1_swedish_ci_colimn = BINARY 'a '
+    */
+    return (charset() == cond->compare_collation() ||
+           (cond->compare_collation()->state & MY_CS_BINSORT));
+  }
+  return false;
+}
+
+
 double Field_string::val_real(void)
 {
   ASSERT_COLUMN_MARKED_FOR_READ;
@@ -7923,6 +8068,38 @@ int Field_geom::store(const char *from,
   return -1;
 }
 
+
+bool Field_geom::can_optimize(const Tool &tool,
+                              const Item_func *cond, const Item *arg)
+
+{
+  if ((tool.key_flags() & HA_SPATIAL) == 0)
+  {
+    // Non spatial operation on a spatial column
+    return Field_blob::can_optimize(tool, cond, arg);
+  }
+
+  // @todo: use is_spatial_operator() instead?
+  switch (cond->functype()) {
+  case Item_func::SP_EQUALS_FUNC:
+  case Item_func::SP_DISJOINT_FUNC:
+  case Item_func::SP_INTERSECTS_FUNC:
+  case Item_func::SP_TOUCHES_FUNC:
+  case Item_func::SP_CROSSES_FUNC:
+  case Item_func::SP_WITHIN_FUNC:
+  case Item_func::SP_CONTAINS_FUNC:
+  case Item_func::SP_OVERLAPS_FUNC:
+    return true;
+  default:
+    /* 
+      We cannot involve spatial indexes for queries that
+      don't use MBREQUALS(), MBRDISJOINT(), etc. functions.
+    */
+    break;
+  }
+  return false;
+}
+
 #endif /*HAVE_SPATIAL*/
 
 /****************************************************************************
@@ -8366,6 +8543,68 @@ const uchar *Field_enum::unpack(uchar *t
 }
 
 
+bool Field_enum::can_optimize(const Tool &tool,
+                              const Item_func *cond, const Item *arg)
+{
+  DBUG_ASSERT((tool.key_flags() & HA_SPATIAL) == 0);
+
+  switch (tool.type())
+  {
+  case TOOL_EQUALITY_FOR_OUTER_JOIN_TABLE_ELIMINATION:
+  {
+    /*
+      We can't eliminate t2 in case of temporal data types in t1:
+        CREATE TABLE t1 (a DATE PRIMARY KEY);
+        CREATE TABLE t2 (a ENUM('2001-01-01','2001/01/01') PRIMARY KEY);
+        SELECT t1.* FROM t1 LEFT JOIN t2 USING (a);
+      because two different ENUM values '2001-01-01' and '2001/01/01'
+      map to the same DATE value '2001-01-01'. Disallow TIME_RESULT.
+
+      Allow all numeric data types (INT, REAL, DECIMAL results).
+      If a set of ENUM values is known to be unique, the set of its
+      val_int(), val_real() and val_decimal() values is also guaranteed
+      to be unique.
+      Allow string data types (STRING result) if the collation of the operation
+      is exactly the same with the collation of the field.
+    */
+    return (cond->compare_type() == STRING_RESULT &&
+            charset() == cond->compare_collation()) ||
+            cond->compare_type() == INT_RESULT ||
+            cond->compare_type() == REAL_RESULT ||
+            cond->compare_type() == DECIMAL_RESULT;
+  }
+  case TOOL_KEY_GROUP_MIN_MAX:
+    /*
+      Can't use GROUP_MIN_MAX optimization, because the values are stored
+      as numbers in index, while MIN() and MAX() work as strings.
+      It would return the records with min and max enum numeric indexes.
+      "Bug#45300 MAX() and ENUM type" should be fixed first.
+    */
+  case TOOL_KEYPART_RANGE:
+  case TOOL_PARTITION_RANGE:
+    return false;
+  case TOOL_KEYPART_RANGE_TRIVIAL:
+  case TOOL_KEYPART_REF:
+  case TOOL_PARTITION_REF:
+  case TOOL_HASH_JOIN:
+  case TOOL_SELECTIVITY:
+    /*
+      ENUM/SET do not allow ref access for TIME_RESULT conditions,
+      because multiple ENUM/SET values can map to the same temporal value:
+      Both values in ENUM('2010/01/01','2010-01-01') equal to DATE'2010-01-01'.
+      So the lines below do not include TIME_RESULT.
+      (these lines may slightly change after fixing MDEV-6993 though)
+    */
+    return (cond->compare_type() == STRING_RESULT &&
+            charset() == cond->compare_collation()) ||
+            arg->cmp_type() == INT_RESULT ||
+            arg->cmp_type() == DECIMAL_RESULT ||
+            arg->cmp_type() == REAL_RESULT;
+  }
+  return false;
+}
+
+
 /**
   @return
   returns 1 if the fields are equally defined

=== modified file 'sql/field.h'
--- sql/field.h	2014-10-20 12:42:00 +0000
+++ sql/field.h	2014-11-03 07:06:48 +0000
@@ -271,7 +271,182 @@ class Virtual_column_info: public Sql_al
   }
 };
 
-class Field
+
+class Item_func;
+
+class Field_optimizer
+{
+protected:
+  enum Tooltype
+  {
+    TOOL_KEYPART_REF,
+    TOOL_KEYPART_RANGE,
+    TOOL_KEYPART_RANGE_TRIVIAL,  // an empty range: a BETWEEN 1 AND 1
+    TOOL_KEY_GROUP_MIN_MAX,
+    TOOL_PARTITION_REF,
+    TOOL_PARTITION_RANGE,
+    TOOL_HASH_JOIN,
+    TOOL_EQUALITY_FOR_OUTER_JOIN_TABLE_ELIMINATION,
+    TOOL_SELECTIVITY
+  };
+
+  class Key_part
+  {
+    uint m_keyno;
+    uint m_partno;
+  public:
+    Key_part(uint keyno, uint partno)
+     :m_keyno(keyno), m_partno(partno)
+    { }
+    bool optimize_range(const TABLE *table) const
+    {
+      return table->optimize_range(m_keyno, m_partno);
+    }
+  };
+
+  class Tool
+  {
+  public:
+    virtual Tooltype type() const = 0;
+    virtual uint key_flags() const { return 0; }
+    virtual bool optimize_range(const TABLE *table) const { return false; }
+  };
+
+  class Tool_key: public Tool
+  {
+    const KEY *m_key;
+  public:
+    Tool_key(const KEY *key) :m_key(key) { }
+    const KEY *key() const { return m_key; }
+    uint key_flags() const { return m_key->flags; }
+  };
+
+  class Tool_key_group_min_max: public Tool_key
+  {
+  public:
+    Tool_key_group_min_max(const KEY *key) :Tool_key(key) { }
+    Tooltype type() const { return TOOL_KEY_GROUP_MIN_MAX; }
+  };
+
+  class Tool_keypart: public Tool_key, public Key_part
+  {
+  public:
+    Tool_keypart(const KEY *key, uint16 keyno, uint16 partno)
+      :Tool_key(key), Key_part(keyno, partno) { }
+    bool optimize_range(const TABLE *table) const
+    { return Key_part::optimize_range(table); }
+  };
+
+  class Tool_keypart_ref: public Tool_keypart
+  {
+  public:
+    Tool_keypart_ref(const KEY *key, uint16 keyno, uint16 partno):
+      Tool_keypart(key, keyno, partno) { }
+    Tooltype type() const { return TOOL_KEYPART_REF; }
+  };
+
+  class Tool_keypart_range: public Tool_keypart
+  {
+  public:
+    Tool_keypart_range(const KEY *key, uint16 keyno, uint16 parno)
+      :Tool_keypart(key, keyno, parno) { }
+    Tooltype type() const { return TOOL_KEYPART_RANGE; }
+  };
+
+  class Tool_keypart_range_trivial: public Tool_keypart
+  {
+  public:
+    Tool_keypart_range_trivial(const KEY *key, uint16 keyno, uint16 parno)
+      :Tool_keypart(key, keyno, parno) { }
+    Tooltype type() const { return TOOL_KEYPART_RANGE_TRIVIAL; }
+  };
+
+  class Tool_partition: public Tool
+  {
+    Tooltype m_type;
+  public:
+    Tool_partition(Tooltype type)
+      :m_type(type)
+    {
+      DBUG_ASSERT(type == TOOL_PARTITION_REF || type == TOOL_PARTITION_RANGE);
+    }
+    Tooltype type() const { return m_type; }
+  };
+
+  class Tool_selectivity: public Tool
+  {
+  public:
+    Tooltype type() const { return TOOL_SELECTIVITY; }
+  };
+
+  class Tool_hash_join: public Tool
+  {
+  public:
+    Tooltype type() const { return TOOL_HASH_JOIN; }
+  };
+
+  class Tool_equality_for_outer_join_table_elimination: public Tool
+  {
+  public:
+    Tooltype type() const
+    {
+      return TOOL_EQUALITY_FOR_OUTER_JOIN_TABLE_ELIMINATION;
+    }
+  };
+
+  virtual bool can_optimize(const Tool &tool,
+                            const Item_func *cond, const Item *arg) = 0;
+
+public:
+  bool can_optimize_keypart_ref(const KEY *key, uint keyno, uint partno,
+                                Item_func *cond, Item *item)
+  {
+    return can_optimize(Tool_keypart_ref(key, keyno, partno), cond, item);
+  }
+  bool can_optimize_keypart_range(const KEY *key, uint keyno, uint partno,
+                                  Item_func *cond, Item *item)
+  {
+    return can_optimize(Tool_keypart_range(key, keyno, partno), cond, item);
+  }
+  bool can_optimize_keypart_range_trivial(const KEY *key,
+                                          uint keyno, uint partno,
+                                          Item_func *cond, Item *item)
+  {
+    return can_optimize(Tool_keypart_range_trivial(key, keyno, partno),
+                                                   cond, item);
+  }
+  bool can_optimize_partition_ref(Item_func *cond, Item *item)
+  {
+    return can_optimize(Tool_partition(TOOL_PARTITION_REF), cond, item);
+  }
+  bool can_optimize_partition_range(Item_func *cond, Item *item)
+  {
+    return can_optimize(Tool_partition(TOOL_PARTITION_RANGE), cond, item);
+  }
+  bool can_optimize_key_group_min_max(const KEY *key,
+                                      Item_func *cond, Item *item)
+  {
+    return can_optimize(Tool_key_group_min_max(key), cond, item);
+  }
+  bool can_optimize_for_selectivity(Item_func *cond, Item *item)
+  {
+    return can_optimize(Tool_selectivity(), cond, item);
+  }
+  bool can_optimize_equality_for_outer_join_table_elimination(
+                                                          Item_func *cond,
+                                                          Item *item)
+  {
+    return can_optimize(Tool_equality_for_outer_join_table_elimination(),
+                        cond, item);
+  }
+  bool can_optimize_hash_join(Item_func *cond, Item *item)
+  {
+    return can_optimize(Tool_hash_join(), cond, item);
+  }
+};
+
+
+class Field: public Field_optimizer
 {
   Field(const Item &);				/* Prevent use of these */
   void operator=(Field &);
@@ -731,7 +906,6 @@ class Field
   void make_sort_key(uchar *buff, uint length);
   virtual void make_field(Send_field *);
   virtual void sort_string(uchar *buff,uint length)=0;
-  virtual bool optimize_range(uint idx, uint part);
   virtual void free() {}
   virtual Field *new_field(MEM_ROOT *root, TABLE *new_table,
                            bool keep_type);
@@ -850,16 +1024,6 @@ class Field
   { return binary() ? &my_charset_bin : charset(); }
   virtual CHARSET_INFO *sort_charset(void) const { return charset(); }
   virtual bool has_charset(void) const { return FALSE; }
-  /*
-    match_collation_to_optimize_range() is to distinguish in
-    range optimizer (see opt_range.cc) between real string types:
-      CHAR, VARCHAR, TEXT
-    and the other string-alike types with result_type() == STRING_RESULT:
-      DATE, TIME, DATETIME, TIMESTAMP
-    We need it to decide whether to test if collation of the operation
-    matches collation of the field (needed only for real string types).
-  */
-  virtual bool match_collation_to_optimize_range() const { return false; }
   virtual void set_charset(CHARSET_INFO *charset_arg) { }
   virtual enum Derivation derivation(void) const
   { return DERIVATION_IMPLICIT; }
@@ -952,6 +1116,7 @@ class Field
     return (double) 0.5; 
   }
 
+  bool can_optimize(const Tool &tool, const Item_func *cond, const Item *arg);
   friend int cre_myisam(char * name, register TABLE *form, uint options,
 			ulonglong auto_increment_value);
   friend class Copy_field;
@@ -1132,7 +1297,7 @@ class Field_longstr :public Field_str
 
   int store_decimal(const my_decimal *d);
   uint32 max_data_length() const;
-  bool match_collation_to_optimize_range() const { return true; }
+  bool can_optimize(const Tool &tool, const Item_func *cond, const Item *arg);
 };
 
 /* base class for float and double and decimal (old one) */
@@ -1560,6 +1725,11 @@ class Field_null :public Field_str {
   uint size_of() const { return sizeof(*this); }
   uint32 max_display_length() { return 4; }
   void move_field_offset(my_ptrdiff_t ptr_diff) {}
+  bool can_optimize(const Tool &tool, const Item_func *cond, const Item *arg)
+  {
+    DBUG_ASSERT(0);
+    return false;
+  }
 };
 
 
@@ -1592,6 +1762,7 @@ class Field_temporal: public Field {
   {
     return pos_in_interval_val_real(min, max);
   }
+  bool can_optimize(const Tool &tool, const Item_func *cond, const Item *arg);
 };
 
 
@@ -2551,7 +2722,6 @@ class Field_geom :public Field_blob {
   { geom_type= geom_type_arg; }
   enum ha_base_keytype key_type() const { return HA_KEYTYPE_VARBINARY2; }
   enum_field_types type() const { return MYSQL_TYPE_GEOMETRY; }
-  bool match_collation_to_optimize_range() const { return false; }
   void sql_type(String &str) const;
   int  store(const char *to, uint length, CHARSET_INFO *charset);
   int  store(double nr);
@@ -2566,6 +2736,7 @@ class Field_geom :public Field_blob {
   int reset(void) { return Field_blob::reset() || !maybe_null(); }
 
   geometry_type get_geometry_type() { return geom_type; };
+  bool can_optimize(const Tool &tool, const Item_func *cond, const Item *arg);
 };
 #endif /*HAVE_SPATIAL*/
 
@@ -2608,7 +2779,7 @@ class Field_enum :public Field_str {
   { return (field_metadata & 0x00ff); }
   uint row_pack_length() const { return pack_length(); }
   virtual bool zero_pack() const { return 0; }
-  bool optimize_range(uint idx, uint part) { return 0; }
+  bool can_optimize(const Tool &tool, const Item_func *cond, const Item *arg);
   bool eq_def(Field *field);
   bool has_charset(void) const { return TRUE; }
   /* enum and set are sorted as integers */

=== modified file 'sql/item.h'
--- sql/item.h	2014-11-01 21:08:09 +0000
+++ sql/item.h	2014-11-03 05:38:06 +0000
@@ -114,6 +114,11 @@ class DTCollation {
     derivation= derivation_arg;
     set_repertoire_from_charset(collation_arg);
   }
+  DTCollation(CHARSET_INFO *collation_arg, Derivation derivation_arg,
+              uint repertoire_arg)
+    :collation(collation_arg), derivation(derivation_arg),
+     repertoire(repertoire_arg)
+  { }
   void set(DTCollation &dt)
   { 
     collation= dt.collation;
@@ -1182,7 +1187,6 @@ class Item {
   virtual Item *get_tmp_table_item(THD *thd) { return copy_or_same(thd); }
 
   static CHARSET_INFO *default_charset();
-  virtual CHARSET_INFO *compare_collation() { return NULL; }
 
   /*
     For backward compatibility, to make numeric

=== modified file 'sql/item_cmpfunc.cc'
--- sql/item_cmpfunc.cc	2014-11-01 21:08:09 +0000
+++ sql/item_cmpfunc.cc	2014-11-03 07:27:47 +0000
@@ -140,8 +140,8 @@ static int cmp_row_type(Item* item1, Ite
   Aggregates result types from the array of items.
 
   SYNOPSIS:
-    agg_cmp_type()
-    type   [out] the aggregated type
+    set_compare_context()
+    name         name of the function, for error reporting
     items        array of items to aggregate the type from
     nitems       number of items in the array
 
@@ -159,13 +159,15 @@ static int cmp_row_type(Item* item1, Ite
     0  otherwise
 */
 
-static int agg_cmp_type(Item_result *type, Item **items, uint nitems)
+bool Hybrid_cmp_context::set_compare_context(const char *name,
+                                             Item **items,
+                                             uint nitems)
 {
   uint i;
-  type[0]= items[0]->cmp_type();
+  m_compare_type= items[0]->cmp_type();
   for (i= 1 ; i < nitems ; i++)
   {
-    type[0]= item_cmp_type(type[0], items[i]->cmp_type());
+    m_compare_type= item_cmp_type(m_compare_type, items[i]->cmp_type());
     /*
       When aggregating types of two row expressions we have to check
       that they have the same cardinality and that each component
@@ -173,10 +175,13 @@ static int agg_cmp_type(Item_result *typ
       the signature of the corresponding component of the second row
       expression.
     */ 
-    if (type[0] == ROW_RESULT && cmp_row_type(items[0], items[i]))
-      return 1;     // error found: invalid usage of rows
+    if (m_compare_type == ROW_RESULT && cmp_row_type(items[0], items[i]))
+      return true;     // error found: invalid usage of rows
   }
-  return 0;
+  if (m_compare_type == STRING_RESULT &&
+      agg_item_charsets_for_comparison(cmp_collation, name, items, nitems))
+    return true;
+  return false;
 }
 
 
@@ -537,26 +542,10 @@ void Item_bool_func2::fix_length_and_dec
   if (!args[0] || !args[1])
     return;
 
-  /* 
-    We allow to convert to Unicode character sets in some cases.
-    The conditions when conversion is possible are:
-    - arguments A and B have different charsets
-    - A wins according to coercibility rules
-    - character set of A is superset for character set of B
-   
-    If all of the above is true, then it's possible to convert
-    B into the character set of A, and then compare according
-    to the collation of A.
-  */
-
-  DTCollation coll;
-  if (args[0]->cmp_type() == STRING_RESULT &&
-      args[1]->cmp_type() == STRING_RESULT &&
-      agg_arg_charsets_for_comparison(coll, args, 2))
+  if (cmp.set_compare_context(func_name(), args, 2))
     return;
-    
-  args[0]->cmp_context= args[1]->cmp_context=
-    item_cmp_type(args[0]->result_type(), args[1]->result_type());
+
+  args[0]->cmp_context= args[1]->cmp_context= cmp.compare_type();
 
   /*
     Make a special case of compare with fields to get nicer comparisons
@@ -578,7 +567,10 @@ void Item_bool_func2::fix_length_and_dec
       if ((field_item->field_type() ==  MYSQL_TYPE_LONGLONG ||
            field_item->field_type() ==  MYSQL_TYPE_YEAR) &&
           convert_const_to_int(thd, field_item, &args[!field]))
+      {
         args[0]->cmp_context= args[1]->cmp_context= INT_RESULT;
+        cmp.set_compare_type(INT_RESULT);
+      }
     }
   }
   set_cmp_func();
@@ -591,6 +583,7 @@ int Arg_comparator::set_compare_func(Ite
   func= comparator_matrix[type]
                          [is_owner_equal_func()];
 
+  set_compare_type(type);
   switch (type) {
   case TIME_RESULT:
     cmp_collation.collation= &my_charset_numeric;
@@ -2247,11 +2240,8 @@ void Item_func_between::fix_length_and_d
   */
   if (!args[0] || !args[1] || !args[2])
     return;
-  if ( agg_cmp_type(&cmp_type, args, 3))
+  if (set_compare_context(func_name(), args, 3))
     return;
-  if (cmp_type == STRING_RESULT &&
-      agg_arg_charsets_for_comparison(cmp_collation, args, 3))
-   return;
 
   /*
     When comparing as date/time, we need to convert non-temporal values
@@ -2261,7 +2251,7 @@ void Item_func_between::fix_length_and_d
     For this to work, we need to know what date/time type we compare
     strings as.
   */
-  if (cmp_type ==  TIME_RESULT)
+  if (compare_type() ==  TIME_RESULT)
     compare_as_dates= find_date_time_item(args, 3, 0);
 
   /* See the comment about the similar block in Item_bool_func2 */
@@ -2275,7 +2265,7 @@ void Item_func_between::fix_length_and_d
       const bool cvt_arg1= convert_const_to_int(thd, field_item, &args[1]);
       const bool cvt_arg2= convert_const_to_int(thd, field_item, &args[2]);
       if (cvt_arg1 && cvt_arg2)
-        cmp_type=INT_RESULT;                    // Works for all types.
+        set_compare_type(INT_RESULT);                    // Works for all types.
     }
   }
 }
@@ -2285,7 +2275,7 @@ longlong Item_func_between::val_int()
 {
   DBUG_ASSERT(fixed == 1);
 
-  switch (cmp_type) {
+  switch (compare_type()) {
   case TIME_RESULT:
   {
     THD *thd= current_thd;
@@ -3993,7 +3983,7 @@ void Item_func_in::fix_length_and_dec()
   Item *date_arg= 0;
   uint found_types= 0;
   uint type_cnt= 0, i;
-  Item_result cmp_type= STRING_RESULT;
+  set_compare_type(STRING_RESULT);
   left_result_type= args[0]->cmp_type();
   if (!(found_types= collect_cmp_types(args, arg_count, true)))
     return;
@@ -4011,18 +4001,18 @@ void Item_func_in::fix_length_and_dec()
     if (found_types & (1U << i))
     {
       (type_cnt)++;
-      cmp_type= (Item_result) i;
+      set_compare_type((Item_result) i);
     }
   }
 
   if (type_cnt == 1)
   {
-    if (cmp_type == STRING_RESULT && 
+    if (compare_type() == STRING_RESULT && 
         agg_arg_charsets_for_comparison(cmp_collation, args, arg_count))
       return;
     arg_types_compatible= TRUE;
 
-    if (cmp_type == ROW_RESULT)
+    if (compare_type() == ROW_RESULT)
     {
       uint cols= args[0]->cols();
       cmp_item_row *cmp= 0;
@@ -4072,7 +4062,7 @@ void Item_func_in::fix_length_and_dec()
       See the comment about the similar block in Item_bool_func2
     */  
     if (args[0]->real_item()->type() == FIELD_ITEM &&
-        !thd->lex->is_view_context_analysis() && cmp_type != INT_RESULT)
+        !thd->lex->is_view_context_analysis() && compare_type() != INT_RESULT)
     {
       Item_field *field_item= (Item_field*) (args[0]->real_item());
       if (field_item->field_type() ==  MYSQL_TYPE_LONGLONG ||
@@ -4085,10 +4075,10 @@ void Item_func_in::fix_length_and_dec()
             all_converted= FALSE;
         }
         if (all_converted)
-          cmp_type= INT_RESULT;
+          set_compare_type(INT_RESULT);
       }
     }
-    switch (cmp_type) {
+    switch (compare_type()) {
     case STRING_RESULT:
       array=new in_string(arg_count-1,(qsort2_cmp) srtcmp_in, 
                           cmp_collation.collation);
@@ -4887,7 +4877,7 @@ longlong Item_func_like::val_int()
   null_value=0;
   if (canDoTurboBM)
     return turboBM_matches(res->ptr(), res->length()) ? 1 : 0;
-  return my_wildcmp(cmp.cmp_collation.collation,
+  return my_wildcmp(compare_collation(),
 		    res->ptr(),res->ptr()+res->length(),
 		    res2->ptr(),res2->ptr()+res2->length(),
 		    escape,wild_one,wild_many) ? 0 : 1;
@@ -4946,7 +4936,7 @@ bool Item_func_like::fix_fields(THD *thd
         return TRUE;
       }
 
-      if (use_mb(cmp.cmp_collation.collation))
+      if (use_mb(compare_collation()))
       {
         CHARSET_INFO *cs= escape_str->charset();
         my_wc_t wc;
@@ -4963,7 +4953,7 @@ bool Item_func_like::fix_fields(THD *thd
           code instead of Unicode code as "escape" argument.
           Convert to "cs" if charset of escape differs.
         */
-        CHARSET_INFO *cs= cmp.cmp_collation.collation;
+        CHARSET_INFO *cs= compare_collation();
         uint32 unused;
         if (escape_str->needs_conversion(escape_str->length(),
                                          escape_str->charset(), cs, &unused))
@@ -5270,7 +5260,7 @@ void Item_func_like::turboBM_compute_suf
   int            f = 0;
   int            g = plm1;
   int *const splm1 = suff + plm1;
-  CHARSET_INFO	*cs= cmp.cmp_collation.collation;
+  CHARSET_INFO	*cs= compare_collation();
 
   *splm1 = pattern_len;
 
@@ -5370,7 +5360,7 @@ void Item_func_like::turboBM_compute_bad
   int *end = bmBc + alphabet_size;
   int j;
   const int plm1 = pattern_len - 1;
-  CHARSET_INFO	*cs= cmp.cmp_collation.collation;
+  CHARSET_INFO	*cs= compare_collation();
 
   for (i = bmBc; i < end; i++)
     *i = pattern_len;
@@ -5402,7 +5392,7 @@ bool Item_func_like::turboBM_matches(con
   int shift = pattern_len;
   int j     = 0;
   int u     = 0;
-  CHARSET_INFO	*cs= cmp.cmp_collation.collation;
+  CHARSET_INFO	*cs= compare_collation();
 
   const int plm1=  pattern_len - 1;
   const int tlmpl= text_len - pattern_len;
@@ -5716,8 +5706,10 @@ Item *Item_bool_rowready_func2::negated_
   of the type Item_field or Item_direct_view_ref(Item_field). 
 */
 
-Item_equal::Item_equal(Item *f1, Item *f2, bool with_const_item)
-  : Item_bool_func(), eval_item(0), cond_false(0), cond_true(0), 
+Item_equal::Item_equal(const Hybrid_cmp_context context,
+                       Item *f1, Item *f2, bool with_const_item)
+  : Item_bool_func_hybrid_cmp_type(context),
+    eval_item(0), cond_false(0), cond_true(0), 
     context_field(NULL), link_equal_fields(FALSE)
 {
   const_item_cache= 0;
@@ -5743,8 +5735,9 @@ Item_equal::Item_equal(Item *f1, Item *f
 */
 
 Item_equal::Item_equal(Item_equal *item_equal)
-  : Item_bool_func(), eval_item(0), cond_false(0), cond_true(0),
-     context_field(NULL), link_equal_fields(FALSE)
+  : Item_bool_func_hybrid_cmp_type((Hybrid_cmp_context) *item_equal),
+    eval_item(0), cond_false(0), cond_true(0),
+    context_field(NULL), link_equal_fields(FALSE)
 {
   const_item_cache= 0;
   List_iterator_fast<Item> li(item_equal->equal_items);
@@ -6286,14 +6279,6 @@ void Item_equal::print(String *str, enum
 }
 
 
-CHARSET_INFO *Item_equal::compare_collation()
-{ 
-  Item_equal_fields_iterator it(*this);
-  Item *item= it++;
-  return item->collation.collation;
-}
-
-
 /*
   @brief Get the first equal field of multiple equality.
   @param[in] field   the field to get equal field to

=== modified file 'sql/item_cmpfunc.h'
--- sql/item_cmpfunc.h	2014-11-01 21:08:09 +0000
+++ sql/item_cmpfunc.h	2014-11-03 07:23:59 +0000
@@ -28,6 +28,25 @@
 #define PCRE_STATIC 1             /* Important on Windows */
 #include "pcre.h"                 /* pcre header file */
 
+
+class Hybrid_cmp_context
+{
+  Item_result m_compare_type;
+protected:
+  DTCollation cmp_collation;
+public:
+  Hybrid_cmp_context() :m_compare_type(REAL_RESULT) { }
+  Hybrid_cmp_context(const Field *field):
+    m_compare_type(field->cmp_type()),
+    cmp_collation(field->charset(), field->derivation(), field->repertoire())
+  { }
+  Item_result compare_type() const { return m_compare_type; }
+  CHARSET_INFO *compare_collation() const { return cmp_collation.collation; }
+  void set_compare_type(Item_result type) { m_compare_type= type; }
+  bool set_compare_context(const char *name, Item **args, uint arg_count);
+};
+
+
 extern Item_result item_cmp_type(Item_result a,Item_result b);
 class Item_bool_func2;
 class Arg_comparator;
@@ -36,7 +55,8 @@ typedef int (Arg_comparator::*arg_cmp_fu
 
 typedef int (*Item_field_cmpfunc)(Item *f1, Item *f2, void *arg); 
 
-class Arg_comparator: public Sql_alloc
+class Arg_comparator: public Sql_alloc,
+                      public Hybrid_cmp_context
 {
   Item **a, **b;
   arg_cmp_func func;
@@ -49,7 +69,6 @@ class Arg_comparator: public Sql_alloc
   Item *a_cache, *b_cache;         // Cached values of a and b items
                                    //   when one of arguments is NULL.
 public:
-  DTCollation cmp_collation;
   /* Allow owner function to use string buffers. */
   String value1, value2;
 
@@ -122,7 +141,9 @@ class Item_bool_func :public Item_int_fu
   Item_bool_func() :Item_int_func() {}
   Item_bool_func(Item *a) :Item_int_func(a) {}
   Item_bool_func(Item *a,Item *b) :Item_int_func(a,b) {}
+  Item_bool_func(Item *a, Item *b, Item *c) :Item_int_func(a, b, c) {}
   Item_bool_func(THD *thd, Item_bool_func *item) :Item_int_func(thd, item) {}
+  Item_bool_func(List<Item> &list) :Item_int_func(list) { }
   bool is_bool_func() { return 1; }
   void fix_length_and_dec() { decimals=0; max_length=1; }
   uint decimal_precision() const { return 1; }
@@ -364,7 +385,7 @@ class Le_creator :public Comp_creator
   virtual bool l_op() const { return 1; }
 };
 
-class Item_bool_func2 :public Item_int_func
+class Item_bool_func2 :public Item_bool_func
 {						/* Bool with 2 string args */
 protected:
   Arg_comparator cmp;
@@ -372,7 +393,7 @@ class Item_bool_func2 :public Item_int_f
 
 public:
   Item_bool_func2(Item *a,Item *b)
-    :Item_int_func(a,b), cmp(tmp_arg, tmp_arg+1),
+    :Item_bool_func(a, b), cmp(tmp_arg, tmp_arg+1),
      abort_on_null(FALSE) { sargable= TRUE; }
   void fix_length_and_dec();
   int set_cmp_func()
@@ -389,14 +410,13 @@ class Item_bool_func2 :public Item_int_f
   }
 
   bool is_null() { return MY_TEST(args[0]->is_null() || args[1]->is_null()); }
-  bool is_bool_func() { return 1; }
-  CHARSET_INFO *compare_collation() { return cmp.cmp_collation.collation; }
-  uint decimal_precision() const { return 1; }
+  CHARSET_INFO *compare_collation() const { return cmp.compare_collation(); }
+  Item_result compare_type() const { return cmp.compare_type(); }
   void top_level_item() { abort_on_null= TRUE; }
   Arg_comparator *get_comparator() { return &cmp; }
   void cleanup()
   {
-    Item_int_func::cleanup();
+    Item_bool_func::cleanup();
     cmp.cleanup();
   }
 
@@ -637,6 +657,23 @@ class Item_func_ne :public Item_bool_row
 };
 
 
+class Item_bool_func_hybrid_cmp_type :public Item_bool_func,
+                                      public Hybrid_cmp_context
+{
+public:
+  Item_bool_func_hybrid_cmp_type(const Hybrid_cmp_context context)
+    :Item_bool_func(), Hybrid_cmp_context(context) { }
+  Item_bool_func_hybrid_cmp_type(Item *a, Item *b, Item *c)
+    :Item_bool_func(a, b, c) { }
+  Item_bool_func_hybrid_cmp_type(List<Item> &list)
+    :Item_bool_func(list) { }
+  Item_result compare_type() const
+  { return Hybrid_cmp_context::compare_type(); }
+  CHARSET_INFO *compare_collation() const
+  { return  Hybrid_cmp_context::compare_collation(); }
+};
+
+
 /*
   The class Item_func_opt_neg is defined to factor out the functionality
   common for the classes Item_func_between and Item_func_in. The objects
@@ -646,16 +683,16 @@ class Item_func_ne :public Item_bool_row
 
 */
 
-class Item_func_opt_neg :public Item_int_func
+class Item_func_opt_neg :public Item_bool_func_hybrid_cmp_type
 {
 public:
   bool negated;     /* <=> the item represents NOT <func> */
   bool pred_level;  /* <=> [NOT] <func> is used on a predicate level */
 public:
   Item_func_opt_neg(Item *a, Item *b, Item *c)
-    :Item_int_func(a, b, c), negated(0), pred_level(0) {}
+    :Item_bool_func_hybrid_cmp_type(a, b, c), negated(0), pred_level(0) {}
   Item_func_opt_neg(List<Item> &list)
-    :Item_int_func(list), negated(0), pred_level(0) {}
+    :Item_bool_func_hybrid_cmp_type(list), negated(0), pred_level(0) {}
 public:
   inline void negate() { negated= !negated; }
   inline void top_level_item() { pred_level= 1; }
@@ -671,9 +708,7 @@ class Item_func_opt_neg :public Item_int
 
 class Item_func_between :public Item_func_opt_neg
 {
-  DTCollation cmp_collation;
 public:
-  Item_result cmp_type;
   String value0,value1,value2;
   /* TRUE <=> arguments will be compared as dates. */
   Item *compare_as_dates;
@@ -686,9 +721,6 @@ class Item_func_between :public Item_fun
   bool fix_fields(THD *, Item **);
   void fix_length_and_dec();
   virtual void print(String *str, enum_query_type query_type);
-  bool is_bool_func() { return 1; }
-  CHARSET_INFO *compare_collation() { return cmp_collation.collation; }
-  uint decimal_precision() const { return 1; }
   bool eval_not_null_tables(uchar *opt_arg);
   void fix_after_pullout(st_select_lex *new_parent, Item **ref);
   bool count_sargable_conds(uchar *arg);
@@ -1226,14 +1258,14 @@ class cmp_item_sort_string_in_static :pu
   function and only comparators for there result types are used.
 */
 
-class Item_func_case :public Item_func_hybrid_field_type
+class Item_func_case :public Item_func_hybrid_field_type,
+                      public Hybrid_cmp_context
 {
   int first_expr_num, else_expr_num;
   enum Item_result left_result_type;
   String tmp_value;
   uint ncases;
   Item_result cmp_type;
-  DTCollation cmp_collation;
   cmp_item *cmp_items[6]; /* For all result types */
   cmp_item *case_item;
 public:
@@ -1267,7 +1299,6 @@ class Item_func_case :public Item_func_h
   const char *func_name() const { return "case"; }
   virtual void print(String *str, enum_query_type query_type);
   Item *find_item(String *str);
-  CHARSET_INFO *compare_collation() { return cmp_collation.collation; }
   void cleanup();
   void agg_str_lengths(Item *arg);
   void agg_num_lengths(Item *arg);
@@ -1303,7 +1334,6 @@ class Item_func_in :public Item_func_opt
   bool arg_types_compatible;
   Item_result left_result_type;
   cmp_item *cmp_items[6]; /* One cmp_item for each result type */
-  DTCollation cmp_collation;
 
   Item_func_in(List<Item> &list)
     :Item_func_opt_neg(list), array(0), have_null(0),
@@ -1316,7 +1346,6 @@ class Item_func_in :public Item_func_opt
   longlong val_int();
   bool fix_fields(THD *, Item **);
   void fix_length_and_dec();
-  uint decimal_precision() const { return 1; }
   void cleanup()
   {
     uint i;
@@ -1337,8 +1366,6 @@ class Item_func_in :public Item_func_opt
   enum Functype functype() const { return IN_FUNC; }
   const char *func_name() const { return " IN "; }
   bool nulls_in_row();
-  bool is_bool_func() { return 1; }
-  CHARSET_INFO *compare_collation() { return cmp_collation.collation; }
   bool eval_not_null_tables(uchar *opt_arg);
   void fix_after_pullout(st_select_lex *new_parent, Item **ref);
 };
@@ -1374,10 +1401,22 @@ class in_row :public in_vector
 
 /* Functions used by where clause */
 
-class Item_func_isnull :public Item_bool_func
+
+class Item_func_null_predicate :public Item_bool_func
 {
 public:
-  Item_func_isnull(Item *a) :Item_bool_func(a) { sargable= TRUE; }
+  Item_func_null_predicate(Item *a) :Item_bool_func(a) { sargable= true; }
+  optimize_type select_optimize() const { return OPTIMIZE_NULL; }
+  CHARSET_INFO *compare_collation() const
+  { return args[0]->collation.collation; }
+  Item_result compare_type() const { return args[0]->cmp_type(); }
+};
+
+
+class Item_func_isnull :public Item_func_null_predicate
+{
+public:
+  Item_func_isnull(Item *a) :Item_func_null_predicate(a) { }
   longlong val_int();
   enum Functype functype() const { return ISNULL_FUNC; }
   void fix_length_and_dec()
@@ -1402,9 +1441,7 @@ class Item_func_isnull :public Item_bool
     }
   }
   table_map not_null_tables() const { return 0; }
-  optimize_type select_optimize() const { return OPTIMIZE_NULL; }
   Item *neg_transformer(THD *thd);
-  CHARSET_INFO *compare_collation() { return args[0]->collation.collation; }
 };
 
 /* Functions used by HAVING for rewriting IN subquery */
@@ -1435,12 +1472,12 @@ class Item_is_not_null_test :public Item
 };
 
 
-class Item_func_isnotnull :public Item_bool_func
+class Item_func_isnotnull :public Item_func_null_predicate
 {
   bool abort_on_null;
 public:
-  Item_func_isnotnull(Item *a) :Item_bool_func(a), abort_on_null(0)
-  { sargable= TRUE; }
+  Item_func_isnotnull(Item *a) :Item_func_null_predicate(a), abort_on_null(0)
+  { }
   longlong val_int();
   enum Functype functype() const { return ISNOTNULL_FUNC; }
   void fix_length_and_dec()
@@ -1448,12 +1485,10 @@ class Item_func_isnotnull :public Item_b
     decimals=0; max_length=1; maybe_null=0;
   }
   const char *func_name() const { return "isnotnull"; }
-  optimize_type select_optimize() const { return OPTIMIZE_NULL; }
   table_map not_null_tables() const
   { return abort_on_null ? not_null_tables_cache : 0; }
   Item *neg_transformer(THD *thd);
   virtual void print(String *str, enum_query_type query_type);
-  CHARSET_INFO *compare_collation() { return args[0]->collation.collation; }
   void top_level_item() { abort_on_null=1; }
 };
 
@@ -1603,7 +1638,6 @@ class Item_func_regex :public Item_bool_
     print_op(str, query_type);
   }
 
-  CHARSET_INFO *compare_collation() { return cmp_collation.collation; }
 };
 
 
@@ -1771,7 +1805,7 @@ template <template<class> class LI, clas
   object represents f1=f2= ...=fn to the projection of known fields fi1=...=fik.
 */
 
-class Item_equal: public Item_bool_func
+class Item_equal: public Item_bool_func_hybrid_cmp_type
 {
   /*
     The list of equal items. Currently the list can contain:
@@ -1835,11 +1869,8 @@ class Item_equal: public Item_bool_func
 
   COND_EQUAL *upper_levels;       /* multiple equalities of upper and levels */
 
-  inline Item_equal()
-    : Item_bool_func(), with_const(FALSE), eval_item(0), cond_false(0),
-      context_field(NULL)
-  { const_item_cache=0; sargable= TRUE; }
-  Item_equal(Item *f1, Item *f2, bool with_const_item);
+  Item_equal(const Hybrid_cmp_context context,
+             Item *f1, Item *f2, bool with_const_item);
   Item_equal(Item_equal *item_equal);
   /* Currently the const item is always the first in the list of equal items */
   inline Item* get_const() { return with_const ? equal_items.head() : NULL; }
@@ -1866,8 +1897,6 @@ class Item_equal: public Item_bool_func
   bool walk(Item_processor processor, bool walk_subquery, uchar *arg);
   Item *transform(Item_transformer transformer, uchar *arg);
   virtual void print(String *str, enum_query_type query_type);
-  CHARSET_INFO *compare_collation();
-
   void set_context_field(Item_field *ctx_field) { context_field= ctx_field; }
   void set_link_equal_fields(bool flag) { link_equal_fields= flag; }
   friend class Item_equal_fields_iterator;

=== modified file 'sql/item_func.h'
--- sql/item_func.h	2014-08-07 16:06:56 +0000
+++ sql/item_func.h	2014-11-03 07:43:02 +0000
@@ -30,7 +30,32 @@ extern "C"				/* Bug in BSDI include fil
 }
 #endif
 
-class Item_func :public Item_result_field
+
+/*
+  Item_func metods that are needed for Field_optimizer->can_optimize_xxx().
+  In fact, these methods are only called for some Item_bool_func descendants.
+  Have DBUG_ASSERT as the default method implementation.
+  All Item_bool_func descendants that really use
+  Field_optimizer->can_optimize_xxx() override these methods.
+*/
+class Cmp_context_owner
+{
+public:
+  virtual CHARSET_INFO *compare_collation() const
+  {
+    DBUG_ASSERT(0);
+    return NULL;
+  }
+  virtual Item_result compare_type() const
+  {
+    DBUG_ASSERT(0);
+    return REAL_RESULT;
+  }
+};
+
+
+class Item_func :public Item_result_field,
+                 public Cmp_context_owner
 {
 protected:
   Item **args, *tmp_arg[2];

=== modified file 'sql/opt_range.cc'
--- sql/opt_range.cc	2014-10-28 21:33:31 +0000
+++ sql/opt_range.cc	2014-11-03 06:40:38 +0000
@@ -853,11 +853,14 @@ class RANGE_OPT_PARAM
   uint keys;
   
   /* 
-    If true, the index descriptions describe real indexes (and it is ok to
-    call field->optimize_range(real_keynr[...], ...).
+    If true, the index descriptions describe real indexes.
     Otherwise index description describes fake indexes.
   */
-  bool using_real_indexes;
+  virtual bool using_real_indexes() const = 0;
+
+  virtual bool can_optimize(Item_func *conf_func, Field *field,
+                            KEY_PART *key_part,
+                            Item_func::Functype type, Item *value) const = 0;
   
   /*
     Aggressively remove "scans" that do not have conditions on first
@@ -892,8 +895,25 @@ class RANGE_OPT_PARAM
       thd->is_error() ||
       alloced_sel_args > SEL_ARG::MAX_SEL_ARGS;
   }
+
+};
+
+
+class RANGE_OPT_PARAM_PARTITION: public RANGE_OPT_PARAM
+{
+public:
+  bool using_real_indexes() const { return false; }
+  bool can_optimize(Item_func *cond, Field *field, KEY_PART *key_part,
+                    Item_func::Functype func_type, Item *value) const
+  {
+    return
+      func_type == Item_func::EQ_FUNC || func_type == Item_func::EQUAL_FUNC ?
+      field->can_optimize_partition_ref(cond, value) :
+      field->can_optimize_partition_range(cond, value);
+  }
 };
 
+
 class PARAM : public RANGE_OPT_PARAM
 {
 public:
@@ -925,6 +945,30 @@ class PARAM : public RANGE_OPT_PARAM
   /* Number of ranges in the last checked tree->key */
   uint n_ranges;
   uint8 first_null_comp; /* first null component if any, 0 - otherwise */
+  bool using_real_indexes() const { return true; }
+  bool can_optimize(Item_func *cond, Field *field, KEY_PART *key_part,
+                    Item_func::Functype type, Item *value) const
+  {
+    uint keyno= real_keynr[key_part->key];
+    KEY *key= &table->key_info[keyno];
+    return (type == Item_func::EQ_FUNC || type == Item_func::EQUAL_FUNC) ?
+      field->can_optimize_keypart_range_trivial(key, keyno, key_part->part,
+                                                cond, value) :
+      field->can_optimize_keypart_range(key, keyno, key_part->part,
+                                        cond, value);
+  }
+};
+
+
+class PARAM_FOR_SELECTIVITY: public PARAM
+{
+public:
+  bool using_real_indexes() const { return false; }
+  bool can_optimize(Item_func *cond, Field *field, KEY_PART *key_part,
+                    Item_func::Functype type, Item *value) const
+  {
+    return field->can_optimize_for_selectivity(cond, value);
+  }
 };
 
 
@@ -939,10 +983,12 @@ class TABLE_READ_PLAN;
 struct st_index_scan_info;
 struct st_ror_scan_info;
 
-static SEL_TREE * get_mm_parts(RANGE_OPT_PARAM *param,COND *cond_func,Field *field,
+static SEL_TREE * get_mm_parts(RANGE_OPT_PARAM *param,
+                               Item_func *cond_func, Field *field,
 			       Item_func::Functype type,Item *value,
 			       Item_result cmp_type);
-static SEL_ARG *get_mm_leaf(RANGE_OPT_PARAM *param,COND *cond_func,Field *field,
+static SEL_ARG *get_mm_leaf(RANGE_OPT_PARAM *param,
+                            Item_func *cond_func, Field *field,
 			    KEY_PART *key_part,
 			    Item_func::Functype type,Item *value);
 static SEL_TREE *get_mm_tree(RANGE_OPT_PARAM *param,COND *cond);
@@ -3047,7 +3093,6 @@ int SQL_SELECT::test_quick_select(THD *t
     param.old_root= thd->mem_root;
     param.needed_reg= &needed_reg;
     param.imerge_cost_buff_size= 0;
-    param.using_real_indexes= TRUE;
     param.remove_jump_scans= TRUE;
     param.force_default_mrr= ordered_output;
     param.possible_keys.clear_all();
@@ -3592,7 +3637,7 @@ bool calculate_cond_selectivity_for_tabl
   if (thd->variables.optimizer_use_condition_selectivity > 2 &&
       !bitmap_is_clear_all(used_fields))
   {
-    PARAM param;
+    PARAM_FOR_SELECTIVITY param;
     MEM_ROOT alloc;
     SEL_TREE *tree;
     SEL_ARG **key, **end;
@@ -3612,7 +3657,6 @@ bool calculate_cond_selectivity_for_tabl
 
     param.prev_tables= param.read_tables= 0;
     param.current_table= table->map;
-    param.using_real_indexes= FALSE;
     param.real_keynr[0]= 0;
     param.alloced_sel_args= 0;
 
@@ -3890,10 +3934,9 @@ typedef void (*mark_full_part_func)(part
 /*
   Partition pruning operation context
 */
-typedef struct st_part_prune_param
+class PART_PRUNE_PARAM: public RANGE_OPT_PARAM_PARTITION
 {
-  RANGE_OPT_PARAM range_param; /* Range analyzer parameters */
-
+public:
   /***************************************************************
    Following fields are filled in based solely on partitioning 
    definition and not modified after that:
@@ -3953,7 +3996,7 @@ typedef struct st_part_prune_param
   uchar *cur_max_key;
 
   uint cur_min_flag, cur_max_flag;
-} PART_PRUNE_PARAM;
+};
 
 static bool create_partition_index_description(PART_PRUNE_PARAM *prune_par);
 static int find_used_partitions(PART_PRUNE_PARAM *ppar, SEL_ARG *key_tree);
@@ -4010,7 +4053,7 @@ bool prune_partitions(THD *thd, TABLE *t
   
   PART_PRUNE_PARAM prune_param;
   MEM_ROOT alloc;
-  RANGE_OPT_PARAM  *range_par= &prune_param.range_param;
+  RANGE_OPT_PARAM  *range_par= &prune_param;
   my_bitmap_map *old_sets[2];
 
   prune_param.part_info= part_info;
@@ -4035,7 +4078,6 @@ bool prune_partitions(THD *thd, TABLE *t
   range_par->current_table= table->map;
 
   range_par->keys= 1; // one index
-  range_par->using_real_indexes= FALSE;
   range_par->remove_jump_scans= FALSE;
   range_par->real_keynr[0]= 0;
   range_par->alloced_sel_args= 0;
@@ -4045,7 +4087,7 @@ bool prune_partitions(THD *thd, TABLE *t
 
   bitmap_clear_all(&part_info->read_partitions);
 
-  prune_param.key= prune_param.range_param.key_parts;
+  prune_param.key= prune_param.key_parts;
   SEL_TREE *tree;
   int res;
 
@@ -4069,8 +4111,8 @@ bool prune_partitions(THD *thd, TABLE *t
     prune_param.cur_part_fields= 0;
     prune_param.cur_subpart_fields= 0;
     
-    prune_param.cur_min_key= prune_param.range_param.min_key;
-    prune_param.cur_max_key= prune_param.range_param.max_key;
+    prune_param.cur_min_key= prune_param.min_key;
+    prune_param.cur_max_key= prune_param.max_key;
     prune_param.cur_min_flag= prune_param.cur_max_flag= 0;
 
     init_all_partitions_iterator(part_info, &prune_param.part_iter);
@@ -4174,7 +4216,7 @@ bool prune_partitions(THD *thd, TABLE *t
 static void store_selargs_to_rec(PART_PRUNE_PARAM *ppar, SEL_ARG **start,
                                  int num)
 {
-  KEY_PART *parts= ppar->range_param.key_parts;
+  KEY_PART *parts= ppar->key_parts;
   for (SEL_ARG **end= start + num; start != end; start++)
   {
     SEL_ARG *sel_arg= (*start);
@@ -4236,7 +4278,7 @@ static int find_used_partitions_imerge_l
   my_bitmap_map *bitmap_buf;
   uint n_bits= ppar->part_info->read_partitions.n_bits;
   bitmap_bytes= bitmap_buffer_size(n_bits);
-  if (!(bitmap_buf= (my_bitmap_map*) alloc_root(ppar->range_param.mem_root,
+  if (!(bitmap_buf= (my_bitmap_map*) alloc_root(ppar->mem_root,
                                                 bitmap_bytes)))
   {
     /*
@@ -4300,8 +4342,8 @@ int find_used_partitions_imerge(PART_PRU
     ppar->cur_part_fields= 0;
     ppar->cur_subpart_fields= 0;
     
-    ppar->cur_min_key= ppar->range_param.min_key;
-    ppar->cur_max_key= ppar->range_param.max_key;
+    ppar->cur_min_key= ppar->min_key;
+    ppar->cur_max_key= ppar->max_key;
     ppar->cur_min_flag= ppar->cur_max_flag= 0;
 
     init_all_partitions_iterator(ppar->part_info, &ppar->part_iter);
@@ -4431,7 +4473,7 @@ int find_used_partitions(PART_PRUNE_PARA
   bool set_full_part_if_bad_ret= FALSE;
   bool ignore_part_fields= ppar->ignore_part_fields;
   bool did_set_ignore_part_fields= FALSE;
-  RANGE_OPT_PARAM *range_par= &(ppar->range_param);
+  RANGE_OPT_PARAM *range_par= ppar;
 
   if (check_stack_overrun(range_par->thd, 3*STACK_MIN_SIZE, NULL))
     return -1;
@@ -4842,7 +4884,7 @@ static bool fields_ok_for_partition_inde
 
 static bool create_partition_index_description(PART_PRUNE_PARAM *ppar)
 {
-  RANGE_OPT_PARAM *range_par= &(ppar->range_param);
+  RANGE_OPT_PARAM *range_par= ppar;
   partition_info *part_info= ppar->part_info;
   uint used_part_fields, used_subpart_fields;
 
@@ -7530,7 +7572,8 @@ QUICK_SELECT_I *TRP_ROR_UNION::make_quic
     0  on error
 */
 
-static SEL_TREE *get_ne_mm_tree(RANGE_OPT_PARAM *param, Item_func *cond_func, 
+static SEL_TREE *get_ne_mm_tree(RANGE_OPT_PARAM *param,
+                                Item_func *cond_func, 
                                 Field *field,
                                 Item *lt_value, Item *gt_value,
                                 Item_result cmp_type)
@@ -7565,7 +7608,8 @@ static SEL_TREE *get_ne_mm_tree(RANGE_OP
     Pointer to the tree built tree
 */
 
-static SEL_TREE *get_func_mm_tree(RANGE_OPT_PARAM *param, Item_func *cond_func, 
+static SEL_TREE *get_func_mm_tree(RANGE_OPT_PARAM *param,
+                                  Item_func *cond_func, 
                                   Field *field, Item *value,
                                   Item_result cmp_type, bool inv)
 {
@@ -7732,7 +7776,7 @@ static SEL_TREE *get_func_mm_tree(RANGE_
 
                   Partitioning indexes are never partial.
                 */
-                if (param->using_real_indexes)
+                if (param->using_real_indexes())
                 {
                   const KEY key=
                     param->table->key_info[param->real_keynr[idx]];
@@ -8086,7 +8130,7 @@ static SEL_TREE *get_mm_tree(RANGE_OPT_P
       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,
+        tree= get_mm_parts(param, item_equal, field, Item_func::EQ_FUNC,
 		           value,cmp_type);
         ftree= !ftree ? tree : tree_and(param, ftree, tree);
       }
@@ -8139,7 +8183,7 @@ static SEL_TREE *get_mm_tree(RANGE_OPT_P
 
 
 static SEL_TREE *
-get_mm_parts(RANGE_OPT_PARAM *param, COND *cond_func, Field *field,
+get_mm_parts(RANGE_OPT_PARAM *param, Item_func *cond_func, Field *field,
 	     Item_func::Functype type,
 	     Item *value, Item_result cmp_type)
 {
@@ -8192,11 +8236,10 @@ get_mm_parts(RANGE_OPT_PARAM *param, CON
 
 
 static SEL_ARG *
-get_mm_leaf(RANGE_OPT_PARAM *param, COND *conf_func, Field *field,
+get_mm_leaf(RANGE_OPT_PARAM *param, Item_func *conf_func, Field *field,
             KEY_PART *key_part, Item_func::Functype type,Item *value)
 {
   uint maybe_null=(uint) field->real_maybe_null();
-  bool optimize_range;
   SEL_ARG *tree= 0;
   MEM_ROOT *alloc= param->mem_root;
   uchar *str;
@@ -8232,55 +8275,9 @@ get_mm_leaf(RANGE_OPT_PARAM *param, COND
     goto end;
   }
 
-  /*
-    1. Usually we can't use an index if the column collation
-       differ from the operation collation.
-
-    2. However, we can reuse a case insensitive index for
-       the binary searches:
-
-       WHERE latin1_swedish_ci_column = 'a' COLLATE lati1_bin;
-
-       WHERE latin1_swedish_ci_colimn = BINARY 'a '
-
-  */
-  if (field->result_type() == STRING_RESULT &&
-      field->match_collation_to_optimize_range() &&
-      value->result_type() == STRING_RESULT &&
-      key_part->image_type == Field::itRAW &&
-      field->charset() != conf_func->compare_collation() &&
-      !(conf_func->compare_collation()->state & MY_CS_BINSORT &&
-        (type == Item_func::EQUAL_FUNC || type == Item_func::EQ_FUNC)))
+  if (!param->can_optimize(conf_func, field, key_part, type, value))
     goto end;
 
-  if (key_part->image_type == Field::itMBR)
-  {
-    // @todo: use is_spatial_operator() instead?
-    switch (type) {
-    case Item_func::SP_EQUALS_FUNC:
-    case Item_func::SP_DISJOINT_FUNC:
-    case Item_func::SP_INTERSECTS_FUNC:
-    case Item_func::SP_TOUCHES_FUNC:
-    case Item_func::SP_CROSSES_FUNC:
-    case Item_func::SP_WITHIN_FUNC:
-    case Item_func::SP_CONTAINS_FUNC:
-    case Item_func::SP_OVERLAPS_FUNC:
-      break;
-    default:
-      /* 
-        We cannot involve spatial indexes for queries that
-        don't use MBREQUALS(), MBRDISJOINT(), etc. functions.
-      */
-      goto end;
-    }
-  }
-
-  if (param->using_real_indexes)
-    optimize_range= field->optimize_range(param->real_keynr[key_part->key],
-                                          key_part->part);
-  else
-    optimize_range= TRUE;
-
   if (type == Item_func::LIKE_FUNC)
   {
     bool like_error;
@@ -8290,8 +8287,6 @@ get_mm_leaf(RANGE_OPT_PARAM *param, COND
     size_t length, offset, min_length, max_length;
     uint field_length= field->pack_length()+maybe_null;
 
-    if (!optimize_range)
-      goto end;
     if (!(res= value->val_str(&tmp)))
     {
       tree= &null_element;
@@ -8361,17 +8356,6 @@ get_mm_leaf(RANGE_OPT_PARAM *param, COND
     goto end;
   }
 
-  if (!optimize_range &&
-      type != Item_func::EQ_FUNC &&
-      type != Item_func::EQUAL_FUNC)
-    goto end;                                   // Can't optimize this
-
-  /*
-    We can't always use indexes when comparing a string index to a number
-    cmp_type() is checked to allow compare of dates to numbers
-  */
-  if (field->cmp_type() == STRING_RESULT && value->cmp_type() != STRING_RESULT)
-    goto end;
   err= value->save_in_field_no_warnings(field, 1);
   if (err == 2 && field->cmp_type() == STRING_RESULT)
   {
@@ -12500,7 +12484,7 @@ static bool get_constant_key_infix(KEY *
                        KEY_PART_INFO **first_non_infix_part);
 static bool
 check_group_min_max_predicates(Item *cond, Item_field *min_max_arg_item,
-                               Field::imagetype image_type,
+                               const KEY *key,
                                bool *has_min_max_fld, bool *has_other_fld);
 
 static void
@@ -13105,8 +13089,7 @@ get_best_group_min_max(PARAM *param, SEL
   bool has_min_max_fld= false, has_other_fld= false;
   if (join->conds && min_max_arg_item &&
       !check_group_min_max_predicates(join->conds, min_max_arg_item,
-                                      (index_info->flags & HA_SPATIAL) ?
-                                      Field::itMBR : Field::itRAW,
+                                      index_info,
                                       &has_min_max_fld, &has_other_fld))
     DBUG_RETURN(NULL);
 
@@ -13157,7 +13140,7 @@ get_best_group_min_max(PARAM *param, SEL
     check_group_min_max_predicates()
     cond        [in]  the expression tree being analyzed
     min_max_arg [in]  the field referenced by the MIN/MAX function(s)
-    image_type  [in]
+    key         [in]
     has_min_max_arg [out] true if the subtree being analyzed references min_max_arg
     has_other_arg   [out] true if the subtree being analyzed references a column
                           other min_max_arg
@@ -13181,7 +13164,7 @@ get_best_group_min_max(PARAM *param, SEL
 
 static bool
 check_group_min_max_predicates(Item *cond, Item_field *min_max_arg_item,
-                               Field::imagetype image_type,
+                               const KEY *key,
                                bool *has_min_max_arg, bool *has_other_arg)
 {
   DBUG_ENTER("check_group_min_max_predicates");
@@ -13205,7 +13188,7 @@ check_group_min_max_predicates(Item *con
             the min/max argument and other columns.
       */
       if (!check_group_min_max_predicates(and_or_arg, min_max_arg_item,      //1
-                                          image_type,
+                                          key,
                                           &has_min_max, &has_other) ||
           (func_type == Item_func::COND_OR_FUNC && has_min_max && has_other))//2
         DBUG_RETURN(FALSE);
@@ -13317,23 +13300,10 @@ check_group_min_max_predicates(Item *con
         if (!simple_pred(pred, args, &inv))
           DBUG_RETURN(FALSE);
 
-        /* Check for compatible string comparisons - similar to get_mm_leaf. */
         if (args[0] && args[1] && !args[2] && // this is a binary function
-            min_max_arg_item->result_type() == STRING_RESULT &&
-            /*
-              Don't use an index when comparing strings of different collations.
-            */
-            ((args[1]->result_type() == STRING_RESULT &&
-              image_type == Field::itRAW &&
-              min_max_arg_item->field->charset() !=
-              pred->compare_collation())
-             ||
-             /*
-               We can't always use indexes when comparing a string index to a
-               number.
-             */
-             (args[1]->result_type() != STRING_RESULT &&
-              min_max_arg_item->field->cmp_type() != args[1]->result_type())))
+            !min_max_arg_item->field->can_optimize_key_group_min_max(key,
+                                                                     pred,
+                                                                     args[1]))
           DBUG_RETURN(FALSE);
       }
       else
@@ -13341,7 +13311,7 @@ check_group_min_max_predicates(Item *con
     }
     else if (cur_arg->type() == Item::FUNC_ITEM)
     {
-      if (!check_group_min_max_predicates(cur_arg, min_max_arg_item, image_type,
+      if (!check_group_min_max_predicates(cur_arg, min_max_arg_item, key,
                                           &has_min_max, &has_other))
         DBUG_RETURN(FALSE);
     }

=== modified file 'sql/opt_table_elimination.cc'
--- sql/opt_table_elimination.cc	2014-02-19 10:05:15 +0000
+++ sql/opt_table_elimination.cc	2014-11-03 04:05:14 +0000
@@ -1486,24 +1486,9 @@ void check_equality(Dep_analysis_context
       left->real_item()->type() == Item::FIELD_ITEM)
   {
     Field *field= ((Item_field*)left->real_item())->field;
-    if (field->result_type() == STRING_RESULT)
-    {
-      if (right->result_type() != STRING_RESULT)
-      {
-        if (field->cmp_type() != right->result_type())
-          return;
-      }
-      else
-      {
-        /*
-          We can't assume there's a functional dependency if the effective
-          collation of the operation differ from the field collation.
-        */
-        if (field->cmp_type() == STRING_RESULT &&
-            field->charset() != cond->compare_collation())
-          return;
-      }
-    }
+    if (!field->can_optimize_equality_for_outer_join_table_elimination(cond,
+                                                                       right))
+      return;
     Dep_value_field *field_val;
     if ((field_val= ctx->get_field_value(field)))
       add_module_expr(ctx, eq_mod, and_level, field_val, right, NULL);

=== modified file 'sql/sql_select.cc'
--- sql/sql_select.cc	2014-10-29 12:20:46 +0000
+++ sql/sql_select.cc	2014-11-03 08:10:05 +0000
@@ -4098,6 +4098,7 @@ make_join_statistics(JOIN *join, List<TA
 typedef struct key_field_t {
   Field		*field;
   Item		*val;			///< May be empty if diff constant
+  Item_func     *cond;
   uint		level;
   uint		optimize;
   bool		eq_func;
@@ -4439,20 +4440,6 @@ add_key_field(JOIN *join,
       }
       if (!eq_func) // eq_func is NEVER true when num_values > 1
         return;
-
-      /*
-	We can't use indexes when comparing a string index to a
-	number or two strings if the effective collation
-        of the operation differ from the field collation.
-       */
-
-      if (field->cmp_type() == STRING_RESULT)
-      {
-        if ((*value)->cmp_type() != STRING_RESULT)
-            return;
-        if (field->charset() != cond->compare_collation())
-          return;
-      }
     }
   }
   /*
@@ -4464,6 +4451,7 @@ add_key_field(JOIN *join,
   (*key_fields)->field=		field;
   (*key_fields)->eq_func=	eq_func;
   (*key_fields)->val=		*value;
+  (*key_fields)->cond=          cond;
   (*key_fields)->level=         and_level;
   (*key_fields)->optimize=      optimize;
   /*
@@ -4905,7 +4893,9 @@ add_key_part(DYNAMIC_ARRAY *keyuse_array
       uint key_parts= form->actual_n_key_parts(keyinfo);
       for (uint part=0 ; part <  key_parts ; part++)
       {
-	if (field->eq(form->key_info[key].key_part[part].field))
+        if (field->eq(form->key_info[key].key_part[part].field) &&
+            field->can_optimize_keypart_ref(keyinfo, key, part,
+                                            key_field->cond, key_field->val))
 	{
           if (add_keyuse(keyuse_array, key_field, key, part))
             return TRUE;
@@ -4916,6 +4906,8 @@ add_key_part(DYNAMIC_ARRAY *keyuse_array
         (key_field->optimize & KEY_OPTIMIZE_EQ) &&
         key_field->val->used_tables())
     {
+      if (!field->can_optimize_hash_join(key_field->cond, key_field->val))
+        return false;
       /* 
         If a key use is extracted from an equi-join predicate then it is
         added not only as a key use for every index whose component can
@@ -12407,7 +12399,7 @@ static bool check_simple_equality(Item *
       else 
       {
         /* None of the fields was found in multiple equalities */
-        Item_equal *item_equal= new Item_equal(orig_left_item,
+        Item_equal *item_equal= new Item_equal(left_field, orig_left_item,
                                                orig_right_item,
                                                FALSE);
         item_equal->set_context_field((Item_field*)left_item);
@@ -12444,20 +12436,26 @@ static bool check_simple_equality(Item *
     {
       bool copyfl;
 
-      if (field_item->cmp_type() == STRING_RESULT)
+      Hybrid_cmp_context cmp;
+      Item *args[2]= {orig_left_item, orig_right_item};
+      if (cmp.set_compare_context("=<mul>=", args, 2))
+      {
+        /*
+          Should not happen, as all comparison arguments (field + const pairs)
+          have previously been checked for compatibility in fix_length_and_dec
+          or their original comparison functions, e.g Item_func_eq.
+        */
+        DBUG_ASSERT(0);
+        return false;
+      }
+
+      if (cmp.compare_type() == STRING_RESULT)
       {
         CHARSET_INFO *cs= field_item->field->charset();
-        if (!item)
-        {
-          Item_func_eq *eq_item;
-          if (!(eq_item= new Item_func_eq(orig_left_item, orig_right_item)) ||
-              eq_item->set_cmp_func())
-            return FALSE;
-          eq_item->quick_fix_field();
-          item= eq_item;
-        }  
-        if ((cs != ((Item_func *) item)->compare_collation()) ||
-            !cs->coll->propagate(cs, 0, 0))
+        CHARSET_INFO *cmpcs= item && item->type() == Item::FUNC_ITEM ?
+                             ((Item_func *) item)->compare_collation() :
+                             cmp.compare_collation();
+        if (cs != cmpcs || !cs->coll->propagate(cs, 0, 0))
           return FALSE;
       }
 
@@ -12480,7 +12478,7 @@ static bool check_simple_equality(Item *
       }
       else
       {
-        item_equal= new Item_equal(const_item, orig_field_item, TRUE);
+        item_equal= new Item_equal(cmp, const_item, orig_field_item, TRUE);
         item_equal->set_context_field(field_item);
         cond_equal->current_level.push_back(item_equal);
       }

=== modified file 'sql/table.h'
--- sql/table.h	2014-09-16 12:03:17 +0000
+++ sql/table.h	2014-11-03 06:45:20 +0000
@@ -1373,6 +1373,11 @@ struct TABLE
     DBUG_VOID_RETURN;
   }
 
+  bool optimize_range(uint idx, uint part) const
+  {
+    return MY_TEST(file->index_flags(idx, part, 1) & HA_READ_RANGE);
+  }
+
   bool update_const_key_parts(COND *conds);
   uint actual_n_key_parts(KEY *keyinfo);
   ulong actual_key_flags(KEY *keyinfo);


Follow ups