commit 1d9f9b8d93fd0d1aad56b77694fb3c2b5a55514d
Author: Alexander Barkov <bar@mariadb.org>
Date:   Mon Apr 3 13:38:20 2017 +0400

    MDEV-12426 Add Field::type_handler() + MDEV-12432
    
    This is a joint patch for:
    
    - MDEV-12426 Add Field::type_handler()
    - MDEV-12432 Range optimizer for ENUM and SET does not return "Impossible WHERE" in some case
    
    With the new type handler approach being added to Field, it was easier to fix
    MDEV-12432 rather than to reproduce the old ENUM/SET behavior.
    
    The patch does the following:
    
    1. Adds Field::type_handler(), according to the task description.
    
    2. Fixes the asymmetry between Fields and Items of ENUM and SET field types.
       Field_enum::cmp_type() returned INT_RESULT
       Item*::cmp_type() returned STRING_RESULT for ENUM and SET expressions
    
    This asymmetry was originally done for easier coding in the optimizer sources.
    However, in 10.1 we moved a lot of code to methods of the class Field:
    - test_if_equality_guarantees_uniqueness()
    - can_be_substituted_to_equal_item()
    - get_equal_const_item()
    - can_optimize_keypart_ref()
    - can_optimize_hash_join()
    - can_optimize_group_min_max()
    - can_optimize_range()
    - can_optimize_outer_join_table_elimination()
    
    As of 10.2 only a few lines of the code in opt_range.cc, field.cc and field.h
    still relayed on the fact that Field_enum::cmp_type() returns INT_RESULT:
    - Some asserts in field.cc
    - Field_year::get_copy_func()
    - Item_func_like::get_mm_leaf()
    - Item_bool_func::get_mm_leaf()
    
    3. Item_bool_func::get_mm_leaf() did not work well for ENUM/SET,
    see MDEV-12432. So the ENUM/SET code was rewritten, and the relevant
    code in Field_enum::store() and Field_set::store() was fixed to
    properly return errors to the caller.

diff --git a/mysql-test/r/type_enum.result b/mysql-test/r/type_enum.result
index 6ad7533..2a08a14 100644
--- a/mysql-test/r/type_enum.result
+++ b/mysql-test/r/type_enum.result
@@ -1803,13 +1803,18 @@ id	c1 + 0	c1
 4	0	
 DROP TABLE t1;
 End of 4.1 tests
-create table t1(f1 set('a','b'), index(f1));
+SET sql_mode='';
+create table t1(f1 enum('a','b'), index(f1));
 insert into t1 values(''),(''),('a'),('b');
+Warnings:
+Warning	1265	Data truncated for column 'f1' at row 1
+Warning	1265	Data truncated for column 'f1' at row 2
 select * from t1 where f1='';
 f1
 
 
 drop table t1;
+SET sql_mode=DEFAULT;
 CREATE TABLE t1 (c1 ENUM('a', '', 'b'));
 INSERT INTO t1 (c1) VALUES ('b');
 INSERT INTO t1 (c1) VALUES ('');
@@ -2219,3 +2224,36 @@ SELECT * FROM t1;
 a
 
 DROP TABLE t1;
+#
+# MDEV-12432 Range optimizer for ENUM and SET does not return "Impossible WHERE" in some case
+#
+CREATE TABLE t1 (a ENUM('a','b','c','1'),KEY(a));
+INSERT INTO t1 VALUES ('a'),('b'),('c'),('1');
+EXPLAIN SELECT * FROM t1 WHERE a='xx';
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	SIMPLE	NULL	NULL	NULL	NULL	NULL	NULL	NULL	Impossible WHERE noticed after reading const tables
+EXPLAIN SELECT * FROM t1 WHERE a='99999999';
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	SIMPLE	NULL	NULL	NULL	NULL	NULL	NULL	NULL	Impossible WHERE noticed after reading const tables
+EXPLAIN SELECT * FROM t1 WHERE a=100.1e0;
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	SIMPLE	NULL	NULL	NULL	NULL	NULL	NULL	NULL	Impossible WHERE noticed after reading const tables
+EXPLAIN SELECT * FROM t1 WHERE a=100;
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	SIMPLE	NULL	NULL	NULL	NULL	NULL	NULL	NULL	Impossible WHERE noticed after reading const tables
+EXPLAIN SELECT * FROM t1 WHERE a=100.1;
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	SIMPLE	NULL	NULL	NULL	NULL	NULL	NULL	NULL	Impossible WHERE noticed after reading const tables
+EXPLAIN SELECT * FROM t1 WHERE a='100';
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	SIMPLE	NULL	NULL	NULL	NULL	NULL	NULL	NULL	Impossible WHERE noticed after reading const tables
+EXPLAIN SELECT * FROM t1 WHERE a='1x';
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	SIMPLE	NULL	NULL	NULL	NULL	NULL	NULL	NULL	Impossible WHERE noticed after reading const tables
+EXPLAIN SELECT * FROM t1 WHERE a='1.0';
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	SIMPLE	NULL	NULL	NULL	NULL	NULL	NULL	NULL	Impossible WHERE noticed after reading const tables
+EXPLAIN SELECT * FROM t1 WHERE a='1.1';
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	SIMPLE	NULL	NULL	NULL	NULL	NULL	NULL	NULL	Impossible WHERE noticed after reading const tables
+DROP TABLE t1;
diff --git a/mysql-test/r/type_set.result b/mysql-test/r/type_set.result
index 742ee5a..1258de3 100644
--- a/mysql-test/r/type_set.result
+++ b/mysql-test/r/type_set.result
@@ -316,3 +316,39 @@ DROP TABLE t1;
 #
 # End of 10.1 tests
 #
+#
+# Start of 10.3 tests
+#
+#
+# MDEV-12432 Range optimizer for ENUM and SET does not return "Impossible WHERE" in some case
+#
+CREATE TABLE t1 (a SET('a','b','c','1'),KEY(a));
+INSERT INTO t1 VALUES ('a'),('b'),('c'),('1');
+EXPLAIN SELECT * FROM t1 WHERE a='xx';
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	SIMPLE	NULL	NULL	NULL	NULL	NULL	NULL	NULL	Impossible WHERE noticed after reading const tables
+EXPLAIN SELECT * FROM t1 WHERE a='99999999';
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	SIMPLE	NULL	NULL	NULL	NULL	NULL	NULL	NULL	Impossible WHERE noticed after reading const tables
+EXPLAIN SELECT * FROM t1 WHERE a=100.1e0;
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	SIMPLE	NULL	NULL	NULL	NULL	NULL	NULL	NULL	Impossible WHERE noticed after reading const tables
+EXPLAIN SELECT * FROM t1 WHERE a=100;
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	SIMPLE	NULL	NULL	NULL	NULL	NULL	NULL	NULL	Impossible WHERE noticed after reading const tables
+EXPLAIN SELECT * FROM t1 WHERE a=100.1;
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	SIMPLE	NULL	NULL	NULL	NULL	NULL	NULL	NULL	Impossible WHERE noticed after reading const tables
+EXPLAIN SELECT * FROM t1 WHERE a='100';
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	SIMPLE	NULL	NULL	NULL	NULL	NULL	NULL	NULL	Impossible WHERE noticed after reading const tables
+EXPLAIN SELECT * FROM t1 WHERE a='1x';
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	SIMPLE	NULL	NULL	NULL	NULL	NULL	NULL	NULL	Impossible WHERE noticed after reading const tables
+EXPLAIN SELECT * FROM t1 WHERE a='1.0';
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	SIMPLE	NULL	NULL	NULL	NULL	NULL	NULL	NULL	Impossible WHERE noticed after reading const tables
+EXPLAIN SELECT * FROM t1 WHERE a='1.1';
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	SIMPLE	NULL	NULL	NULL	NULL	NULL	NULL	NULL	Impossible WHERE noticed after reading const tables
+DROP TABLE t1;
diff --git a/mysql-test/t/type_enum.test b/mysql-test/t/type_enum.test
index 105da42..d00cf66 100644
--- a/mysql-test/t/type_enum.test
+++ b/mysql-test/t/type_enum.test
@@ -179,10 +179,12 @@ DROP TABLE t1;
 #
 # Bug#28729: Field_enum wrongly reported an error while storing an empty string.
 #
-create table t1(f1 set('a','b'), index(f1));
+SET sql_mode='';
+create table t1(f1 enum('a','b'), index(f1));
 insert into t1 values(''),(''),('a'),('b');
 select * from t1 where f1='';
 drop table t1;
+SET sql_mode=DEFAULT;
 
 #
 # Bug#29360: Confluence of the special 0 enum value with the normal empty string
@@ -454,3 +456,20 @@ SET STATEMENT sql_mode = 'NO_ENGINE_SUBSTITUTION' FOR
 ALTER TABLE t1 MODIFY a ENUM('2001','2002');
 SELECT * FROM t1;
 DROP TABLE t1;
+
+--echo #
+--echo # MDEV-12432 Range optimizer for ENUM and SET does not return "Impossible WHERE" in some case
+--echo #
+
+CREATE TABLE t1 (a ENUM('a','b','c','1'),KEY(a));
+INSERT INTO t1 VALUES ('a'),('b'),('c'),('1');
+EXPLAIN SELECT * FROM t1 WHERE a='xx';
+EXPLAIN SELECT * FROM t1 WHERE a='99999999';
+EXPLAIN SELECT * FROM t1 WHERE a=100.1e0;
+EXPLAIN SELECT * FROM t1 WHERE a=100;
+EXPLAIN SELECT * FROM t1 WHERE a=100.1;
+EXPLAIN SELECT * FROM t1 WHERE a='100';
+EXPLAIN SELECT * FROM t1 WHERE a='1x';
+EXPLAIN SELECT * FROM t1 WHERE a='1.0';
+EXPLAIN SELECT * FROM t1 WHERE a='1.1';
+DROP TABLE t1;
diff --git a/mysql-test/t/type_set.test b/mysql-test/t/type_set.test
index 7a79cd1..8c26d5a 100644
--- a/mysql-test/t/type_set.test
+++ b/mysql-test/t/type_set.test
@@ -218,3 +218,25 @@ DROP TABLE t1;
 --echo #
 --echo # End of 10.1 tests
 --echo #
+
+
+--echo #
+--echo # Start of 10.3 tests
+--echo #
+
+--echo #
+--echo # MDEV-12432 Range optimizer for ENUM and SET does not return "Impossible WHERE" in some case
+--echo #
+
+CREATE TABLE t1 (a SET('a','b','c','1'),KEY(a));
+INSERT INTO t1 VALUES ('a'),('b'),('c'),('1');
+EXPLAIN SELECT * FROM t1 WHERE a='xx';
+EXPLAIN SELECT * FROM t1 WHERE a='99999999';
+EXPLAIN SELECT * FROM t1 WHERE a=100.1e0;
+EXPLAIN SELECT * FROM t1 WHERE a=100;
+EXPLAIN SELECT * FROM t1 WHERE a=100.1;
+EXPLAIN SELECT * FROM t1 WHERE a='100';
+EXPLAIN SELECT * FROM t1 WHERE a='1x';
+EXPLAIN SELECT * FROM t1 WHERE a='1.0';
+EXPLAIN SELECT * FROM t1 WHERE a='1.1';
+DROP TABLE t1;
diff --git a/sql/field.cc b/sql/field.cc
index 362c49b..44b1acd 100644
--- a/sql/field.cc
+++ b/sql/field.cc
@@ -1255,14 +1255,14 @@ bool Field::can_optimize_group_min_max(const Item_bool_func *cond,
 
 
 /*
-  This covers all numeric types, ENUM, SET, BIT
+  This covers all numeric types, BIT
 */
 bool Field::can_optimize_range(const Item_bool_func *cond,
                                const Item *item,
                                bool is_eq_func) const
 {
   DBUG_ASSERT(cmp_type() != TIME_RESULT);   // Handled in Field_temporal
-  DBUG_ASSERT(cmp_type() != STRING_RESULT); // Handled in Field_longstr
+  DBUG_ASSERT(cmp_type() != STRING_RESULT); // Handled in Field_str descendants
   return item->cmp_type() != TIME_RESULT;
 }
 
@@ -8632,12 +8632,16 @@ int Field_enum::store(const char *from,uint length,CHARSET_INFO *cs)
       {
 	tmp=0;
 	set_warning(WARN_DATA_TRUNCATED, 1);
+	err= 1;
       }
-      if (!get_thd()->count_cuted_fields)
+      if (!get_thd()->count_cuted_fields && !length)
         err= 0;
     }
     else
+    {
       set_warning(WARN_DATA_TRUNCATED, 1);
+      err= 1;
+    }
   }
   store_type((ulonglong) tmp);
   return err;
@@ -8811,6 +8815,7 @@ int Field_set::store(const char *from,uint length,CHARSET_INFO *cs)
     {
       tmp=0;      
       set_warning(WARN_DATA_TRUNCATED, 1);
+      err= 1;
     }
   }
   else if (got_warning)
@@ -9049,12 +9054,17 @@ uint Field_num::is_equal(Create_field *new_field)
 }
 
 
+bool Field_enum::can_optimize_range(const Item_bool_func *cond,
+                                    const Item *item,
+                                    bool is_eq_func) const
+{
+  return item->cmp_type() != TIME_RESULT;
+}
+
+
 bool Field_enum::can_optimize_keypart_ref(const Item_bool_func *cond,
                                           const Item *item) const
 {
-  DBUG_ASSERT(cmp_type() == INT_RESULT);
-  DBUG_ASSERT(result_type() == STRING_RESULT);
-
   switch (item->cmp_type())
   {
   case TIME_RESULT:
diff --git a/sql/field.h b/sql/field.h
index 2ed84b8..26593e3 100644
--- a/sql/field.h
+++ b/sql/field.h
@@ -849,11 +849,13 @@ class Field: public Value_source
    to be quoted when used in constructing an SQL query.
   */
   virtual bool str_needs_quotes() { return FALSE; }
-  virtual Item_result result_type () const=0;
-  virtual Item_result cmp_type () const { return result_type(); }
-  virtual const Type_handler *cast_to_int_type_handler() const
+  Item_result result_type () const
   {
-    return Type_handler::get_handler_by_field_type(type());
+    return type_handler()->result_type();
+  }
+  Item_result cmp_type () const
+  {
+    return type_handler()->cmp_type();
   }
   static bool type_can_have_key_part(enum_field_types);
   static enum_field_types field_type_merge(enum_field_types, enum_field_types);
@@ -969,8 +971,15 @@ class Field: public Value_source
   virtual bool zero_pack() const { return 1; }
   virtual enum ha_base_keytype key_type() const { return HA_KEYTYPE_BINARY; }
   virtual uint32 key_length() const { return pack_length(); }
-  virtual enum_field_types type() const =0;
-  virtual enum_field_types real_type() const { return type(); }
+  virtual const Type_handler *type_handler() const= 0;
+  enum_field_types type() const
+  {
+    return type_handler()->field_type();
+  }
+  enum_field_types real_type() const
+  {
+    return type_handler()->real_field_type();
+  }
   virtual enum_field_types binlog_type() const
   {
     /*
@@ -1587,7 +1596,6 @@ class Field_num :public Field {
 	    uchar null_bit_arg, utype unireg_check_arg,
 	    const char *field_name_arg,
             uint8 dec_arg, bool zero_arg, bool unsigned_arg);
-  enum Item_result result_type () const { return INT_RESULT; }
   enum Derivation derivation(void) const { return DERIVATION_NUMERIC; }
   uint repertoire(void) const { return MY_REPERTOIRE_NUMERIC; }
   CHARSET_INFO *charset(void) const { return &my_charset_numeric; }
@@ -1650,7 +1658,6 @@ class Field_str :public Field {
   Field_str(uchar *ptr_arg,uint32 len_arg, uchar *null_ptr_arg,
 	    uchar null_bit_arg, utype unireg_check_arg,
 	    const char *field_name_arg, CHARSET_INFO *charset);
-  Item_result result_type () const { return STRING_RESULT; }
   uint decimals() const { return NOT_FIXED_DEC; }
   int  save_in_field(Field *to) { return save_in_field_str(to); }
   bool memcpy_field_possible(const Field *from) const
@@ -1758,7 +1765,6 @@ class Field_real :public Field_num {
                field_name_arg, dec_arg, zero_arg, unsigned_arg),
     not_fixed(dec_arg >= FLOATING_POINT_DECIMALS)
     {}
-  Item_result result_type () const { return REAL_RESULT; }
   Copy_func *get_copy_func(const Field *from) const
   {
     return do_field_real;
@@ -1795,7 +1801,7 @@ class Field_decimal :public Field_real {
                 unireg_check_arg, field_name_arg,
                 dec_arg, zero_arg, unsigned_arg)
     {}
-  enum_field_types type() const { return MYSQL_TYPE_DECIMAL;}
+  const Type_handler *type_handler() const { return &type_handler_olddecimal; }
   enum ha_base_keytype key_type() const
   { return zerofill ? HA_KEYTYPE_BINARY : HA_KEYTYPE_NUM; }
   Copy_func *get_copy_func(const Field *from) const
@@ -1842,9 +1848,8 @@ class Field_new_decimal :public Field_num {
   Field_new_decimal(uint32 len_arg, bool maybe_null_arg,
                     const char *field_name_arg, uint8 dec_arg,
                     bool unsigned_arg);
-  enum_field_types type() const { return MYSQL_TYPE_NEWDECIMAL;}
+  const Type_handler *type_handler() const { return &type_handler_newdecimal; }
   enum ha_base_keytype key_type() const { return HA_KEYTYPE_BINARY; }
-  Item_result result_type () const { return DECIMAL_RESULT; }
   Copy_func *get_copy_func(const Field *from) const
   {
     //  if (from->real_type() == MYSQL_TYPE_BIT) // QQ: why?
@@ -1909,7 +1914,7 @@ class Field_tiny :public Field_num {
 	       unireg_check_arg, field_name_arg,
 	       0, zero_arg,unsigned_arg)
     {}
-  enum_field_types type() const { return MYSQL_TYPE_TINY;}
+  const Type_handler *type_handler() const { return &type_handler_tiny; }
   enum ha_base_keytype key_type() const
     { return unsigned_flag ? HA_KEYTYPE_BINARY : HA_KEYTYPE_INT8; }
   int store(const char *to,uint length,CHARSET_INFO *charset);
@@ -1958,7 +1963,7 @@ class Field_short :public Field_num {
     :Field_num((uchar*) 0, len_arg, maybe_null_arg ? (uchar*) "": 0,0,
 	       NONE, field_name_arg, 0, 0, unsigned_arg)
     {}
-  enum_field_types type() const { return MYSQL_TYPE_SHORT;}
+  const Type_handler *type_handler() const { return &type_handler_short; }
   enum ha_base_keytype key_type() const
     { return unsigned_flag ? HA_KEYTYPE_USHORT_INT : HA_KEYTYPE_SHORT_INT;}
   int store(const char *to,uint length,CHARSET_INFO *charset);
@@ -1993,7 +1998,7 @@ class Field_medium :public Field_num {
 	       unireg_check_arg, field_name_arg,
 	       0, zero_arg,unsigned_arg)
     {}
-  enum_field_types type() const { return MYSQL_TYPE_INT24;}
+  const Type_handler *type_handler() const { return &type_handler_int24; }
   enum ha_base_keytype key_type() const
     { return unsigned_flag ? HA_KEYTYPE_UINT24 : HA_KEYTYPE_INT24; }
   int store(const char *to,uint length,CHARSET_INFO *charset);
@@ -2032,7 +2037,7 @@ class Field_long :public Field_num {
     :Field_num((uchar*) 0, len_arg, maybe_null_arg ? (uchar*) "": 0,0,
 	       NONE, field_name_arg,0,0,unsigned_arg)
     {}
-  enum_field_types type() const { return MYSQL_TYPE_LONG;}
+  const Type_handler *type_handler() const { return &type_handler_long; }
   enum ha_base_keytype key_type() const
     { return unsigned_flag ? HA_KEYTYPE_ULONG_INT : HA_KEYTYPE_LONG_INT; }
   int store(const char *to,uint length,CHARSET_INFO *charset);
@@ -2078,7 +2083,7 @@ class Field_longlong :public Field_num {
     :Field_num((uchar*) 0, len_arg, maybe_null_arg ? (uchar*) "": 0,0,
 	       NONE, field_name_arg,0,0,unsigned_arg)
     {}
-  enum_field_types type() const { return MYSQL_TYPE_LONGLONG;}
+  const Type_handler *type_handler() const { return &type_handler_longlong; }
   enum ha_base_keytype key_type() const
     { return unsigned_flag ? HA_KEYTYPE_ULONGLONG : HA_KEYTYPE_LONGLONG; }
   int store(const char *to,uint length,CHARSET_INFO *charset);
@@ -2132,7 +2137,7 @@ class Field_float :public Field_real {
       if (dec_arg >= FLOATING_POINT_DECIMALS)
         dec_arg= NOT_FIXED_DEC;
     }
-  enum_field_types type() const { return MYSQL_TYPE_FLOAT;}
+  const Type_handler *type_handler() const { return &type_handler_float; }
   enum ha_base_keytype key_type() const { return HA_KEYTYPE_FLOAT; }
   int store(const char *to,uint length,CHARSET_INFO *charset);
   int store(double nr);
@@ -2182,7 +2187,7 @@ class Field_double :public Field_real {
       if (dec_arg >= FLOATING_POINT_DECIMALS)
         dec_arg= NOT_FIXED_DEC;
     }
-  enum_field_types type() const { return MYSQL_TYPE_DOUBLE;}
+  const Type_handler *type_handler() const { return &type_handler_double; }
   enum ha_base_keytype key_type() const { return HA_KEYTYPE_DOUBLE; }
   int  store(const char *to,uint length,CHARSET_INFO *charset);
   int  store(double nr);
@@ -2219,7 +2224,7 @@ class Field_null :public Field_str {
     :Field_str(ptr_arg, len_arg, null, 1,
 	       unireg_check_arg, field_name_arg, cs)
     {}
-  enum_field_types type() const { return MYSQL_TYPE_NULL;}
+  const Type_handler *type_handler() const { return &type_handler_null; }
   Copy_func *get_copy_func(const Field *from) const
   {
     return do_field_string;
@@ -2269,7 +2274,6 @@ class Field_temporal: public Field {
     :Field(ptr_arg, len_arg, null_ptr_arg, null_bit_arg, unireg_check_arg,
                field_name_arg)
     { flags|= BINARY_FLAG; }
-  Item_result result_type () const { return STRING_RESULT; }   
   int  store_hex_hybrid(const char *str, uint length)
   {
     return store(str, length, &my_charset_bin);
@@ -2290,7 +2294,6 @@ class Field_temporal: public Field {
   CHARSET_INFO *charset(void) const { return &my_charset_numeric; }
   CHARSET_INFO *sort_charset(void) const { return &my_charset_bin; }
   bool binary() const { return true; }
-  enum Item_result cmp_type () const { return TIME_RESULT; }
   bool val_bool() { return val_real() != 0e0; }
   uint is_equal(Create_field *new_field);
   bool eq_def(const Field *field) const
@@ -2366,7 +2369,7 @@ class Field_timestamp :public Field_temporal {
                   uchar *null_ptr_arg, uchar null_bit_arg,
 		  enum utype unireg_check_arg, const char *field_name_arg,
 		  TABLE_SHARE *share);
-  enum_field_types type() const { return MYSQL_TYPE_TIMESTAMP;}
+  const Type_handler *type_handler() const { return &type_handler_timestamp; }
   enum ha_base_keytype key_type() const { return HA_KEYTYPE_ULONG_INT; }
   int  store(const char *to,uint length,CHARSET_INFO *charset);
   int  store(double nr);
@@ -2500,7 +2503,7 @@ class Field_timestampf :public Field_timestamp_with_dec {
     Field_timestamp_with_dec(ptr_arg, null_ptr_arg, null_bit_arg,
                              unireg_check_arg, field_name_arg, share, dec_arg)
     {}
-  enum_field_types real_type() const { return MYSQL_TYPE_TIMESTAMP2; }
+  const Type_handler *type_handler() const { return &type_handler_timestamp2; }
   enum_field_types binlog_type() const { return MYSQL_TYPE_TIMESTAMP2; }
   uint32 pack_length() const
   {
@@ -2531,14 +2534,18 @@ class Field_year :public Field_tiny {
     :Field_tiny(ptr_arg, len_arg, null_ptr_arg, null_bit_arg,
 		unireg_check_arg, field_name_arg, 1, 1)
     {}
-  enum_field_types type() const { return MYSQL_TYPE_YEAR;}
+  const Type_handler *type_handler() const { return &type_handler_year; }
   Copy_func *get_copy_func(const Field *from) const
   {
     if (eq_def(from))
       return get_identical_copy_func();
     switch (from->cmp_type()) {
     case STRING_RESULT:
-      return do_field_string;
+    {
+      const Type_handler *handler= from->type_handler();
+      return handler == &type_handler_enum ||
+             handler == &type_handler_set ? do_field_int : do_field_string;
+    }
     case TIME_RESULT:
       return do_field_temporal;
     case DECIMAL_RESULT:
@@ -2576,7 +2583,7 @@ class Field_date :public Field_temporal_with_date {
 	     enum utype unireg_check_arg, const char *field_name_arg)
     :Field_temporal_with_date(ptr_arg, MAX_DATE_WIDTH, null_ptr_arg, null_bit_arg,
                               unireg_check_arg, field_name_arg) {}
-  enum_field_types type() const { return MYSQL_TYPE_DATE;}
+  const Type_handler *type_handler() const { return &type_handler_date; }
   enum ha_base_keytype key_type() const { return HA_KEYTYPE_ULONG_INT; }
   int reset(void) { ptr[0]=ptr[1]=ptr[2]=ptr[3]=0; return 0; }
   bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate)
@@ -2612,8 +2619,7 @@ class Field_newdate :public Field_temporal_with_date {
     :Field_temporal_with_date(ptr_arg, MAX_DATE_WIDTH, null_ptr_arg, null_bit_arg,
                               unireg_check_arg, field_name_arg)
     {}
-  enum_field_types type() const { return MYSQL_TYPE_DATE;}
-  enum_field_types real_type() const { return MYSQL_TYPE_NEWDATE; }
+  const Type_handler *type_handler() const { return &type_handler_newdate; }
   enum ha_base_keytype key_type() const { return HA_KEYTYPE_UINT24; }
   int reset(void) { ptr[0]=ptr[1]=ptr[2]=0; return 0; }
   double val_real(void);
@@ -2651,7 +2657,7 @@ class Field_time :public Field_temporal {
     :Field_temporal(ptr_arg, length_arg, null_ptr_arg, null_bit_arg,
                     unireg_check_arg, field_name_arg), curdays(0)
     {}
-  enum_field_types type() const { return MYSQL_TYPE_TIME;}
+  const Type_handler *type_handler() const { return &type_handler_time; }
   enum ha_base_keytype key_type() const { return HA_KEYTYPE_INT24; }
   Copy_func *get_copy_func(const Field *from) const
   {
@@ -2762,7 +2768,7 @@ class Field_timef :public Field_time_with_dec {
   {
     DBUG_ASSERT(dec <= TIME_SECOND_PART_DIGITS);
   }
-  enum_field_types real_type() const { return MYSQL_TYPE_TIME2; }
+  const Type_handler *type_handler() const { return &type_handler_time2; }
   enum_field_types binlog_type() const { return MYSQL_TYPE_TIME2; }
   uint32 pack_length() const
   {
@@ -2804,7 +2810,7 @@ class Field_datetime :public Field_temporal_with_date {
           unireg_check == TIMESTAMP_DNUN_FIELD)
         flags|= ON_UPDATE_NOW_FLAG;
     }
-  enum_field_types type() const { return MYSQL_TYPE_DATETIME;}
+  const Type_handler *type_handler() const { return &type_handler_datetime; }
   enum ha_base_keytype key_type() const { return HA_KEYTYPE_ULONGLONG; }
   double val_real(void);
   longlong val_int(void);
@@ -2921,7 +2927,7 @@ class Field_datetimef :public Field_datetime_with_dec {
     :Field_datetime_with_dec(ptr_arg, null_ptr_arg, null_bit_arg,
                              unireg_check_arg, field_name_arg, dec_arg)
   {}
-  enum_field_types real_type() const { return MYSQL_TYPE_DATETIME2; }
+  const Type_handler *type_handler() const { return &type_handler_datetime2; }
   enum_field_types binlog_type() const { return MYSQL_TYPE_DATETIME2; }
   uint32 pack_length() const
   {
@@ -2998,6 +3004,14 @@ class Field_string :public Field_longstr {
   public:
     Warn_filter_string(const THD *thd, const Field_string *field);
   };
+  bool is_var_string() const
+  {
+    return can_alter_field_type &&
+           orig_table &&
+           (orig_table->s->db_create_options & HA_OPTION_PACK_RECORD) &&
+           field_length >= 4 &&
+           orig_table->s->frm_version < FRM_VER_TRUE_VARCHAR;
+  }
 public:
   bool can_alter_field_type;
   Field_string(uchar *ptr_arg, uint32 len_arg,uchar *null_ptr_arg,
@@ -3013,13 +3027,11 @@ class Field_string :public Field_longstr {
                    NONE, field_name_arg, cs),
      can_alter_field_type(1) {};
 
-  enum_field_types type() const
+  const Type_handler *type_handler() const
   {
-    return ((can_alter_field_type && orig_table &&
-             orig_table->s->db_create_options & HA_OPTION_PACK_RECORD &&
-	     field_length >= 4) &&
-            orig_table->s->frm_version < FRM_VER_TRUE_VARCHAR ?
-	    MYSQL_TYPE_VAR_STRING : MYSQL_TYPE_STRING);
+    return is_var_string() ?
+           static_cast<const Type_handler*>(&type_handler_var_string) :
+           static_cast<const Type_handler*>(&type_handler_string);
   }
   enum ha_base_keytype key_type() const
     { return binary() ? HA_KEYTYPE_BINARY : HA_KEYTYPE_TEXT; }
@@ -3061,7 +3073,6 @@ class Field_string :public Field_longstr {
   uint packed_col_length(const uchar *to, uint length);
   uint max_packed_col_length(uint max_length);
   uint size_of() const { return sizeof(*this); }
-  enum_field_types real_type() const { return MYSQL_TYPE_STRING; }
   bool has_charset(void) const
   { return charset() == &my_charset_bin ? FALSE : TRUE; }
   Field *make_new_field(MEM_ROOT *root, TABLE *new_table, bool keep_type);
@@ -3109,7 +3120,7 @@ class Field_varstring :public Field_longstr {
     share->varchar_fields++;
   }
 
-  enum_field_types type() const { return MYSQL_TYPE_VARCHAR; }
+  const Type_handler *type_handler() const { return &type_handler_varchar; }
   enum ha_base_keytype key_type() const;
   uint row_pack_length() const { return field_length; }
   bool zero_pack() const { return 0; }
@@ -3153,7 +3164,6 @@ class Field_varstring :public Field_longstr {
   uint max_packed_col_length(uint max_length);
   uint32 data_length();
   uint size_of() const { return sizeof(*this); }
-  enum_field_types real_type() const { return MYSQL_TYPE_VARCHAR; }
   bool has_charset(void) const
   { return charset() == &my_charset_bin ? FALSE : TRUE; }
   Field *make_new_field(MEM_ROOT *root, TABLE *new_table, bool keep_type);
@@ -3218,7 +3228,7 @@ class Field_blob :public Field_longstr {
     :Field_longstr((uchar*) 0, 0, (uchar*) "", 0, NONE, "temp", system_charset_info),
     packlength(packlength_arg) {}
   /* Note that the default copy constructor is used, in clone() */
-  enum_field_types type() const { return MYSQL_TYPE_BLOB;}
+  const Type_handler *type_handler() const { return &type_handler_blob; }
   enum ha_base_keytype key_type() const
     { return binary() ? HA_KEYTYPE_VARBINARY2 : HA_KEYTYPE_VARTEXT2; }
   Copy_func *get_copy_func(const Field *from) const
@@ -3408,7 +3418,7 @@ class Field_geom :public Field_blob {
     :Field_blob(len_arg, maybe_null_arg, field_name_arg, &my_charset_bin)
   { geom_type= geom_type_arg; srid= 0; }
   enum ha_base_keytype key_type() const { return HA_KEYTYPE_VARBINARY2; }
-  enum_field_types type() const { return MYSQL_TYPE_GEOMETRY; }
+  const Type_handler *type_handler() const { return &type_handler_geometry; }
   bool can_optimize_range(const Item_bool_func *cond,
                                   const Item *item,
                                   bool is_eq_func) const;
@@ -3465,12 +3475,7 @@ class Field_enum :public Field_str {
       flags|=ENUM_FLAG;
   }
   Field *make_new_field(MEM_ROOT *root, TABLE *new_table, bool keep_type);
-  enum_field_types type() const { return MYSQL_TYPE_STRING; }
-  enum Item_result cmp_type () const { return INT_RESULT; }
-  const Type_handler *cast_to_int_type_handler() const
-  {
-    return &type_handler_longlong;
-  }
+  const Type_handler *type_handler() const { return &type_handler_enum; }
   enum ha_base_keytype key_type() const;
   Copy_func *get_copy_func(const Field *from) const
   {
@@ -3511,7 +3516,6 @@ class Field_enum :public Field_str {
   void store_type(ulonglong value);
   void sql_type(String &str) const;
   uint size_of() const { return sizeof(*this); }
-  enum_field_types real_type() const { return MYSQL_TYPE_ENUM; }
   uint pack_length_from_metadata(uint field_metadata)
   { return (field_metadata & 0x00ff); }
   uint row_pack_length() const { return pack_length(); }
@@ -3541,6 +3545,9 @@ class Field_enum :public Field_str {
     */
     return false;
   }
+  bool can_optimize_range(const Item_bool_func *cond,
+                          const Item *item,
+                          bool is_eq_func) const;
 private:
   int do_save_field_metadata(uchar *first_byte);
   uint is_equal(Create_field *new_field);
@@ -3571,7 +3578,7 @@ class Field_set :public Field_enum {
   String *val_str(String*,String *);
   void sql_type(String &str) const;
   uint size_of() const { return sizeof(*this); }
-  enum_field_types real_type() const { return MYSQL_TYPE_SET; }
+  const Type_handler *type_handler() const { return &type_handler_set; }
   bool has_charset(void) const { return TRUE; }
 private:
   const String empty_set_string;
@@ -3601,13 +3608,12 @@ class Field_bit :public Field {
   Field_bit(uchar *ptr_arg, uint32 len_arg, uchar *null_ptr_arg,
             uchar null_bit_arg, uchar *bit_ptr_arg, uchar bit_ofs_arg,
             enum utype unireg_check_arg, const char *field_name_arg);
-  enum_field_types type() const { return MYSQL_TYPE_BIT; }
+  const Type_handler *type_handler() const { return &type_handler_bit; }
   enum ha_base_keytype key_type() const { return HA_KEYTYPE_BIT; }
   uint32 key_length() const { return (uint32) (field_length + 7) / 8; }
   uint32 max_data_length() const { return (field_length + 7) / 8; }
   uint32 max_display_length() { return field_length; }
   uint size_of() const { return sizeof(*this); }
-  Item_result result_type () const { return INT_RESULT; }
   int reset(void) { 
     bzero(ptr, bytes_in_rec); 
     if (bit_ptr && (bit_len > 0))  // reset odd bits among null bits
diff --git a/sql/item.cc b/sql/item.cc
index 308ed1d..65f1e50 100644
--- a/sql/item.cc
+++ b/sql/item.cc
@@ -5682,13 +5682,7 @@ const Type_handler *Item_field::real_type_handler() const
   */
   if (field->is_created_from_null_item)
     return &type_handler_null;
-  /* work around about varchar type field detection */
-  enum_field_types type= field->real_type();
-  // TODO: We should add Field::real_type_handler() eventually
-  if (type == MYSQL_TYPE_STRING && field->type() == MYSQL_TYPE_VAR_STRING)
-    type= MYSQL_TYPE_VAR_STRING;
-  return Type_handler::get_handler_by_real_type(type);
-
+  return field->type_handler();
 }
 
 
diff --git a/sql/item.h b/sql/item.h
index 6f22c05..f30826f 100644
--- a/sql/item.h
+++ b/sql/item.h
@@ -2407,7 +2407,7 @@ class Item_field :public Item_ident
   }
   const Type_handler *cast_to_int_type_handler() const
   {
-    return field->cast_to_int_type_handler();
+    return field->type_handler()->cast_to_int_type_handler();
   }
   enum_field_types field_type() const
   {
diff --git a/sql/opt_range.cc b/sql/opt_range.cc
index 6d088ca..1df7286 100644
--- a/sql/opt_range.cc
+++ b/sql/opt_range.cc
@@ -7707,7 +7707,9 @@ Item_func_like::get_mm_leaf(RANGE_OPT_PARAM *param,
   if (!(res= value->val_str(&tmp)))
     DBUG_RETURN(&null_element);
 
-  if (field->cmp_type() != STRING_RESULT)
+  if (field->cmp_type() != STRING_RESULT ||
+      field->type_handler() == &type_handler_enum ||
+      field->type_handler() == &type_handler_set)
     DBUG_RETURN(0);
 
   /*
@@ -7803,19 +7805,31 @@ Item_bool_func::get_mm_leaf(RANGE_OPT_PARAM *param,
     goto end;
 
   err= value->save_in_field_no_warnings(field, 1);
-  if (err == 2 && field->cmp_type() == STRING_RESULT)
+  if (err > 0)
   {
-    if (type == EQ_FUNC || type == EQUAL_FUNC)
+    if (field->type_handler() == &type_handler_enum ||
+        field->type_handler() == &type_handler_set)
     {
-      tree= new (alloc) SEL_ARG(field, 0, 0);
-      tree->type= SEL_ARG::IMPOSSIBLE;
+      if (type == EQ_FUNC || type == EQUAL_FUNC)
+      {
+        tree= new (alloc) SEL_ARG(field, 0, 0);
+        tree->type= SEL_ARG::IMPOSSIBLE;
+      }
+      goto end;
     }
-    else 
-      tree= NULL; /*  Cannot infer anything */
-    goto end;
-  }
-  if (err > 0)
-  {
+
+    if (err == 2 && field->cmp_type() == STRING_RESULT)
+    {
+      if (type == EQ_FUNC || type == EQUAL_FUNC)
+      {
+        tree= new (alloc) SEL_ARG(field, 0, 0);
+        tree->type= SEL_ARG::IMPOSSIBLE;
+      }
+      else 
+        tree= NULL; /*  Cannot infer anything */
+      goto end;
+    }
+
     if (field->cmp_type() != value->result_type())
     {
       if ((type == EQ_FUNC || type == EQUAL_FUNC) &&
diff --git a/sql/sql_type.cc b/sql/sql_type.cc
index 67ecd2a..6475f50 100644
--- a/sql/sql_type.cc
+++ b/sql/sql_type.cc
@@ -20,25 +20,21 @@
 #include "item.h"
 #include "log.h"
 
-static Type_handler_tiny        type_handler_tiny;
-static Type_handler_short       type_handler_short;
-static Type_handler_long        type_handler_long;
-static Type_handler_int24       type_handler_int24;
-static Type_handler_year        type_handler_year;
-static Type_handler_time        type_handler_time;
-static Type_handler_time2       type_handler_time2;
-static Type_handler_date        type_handler_date;
-static Type_handler_newdate     type_handler_newdate;
-static Type_handler_datetime2   type_handler_datetime2;
-static Type_handler_timestamp   type_handler_timestamp;
-static Type_handler_timestamp2  type_handler_timestamp2;
-static Type_handler_olddecimal  type_handler_olddecimal;
-static Type_handler_string      type_handler_string;
-static Type_handler_tiny_blob   type_handler_tiny_blob;
-static Type_handler_medium_blob type_handler_medium_blob;
-static Type_handler_long_blob   type_handler_long_blob;
-static Type_handler_blob        type_handler_blob;
-
+Type_handler_tiny        type_handler_tiny;
+Type_handler_short       type_handler_short;
+Type_handler_long        type_handler_long;
+Type_handler_int24       type_handler_int24;
+
+Type_handler_year        type_handler_year;
+Type_handler_time        type_handler_time;
+Type_handler_time2       type_handler_time2;
+Type_handler_date        type_handler_date;
+Type_handler_datetime2   type_handler_datetime2;
+Type_handler_timestamp   type_handler_timestamp;
+Type_handler_timestamp2  type_handler_timestamp2;
+Type_handler_olddecimal  type_handler_olddecimal;
+Type_handler_string      type_handler_string;
+Type_handler_var_string  type_handler_var_string;
 
 Type_handler_null        type_handler_null;
 Type_handler_row         type_handler_row;
@@ -48,10 +44,16 @@ Type_handler_float       type_handler_float;
 Type_handler_double      type_handler_double;
 Type_handler_newdecimal  type_handler_newdecimal;
 Type_handler_datetime    type_handler_datetime;
+Type_handler_newdate     type_handler_newdate;
 Type_handler_bit         type_handler_bit;
 Type_handler_enum        type_handler_enum;
 Type_handler_set         type_handler_set;
 
+Type_handler_tiny_blob   type_handler_tiny_blob;
+Type_handler_medium_blob type_handler_medium_blob;
+Type_handler_long_blob   type_handler_long_blob;
+Type_handler_blob        type_handler_blob;
+
 #ifdef HAVE_SPATIAL
 Type_handler_geometry    type_handler_geometry;
 #endif
@@ -193,6 +195,7 @@ const Name Type_handler_null::m_name_null(C_STRING_WITH_LEN("null"));
 
 const Name
   Type_handler_string::m_name_char(C_STRING_WITH_LEN("char")),
+  Type_handler_var_string::m_name_var_string(C_STRING_WITH_LEN("varchar")),
   Type_handler_varchar::m_name_varchar(C_STRING_WITH_LEN("varchar")),
   Type_handler_tiny_blob::m_name_tinyblob(C_STRING_WITH_LEN("tinyblob")),
   Type_handler_medium_blob::m_name_mediumblob(C_STRING_WITH_LEN("mediumblob")),
@@ -274,6 +277,18 @@ const Type_handler *Type_handler_row::type_handler_for_comparison() const
   return &type_handler_row;
 }
 
+/***************************************************************************/
+
+const Type_handler *Type_handler_enum::cast_to_int_type_handler() const
+{
+  return &type_handler_longlong;
+}
+
+
+const Type_handler *Type_handler_set::cast_to_int_type_handler() const
+{
+  return &type_handler_longlong;
+}
 
 /***************************************************************************/
 
diff --git a/sql/sql_type.h b/sql/sql_type.h
index 189b1b0..fb66f52 100644
--- a/sql/sql_type.h
+++ b/sql/sql_type.h
@@ -297,6 +297,10 @@ class Type_handler
   virtual Item_result result_type() const= 0;
   virtual Item_result cmp_type() const= 0;
   virtual const Type_handler *type_handler_for_comparison() const= 0;
+  virtual const Type_handler *cast_to_int_type_handler() const
+  {
+    return this;
+  }
   virtual CHARSET_INFO *charset_for_protocol(const Item *item) const;
   virtual const Type_handler*
   type_handler_adjusted_to_max_octet_length(uint max_octet_length,
@@ -1147,6 +1151,7 @@ class Type_handler_newdate: public Type_handler_date_common
 {
 public:
   virtual ~Type_handler_newdate() {}
+  enum_field_types real_field_type() const { return MYSQL_TYPE_NEWDATE; }
   Field *make_conversion_table_field(TABLE *, uint metadata,
                                      const Field *target) const;
 };
@@ -1266,6 +1271,18 @@ class Type_handler_string: public Type_handler_string_result
 };
 
 
+/* Old varchar */
+class Type_handler_var_string: public Type_handler_string
+{
+  static const Name m_name_var_string;
+public:
+  virtual ~Type_handler_var_string() {}
+  const Name name() const { return m_name_var_string; }
+  enum_field_types field_type() const { return MYSQL_TYPE_VAR_STRING; }
+  enum_field_types real_field_type() const { return MYSQL_TYPE_STRING; }
+};
+
+
 class Type_handler_varchar: public Type_handler_string_result
 {
   static const Name m_name_varchar;
@@ -1360,8 +1377,9 @@ class Type_handler_enum: public Type_handler_string_result
 public:
   virtual ~Type_handler_enum() {}
   const Name name() const { return m_name_enum; }
-  enum_field_types field_type() const { return MYSQL_TYPE_VARCHAR; }
+  enum_field_types field_type() const { return MYSQL_TYPE_STRING; }
   virtual enum_field_types real_field_type() const { return MYSQL_TYPE_ENUM; }
+  const Type_handler *cast_to_int_type_handler() const;
   Field *make_conversion_table_field(TABLE *, uint metadata,
                                      const Field *target) const;
 };
@@ -1373,8 +1391,9 @@ class Type_handler_set: public Type_handler_string_result
 public:
   virtual ~Type_handler_set() {}
   const Name name() const { return m_name_set; }
-  enum_field_types field_type() const { return MYSQL_TYPE_VARCHAR; }
+  enum_field_types field_type() const { return MYSQL_TYPE_STRING; }
   virtual enum_field_types real_field_type() const { return MYSQL_TYPE_SET; }
+  const Type_handler *cast_to_int_type_handler() const;
   Field *make_conversion_table_field(TABLE *, uint metadata,
                                      const Field *target) const;
 };
@@ -1450,18 +1469,45 @@ class Type_handler_hybrid_field_type
 };
 
 
-extern Type_handler_row   type_handler_row;
-extern Type_handler_null  type_handler_null;
-extern Type_handler_varchar type_handler_varchar;
-extern Type_handler_longlong type_handler_longlong;
-extern Type_handler_float type_handler_float;
-extern Type_handler_double type_handler_double;
-extern Type_handler_newdecimal type_handler_newdecimal;
-extern Type_handler_datetime type_handler_datetime;
-extern Type_handler_longlong type_handler_longlong;
-extern Type_handler_bit type_handler_bit;
-extern Type_handler_enum type_handler_enum;
-extern Type_handler_set type_handler_set;
+extern Type_handler_row         type_handler_row;
+extern Type_handler_null        type_handler_null;
+
+extern Type_handler_float       type_handler_float;
+extern Type_handler_double      type_handler_double;
+
+extern Type_handler_bit         type_handler_bit;
+
+extern Type_handler_enum        type_handler_enum;
+extern Type_handler_set         type_handler_set;
+
+extern Type_handler_string      type_handler_string;
+extern Type_handler_var_string  type_handler_var_string;
+extern Type_handler_varchar     type_handler_varchar;
+
+extern Type_handler_tiny_blob   type_handler_tiny_blob;
+extern Type_handler_medium_blob type_handler_medium_blob;
+extern Type_handler_long_blob   type_handler_long_blob;
+extern Type_handler_blob        type_handler_blob;
+
+extern Type_handler_tiny        type_handler_tiny;
+extern Type_handler_short       type_handler_short;
+extern Type_handler_int24       type_handler_int24;
+extern Type_handler_long        type_handler_long;
+extern Type_handler_longlong    type_handler_longlong;
+
+extern Type_handler_newdecimal  type_handler_newdecimal;
+extern Type_handler_olddecimal  type_handler_olddecimal;
+
+extern Type_handler_year        type_handler_year;
+extern Type_handler_newdate     type_handler_newdate;
+extern Type_handler_date        type_handler_date;
+extern Type_handler_time        type_handler_time;
+extern Type_handler_time2       type_handler_time2;
+extern Type_handler_datetime    type_handler_datetime;
+extern Type_handler_datetime2   type_handler_datetime2;
+extern Type_handler_timestamp   type_handler_timestamp;
+extern Type_handler_timestamp2  type_handler_timestamp2;
+
 
 
 class Type_aggregator
