← Back to team overview

maria-developers team mailing list archive

Please review: MDEV-7199 Introduce the concept of data type handlers

 

  Hi Sergei,

while working on addressing your review suggestions for
"MDEV-4912 Add a plugin to field types (column types)",
I decided to create a separate tasks for a piece of additional changes:

https://mariadb.atlassian.net/browse/MDEV-7199
MDEV-7199 Introduce the concept of data type handlers

Please review.

Thanks.
diff --git a/include/mysql.h.pp b/include/mysql.h.pp
index 6b60389..e19f1bd 100644
--- a/include/mysql.h.pp
+++ b/include/mysql.h.pp
@@ -102,7 +102,7 @@ struct my_rnd_struct;
 enum Item_result
 {
   STRING_RESULT=0, REAL_RESULT, INT_RESULT, ROW_RESULT, DECIMAL_RESULT,
-  TIME_RESULT,IMPOSSIBLE_RESULT
+  TIME_RESULT
 };
 typedef struct st_udf_args
 {
diff --git a/include/mysql_com.h b/include/mysql_com.h
index 0da24bc..efb53b9 100644
--- a/include/mysql_com.h
+++ b/include/mysql_com.h
@@ -550,7 +550,7 @@ int my_connect(my_socket s, const struct sockaddr *name, unsigned int namelen,
 enum Item_result
 {
   STRING_RESULT=0, REAL_RESULT, INT_RESULT, ROW_RESULT, DECIMAL_RESULT,
-  TIME_RESULT,IMPOSSIBLE_RESULT
+  TIME_RESULT
 };
 
 typedef struct st_udf_args
diff --git a/mysql-test/r/olap.result b/mysql-test/r/olap.result
index b10f175..a8219e5 100644
--- a/mysql-test/r/olap.result
+++ b/mysql-test/r/olap.result
@@ -766,3 +766,26 @@ b
 NULL
 DROP TABLE t1, t2;
 End of 5.0 tests
+#
+# Start of 10.1 tests
+#
+#
+# MDEV-7195 AVG() loses precision in INT context
+#
+CREATE TABLE t1 (
+auto SERIAL,
+fld1 bigint unsigned NOT NULL,
+companynr tinyint(2) unsigned zerofill DEFAULT '00' NOT NULL,
+UNIQUE fld1 (fld1)
+);
+INSERT INTO t1 VALUES (1,0x7FFFFFFFFFFFFFFF,00);
+INSERT INTO t1 VALUES (2,0x7FFFFFFFFFFFFFFE,37);
+INSERT INTO t1 VALUES (3,0x7FFFFFFFFFFFFFFC,37);
+SELECT companynr, AVG(fld1), AVG(fld1)<<0 AS avg1, CAST(AVG(fld1) AS UNSIGNED)<<0 AS avg2 FROM t1 GROUP BY companynr;
+companynr	AVG(fld1)	avg1	avg2
+00	9223372036854775807.0000	9223372036854775807	9223372036854775807
+37	9223372036854775805.0000	9223372036854775805	9223372036854775805
+DROP TABLE t1;
+#
+# End of 10.1 tests
+#
diff --git a/mysql-test/t/olap.test b/mysql-test/t/olap.test
index fec5df1..6a79900 100644
--- a/mysql-test/t/olap.test
+++ b/mysql-test/t/olap.test
@@ -404,3 +404,28 @@ SELECT DISTINCT b FROM t1, t2 GROUP BY a, b WITH ROLLUP;
 DROP TABLE t1, t2;
 
 --echo End of 5.0 tests
+
+
+--echo #
+--echo # Start of 10.1 tests
+--echo #
+
+--echo #
+--echo # MDEV-7195 AVG() loses precision in INT context
+--echo #
+CREATE TABLE t1 (
+  auto SERIAL,
+  fld1 bigint unsigned NOT NULL,
+  companynr tinyint(2) unsigned zerofill DEFAULT '00' NOT NULL,
+  UNIQUE fld1 (fld1)
+);
+INSERT INTO t1 VALUES (1,0x7FFFFFFFFFFFFFFF,00);
+INSERT INTO t1 VALUES (2,0x7FFFFFFFFFFFFFFE,37); 
+INSERT INTO t1 VALUES (3,0x7FFFFFFFFFFFFFFC,37);  
+SELECT companynr, AVG(fld1), AVG(fld1)<<0 AS avg1, CAST(AVG(fld1) AS UNSIGNED)<<0 AS avg2 FROM t1 GROUP BY companynr;
+DROP TABLE t1;
+
+
+--echo #
+--echo # End of 10.1 tests
+--echo #
diff --git a/sql/CMakeLists.txt b/sql/CMakeLists.txt
index 1c41ff4..5fe0f43 100644
--- a/sql/CMakeLists.txt
+++ b/sql/CMakeLists.txt
@@ -113,6 +113,7 @@ SET (SQL_SOURCE
 			   ../sql-common/mysql_async.c
                my_apc.cc my_apc.h
                rpl_gtid.cc rpl_parallel.cc
+               sql_type.cc
 	       ${WSREP_SOURCES}
                table_cache.cc
                ${CMAKE_CURRENT_BINARY_DIR}/sql_builtin.cc
diff --git a/sql/field.cc b/sql/field.cc
index 3d5ba98..27e79e2 100644
--- a/sql/field.cc
+++ b/sql/field.cc
@@ -3136,7 +3136,7 @@ int Field_num::store_time_dec(MYSQL_TIME *ltime, uint dec)
 ** tiny int
 ****************************************************************************/
 
-int Field_tiny::store(const char *from,uint len,CHARSET_INFO *cs)
+int Field_int8::store(const char *from,uint len,CHARSET_INFO *cs)
 {
   ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED;
   int error;
@@ -3148,7 +3148,7 @@ int Field_tiny::store(const char *from,uint len,CHARSET_INFO *cs)
 }
 
 
-int Field_tiny::store(double nr)
+int Field_int8::store(double nr)
 {
   ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED;
   int error= 0;
@@ -3191,7 +3191,7 @@ int Field_tiny::store(double nr)
 }
 
 
-int Field_tiny::store(longlong nr, bool unsigned_val)
+int Field_int8::store(longlong nr, bool unsigned_val)
 {
   ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED;
   int error= 0;
@@ -3236,7 +3236,7 @@ int Field_tiny::store(longlong nr, bool unsigned_val)
 }
 
 
-double Field_tiny::val_real(void)
+double Field_int8::val_real(void)
 {
   ASSERT_COLUMN_MARKED_FOR_READ;
   int tmp= unsigned_flag ? (int) ptr[0] :
@@ -3245,7 +3245,7 @@ double Field_tiny::val_real(void)
 }
 
 
-longlong Field_tiny::val_int(void)
+longlong Field_int8::val_int(void)
 {
   ASSERT_COLUMN_MARKED_FOR_READ;
   int tmp= unsigned_flag ? (int) ptr[0] :
@@ -3254,7 +3254,7 @@ longlong Field_tiny::val_int(void)
 }
 
 
-String *Field_tiny::val_str(String *val_buffer,
+String *Field_int8::val_str(String *val_buffer,
 			    String *val_ptr __attribute__((unused)))
 {
   ASSERT_COLUMN_MARKED_FOR_READ;
@@ -3278,12 +3278,12 @@ String *Field_tiny::val_str(String *val_buffer,
   return val_buffer;
 }
 
-bool Field_tiny::send_binary(Protocol *protocol)
+bool Field_int8::send_binary(Protocol *protocol)
 {
   return protocol->store_tiny((longlong) (int8) ptr[0]);
 }
 
-int Field_tiny::cmp(const uchar *a_ptr, const uchar *b_ptr)
+int Field_int8::cmp(const uchar *a_ptr, const uchar *b_ptr)
 {
   signed char a,b;
   a=(signed char) a_ptr[0]; b= (signed char) b_ptr[0];
@@ -3292,7 +3292,7 @@ int Field_tiny::cmp(const uchar *a_ptr, const uchar *b_ptr)
   return (a < b) ? -1 : (a > b) ? 1 : 0;
 }
 
-void Field_tiny::sort_string(uchar *to,uint length __attribute__((unused)))
+void Field_int8::sort_string(uchar *to,uint length __attribute__((unused)))
 {
   if (unsigned_flag)
     *to= *ptr;
@@ -3300,7 +3300,7 @@ void Field_tiny::sort_string(uchar *to,uint length __attribute__((unused)))
     to[0] = (char) (ptr[0] ^ (uchar) 128);	/* Revers signbit */
 }
 
-void Field_tiny::sql_type(String &res) const
+void Field_int8::sql_type(String &res) const
 {
   CHARSET_INFO *cs=res.charset();
   res.length(cs->cset->snprintf(cs,(char*) res.ptr(),res.alloced_length(),
@@ -7319,10 +7319,10 @@ void Field_varstring::hash(ulong *nr, ulong *nr2)
 ** packlength slot and may be from 1-4.
 ****************************************************************************/
 
-Field_blob::Field_blob(uchar *ptr_arg, uchar *null_ptr_arg, uchar null_bit_arg,
-		       enum utype unireg_check_arg, const char *field_name_arg,
-                       TABLE_SHARE *share, uint blob_pack_length,
-		       CHARSET_INFO *cs)
+Field_lob::Field_lob(uchar *ptr_arg, uchar *null_ptr_arg, uchar null_bit_arg,
+		     enum utype unireg_check_arg, const char *field_name_arg,
+                     TABLE_SHARE *share, uint blob_pack_length,
+                     CHARSET_INFO *cs)
   :Field_longstr(ptr_arg, BLOB_PACK_LENGTH_TO_MAX_LENGH(blob_pack_length),
                  null_ptr_arg, null_bit_arg, unireg_check_arg, field_name_arg,
                  cs),
@@ -7335,19 +7335,19 @@ void Field_varstring::hash(ulong *nr, ulong *nr2)
 }
 
 
-void Field_blob::store_length(uchar *i_ptr, uint i_packlength, uint32 i_number)
+void Field_lob::store_length(uchar *i_ptr, uint i_packlength, uint32 i_number)
 {
   store_lowendian(i_number, i_ptr, i_packlength);
 }
 
 
-uint32 Field_blob::get_length(const uchar *pos, uint packlength_arg)
+uint32 Field_lob::get_length(const uchar *pos, uint packlength_arg)
 {
   return (uint32)read_lowendian(pos, packlength_arg);
 }
 
 
-int Field_blob::store(const char *from,uint length,CHARSET_INFO *cs)
+int Field_lob::store(const char *from,uint length,CHARSET_INFO *cs)
 {
   ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED;
   uint copy_length, new_length;
@@ -7359,7 +7359,7 @@ int Field_blob::store(const char *from,uint length,CHARSET_INFO *cs)
 
   if (!length)
   {
-    bzero(ptr,Field_blob::pack_length());
+    bzero(ptr,Field_lob::pack_length());
     return 0;
   }
 
@@ -7377,7 +7377,7 @@ int Field_blob::store(const char *from,uint length,CHARSET_INFO *cs)
     */
     if (!String::needs_conversion_on_storage(length, cs, field_charset))
     {
-      Field_blob::store_length(length);
+      Field_lob::store_length(length);
       bmove(ptr + packlength, &from, sizeof(char*));
       return 0;
     }
@@ -7396,7 +7396,7 @@ int Field_blob::store(const char *from,uint length,CHARSET_INFO *cs)
     copy_length= my_copy_with_hex_escaping(field_charset,
                                            (char*) value.ptr(), new_length,
                                             from, length);
-    Field_blob::store_length(copy_length);
+    Field_lob::store_length(copy_length);
     tmp= value.ptr();
     bmove(ptr + packlength, (uchar*) &tmp, sizeof(char*));
     return 0;
@@ -7414,7 +7414,7 @@ int Field_blob::store(const char *from,uint length,CHARSET_INFO *cs)
                                        &cannot_convert_error_pos,
                                        &from_end_pos);
 
-  Field_blob::store_length(copy_length);
+  Field_lob::store_length(copy_length);
   tmp= value.ptr();
   bmove(ptr+packlength,(uchar*) &tmp,sizeof(char*));
 
@@ -7426,28 +7426,28 @@ int Field_blob::store(const char *from,uint length,CHARSET_INFO *cs)
 
 oom_error:
   /* Fatal OOM error */
-  bzero(ptr,Field_blob::pack_length());
+  bzero(ptr,Field_lob::pack_length());
   return -1; 
 }
 
 
-int Field_blob::store(double nr)
+int Field_lob::store(double nr)
 {
   CHARSET_INFO *cs=charset();
   value.set_real(nr, NOT_FIXED_DEC, cs);
-  return Field_blob::store(value.ptr(),(uint) value.length(), cs);
+  return Field_lob::store(value.ptr(),(uint) value.length(), cs);
 }
 
 
-int Field_blob::store(longlong nr, bool unsigned_val)
+int Field_lob::store(longlong nr, bool unsigned_val)
 {
   CHARSET_INFO *cs=charset();
   value.set_int(nr, unsigned_val, cs);
-  return Field_blob::store(value.ptr(), (uint) value.length(), cs);
+  return Field_lob::store(value.ptr(), (uint) value.length(), cs);
 }
 
 
-double Field_blob::val_real(void)
+double Field_lob::val_real(void)
 {
   ASSERT_COLUMN_MARKED_FOR_READ;
   int not_used;
@@ -7464,7 +7464,7 @@ double Field_blob::val_real(void)
 }
 
 
-longlong Field_blob::val_int(void)
+longlong Field_lob::val_int(void)
 {
   ASSERT_COLUMN_MARKED_FOR_READ;
   int not_used;
@@ -7476,7 +7476,7 @@ longlong Field_blob::val_int(void)
   return my_strntoll(charset(),blob,length,10,NULL,&not_used);
 }
 
-String *Field_blob::val_str(String *val_buffer __attribute__((unused)),
+String *Field_lob::val_str(String *val_buffer __attribute__((unused)),
 			    String *val_ptr)
 {
   ASSERT_COLUMN_MARKED_FOR_READ;
@@ -7490,7 +7490,7 @@ String *Field_blob::val_str(String *val_buffer __attribute__((unused)),
 }
 
 
-my_decimal *Field_blob::val_decimal(my_decimal *decimal_value)
+my_decimal *Field_lob::val_decimal(my_decimal *decimal_value)
 {
   ASSERT_COLUMN_MARKED_FOR_READ;
   const char *blob;
@@ -7510,7 +7510,7 @@ my_decimal *Field_blob::val_decimal(my_decimal *decimal_value)
 }
 
 
-int Field_blob::cmp(const uchar *a,uint32 a_length, const uchar *b,
+int Field_lob::cmp(const uchar *a,uint32 a_length, const uchar *b,
 		    uint32 b_length)
 {
   return field_charset->coll->strnncollsp(field_charset, 
@@ -7519,7 +7519,7 @@ int Field_blob::cmp(const uchar *a,uint32 a_length, const uchar *b,
 }
 
 
-int Field_blob::cmp_max(const uchar *a_ptr, const uchar *b_ptr,
+int Field_lob::cmp_max(const uchar *a_ptr, const uchar *b_ptr,
                         uint max_length)
 {
   uchar *blob1,*blob2;
@@ -7528,11 +7528,11 @@ int Field_blob::cmp_max(const uchar *a_ptr, const uchar *b_ptr,
   uint a_len= get_length(a_ptr), b_len= get_length(b_ptr);
   set_if_smaller(a_len, max_length);
   set_if_smaller(b_len, max_length);
-  return Field_blob::cmp(blob1,a_len,blob2,b_len);
+  return Field_lob::cmp(blob1,a_len,blob2,b_len);
 }
 
 
-int Field_blob::cmp_binary(const uchar *a_ptr, const uchar *b_ptr,
+int Field_lob::cmp_binary(const uchar *a_ptr, const uchar *b_ptr,
 			   uint32 max_length)
 {
   char *a,*b;
@@ -7553,7 +7553,7 @@ int Field_blob::cmp_binary(const uchar *a_ptr, const uchar *b_ptr,
 
 /* The following is used only when comparing a key */
 
-uint Field_blob::get_key_image(uchar *buff,uint length, imagetype type_arg)
+uint Field_lob::get_key_image(uchar *buff,uint length, imagetype type_arg)
 {
   uint32 blob_length= get_length(ptr);
   uchar *blob;
@@ -7608,15 +7608,15 @@ uint Field_blob::get_key_image(uchar *buff,uint length, imagetype type_arg)
 }
 
 
-void Field_blob::set_key_image(const uchar *buff,uint length)
+void Field_lob::set_key_image(const uchar *buff,uint length)
 {
   length= uint2korr(buff);
-  (void) Field_blob::store((const char*) buff+HA_KEY_BLOB_LENGTH, length,
+  (void) Field_lob::store((const char*) buff+HA_KEY_BLOB_LENGTH, length,
                            field_charset);
 }
 
 
-int Field_blob::key_cmp(const uchar *key_ptr, uint max_key_length)
+int Field_lob::key_cmp(const uchar *key_ptr, uint max_key_length)
 {
   uchar *blob1;
   uint blob_length=get_length(ptr);
@@ -7626,19 +7626,19 @@ int Field_blob::key_cmp(const uchar *key_ptr, uint max_key_length)
   local_char_length= my_charpos(cs, blob1, blob1+blob_length,
                                 local_char_length);
   set_if_smaller(blob_length, local_char_length);
-  return Field_blob::cmp(blob1, blob_length,
+  return Field_lob::cmp(blob1, blob_length,
 			 key_ptr+HA_KEY_BLOB_LENGTH,
 			 uint2korr(key_ptr));
 }
 
-int Field_blob::key_cmp(const uchar *a,const uchar *b)
+int Field_lob::key_cmp(const uchar *a,const uchar *b)
 {
-  return Field_blob::cmp(a+HA_KEY_BLOB_LENGTH, uint2korr(a),
+  return Field_lob::cmp(a+HA_KEY_BLOB_LENGTH, uint2korr(a),
 			 b+HA_KEY_BLOB_LENGTH, uint2korr(b));
 }
 
 
-Field *Field_blob::new_key_field(MEM_ROOT *root, TABLE *new_table,
+Field *Field_lob::new_key_field(MEM_ROOT *root, TABLE *new_table,
                                  uchar *new_ptr, uint32 length,
                                  uchar *new_null_ptr, uint new_null_bit)
 {
@@ -7660,23 +7660,23 @@ Field *Field_blob::new_key_field(MEM_ROOT *root, TABLE *new_table,
 
    @returns number of bytes written to metadata_ptr
 */
-int Field_blob::do_save_field_metadata(uchar *metadata_ptr)
+int Field_lob::do_save_field_metadata(uchar *metadata_ptr)
 {
-  DBUG_ENTER("Field_blob::do_save_field_metadata");
+  DBUG_ENTER("Field_lob::do_save_field_metadata");
   *metadata_ptr= pack_length_no_ptr();
   DBUG_PRINT("debug", ("metadata: %u (pack_length_no_ptr)", *metadata_ptr));
   DBUG_RETURN(1);
 }
 
 
-uint32 Field_blob::sort_length() const
+uint32 Field_lob::sort_length() const
 {
   return (uint32) (current_thd->variables.max_sort_length + 
                    (field_charset == &my_charset_bin ? 0 : packlength));
 }
 
 
-void Field_blob::sort_string(uchar *to,uint length)
+void Field_lob::sort_string(uchar *to,uint length)
 {
   uchar *blob;
   uint blob_length=get_length();
@@ -7709,7 +7709,7 @@ void Field_blob::sort_string(uchar *to,uint length)
 }
 
 
-void Field_blob::sql_type(String &res) const
+void Field_lob::sql_type(String &res) const
 {
   const char *str;
   uint length;
@@ -7728,7 +7728,7 @@ void Field_blob::sql_type(String &res) const
   }
 }
 
-uchar *Field_blob::pack(uchar *to, const uchar *from, uint max_length)
+uchar *Field_lob::pack(uchar *to, const uchar *from, uint max_length)
 {
   uchar *save= ptr;
   ptr= (uchar*) from;
@@ -7771,10 +7771,10 @@ uchar *Field_blob::pack(uchar *to, const uchar *from, uint max_length)
 
    @return  New pointer into memory based on from + length of the data
 */
-const uchar *Field_blob::unpack(uchar *to, const uchar *from,
+const uchar *Field_lob::unpack(uchar *to, const uchar *from,
                                 const uchar *from_end, uint param_data)
 {
-  DBUG_ENTER("Field_blob::unpack");
+  DBUG_ENTER("Field_lob::unpack");
   DBUG_PRINT("enter", ("to: 0x%lx; from: 0x%lx; param_data: %u",
                        (ulong) to, (ulong) from, param_data));
   uint const master_packlength=
@@ -7793,7 +7793,7 @@ const uchar *Field_blob::unpack(uchar *to, const uchar *from,
 }
 
 
-uint Field_blob::packed_col_length(const uchar *data_ptr, uint length)
+uint Field_lob::packed_col_length(const uchar *data_ptr, uint length)
 {
   if (length > 255)
     return uint2korr(data_ptr)+2;
@@ -7801,13 +7801,13 @@ uint Field_blob::packed_col_length(const uchar *data_ptr, uint length)
 }
 
 
-uint Field_blob::max_packed_col_length(uint max_length)
+uint Field_lob::max_packed_col_length(uint max_length)
 {
   return (max_length > 255 ? 2 : 1)+max_length;
 }
 
 
-uint Field_blob::is_equal(Create_field *new_field)
+uint Field_lob::is_equal(Create_field *new_field)
 {
   if (field_flags_are_binary() != new_field->field_flags_are_binary())
     return 0;
@@ -7879,7 +7879,7 @@ int Field_geom::store_decimal(const my_decimal *)
 int Field_geom::store(const char *from, uint length, CHARSET_INFO *cs)
 {
   if (!length)
-    bzero(ptr, Field_blob::pack_length());
+    bzero(ptr, Field_lob::pack_length());
   else
   {
     if (from == Geometry::bad_geometry_data.ptr())
@@ -7905,7 +7905,7 @@ int Field_geom::store(const char *from, uint length, CHARSET_INFO *cs)
       goto err_exit;
     }
 
-    Field_blob::store_length(length);
+    Field_lob::store_length(length);
     if ((table->copy_blobs || length <= MAX_FIELD_WIDTH) &&
         from != value.ptr())
     {						// Must make a copy
@@ -7920,7 +7920,7 @@ int Field_geom::store(const char *from, uint length, CHARSET_INFO *cs)
   my_message(ER_CANT_CREATE_GEOMETRY_OBJECT,
              ER(ER_CANT_CREATE_GEOMETRY_OBJECT), MYF(0));
 err_exit:
-  bzero(ptr, Field_blob::pack_length());  
+  bzero(ptr, Field_lob::pack_length());  
   return -1;
 }
 
@@ -9971,7 +9971,7 @@ Field *make_field(TABLE_SHARE *share, uchar *ptr, uint32 field_length,
     length
 */
 
-uint32 Field_blob::char_length()
+uint32 Field_lob::char_length()
 {
   switch (packlength)
   {
@@ -10010,7 +10010,7 @@ Create_field *Create_field::clone(MEM_ROOT *mem_root) const
     length
 */
 
-uint32 Field_blob::max_display_length()
+uint32 Field_lob::max_display_length()
 {
   switch (packlength)
   {
diff --git a/sql/field.h b/sql/field.h
index b4b94bf..f8695d5 100644
--- a/sql/field.h
+++ b/sql/field.h
@@ -30,6 +30,7 @@
 #include "sql_string.h"                         /* String */
 #include "my_decimal.h"                         /* my_decimal */
 #include "sql_error.h"                          /* Sql_condition */
+#include "sql_type.h"                           /* Item_cmp */
 #include "compat56.h"
 
 class Send_field;
@@ -271,7 +272,7 @@ class Virtual_column_info: public Sql_alloc
   }
 };
 
-class Field
+class Field: public virtual Type_handler
 {
   Field(const Item &);				/* Prevent use of these */
   void operator=(Field &);
@@ -426,8 +427,6 @@ class Field
    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(); }
   static bool type_can_have_key_part(enum_field_types);
   static enum_field_types field_type_merge(enum_field_types, enum_field_types);
   static Item_result result_merge_type(enum_field_types);
@@ -553,7 +552,10 @@ class Field
   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;
+  enum_field_types type() const
+  {
+    return field_type(); // Type_handler's field_type()
+  }
   virtual enum_field_types real_type() const { return type(); }
   virtual enum_field_types binlog_type() const
   {
@@ -1050,7 +1052,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; }
@@ -1059,7 +1060,6 @@ class Field_num :public Field {
   friend class Create_field;
   void make_field(Send_field *);
   uint decimals() const { return (uint) dec; }
-  uint size_of() const { return sizeof(*this); }
   bool eq_def(Field *field);
   int store_decimal(const my_decimal *);
   my_decimal *val_decimal(my_decimal *);
@@ -1092,7 +1092,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  store(double nr);
   int  store(longlong nr, bool unsigned_val)=0;
@@ -1154,17 +1153,15 @@ class Field_real :public Field_num {
                field_name_arg, dec_arg, zero_arg, unsigned_arg),
     not_fixed(dec_arg >= NOT_FIXED_DEC)
     {}
-  Item_result result_type () const { return REAL_RESULT; }
   int store_decimal(const my_decimal *);
   int  store_time_dec(MYSQL_TIME *ltime, uint dec);
   bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate);
   my_decimal *val_decimal(my_decimal *);
   uint32 max_display_length() { return field_length; }
-  uint size_of() const { return sizeof(*this); }
 };
 
 
-class Field_decimal :public Field_real {
+class Field_decimal :public Field_real, Type_handler_decimal {
 public:
   Field_decimal(uchar *ptr_arg, uint32 len_arg, uchar *null_ptr_arg,
 		uchar null_bit_arg,
@@ -1174,7 +1171,6 @@ 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;}
   enum ha_base_keytype key_type() const
   { return zerofill ? HA_KEYTYPE_BINARY : HA_KEYTYPE_NUM; }
   int reset(void);
@@ -1193,11 +1189,12 @@ class Field_decimal :public Field_real {
   {
     return Field::pack(to, from, max_length);
   }
+  uint size_of() const { return sizeof(*this); }
 };
 
 
 /* New decimal/numeric field which use fixed point arithmetic */
-class Field_new_decimal :public Field_num {
+class Field_new_decimal :public Field_num, public Type_handler_newdecimal {
 private:
   int do_save_field_metadata(uchar *first_byte);
 public:
@@ -1217,9 +1214,7 @@ 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;}
   enum ha_base_keytype key_type() const { return HA_KEYTYPE_BINARY; }
-  Item_result result_type () const { return DECIMAL_RESULT; }
   int  reset(void);
   bool store_value(const my_decimal *decimal_value);
   void set_value_on_overflow(my_decimal *decimal_value, bool sign);
@@ -1249,9 +1244,9 @@ class Field_new_decimal :public Field_num {
 };
 
 
-class Field_tiny :public Field_num {
+class Field_int8 :public Field_num {
 public:
-  Field_tiny(uchar *ptr_arg, uint32 len_arg, uchar *null_ptr_arg,
+  Field_int8(uchar *ptr_arg, uint32 len_arg, uchar *null_ptr_arg,
 	     uchar null_bit_arg,
 	     enum utype unireg_check_arg, const char *field_name_arg,
 	     bool zero_arg, bool unsigned_arg)
@@ -1259,7 +1254,6 @@ 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;}
   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);
@@ -1293,7 +1287,21 @@ class Field_tiny :public Field_num {
 };
 
 
-class Field_short :public Field_num {
+class Field_tiny :public Field_int8, public Type_handler_tiny {
+public:
+  Field_tiny(uchar *ptr_arg, uint32 len_arg, uchar *null_ptr_arg,
+	     uchar null_bit_arg,
+	     enum utype unireg_check_arg, const char *field_name_arg,
+	     bool zero_arg, bool unsigned_arg)
+   :Field_int8(ptr_arg, len_arg, null_ptr_arg, null_bit_arg,
+               unireg_check_arg, field_name_arg,
+               zero_arg, unsigned_arg)
+  { }
+  uint size_of() const { return sizeof(*this); }
+};
+
+
+class Field_short :public Field_num, public Type_handler_short {
 public:
   Field_short(uchar *ptr_arg, uint32 len_arg, uchar *null_ptr_arg,
 	      uchar null_bit_arg,
@@ -1308,7 +1316,6 @@ 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;}
   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);
@@ -1331,9 +1338,10 @@ class Field_short :public Field_num {
   virtual const uchar *unpack(uchar* to, const uchar *from,
                               const uchar *from_end, uint param_data)
   { return unpack_int16(to, from, from_end); }
+  uint size_of() const { return sizeof(*this); }
 };
 
-class Field_medium :public Field_num {
+class Field_medium :public Field_num, Type_handler_medium {
 public:
   Field_medium(uchar *ptr_arg, uint32 len_arg, uchar *null_ptr_arg,
 	      uchar null_bit_arg,
@@ -1343,7 +1351,6 @@ 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;}
   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);
@@ -1364,10 +1371,11 @@ class Field_medium :public Field_num {
   {
     return Field::pack(to, from, max_length);
   }
+  uint size_of() const { return sizeof(*this); }
 };
 
 
-class Field_long :public Field_num {
+class Field_long :public Field_num, public Type_handler_long {
 public:
   Field_long(uchar *ptr_arg, uint32 len_arg, uchar *null_ptr_arg,
 	     uchar null_bit_arg,
@@ -1382,7 +1390,6 @@ 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;}
   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);
@@ -1409,10 +1416,11 @@ class Field_long :public Field_num {
   {
     return unpack_int32(to, from, from_end);
   }
+  uint size_of() const { return sizeof(*this); }
 };
 
 
-class Field_longlong :public Field_num {
+class Field_longlong :public Field_num, public Type_handler_longlong {
 public:
   Field_longlong(uchar *ptr_arg, uint32 len_arg, uchar *null_ptr_arg,
 	      uchar null_bit_arg,
@@ -1428,7 +1436,6 @@ 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;}
   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);
@@ -1458,10 +1465,11 @@ class Field_longlong :public Field_num {
   {
     return unpack_int64(to, from, from_end);
   }
+  uint size_of() const { return sizeof(*this); }
 };
 
 
-class Field_float :public Field_real {
+class Field_float :public Field_real, public Type_handler_float {
 public:
   Field_float(uchar *ptr_arg, uint32 len_arg, uchar *null_ptr_arg,
 	      uchar null_bit_arg,
@@ -1476,7 +1484,6 @@ class Field_float :public Field_real {
     :Field_real((uchar*) 0, len_arg, maybe_null_arg ? (uchar*) "": 0, (uint) 0,
                 NONE, field_name_arg, dec_arg, 0, 0)
     {}
-  enum_field_types type() const { return MYSQL_TYPE_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);
@@ -1491,12 +1498,13 @@ class Field_float :public Field_real {
   uint32 pack_length() const { return sizeof(float); }
   uint row_pack_length() const { return pack_length(); }
   void sql_type(String &str) const;
+  uint size_of() const { return sizeof(*this); }
 private:
   int do_save_field_metadata(uchar *first_byte);
 };
 
 
-class Field_double :public Field_real {
+class Field_double :public Field_real, public Type_handler_double {
 public:
   Field_double(uchar *ptr_arg, uint32 len_arg, uchar *null_ptr_arg,
 	       uchar null_bit_arg,
@@ -1516,7 +1524,6 @@ class Field_double :public Field_real {
     :Field_real((uchar*) 0, len_arg, maybe_null_arg ? (uchar*) "" : 0, (uint) 0,
                 NONE, field_name_arg, dec_arg, 0, 0)
     {not_fixed= not_fixed_arg; }
-  enum_field_types type() const { return MYSQL_TYPE_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);
@@ -1531,6 +1538,7 @@ class Field_double :public Field_real {
   uint32 pack_length() const { return sizeof(double); }
   uint row_pack_length() const { return pack_length(); }
   void sql_type(String &str) const;
+  uint size_of() const { return sizeof(*this); }
 private:
   int do_save_field_metadata(uchar *first_byte);
 };
@@ -1538,7 +1546,7 @@ class Field_double :public Field_real {
 
 /* Everything saved in this will disappear. It will always return NULL */
 
-class Field_null :public Field_str {
+class Field_null :public Field_str, public Type_handler_null {
   static uchar null[1];
 public:
   Field_null(uchar *ptr_arg, uint32 len_arg,
@@ -1547,7 +1555,6 @@ 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;}
   int  store(const char *to, uint length, CHARSET_INFO *cs)
   { null[0]=1; return 0; }
   int store(double nr)   { null[0]=1; return 0; }
@@ -1577,7 +1584,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; }   
   uint32 max_display_length() { return field_length; }
   bool str_needs_quotes() { return TRUE; }
   enum Derivation derivation(void) const { return DERIVATION_NUMERIC; }
@@ -1585,7 +1591,6 @@ class Field_temporal: public Field {
   CHARSET_INFO *charset(void) const { return &my_charset_numeric; }
   const CHARSET_INFO *sort_charset(void) const { return &my_charset_bin; }
   bool binary() const { return true; }
-  enum Item_result cmp_type () const { return TIME_RESULT; }
   uint is_equal(Create_field *new_field);
   bool eq_def(Field *field)
   {
@@ -1629,7 +1634,8 @@ class Field_temporal_with_date: public Field_temporal {
 };
 
 
-class Field_timestamp :public Field_temporal {
+class Field_timestamp :public Field_temporal, public Type_handler_timestamp
+{
 protected:
   int store_TIME_with_warning(THD *, MYSQL_TIME *, const ErrConv *,
                               int warnings, bool have_smth_to_conv);
@@ -1638,7 +1644,6 @@ 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;}
   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);
@@ -1800,15 +1805,14 @@ class Field_timestampf :public Field_timestamp_with_dec {
 };
 
 
-class Field_year :public Field_tiny {
+class Field_year :public Field_int8, public Type_handler_year {
 public:
   Field_year(uchar *ptr_arg, uint32 len_arg, uchar *null_ptr_arg,
 	     uchar null_bit_arg,
 	     enum utype unireg_check_arg, const char *field_name_arg)
-    :Field_tiny(ptr_arg, len_arg, null_ptr_arg, null_bit_arg,
+    :Field_int8(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;}
   int  store(const char *to,uint length,CHARSET_INFO *charset);
   int  store(double nr);
   int  store(longlong nr, bool unsigned_val);
@@ -1820,17 +1824,17 @@ class Field_year :public Field_tiny {
   bool send_binary(Protocol *protocol);
   uint32 max_display_length() { return field_length; }
   void sql_type(String &str) const;
+  uint size_of() const { return sizeof(*this); }
 };
 
 
-class Field_date :public Field_temporal_with_date {
+class Field_date :public Field_temporal_with_date, public Type_handler_date {
   void store_TIME(MYSQL_TIME *ltime);
 public:
   Field_date(uchar *ptr_arg, uchar *null_ptr_arg, uchar null_bit_arg,
 	     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;}
   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; }
   double val_real(void);
@@ -1855,7 +1859,8 @@ class Field_date :public Field_temporal_with_date {
 };
 
 
-class Field_newdate :public Field_temporal_with_date {
+class Field_newdate :public Field_temporal_with_date, public Type_handler_date
+{
   void store_TIME(MYSQL_TIME *ltime);
 public:
   Field_newdate(uchar *ptr_arg, uchar *null_ptr_arg, uchar null_bit_arg,
@@ -1863,7 +1868,6 @@ 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; }
   enum ha_base_keytype key_type() const { return HA_KEYTYPE_UINT24; }
   int reset(void) { ptr[0]=ptr[1]=ptr[2]=0; return 0; }
@@ -1880,7 +1884,7 @@ class Field_newdate :public Field_temporal_with_date {
 };
 
 
-class Field_time :public Field_temporal {
+class Field_time :public Field_temporal, public Type_handler_time {
   /*
     when this Field_time instance is used for storing values for index lookups
     (see class store_key, Field::new_key_field(), etc), the following
@@ -1899,7 +1903,6 @@ 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;}
   enum ha_base_keytype key_type() const { return HA_KEYTYPE_INT24; }
   int store_time_dec(MYSQL_TIME *ltime, uint dec);
   int store(const char *to,uint length,CHARSET_INFO *charset);
@@ -2024,7 +2027,9 @@ class Field_timef :public Field_time_with_dec {
 };
 
 
-class Field_datetime :public Field_temporal_with_date {
+class Field_datetime :public Field_temporal_with_date,
+                      public Type_handler_datetime
+{
   void store_TIME(MYSQL_TIME *ltime);
 public:
   Field_datetime(uchar *ptr_arg, uint length_arg, uchar *null_ptr_arg,
@@ -2033,7 +2038,6 @@ class Field_datetime :public Field_temporal_with_date {
     :Field_temporal_with_date(ptr_arg, length_arg, null_ptr_arg, null_bit_arg,
                               unireg_check_arg, field_name_arg)
     {}
-  enum_field_types type() const { return MYSQL_TYPE_DATETIME;}
   enum ha_base_keytype key_type() const { return HA_KEYTYPE_ULONGLONG; }
   double val_real(void);
   longlong val_int(void);
@@ -2221,7 +2225,7 @@ class Field_datetimef :public Field_datetime_with_dec {
                                   unireg_check, field_name, dec);
 }
 
-class Field_string :public Field_longstr {
+class Field_string :public Field_longstr, public Type_handler_char {
 public:
   bool can_alter_field_type;
   Field_string(uchar *ptr_arg, uint32 len_arg,uchar *null_ptr_arg,
@@ -2237,7 +2241,7 @@ class Field_string :public Field_longstr {
                    NONE, field_name_arg, cs),
      can_alter_field_type(1) {};
 
-  enum_field_types type() const
+  enum_field_types field_type() const
   {
     return ((can_alter_field_type && orig_table &&
              orig_table->s->db_create_options & HA_OPTION_PACK_RECORD &&
@@ -2294,7 +2298,7 @@ class Field_string :public Field_longstr {
 };
 
 
-class Field_varstring :public Field_longstr {
+class Field_varstring :public Field_longstr, public Type_handler_varchar {
 public:
   /*
     The maximum space available in a Field_varstring, in bytes. See
@@ -2324,7 +2328,6 @@ class Field_varstring :public Field_longstr {
     share->varchar_fields++;
   }
 
-  enum_field_types type() const { return MYSQL_TYPE_VARCHAR; }
   enum ha_base_keytype key_type() const;
   uint row_pack_length() const { return field_length; }
   bool zero_pack() const { return 0; }
@@ -2377,7 +2380,10 @@ class Field_varstring :public Field_longstr {
 };
 
 
-class Field_blob :public Field_longstr {
+/**
+  Abstract class for large objects: BLOB, TEXT, GEOMETRY
+*/
+class Field_lob :public Field_longstr {
 protected:
   /**
     The number of bytes used to represent the length of the blob.
@@ -2390,19 +2396,19 @@ class Field_blob :public Field_longstr {
   String value;
   
 public:
-  Field_blob(uchar *ptr_arg, uchar *null_ptr_arg, uchar null_bit_arg,
-	     enum utype unireg_check_arg, const char *field_name_arg,
-	     TABLE_SHARE *share, uint blob_pack_length, CHARSET_INFO *cs);
-  Field_blob(uint32 len_arg,bool maybe_null_arg, const char *field_name_arg,
-             CHARSET_INFO *cs)
+  Field_lob(uchar *ptr_arg, uchar *null_ptr_arg, uchar null_bit_arg,
+	    enum utype unireg_check_arg, const char *field_name_arg,
+	    TABLE_SHARE *share, uint blob_pack_length, CHARSET_INFO *cs);
+  Field_lob(uint32 len_arg,bool maybe_null_arg, const char *field_name_arg,
+            CHARSET_INFO *cs)
     :Field_longstr((uchar*) 0, len_arg, maybe_null_arg ? (uchar*) "": 0, 0,
                    NONE, field_name_arg, cs),
     packlength(4)
   {
     flags|= BLOB_FLAG;
   }
-  Field_blob(uint32 len_arg,bool maybe_null_arg, const char *field_name_arg,
-	     CHARSET_INFO *cs, bool set_packlength)
+  Field_lob(uint32 len_arg,bool maybe_null_arg, const char *field_name_arg,
+	    CHARSET_INFO *cs, bool set_packlength)
     :Field_longstr((uchar*) 0,len_arg, maybe_null_arg ? (uchar*) "": 0, 0,
                    NONE, field_name_arg, cs)
   {
@@ -2416,11 +2422,10 @@ class Field_blob :public Field_longstr {
                   l_char_length <= 16777215 ? 3 : 4;
     }
   }
-  Field_blob(uint32 packlength_arg)
+  Field_lob(uint32 packlength_arg)
     :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;}
   enum ha_base_keytype key_type() const
     { return binary() ? HA_KEYTYPE_VARBINARY2 : HA_KEYTYPE_VARTEXT2; }
   int  store(const char *to,uint length,CHARSET_INFO *charset);
@@ -2511,7 +2516,7 @@ class Field_blob :public Field_longstr {
     get_ptr(&tmp);
     if (value.copy((char*) tmp, get_length(), charset()))
     {
-      Field_blob::reset();
+      Field_lob::reset();
       return 1;
     }
     tmp=(uchar*) value.ptr();
@@ -2539,8 +2544,31 @@ class Field_blob :public Field_longstr {
 };
 
 
+class Field_blob :public Field_lob, public Type_handler_blob {
+public:
+  Field_blob(uchar *ptr_arg, uchar *null_ptr_arg, uchar null_bit_arg,
+	     enum utype unireg_check_arg, const char *field_name_arg,
+	     TABLE_SHARE *share, uint blob_pack_length, CHARSET_INFO *cs)
+   :Field_lob(ptr_arg, null_ptr_arg, null_bit_arg, unireg_check_arg,
+              field_name_arg, share, blob_pack_length, cs)
+  { }
+  Field_blob(uint32 len_arg,bool maybe_null_arg, const char *field_name_arg,
+             CHARSET_INFO *cs)
+   :Field_lob(len_arg, maybe_null_arg, field_name_arg, cs)
+  { }
+  Field_blob(uint32 len_arg,bool maybe_null_arg, const char *field_name_arg,
+	    CHARSET_INFO *cs, bool set_packlength)
+   :Field_lob(len_arg, maybe_null_arg, field_name_arg, cs, set_packlength)
+  { }
+  Field_blob(uint32 packlength_arg)
+   :Field_lob(packlength_arg)
+  { }
+  uint size_of() const { return sizeof(*this); }
+};
+
+
 #ifdef HAVE_SPATIAL
-class Field_geom :public Field_blob {
+class Field_geom :public Field_lob, public Type_handler_geometry {
 public:
   enum geometry_type geom_type;
 
@@ -2548,15 +2576,14 @@ class Field_geom :public Field_blob {
 	     enum utype unireg_check_arg, const char *field_name_arg,
 	     TABLE_SHARE *share, uint blob_pack_length,
 	     enum geometry_type geom_type_arg)
-     :Field_blob(ptr_arg, null_ptr_arg, null_bit_arg, unireg_check_arg, 
-                 field_name_arg, share, blob_pack_length, &my_charset_bin)
+     :Field_lob(ptr_arg, null_ptr_arg, null_bit_arg, unireg_check_arg, 
+                field_name_arg, share, blob_pack_length, &my_charset_bin)
   { geom_type= geom_type_arg; }
   Field_geom(uint32 len_arg,bool maybe_null_arg, const char *field_name_arg,
-	     TABLE_SHARE *share, enum geometry_type geom_type_arg)
-    :Field_blob(len_arg, maybe_null_arg, field_name_arg, &my_charset_bin)
+	    TABLE_SHARE *share, enum geometry_type geom_type_arg)
+    :Field_lob(len_arg, maybe_null_arg, field_name_arg, &my_charset_bin)
   { 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);
@@ -2569,7 +2596,7 @@ class Field_geom :public Field_blob {
     Non-nullable GEOMETRY types cannot have defaults,
     but the underlying blob must still be reset.
    */
-  int reset(void) { return Field_blob::reset() || !maybe_null(); }
+  int reset(void) { return Field_lob::reset() || !maybe_null(); }
 
   geometry_type get_geometry_type() { return geom_type; };
   uint get_srid() { return 0; }
@@ -2577,7 +2604,7 @@ class Field_geom :public Field_blob {
 #endif /*HAVE_SPATIAL*/
 
 
-class Field_enum :public Field_str {
+class Field_enum :public Field_str, public Type_handler_enum {
 protected:
   uint packlength;
 public:
@@ -2595,8 +2622,6 @@ class Field_enum :public Field_str {
       flags|=ENUM_FLAG;
   }
   Field *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; }
   enum ha_base_keytype key_type() const;
   int  store(const char *to,uint length,CHARSET_INFO *charset);
   int  store(double nr);
@@ -2676,7 +2701,7 @@ class Field_set :public Field_enum {
     - pack_length() includes size of the bits stored in the NULL bytes
       of the record.
 */
-class Field_bit :public Field {
+class Field_bit :public Field, public Type_handler_bit {
 public:
   uchar *bit_ptr;     // position in record where 'uneven' bits store
   uchar bit_ofs;      // offset to 'uneven' high bits
@@ -2685,13 +2710,11 @@ 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; }
   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/field_conv.cc b/sql/field_conv.cc
index f13694e..9c62bd5 100644
--- a/sql/field_conv.cc
+++ b/sql/field_conv.cc
@@ -724,7 +724,7 @@ void Copy_field::set(Field *to,Field *from,bool save)
       return do_field_int;
     if (to->result_type() == DECIMAL_RESULT)
       return do_field_decimal;
-    if (from->cmp_type() == TIME_RESULT)
+    if (from->cmp_type() == TIME_CMP)
     {
       /* If types are not 100 % identical then convert trough get_date() */
       if (!to->eq_def(from) ||
@@ -923,7 +923,7 @@ int field_conv_incompatible(Field *to, Field *from)
     my_decimal buff;
     return to->store_decimal(from->val_decimal(&buff));
   }
-  if (from->cmp_type() == TIME_RESULT)
+  if (from->cmp_type() == TIME_CMP)
   {
     MYSQL_TIME ltime;
     if (from->get_date(&ltime, 0))
diff --git a/sql/filesort.cc b/sql/filesort.cc
index 23cfd6a..cba319b 100644
--- a/sql/filesort.cc
+++ b/sql/filesort.cc
@@ -985,8 +985,8 @@ static void make_sortkey(register Sort_param *param,
     {						// Item
       Item *item=sort_field->item;
       maybe_null= item->maybe_null;
-      switch (sort_field->result_type) {
-      case STRING_RESULT:
+      switch (sort_field->cmp_type) {
+      case STRING_CMP:
       {
         const CHARSET_INFO *cs=item->collation.collation;
         char fill_char= ((cs->state & MY_CS_BINSORT) ? (char) 0 : ' ');
@@ -1052,11 +1052,11 @@ static void make_sortkey(register Sort_param *param,
         }
         break;
       }
-      case INT_RESULT:
-      case TIME_RESULT:
+      case INT_CMP:
+      case TIME_CMP:
 	{
           longlong UNINIT_VAR(value);
-          if (sort_field->result_type == INT_RESULT)
+          if (sort_field->cmp_type == INT_CMP)
             value= item->val_int_result();
           else
           {
@@ -1098,7 +1098,7 @@ static void make_sortkey(register Sort_param *param,
             to[0]= (uchar) (value >> 56) ^ 128;	/* Reverse signbit */
 	  break;
 	}
-      case DECIMAL_RESULT:
+      case DECIMAL_CMP:
         {
           my_decimal dec_buf, *dec_val= item->val_decimal_result(&dec_buf);
           if (maybe_null)
@@ -1116,7 +1116,7 @@ static void make_sortkey(register Sort_param *param,
                             item->decimals);
          break;
         }
-      case REAL_RESULT:
+      case REAL_CMP:
 	{
           double value= item->val_result();
 	  if (maybe_null)
@@ -1132,7 +1132,7 @@ static void make_sortkey(register Sort_param *param,
 	  change_double_for_sort(value,(uchar*) to);
 	  break;
 	}
-      case ROW_RESULT:
+      case ROW_CMP:
       default: 
 	// This case should never be choosen
 	DBUG_ASSERT(0);
@@ -1888,9 +1888,9 @@ static uint suffix_length(ulong string_length)
     }
     else
     {
-      sortorder->result_type= sortorder->item->cmp_type();
-      switch (sortorder->result_type) {
-      case STRING_RESULT:
+      sortorder->cmp_type= sortorder->item->cmp_type();
+      switch (sortorder->cmp_type) {
+      case STRING_CMP:
 	sortorder->length=sortorder->item->max_length;
         set_if_smaller(sortorder->length, thd->variables.max_sort_length);
 	if (use_strnxfrm((cs=sortorder->item->collation.collation)))
@@ -1906,21 +1906,21 @@ static uint suffix_length(ulong string_length)
           sortorder->length+= sortorder->suffix_length;
         }
 	break;
-      case TIME_RESULT:
-      case INT_RESULT:
+      case TIME_CMP:
+      case INT_CMP:
 	sortorder->length=8;			// Size of intern longlong
 	break;
-      case DECIMAL_RESULT:
+      case DECIMAL_CMP:
         sortorder->length=
           my_decimal_get_binary_size(sortorder->item->max_length - 
                                      (sortorder->item->decimals ? 1 : 0),
                                      sortorder->item->decimals);
         break;
-      case REAL_RESULT:
+      case REAL_CMP:
 	sortorder->length=sizeof(double);
 	break;
-      case ROW_RESULT:
-      default: 
+      case ROW_CMP:
+      case IMPOSSIBLE_CMP:
 	// This case should never be choosen
 	DBUG_ASSERT(0);
 	break;
diff --git a/sql/item.cc b/sql/item.cc
index 4fd1ddc..193e580 100644
--- a/sql/item.cc
+++ b/sql/item.cc
@@ -225,7 +225,6 @@ bool Item::val_bool()
     return val_real() != 0.0;
   case ROW_RESULT:
   case TIME_RESULT:
-  case IMPOSSIBLE_RESULT:
     DBUG_ASSERT(0);
     return 0;                                   // Wrong (but safe)
   }
@@ -546,9 +545,9 @@ int Item::save_str_value_in_field(Field *field, String *result)
   marker= 0;
   maybe_null=null_value=with_sum_func=with_field=unsigned_flag=0;
   in_rollup= 0;
-  decimals= 0; max_length= 0;
+  decimals= 0;
   with_subselect= 0;
-  cmp_context= IMPOSSIBLE_RESULT;
+  cmp_context= IMPOSSIBLE_CMP;
    /* Initially this item is not attached to any JOIN_TAB. */
   join_tab_idx= MAX_TABLES;
 
@@ -579,13 +578,13 @@ int Item::save_str_value_in_field(Field *field, String *result)
   tables.
 */
 Item::Item(THD *thd, Item *item):
+  Type_attributes_for_item(item),
   join_tab_idx(item->join_tab_idx),
   is_expensive_cache(-1),
   rsize(0),
   str_value(item->str_value),
   name(item->name),
   orig_name(item->orig_name),
-  max_length(item->max_length),
   name_length(item->name_length),
   decimals(item->decimals),
   marker(item->marker),
@@ -675,7 +674,6 @@ void Item::print_value(String *str)
       break;
     case ROW_RESULT:
     case TIME_RESULT:
-    case IMPOSSIBLE_RESULT:
       DBUG_ASSERT(0);
     }
   }
@@ -726,12 +724,12 @@ void Item::rename(char *new_name)
   name= new_name;
 }
 
-Item_result Item::cmp_type() const
+Item_cmp Item::cmp_type_by_field_type() const
 {
   switch (field_type()) {
   case MYSQL_TYPE_DECIMAL:
   case MYSQL_TYPE_NEWDECIMAL:
-                           return DECIMAL_RESULT;
+                           return DECIMAL_CMP;
   case MYSQL_TYPE_TINY:
   case MYSQL_TYPE_SHORT:
   case MYSQL_TYPE_LONG:
@@ -739,10 +737,10 @@ Item_result Item::cmp_type() const
   case MYSQL_TYPE_INT24:
   case MYSQL_TYPE_YEAR:
   case MYSQL_TYPE_BIT:
-                           return INT_RESULT;
+                           return INT_CMP;
   case MYSQL_TYPE_FLOAT:
   case MYSQL_TYPE_DOUBLE:
-                           return REAL_RESULT;
+                           return REAL_CMP;
   case MYSQL_TYPE_NULL:
   case MYSQL_TYPE_VARCHAR:
   case MYSQL_TYPE_TINY_BLOB:
@@ -754,7 +752,7 @@ Item_result Item::cmp_type() const
   case MYSQL_TYPE_ENUM:
   case MYSQL_TYPE_SET:
   case MYSQL_TYPE_GEOMETRY:
-                           return STRING_RESULT;
+                           return STRING_CMP;
   case MYSQL_TYPE_TIMESTAMP:
   case MYSQL_TYPE_TIMESTAMP2:
   case MYSQL_TYPE_DATE:
@@ -763,10 +761,10 @@ Item_result Item::cmp_type() const
   case MYSQL_TYPE_DATETIME:
   case MYSQL_TYPE_DATETIME2:
   case MYSQL_TYPE_NEWDATE:
-                           return TIME_RESULT;
+                           return TIME_CMP;
   };
   DBUG_ASSERT(0);
-  return IMPOSSIBLE_RESULT;
+  return IMPOSSIBLE_CMP;
 }
 
 /**
@@ -1524,6 +1522,7 @@ bool Item_sp_variable::is_null()
                            uint pos_in_q, uint len_in_q)
   :Item_sp_variable(sp_var_name.str, sp_var_name.length),
    Rewritable_query_parameter(pos_in_q, len_in_q),
+   Result_type(sp_map_result_type(sp_var_type)),
    m_var_idx(sp_var_idx)
 {
   maybe_null= TRUE;
@@ -1531,7 +1530,6 @@ bool Item_sp_variable::is_null()
   sp_var_type= real_type_to_type(sp_var_type);
   m_type= sp_map_item_type(sp_var_type);
   m_field_type= sp_var_type;
-  m_result_type= sp_map_result_type(sp_var_type);
 }
 
 
@@ -2642,7 +2640,6 @@ bool Item_field::val_bool_result()
     return result_field->val_real() != 0.0;
   case ROW_RESULT:
   case TIME_RESULT:
-  case IMPOSSIBLE_RESULT:
     DBUG_ASSERT(0);
     return 0;                                   // Shut up compiler
   }
@@ -3148,8 +3145,8 @@ Item *Item_null::safe_charset_converter(CHARSET_INFO *tocs)
 
 Item_param::Item_param(uint pos_in_query_arg) :
   Rewritable_query_parameter(pos_in_query_arg, 1),
+  Result_type(STRING_RESULT),
   state(NO_VALUE), inout(IN_PARAM),
-  item_result_type(STRING_RESULT),
   /* Don't pretend to be a literal unless value for this item is set. */
   item_type(PARAM_ITEM),
   param_type(MYSQL_TYPE_VARCHAR),
@@ -3352,7 +3349,7 @@ bool Item_param::set_from_user_var(THD *thd, const user_var_entry *entry)
   DBUG_ENTER("Item_param::set_from_user_var");
   if (entry && entry->value)
   {
-    item_result_type= entry->type;
+    Result_type::set(entry);
     unsigned_flag= entry->unsigned_flag;
     if (limit_clause_param)
     {
@@ -3361,7 +3358,7 @@ bool Item_param::set_from_user_var(THD *thd, const user_var_entry *entry)
       item_type= Item::INT_ITEM;
       DBUG_RETURN(!unsigned_flag && value.integer < 0 ? 1 : 0);
     }
-    switch (item_result_type) {
+    switch (Result_type::type()) {
     case REAL_RESULT:
       set_double(*(double*)entry->value);
       item_type= Item::REAL_ITEM;
@@ -3414,7 +3411,6 @@ bool Item_param::set_from_user_var(THD *thd, const user_var_entry *entry)
     }
     case ROW_RESULT:
     case TIME_RESULT:
-    case IMPOSSIBLE_RESULT:
       DBUG_ASSERT(0);
       set_null();
     }
@@ -3840,7 +3836,7 @@ void Item_param::print(String *str, enum_query_type query_type)
   param_type= src->param_type;
   set_param_func= src->set_param_func;
   item_type= src->item_type;
-  item_result_type= src->item_result_type;
+  Result_type::set(src->result_type());
 
   collation.set(src->collation);
   maybe_null= src->maybe_null;
@@ -3931,7 +3927,7 @@ void Item_param::print(String *str, enum_query_type query_type)
     return FALSE;
   }
 
-  item_result_type= arg->result_type();
+  Result_type::set(arg->result_type());
   item_type= arg->type();
   return FALSE;
 }
@@ -4030,7 +4026,6 @@ Item_copy *Item_copy::create (Item *item)
       return new Item_copy_decimal (item);
     case TIME_RESULT:
     case ROW_RESULT:
-  case IMPOSSIBLE_RESULT:
       DBUG_ASSERT (0);
   }
   /* should not happen */
@@ -5453,7 +5448,7 @@ Item *Item_field::equal_fields_propagator(uchar *arg)
     item= this;
   else if (field && (field->flags & ZEROFILL_FLAG) && IS_NUM(field->type()))
   {
-    if (item && (cmp_context == STRING_RESULT || cmp_context == IMPOSSIBLE_RESULT))
+    if (item && (cmp_context == STRING_CMP || cmp_context == IMPOSSIBLE_CMP))
       convert_zerofill_number_to_string(&item, (Field_num *)field);
     else
       item= this;
@@ -5551,40 +5546,12 @@ void Item::make_field(Send_field *tmp_field)
 }
 
 
-enum_field_types Item::string_field_type() const
-{
-  enum_field_types f_type= MYSQL_TYPE_VAR_STRING;
-  if (max_length >= 16777216)
-    f_type= MYSQL_TYPE_LONG_BLOB;
-  else if (max_length >= 65536)
-    f_type= MYSQL_TYPE_MEDIUM_BLOB;
-  return f_type;
-}
-
-
 void Item_empty_string::make_field(Send_field *tmp_field)
 {
   init_make_field(tmp_field, string_field_type());
 }
 
 
-enum_field_types Item::field_type() const
-{
-  switch (result_type()) {
-  case STRING_RESULT:  return string_field_type();
-  case INT_RESULT:     return MYSQL_TYPE_LONGLONG;
-  case DECIMAL_RESULT: return MYSQL_TYPE_NEWDECIMAL;
-  case REAL_RESULT:    return MYSQL_TYPE_DOUBLE;
-  case ROW_RESULT:
-  case TIME_RESULT:
-  case IMPOSSIBLE_RESULT:
-    DBUG_ASSERT(0);
-    return MYSQL_TYPE_VARCHAR;
-  }
-  return MYSQL_TYPE_VARCHAR;
-}
-
-
 String *Item::check_well_formed_result(String *str, bool send_error)
 {
   /* Check whether we got a well-formed string */
@@ -7272,7 +7239,6 @@ bool Item_ref::val_bool_result()
       return result_field->val_real() != 0.0;
     case ROW_RESULT:
     case TIME_RESULT:
-    case IMPOSSIBLE_RESULT:
       DBUG_ASSERT(0);
     }
   }
@@ -8530,20 +8496,20 @@ void Item_trigger_field::cleanup()
 }
 
 
-Item_result item_cmp_type(Item_result a,Item_result b)
+Item_cmp item_cmp_type(Item_cmp a, Item_cmp b)
 {
-  if (a == STRING_RESULT && b == STRING_RESULT)
-    return STRING_RESULT;
-  if (a == INT_RESULT && b == INT_RESULT)
-    return INT_RESULT;
-  else if (a == ROW_RESULT || b == ROW_RESULT)
-    return ROW_RESULT;
-  else if (a == TIME_RESULT || b == TIME_RESULT)
-    return TIME_RESULT;
-  if ((a == INT_RESULT || a == DECIMAL_RESULT) &&
-      (b == INT_RESULT || b == DECIMAL_RESULT))
-    return DECIMAL_RESULT;
-  return REAL_RESULT;
+  if (a == STRING_CMP && b == STRING_CMP)
+    return STRING_CMP;
+  if (a == INT_CMP && b == INT_CMP)
+    return INT_CMP;
+  else if (a == ROW_CMP || b == ROW_CMP)
+    return ROW_CMP;
+  else if (a == TIME_CMP || b == TIME_CMP)
+    return TIME_CMP;
+  if ((a == INT_CMP || a == DECIMAL_CMP) &&
+      (b == INT_CMP || b == DECIMAL_CMP))
+    return DECIMAL_CMP;
+  return REAL_CMP;
 }
 
 
@@ -8553,11 +8519,11 @@ void resolve_const_item(THD *thd, Item **ref, Item *comp_item)
   Item *new_item= NULL;
   if (item->basic_const_item())
     return;                                     // Can't be better
-  Item_result res_type=item_cmp_type(comp_item->cmp_type(), item->cmp_type());
+  Item_cmp cmp_type= item_cmp_type(comp_item->cmp_type(), item->cmp_type());
   char *name=item->name;			// Alloced by sql_alloc
 
-  switch (res_type) {
-  case TIME_RESULT:
+  switch (cmp_type) {
+  case TIME_CMP:
   {
     bool is_null;
     Item **ref_copy= ref;
@@ -8567,7 +8533,7 @@ void resolve_const_item(THD *thd, Item **ref, Item *comp_item)
       new_item= new Item_null(name);
     break;
   }
-  case STRING_RESULT:
+  case STRING_CMP:
   {
     char buff[MAX_FIELD_WIDTH];
     String tmp(buff,sizeof(buff),&my_charset_bin),*result;
@@ -8582,7 +8548,7 @@ void resolve_const_item(THD *thd, Item **ref, Item *comp_item)
     }
     break;
   }
-  case INT_RESULT:
+  case INT_CMP:
   {
     longlong result=item->val_int();
     uint length=item->max_length;
@@ -8591,7 +8557,7 @@ void resolve_const_item(THD *thd, Item **ref, Item *comp_item)
                (Item*) new Item_int(name, result, length));
     break;
   }
-  case ROW_RESULT:
+  case ROW_CMP:
   if (item->type() == Item::ROW_ITEM && comp_item->type() == Item::ROW_ITEM)
   {
     /*
@@ -8621,7 +8587,7 @@ void resolve_const_item(THD *thd, Item **ref, Item *comp_item)
     break;
   }
   /* Fallthrough */
-  case REAL_RESULT:
+  case REAL_CMP:
   {						// It must REAL_RESULT
     double result= item->val_real();
     uint length=item->max_length,decimals=item->decimals;
@@ -8630,7 +8596,7 @@ void resolve_const_item(THD *thd, Item **ref, Item *comp_item)
                new Item_float(name, result, decimals, length));
     break;
   }
-  case DECIMAL_RESULT:
+  case DECIMAL_CMP:
   {
     my_decimal decimal_value;
     my_decimal *result= item->val_decimal(&decimal_value);
@@ -8641,7 +8607,7 @@ void resolve_const_item(THD *thd, Item **ref, Item *comp_item)
                (Item*) new Item_decimal(name, result, length, decimals));
     break;
   }
-  case IMPOSSIBLE_RESULT:
+  case IMPOSSIBLE_CMP:
     DBUG_ASSERT(0);
     break;
   }
@@ -8674,13 +8640,13 @@ void resolve_const_item(THD *thd, Item **ref, Item *comp_item)
 
 int stored_field_cmp_to_item(THD *thd, Field *field, Item *item)
 {
-  Item_result res_type=item_cmp_type(field->result_type(),
-				     item->result_type());
+  Item_cmp cmp_type=item_cmp_type((Item_cmp) field->result_type(),
+                                  (Item_cmp) item->result_type());
   /*
     We have to check field->cmp_type() instead of res_type,
     as result_type() - and thus res_type - can never be TIME_RESULT (yet).
   */
-  if (field->cmp_type() == TIME_RESULT)
+  if (field->cmp_type() == TIME_CMP)
   {
     MYSQL_TIME field_time, item_time, item_time2, *item_time_cmp= &item_time;
     if (field->type() == MYSQL_TYPE_TIME)
@@ -8698,7 +8664,7 @@ int stored_field_cmp_to_item(THD *thd, Field *field, Item *item)
     }
     return my_time_compare(&field_time, item_time_cmp);
   }
-  if (res_type == STRING_RESULT)
+  if (cmp_type == STRING_CMP)
   {
     char item_buff[MAX_FIELD_WIDTH];
     char field_buff[MAX_FIELD_WIDTH];
@@ -8735,9 +8701,9 @@ int stored_field_cmp_to_item(THD *thd, Field *field, Item *item)
     }
     return sortcmp(field_result, item_result, field->charset());
   }
-  if (res_type == INT_RESULT)
+  if (cmp_type == INT_CMP)
     return 0;					// Both are of type int
-  if (res_type == DECIMAL_RESULT)
+  if (cmp_type == DECIMAL_CMP)
   {
     my_decimal item_buf, *item_val,
                field_buf, *field_val;
@@ -8778,22 +8744,22 @@ Item_cache* Item_cache::get_cache(const Item *item)
   @return cache item
 */
 
-Item_cache* Item_cache::get_cache(const Item *item, const Item_result type)
+Item_cache* Item_cache::get_cache(const Item *item, const Item_cmp type)
 {
   switch (type) {
-  case INT_RESULT:
+  case INT_CMP:
     return new Item_cache_int(item->field_type());
-  case REAL_RESULT:
+  case REAL_CMP:
     return new Item_cache_real();
-  case DECIMAL_RESULT:
+  case DECIMAL_CMP:
     return new Item_cache_decimal();
-  case STRING_RESULT:
+  case STRING_CMP:
     return new Item_cache_str(item);
-  case ROW_RESULT:
+  case ROW_CMP:
     return new Item_cache_row();
-  case TIME_RESULT:
+  case TIME_CMP:
     return new Item_cache_temporal(item->field_type());
-  case IMPOSSIBLE_RESULT:
+  case IMPOSSIBLE_CMP:
     DBUG_ASSERT(0);
     break;
   }
@@ -9426,7 +9392,6 @@ enum_field_types Item_type_holder::get_real_type(Item *item)
         return MYSQL_TYPE_NEWDECIMAL;
       case ROW_RESULT:
       case TIME_RESULT:
-      case IMPOSSIBLE_RESULT:
         DBUG_ASSERT(0);
         return MYSQL_TYPE_VAR_STRING;
       }
diff --git a/sql/item.h b/sql/item.h
index e006622..a9d7c84 100644
--- a/sql/item.h
+++ b/sql/item.h
@@ -28,6 +28,7 @@
 #include "unireg.h"                    // REQUIRED: for other includes
 #include "thr_malloc.h"                         /* sql_calloc */
 #include "field.h"                              /* Derivation */
+#include "sql_type.h"
 
 C_MODE_START
 #include <ma_dyncol.h>
@@ -636,7 +637,8 @@ struct find_selective_predicates_list_processor_data
 class Item_func_not;
 class Item_splocal;
 
-class Item {
+class Item: public Type_attributes_for_item, public virtual Type_handler
+{
   Item(const Item &);			/* Prevent use of these */
   void operator=(Item &);
   /**
@@ -705,11 +707,6 @@ class Item {
    */
   Item *next;
   /*
-    The maximum value length in characters multiplied by collation->mbmaxlen.
-    Almost always it's the maximum value length in bytes.
-  */
-  uint32 max_length;
-  /*
     TODO: convert name and name_length fields into LEX_STRING to keep them in
     sync (see bug #11829681/60295 etc). Then also remove some strlen(name)
     calls.
@@ -734,7 +731,7 @@ class Item {
                                            of its arguments is or contains a
                                            subselect */
   DTCollation collation;
-  Item_result cmp_context;              /* Comparison context */
+  Item_cmp cmp_context;                 /* Comparison context */
   // alloc & destruct is done as start of select using sql_alloc
   Item();
   /*
@@ -793,13 +790,8 @@ class Item {
   { return save_in_field(field, 1); }
   virtual bool send(Protocol *protocol, String *str);
   virtual bool eq(const Item *, bool binary_cmp) const;
-  /* result_type() of an item specifies how the value should be returned */
-  virtual Item_result result_type() const { return REAL_RESULT; }
-  /* ... while cmp_type() specifies how it should be compared */
-  virtual Item_result cmp_type() const;
-  virtual Item_result cast_to_int_type() const { return cmp_type(); }
-  virtual enum_field_types string_field_type() const;
-  virtual enum_field_types field_type() const;
+  Item_cmp cmp_type_by_field_type() const;
+  virtual Item_cmp cast_to_int_type() const { return cmp_type(); }
   virtual enum Type type() const =0;
   /*
     real_type() is the type of base item.  This is same as type() for
@@ -1249,8 +1241,8 @@ class Item {
   */
   virtual CHARSET_INFO *charset_for_protocol(void) const
   {
-    return cmp_type() == STRING_RESULT ? collation.collation :
-                                         &my_charset_bin;
+    return cmp_type() == STRING_CMP ? collation.collation :
+                                      &my_charset_bin;
   };
 
   virtual bool walk(Item_processor processor, bool walk_subquery, uchar *arg)
@@ -1606,7 +1598,7 @@ class Item {
   */
   inline bool has_compatible_context(Item *item) const
   {
-    return cmp_context == IMPOSSIBLE_RESULT || item->cmp_context == cmp_context;
+    return cmp_context == IMPOSSIBLE_CMP || item->cmp_context == cmp_context;
   }
   /**
     Test whether an expression is expensive to compute. Used during
@@ -1936,12 +1928,12 @@ inline bool Item_sp_variable::send(Protocol *protocol, String *str)
 
 class Item_splocal :public Item_sp_variable,
                     private Settable_routine_parameter,
-                    public Rewritable_query_parameter
+                    public Rewritable_query_parameter,
+                    public Result_type
 {
   uint m_var_idx;
 
   Type m_type;
-  Item_result m_result_type;
   enum_field_types m_field_type;
 public:
   Item_splocal(const LEX_STRING &sp_var_name, uint sp_var_idx,
@@ -1962,6 +1954,7 @@ class Item_splocal :public Item_sp_variable,
   inline enum Type type() const;
   inline Item_result result_type() const;
   inline enum_field_types field_type() const { return m_field_type; }
+  Item_cmp cmp_type() const { return cmp_type_by_field_type(); }
 
 private:
   bool set_value(THD *thd, sp_rcontext *ctx, Item **it);
@@ -1999,7 +1992,7 @@ inline enum Item::Type Item_splocal::type() const
 
 inline Item_result Item_splocal::result_type() const
 {
-  return m_result_type;
+  return Result_type::type();
 }
 
 
@@ -2019,6 +2012,9 @@ class Item_case_expr :public Item_sp_variable
 
   inline enum Type type() const;
   inline Item_result result_type() const;
+  enum_field_types field_type() const
+  { return Result_type(result_type()).field_type(*this); }
+  Item_cmp cmp_type() const { return cmp_type_by_field_type(); }
 
 public:
   /*
@@ -2083,6 +2079,9 @@ class Item_name_const : public Item
   {
     return value_item->result_type();
   }
+  enum_field_types field_type() const
+  { return Result_type(result_type()).field_type(*this); }
+  Item_cmp cmp_type() const { return cmp_type_by_field_type(); }
 
   bool const_item() const
   {
@@ -2247,6 +2246,12 @@ class Item_ident_for_show :public Item
   void make_field(Send_field *tmp_field);
   CHARSET_INFO *charset_for_protocol(void) const
   { return field->charset_for_protocol(); }
+
+  Item_result result_type() const { return field->result_type(); }
+  enum_field_types field_type() const
+  { return Result_type(result_type()).field_type(*this); }
+  Item_cmp cmp_type() const { return cmp_type_by_field_type(); }
+
 };
 
 
@@ -2311,7 +2316,12 @@ class Item_field :public Item_ident
   {
     return field->result_type();
   }
-  Item_result cast_to_int_type() const
+  Item_cmp cmp_type() const
+  {
+    /* Note, this makes ENUM and SET compare as STRING_CMP */
+    return cmp_type_by_field_type();
+  }
+  Item_cmp cast_to_int_type() const
   {
     return field->cmp_type();
   }
@@ -2393,7 +2403,7 @@ class Item_field :public Item_ident
   friend class st_select_lex_unit;
 };
 
-class Item_null :public Item_basic_constant
+class Item_null :public Item_basic_constant, public Type_handler_null
 {
 public:
   Item_null(char *name_par=0, CHARSET_INFO *cs= &my_charset_bin)
@@ -2413,8 +2423,6 @@ class Item_null :public Item_basic_constant
   int save_in_field(Field *field, bool no_conversions);
   int save_safe_in_field(Field *field);
   bool send(Protocol *protocol, String *str);
-  enum Item_result result_type () const { return STRING_RESULT; }
-  enum_field_types field_type() const   { return MYSQL_TYPE_NULL; }
   bool basic_const_item() const { return 1; }
   Item *clone_item() { return new Item_null(name); }
   bool is_null() { return 1; }
@@ -2450,7 +2458,8 @@ class Item_null_result :public Item_null
 
 class Item_param :public Item_basic_value,
                   private Settable_routine_parameter,
-                  public Rewritable_query_parameter
+                  public Rewritable_query_parameter,
+                  public Result_type
 {
 public:
   enum enum_item_param_state
@@ -2499,7 +2508,6 @@ class Item_param :public Item_basic_value,
   } value;
 
   /* Cached values for virtual methods to save us one switch.  */
-  enum Item_result item_result_type;
   enum Type item_type;
 
   /*
@@ -2514,9 +2522,10 @@ class Item_param :public Item_basic_value,
 
   Item_param(uint pos_in_query_arg);
 
-  enum Item_result result_type () const { return item_result_type; }
+  enum Item_result result_type () const { return Result_type::type(); }
   enum Type type() const { return item_type; }
   enum_field_types field_type() const { return param_type; }
+  Item_cmp cmp_type() const { return cmp_type_by_field_type(); }
 
   double val_real();
   longlong val_int();
@@ -2600,7 +2609,7 @@ class Item_param :public Item_basic_value,
 };
 
 
-class Item_int :public Item_num
+class Item_int :public Item_num, public Type_handler_longlong
 {
 public:
   longlong value;
@@ -2617,8 +2626,6 @@ class Item_int :public Item_num
     { max_length=length; name=(char*) str_arg; fixed= 1; }
   Item_int(const char *str_arg, uint length=64);
   enum Type type() const { return INT_ITEM; }
-  enum Item_result result_type () const { return INT_RESULT; }
-  enum_field_types field_type() const { return MYSQL_TYPE_LONGLONG; }
   longlong val_int() { DBUG_ASSERT(fixed == 1); return value; }
   double val_real() { DBUG_ASSERT(fixed == 1); return (double) value; }
   my_decimal *val_decimal(my_decimal *);
@@ -2667,7 +2674,7 @@ class Item_datetime :public Item_int
 
 
 /* decimal (fixed point) constant */
-class Item_decimal :public Item_num
+class Item_decimal :public Item_num, public Type_handler_newdecimal
 {
 protected:
   my_decimal decimal_value;
@@ -2681,8 +2688,6 @@ class Item_decimal :public Item_num
   Item_decimal(const uchar *bin, int precision, int scale);
 
   enum Type type() const { return DECIMAL_ITEM; }
-  enum Item_result result_type () const { return DECIMAL_RESULT; }
-  enum_field_types field_type() const { return MYSQL_TYPE_NEWDECIMAL; }
   longlong val_int();
   double val_real();
   String *val_str(String*);
@@ -2708,7 +2713,7 @@ class Item_decimal :public Item_num
 };
 
 
-class Item_float :public Item_num
+class Item_float :public Item_num, public Type_handler_double
 {
   char *presentation;
 public:
@@ -2730,7 +2735,6 @@ class Item_float :public Item_num
   }
   int save_in_field(Field *field, bool no_conversions);
   enum Type type() const { return REAL_ITEM; }
-  enum_field_types field_type() const { return MYSQL_TYPE_DOUBLE; }
   double val_real() { DBUG_ASSERT(fixed == 1); return value; }
   longlong val_int()
   {
@@ -2778,7 +2782,8 @@ class Item_static_float_func :public Item_float
 };
 
 
-class Item_string :public Item_basic_constant
+class Item_string :public Item_basic_constant,
+                   public Type_handler_varchar
 {
   bool m_cs_specified;
 protected:
@@ -2884,8 +2889,6 @@ class Item_string :public Item_basic_constant
   }
   my_decimal *val_decimal(my_decimal *);
   int save_in_field(Field *field, bool no_conversions);
-  enum Item_result result_type () const { return STRING_RESULT; }
-  enum_field_types field_type() const { return MYSQL_TYPE_VARCHAR; }
   bool basic_const_item() const { return 1; }
   bool eq(const Item *item, bool binary_cmp) const
   {
@@ -3123,7 +3126,8 @@ class Item_return_int :public Item_int
 /**
   Item_hex_constant -- a common class for hex literals: X'HHHH' and 0xHHHH
 */
-class Item_hex_constant: public Item_basic_constant
+class Item_hex_constant: public Item_basic_constant,
+                         public Type_handler_varchar
 {
 private:
   void hex_string_init(const char *str, uint str_length);
@@ -3137,8 +3141,6 @@ class Item_hex_constant: public Item_basic_constant
     hex_string_init(str, str_length);
   }
   enum Type type() const { return VARBIN_ITEM; }
-  enum Item_result result_type () const { return STRING_RESULT; }
-  enum_field_types field_type() const { return MYSQL_TYPE_VARCHAR; }
   virtual Item *safe_charset_converter(CHARSET_INFO *tocs)
   {
     return const_charset_converter(tocs, true);
@@ -3182,7 +3184,7 @@ class Item_hex_hybrid: public Item_hex_constant
     return decimal_value;
   }
   int save_in_field(Field *field, bool no_conversions);
-  enum Item_result cast_to_int_type() const { return INT_RESULT; }
+  enum Item_cmp cast_to_int_type() const { return INT_CMP; }
   void print(String *str, enum_query_type query_type);
 };
 
@@ -3228,7 +3230,8 @@ class Item_hex_string: public Item_hex_constant
     return field->store(str_value.ptr(), str_value.length(), 
                         collation.collation);
   }
-  enum Item_result cast_to_int_type() const { return STRING_RESULT; }
+  Item_cmp cmp_type() const { return STRING_CMP; }
+  enum Item_cmp cast_to_int_type() const { return STRING_CMP; }
   void print(String *str, enum_query_type query_type);
 };
 
@@ -3265,8 +3268,6 @@ class Item_temporal_literal :public Item_basic_constant
   bool const_item() const { return true; }
   enum Type type() const { return DATE_ITEM; }
   bool eq(const Item *item, bool binary_cmp) const;
-  enum Item_result result_type () const { return STRING_RESULT; }
-  Item_result cmp_type() const { return TIME_RESULT; }
 
   bool check_partition_func_processor(uchar *int_arg) {return FALSE;}
   bool check_vcol_func_processor(uchar *arg) { return FALSE;}
@@ -3292,7 +3293,8 @@ class Item_temporal_literal :public Item_basic_constant
 /**
   DATE'2010-01-01'
 */
-class Item_date_literal: public Item_temporal_literal
+class Item_date_literal: public Item_temporal_literal,
+                         public Type_handler_date
 {
 public:
   Item_date_literal(MYSQL_TIME *ltime)
@@ -3309,7 +3311,6 @@ class Item_date_literal: public Item_temporal_literal
     */
     maybe_null= !ltime->month || !ltime->day;
   }
-  enum_field_types field_type() const { return MYSQL_TYPE_DATE; }
   void print(String *str, enum_query_type query_type);
   bool get_date(MYSQL_TIME *res, ulonglong fuzzy_date);
 };
@@ -3318,7 +3319,8 @@ class Item_date_literal: public Item_temporal_literal
 /**
   TIME'10:10:10'
 */
-class Item_time_literal: public Item_temporal_literal
+class Item_time_literal: public Item_temporal_literal,
+                         public Type_handler_time
 {
 public:
   Item_time_literal(MYSQL_TIME *ltime, uint dec_arg)
@@ -3327,7 +3329,6 @@ class Item_time_literal: public Item_temporal_literal
     max_length= MIN_TIME_WIDTH + (decimals ? decimals + 1 : 0);
     fixed= 1;
   }
-  enum_field_types field_type() const { return MYSQL_TYPE_TIME; }
   void print(String *str, enum_query_type query_type);
   bool get_date(MYSQL_TIME *res, ulonglong fuzzy_date);
 };
@@ -3336,7 +3337,8 @@ class Item_time_literal: public Item_temporal_literal
 /**
   TIMESTAMP'2001-01-01 10:20:30'
 */
-class Item_datetime_literal: public Item_temporal_literal
+class Item_datetime_literal: public Item_temporal_literal,
+                             public Type_handler_datetime
 {
 public:
   Item_datetime_literal(MYSQL_TIME *ltime, uint dec_arg)
@@ -3347,7 +3349,6 @@ class Item_datetime_literal: public Item_temporal_literal
     // See the comment on maybe_null in Item_date_literal
     maybe_null= !ltime->month || !ltime->day;
   }
-  enum_field_types field_type() const { return MYSQL_TYPE_DATETIME; }
   void print(String *str, enum_query_type query_type);
   bool get_date(MYSQL_TIME *res, ulonglong fuzzy_date);
 };
@@ -3466,6 +3467,7 @@ class Item_ref :public Item_ident
   { return (*ref)->setup_fast_field_copier(field); }
   enum Item_result result_type () const { return (*ref)->result_type(); }
   enum_field_types field_type() const   { return (*ref)->field_type(); }
+  Item_cmp cmp_type() const { return cmp_type_by_field_type(); }
   Field *get_tmp_table_field()
   { return result_field ? result_field : (*ref)->get_tmp_table_field(); }
   Item *get_tmp_table_item(THD *thd);
@@ -3720,6 +3722,7 @@ class Item_cache_wrapper :public Item_result_field
   int save_in_field(Field *to, bool no_conversions);
   enum Item_result result_type () const { return orig_item->result_type(); }
   enum_field_types field_type() const   { return orig_item->field_type(); }
+  Item_cmp cmp_type() const { return cmp_type_by_field_type(); }
   table_map used_tables() const { return orig_item->used_tables(); }
   void update_used_tables()
   {
@@ -4066,7 +4069,12 @@ class Item_int_with_ref :public Item_int
   from Item_).
 */
 
-class Item_copy :public Item
+class Item_copy :public Item,
+                 public Result_type
+  /**
+    Result_type stores the result type of the original item, so it can be
+    returned without calling the original item's method
+  */
 {
 protected:  
 
@@ -4080,18 +4088,13 @@ class Item_copy :public Item
   Item *item;
 
   /**
-    Stores the result type of the original item, so it can be returned
-    without calling the original item's method
-  */
-  Item_result cached_result_type;
-
-  /**
     Constructor of the Item_copy class
 
     stores metadata information about the original class as well as a 
     pointer to it.
   */
   Item_copy(Item *i)
+   :Result_type(i->result_type())
   {
     item= i;
     null_value=maybe_null=item->maybe_null;
@@ -4099,7 +4102,6 @@ class Item_copy :public Item
     max_length=item->max_length;
     name=item->name;
     cached_field_type= item->field_type();
-    cached_result_type= item->result_type();
     unsigned_flag= item->unsigned_flag;
     fixed= item->fixed;
     collation.set(item->collation);
@@ -4127,7 +4129,8 @@ class Item_copy :public Item
   /** All of the subclasses should have the same type tag */
   enum Type type() const { return COPY_STR_ITEM; }
   enum_field_types field_type() const { return cached_field_type; }
-  enum Item_result result_type () const { return cached_result_type; }
+  enum Item_result result_type () const { return Result_type::type(); }
+  Item_cmp cmp_type() const { return cmp_type_by_field_type(); }
 
   void make_field(Send_field *field) { item->make_field(field); }
   table_map used_tables() const { return (table_map) 1L; }
@@ -4562,8 +4565,9 @@ class Item_cache: public Item_basic_constant
   };
   enum Type type() const { return CACHE_ITEM; }
   enum_field_types field_type() const { return cached_field_type; }
+  Item_cmp cmp_type() const { return cmp_type_by_field_type(); }
   static Item_cache* get_cache(const Item *item);
-  static Item_cache* get_cache(const Item* item, const Item_result type);
+  static Item_cache* get_cache(const Item* item, const Item_cmp type);
   table_map used_tables() const { return used_table_map; }
   virtual void keep_array() {}
   virtual void print(String *str, enum_query_type query_type);
@@ -4650,7 +4654,7 @@ class Item_cache_temporal: public Item_cache_int
   bool cache_value();
   bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate);
   int save_in_field(Field *field, bool no_conversions);
-  Item_result cmp_type() const { return TIME_RESULT; }
+  Item_cmp cmp_type() const { return TIME_CMP; }
   void store_packed(longlong val_arg, Item *example);
   /*
     Having a clone_item method tells optimizer that this object
@@ -4817,6 +4821,7 @@ class Item_type_holder: public Item
 
   Item_result result_type() const;
   enum_field_types field_type() const { return fld_type; };
+  Item_cmp cmp_type() const { return cmp_type_by_field_type(); }
   enum Type type() const { return TYPE_HOLDER; }
   double val_real();
   longlong val_int();
@@ -4839,7 +4844,7 @@ void mark_select_range_as_dependent(THD *thd,
 
 extern Cached_item *new_Cached_item(THD *thd, Item *item,
                                     bool pass_through_ref);
-extern Item_result item_cmp_type(Item_result a,Item_result b);
+extern Item_cmp item_cmp_type(Item_cmp a,Item_cmp b);
 extern void resolve_const_item(THD *thd, Item **ref, Item *cmp_item);
 extern int stored_field_cmp_to_item(THD *thd, Field *field, Item *item);
 
diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc
index f01a574..778698b 100644
--- a/sql/item_cmpfunc.cc
+++ b/sql/item_cmpfunc.cc
@@ -33,48 +33,6 @@
 #include "sql_time.h"                  // make_truncated_value_warning
 #include "sql_base.h"                  // dynamic_column_error_message
 
-static Item_result item_store_type(Item_result a, Item *item,
-                                   my_bool unsigned_flag)
-{
-  Item_result b= item->result_type();
-
-  if (a == STRING_RESULT || b == STRING_RESULT)
-    return STRING_RESULT;
-  else if (a == REAL_RESULT || b == REAL_RESULT)
-    return REAL_RESULT;
-  else if (a == DECIMAL_RESULT || b == DECIMAL_RESULT ||
-           unsigned_flag != item->unsigned_flag)
-    return DECIMAL_RESULT;
-  else
-    return INT_RESULT;
-}
-
-static void agg_result_type(Item_result *type, Item **items, uint nitems)
-{
-  Item **item, **item_end;
-  my_bool unsigned_flag= 0;
-
-  *type= STRING_RESULT;
-  /* Skip beginning NULL items */
-  for (item= items, item_end= item + nitems; item < item_end; item++)
-  {
-    if ((*item)->type() != Item::NULL_ITEM)
-    {
-      *type= (*item)->result_type();
-      unsigned_flag= (*item)->unsigned_flag;
-      item++;
-      break;
-    }
-  }
-  /* Combine result types. Note: NULL items don't affect the result */
-  for (; item < item_end; item++)
-  {
-    if ((*item)->type() != Item::NULL_ITEM)
-      *type= item_store_type(*type, *item, unsigned_flag);
-  }
-}
-
-
 /**
   find an temporal type (item) that others will be converted to
   for the purpose of comparison.
@@ -88,7 +46,7 @@ Item *find_date_time_item(Item **args, uint nargs, uint col)
   for (arg= args, arg_end= args + nargs; arg != arg_end ; arg++)
   {
     Item *item= arg[0]->element_index(col);
-    if (item->cmp_type() != TIME_RESULT)
+    if (item->cmp_type() != TIME_CMP)
       continue;
     if (item->field_type() == MYSQL_TYPE_DATETIME)
       return item;
@@ -158,7 +116,7 @@ static int cmp_row_type(Item* item1, Item* item2)
     0  otherwise
 */
 
-static int agg_cmp_type(Item_result *type, Item **items, uint nitems)
+static int agg_cmp_type(Item_cmp *type, Item **items, uint nitems)
 {
   uint i;
   type[0]= items[0]->cmp_type();
@@ -172,7 +130,7 @@ static int agg_cmp_type(Item_result *type, Item **items, uint nitems)
       the signature of the corresponding component of the second row
       expression.
     */ 
-    if (type[0] == ROW_RESULT && cmp_row_type(items[0], items[i]))
+    if (type[0] == ROW_CMP && cmp_row_type(items[0], items[i]))
       return 1;     // error found: invalid usage of rows
   }
   return 0;
@@ -230,15 +188,15 @@ static uint collect_cmp_types(Item **items, uint nitems, bool skip_nulls= FALSE)
 {
   uint i;
   uint found_types;
-  Item_result left_result= items[0]->cmp_type();
+  Item_cmp left_result= items[0]->cmp_type();
   DBUG_ASSERT(nitems > 1);
   found_types= 0;
   for (i= 1; i < nitems ; i++)
   {
     if (skip_nulls && items[i]->type() == Item::NULL_ITEM)
       continue; // Skip NULL constant items
-    if ((left_result == ROW_RESULT || 
-         items[i]->cmp_type() == ROW_RESULT) &&
+    if ((left_result == ROW_CMP || 
+         items[i]->cmp_type() == ROW_CMP) &&
         cmp_row_type(items[0], items[i]))
       return 0;
     found_types|= 1U << (uint)item_cmp_type(left_result,
@@ -457,7 +415,7 @@ static bool convert_const_to_int(THD *thd, Item_field *field_item,
     as YEAR(2) may change the value of an integer when converting it
     to an integer (say, 0 to 70).
   */
-  if ((*item)->cmp_type() == INT_RESULT &&
+  if ((*item)->cmp_type() == INT_CMP &&
       field_item->field_type() != MYSQL_TYPE_YEAR)
     return 1;
 
@@ -545,7 +503,7 @@ void Item_func::convert_const_compared_to_int_field(THD *thd)
       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;
+        args[0]->cmp_context= args[1]->cmp_context= INT_CMP;
     }
   }
 }
@@ -575,13 +533,14 @@ void Item_bool_func2::fix_length_and_dec()
   */
 
   DTCollation coll;
-  if (args[0]->cmp_type() == STRING_RESULT &&
-      args[1]->cmp_type() == STRING_RESULT &&
+  if (args[0]->cmp_type() == STRING_CMP &&
+      args[1]->cmp_type() == STRING_CMP &&
       agg_arg_charsets_for_comparison(coll, args, 2))
     return;
     
   args[0]->cmp_context= args[1]->cmp_context=
-    item_cmp_type(args[0]->result_type(), args[1]->result_type());
+    item_cmp_type((Item_cmp) args[0]->result_type(),
+                  (Item_cmp) args[1]->result_type());
 
   //  Convert constants when compared to int/year field, unless this is LIKE
   if (functype() != LIKE_FUNC)
@@ -590,17 +549,17 @@ void Item_bool_func2::fix_length_and_dec()
 }
 
 
-int Arg_comparator::set_compare_func(Item_result_field *item, Item_result type)
+int Arg_comparator::set_compare_func(Item_result_field *item, Item_cmp type)
 {
   owner= item;
   func= comparator_matrix[type]
                          [is_owner_equal_func()];
 
   switch (type) {
-  case TIME_RESULT:
+  case TIME_CMP:
     cmp_collation.collation= &my_charset_numeric;
     break;
-  case ROW_RESULT:
+  case ROW_CMP:
   {
     uint n= (*a)->cols();
     if (n != (*b)->cols())
@@ -624,7 +583,7 @@ int Arg_comparator::set_compare_func(Item_result_field *item, Item_result type)
     }
     break;
   }
-  case STRING_RESULT:
+  case STRING_CMP:
   {
     /*
       We must set cmp_charset here as we may be called from for an automatic
@@ -661,7 +620,7 @@ int Arg_comparator::set_compare_func(Item_result_field *item, Item_result type)
     }
     break;
   }
-  case INT_RESULT:
+  case INT_CMP:
   {
     if (func == &Arg_comparator::compare_int_signed)
     {
@@ -679,9 +638,9 @@ int Arg_comparator::set_compare_func(Item_result_field *item, Item_result type)
     }
     break;
   }
-  case DECIMAL_RESULT:
+  case DECIMAL_CMP:
     break;
-  case REAL_RESULT:
+  case REAL_CMP:
   {
     if ((*a)->decimals < NOT_FIXED_DEC && (*b)->decimals < NOT_FIXED_DEC)
     {
@@ -693,7 +652,7 @@ int Arg_comparator::set_compare_func(Item_result_field *item, Item_result type)
     }
     break;
   }
-  case IMPOSSIBLE_RESULT:
+  case IMPOSSIBLE_CMP:
     DBUG_ASSERT(0);
     break;
   }
@@ -774,7 +733,7 @@ bool get_mysql_time_from_str(THD *thd, String *str, timestamp_type warn_type,
 
 int Arg_comparator::set_cmp_func(Item_result_field *owner_arg,
                                         Item **a1, Item **a2,
-                                        Item_result type)
+                                        Item_cmp type)
 {
   thd= current_thd;
   owner= owner_arg;
@@ -782,7 +741,7 @@ int Arg_comparator::set_cmp_func(Item_result_field *owner_arg,
   a= a1;
   b= a2;
 
-  if (type == STRING_RESULT &&
+  if (type == STRING_CMP &&
       (*a)->result_type() == STRING_RESULT &&
       (*b)->result_type() == STRING_RESULT)
   {
@@ -792,10 +751,10 @@ int Arg_comparator::set_cmp_func(Item_result_field *owner_arg,
                                b, 1, MY_COLL_CMP_CONV, 1))
       return 1;
   }
-  if (type == INT_RESULT &&
+  if (type == INT_CMP &&
       (*a)->field_type() == MYSQL_TYPE_YEAR &&
       (*b)->field_type() == MYSQL_TYPE_YEAR)
-    type= TIME_RESULT;
+    type= TIME_CMP;
 
   a= cache_converted_constant(thd, a, &a_cache, type);
   b= cache_converted_constant(thd, b, &b_cache, type);
@@ -822,7 +781,7 @@ int Arg_comparator::set_cmp_func(Item_result_field *owner_arg,
 
 Item** Arg_comparator::cache_converted_constant(THD *thd_arg, Item **value,
                                                 Item **cache_item,
-                                                Item_result type)
+                                                Item_cmp type)
 {
   /*
     Don't need cache if doing context analysis only.
@@ -830,8 +789,8 @@ Item** Arg_comparator::cache_converted_constant(THD *thd_arg, Item **value,
     Unless fixed, we should not do it here.
   */
   if (!thd_arg->lex->is_ps_or_view_context_analysis() &&
-      (*value)->const_item() && type != (*value)->result_type() &&
-      type != TIME_RESULT)
+      (*value)->const_item() && type != (Item_cmp) (*value)->result_type() &&
+      type != TIME_CMP)
   {
     Item_cache *cache= Item_cache::get_cache(*value, type);
     cache->setup(*value);
@@ -851,7 +810,7 @@ void Arg_comparator::set_datetime_cmp_func(Item_result_field *owner_arg,
   b= b1;
   a_cache= 0;
   b_cache= 0;
-  func= comparator_matrix[TIME_RESULT][is_owner_equal_func()];
+  func= comparator_matrix[TIME_CMP][is_owner_equal_func()];
 }
 
 /**
@@ -867,7 +826,7 @@ void Arg_comparator::set_datetime_cmp_func(Item_result_field *owner_arg,
     Retrieves the correct DATETIME value from given item for comparison by the
     compare_datetime() function.
 
-    If the value should be compared as time (TIME_RESULT), it's retrieved as
+    If the value should be compared as time (TIME_CMP), it's retrieved as
     MYSQL_TIME. Otherwise it's read as a number/string and converted to time.
     Constant items are cached, so the convertion is only done once for them.
 
@@ -891,11 +850,11 @@ void Arg_comparator::set_datetime_cmp_func(Item_result_field *owner_arg,
 {
   longlong UNINIT_VAR(value);
   Item *item= **item_arg;
-  enum_field_types f_type= item->cmp_type() == TIME_RESULT ?
+  enum_field_types f_type= item->cmp_type() == TIME_CMP ?
                            item->field_type() : warn_item->field_type();
 
   if (item->result_type() == INT_RESULT &&
-      item->cmp_type() == TIME_RESULT &&
+      item->cmp_type() == TIME_CMP &&
       item->type() == Item::CACHE_ITEM)
   {
     /* it's our Item_cache_temporal, as created below */
@@ -918,7 +877,7 @@ void Arg_comparator::set_datetime_cmp_func(Item_result_field *owner_arg,
   if ((*is_null= item->null_value))
     return ~(ulonglong) 0;
   if (cache_arg && item->const_item() &&
-      !(item->type() == Item::CACHE_ITEM && item->cmp_type() == TIME_RESULT))
+      !(item->type() == Item::CACHE_ITEM && item->cmp_type() == TIME_CMP))
   {
     Query_arena backup;
     Query_arena *save_arena= thd->switch_to_arena_for_cached_items(&backup);
@@ -2254,7 +2213,7 @@ void Item_func_between::fix_length_and_dec()
     return;
   if ( agg_cmp_type(&cmp_type, args, 3))
     return;
-  if (cmp_type == STRING_RESULT &&
+  if (cmp_type == STRING_CMP &&
       agg_arg_charsets_for_comparison(cmp_collation, args, 3))
    return;
 
@@ -2266,7 +2225,7 @@ void Item_func_between::fix_length_and_dec()
     For this to work, we need to know what date/time type we compare
     strings as.
   */
-  if (cmp_type ==  TIME_RESULT)
+  if (cmp_type == TIME_CMP)
     compare_as_dates= find_date_time_item(args, 3, 0);
 
   /* See the comment about the similar block in Item_bool_func2 */
@@ -2280,7 +2239,7 @@ void Item_func_between::fix_length_and_dec()
       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.
+        cmp_type= INT_CMP;                    // Works for all types.
     }
   }
 }
@@ -2291,7 +2250,7 @@ longlong Item_func_between::val_int()
   DBUG_ASSERT(fixed == 1);
 
   switch (cmp_type) {
-  case TIME_RESULT:
+  case TIME_CMP:
   {
     THD *thd= current_thd;
     longlong value, a, b;
@@ -2328,7 +2287,7 @@ longlong Item_func_between::val_int()
     break;
   }
 
-  case STRING_RESULT:
+  case STRING_CMP:
   {
     String *value,*a,*b;
     value=args[0]->val_str(&value0);
@@ -2354,7 +2313,7 @@ longlong Item_func_between::val_int()
     }
     break;
   }
-  case INT_RESULT:
+  case INT_CMP:
   {
     longlong value=args[0]->val_int(), a, b;
     if ((null_value=args[0]->null_value))
@@ -2375,7 +2334,7 @@ longlong Item_func_between::val_int()
     }
     break;
   }
-  case DECIMAL_RESULT:
+  case DECIMAL_CMP:
   {
     my_decimal dec_buf, *dec= args[0]->val_decimal(&dec_buf),
                a_buf, *a_dec, b_buf, *b_dec;
@@ -2394,7 +2353,7 @@ longlong Item_func_between::val_int()
       null_value= (my_decimal_cmp(dec, a_dec) >= 0);
     break;
   }
-  case REAL_RESULT:
+  case REAL_CMP:
   {
     double value= args[0]->val_real(),a,b;
     if ((null_value=args[0]->null_value))
@@ -2415,8 +2374,8 @@ longlong Item_func_between::val_int()
     }
     break;
   }
-  case ROW_RESULT:
-  case IMPOSSIBLE_RESULT:
+  case ROW_CMP:
+  case IMPOSSIBLE_CMP:
     DBUG_ASSERT(0);
     null_value= 1;
     return 0;
@@ -2443,13 +2402,14 @@ void Item_func_between::print(String *str, enum_query_type query_type)
 Item_func_case_abbreviation2::fix_length_and_dec(Item **args)
 {
   uint32 char_length;
-  agg_result_type(&cached_result_type, args, 2);
+  Result_type::aggregate(args, 2);
   cached_field_type= agg_field_type(args, 2);
   maybe_null=args[0]->maybe_null || args[1]->maybe_null;
   decimals= MY_MAX(args[0]->decimals, args[1]->decimals);
   unsigned_flag= args[0]->unsigned_flag && args[1]->unsigned_flag;
 
-  if (cached_result_type == DECIMAL_RESULT || cached_result_type == INT_RESULT) 
+  if (Result_type::type() == DECIMAL_RESULT ||
+      Result_type::type() == INT_RESULT) 
   {
     int len0= args[0]->max_char_length() - args[0]->decimals
       - (args[0]->unsigned_flag ? 0 : 1);
@@ -2462,7 +2422,7 @@ void Item_func_between::print(String *str, enum_query_type query_type)
   else
     char_length= MY_MAX(args[0]->max_char_length(), args[1]->max_char_length());
 
-  switch (cached_result_type) {
+  switch (Result_type::type()) {
   case STRING_RESULT:
     if (count_string_result_length(cached_field_type, args, 2))
       return;
@@ -2475,7 +2435,6 @@ void Item_func_between::print(String *str, enum_query_type query_type)
     break;
   case ROW_RESULT:
   case TIME_RESULT:
-  case IMPOSSIBLE_RESULT:
     DBUG_ASSERT(0);
   }
   fix_char_length(char_length);
@@ -2642,8 +2601,8 @@ void Item_func_if::fix_after_pullout(st_select_lex *new_parent, Item **ref)
 void Item_func_if::cache_type_info(Item *source)
 {
   collation.set(source->collation);
+  Result_type::set(source->result_type());
   cached_field_type=  source->field_type();
-  cached_result_type= source->result_type();
   decimals=           source->decimals;
   max_length=         source->max_length;
   maybe_null=         source->maybe_null;
@@ -2732,7 +2691,7 @@ bool Item_func_if::date_op(MYSQL_TIME *ltime, uint fuzzydate)
   if (!args[0])					// Only false if EOM
     return;
 
-  cached_result_type= args[0]->result_type();
+  Result_type::set(args[0]->result_type());
   cached_field_type= args[0]->field_type();
   collation.set(args[0]->collation);
   decimals= args[0]->decimals;
@@ -2741,7 +2700,8 @@ bool Item_func_if::date_op(MYSQL_TIME *ltime, uint fuzzydate)
 
   convert_const_compared_to_int_field(current_thd);
   args[0]->cmp_context= args[1]->cmp_context=
-    item_cmp_type(args[0]->result_type(), args[1]->result_type());
+    item_cmp_type((Item_cmp) args[0]->result_type(),
+                  (Item_cmp) args[1]->result_type());
   cmp.set_cmp_func(this, tmp_arg, tmp_arg + 1, args[0]->cmp_context);
   maybe_null=1;
 }
@@ -2878,8 +2838,8 @@ Item *Item_func_case::find_item(String *str)
     {
       if (args[i]->real_item()->type() == NULL_ITEM)
         continue;
-      cmp_type= item_cmp_type(left_result_type, args[i]->cmp_type());
-      DBUG_ASSERT(cmp_type != ROW_RESULT);
+      cmp_type= item_cmp_type(left_cmp_type, args[i]->cmp_type());
+      DBUG_ASSERT(cmp_type != ROW_CMP);
       DBUG_ASSERT(cmp_items[(uint)cmp_type]);
       if (!(value_added_map & (1U << (uint)cmp_type)))
       {
@@ -3066,10 +3026,10 @@ void Item_func_case::fix_length_and_dec()
   if (else_expr_num != -1)
     agg[nagg++]= args[else_expr_num];
   
-  agg_result_type(&cached_result_type, agg, nagg);
+  Result_type::aggregate(agg, nagg);
   cached_field_type= agg_field_type(agg, nagg);
 
-  if (cached_result_type == STRING_RESULT)
+  if (Result_type::type() == STRING_RESULT)
   {
     if (count_string_result_length(cached_field_type, agg, nagg))
       return;
@@ -3106,7 +3066,7 @@ void Item_func_case::fix_length_and_dec()
   {
     uint i;
     agg[0]= args[first_expr_num];
-    left_result_type= agg[0]->cmp_type();
+    left_cmp_type= agg[0]->cmp_type();
 
     /*
       As the first expression and WHEN expressions
@@ -3121,10 +3081,10 @@ void Item_func_case::fix_length_and_dec()
       return;
 
     Item *date_arg= 0;
-    if (found_types & (1U << TIME_RESULT))
+    if (found_types & (1U << TIME_CMP))
       date_arg= find_date_time_item(args, arg_count, 0);
 
-    if (found_types & (1U << STRING_RESULT))
+    if (found_types & (1U << STRING_CMP))
     {
       /*
         If we'll do string comparison, we also need to aggregate
@@ -3163,14 +3123,14 @@ void Item_func_case::fix_length_and_dec()
         change_item_tree_if_needed(thd, &args[nagg * 2], agg[nagg + 1]);
     }
 
-    for (i= 0; i <= (uint)TIME_RESULT; i++)
+    for (i= 0; i <= (uint) TIME_CMP; i++)
     {
       if (found_types & (1U << i) && !cmp_items[i])
       {
-        DBUG_ASSERT((Item_result)i != ROW_RESULT);
+        DBUG_ASSERT((Item_cmp)i != ROW_CMP);
 
         if (!(cmp_items[i]=
-            cmp_item::get_comparator((Item_result)i, date_arg,
+            cmp_item::get_comparator((Item_cmp) i, date_arg,
                                      cmp_collation.collation)))
           return;
       }
@@ -3182,8 +3142,8 @@ void Item_func_case::fix_length_and_dec()
       require rebuilding cmp_items.
     */
     for (i= 0; i < ncases; i+= 2)
-      args[i]->cmp_context= item_cmp_type(left_result_type,
-                                          args[i]->result_type());
+      args[i]->cmp_context= item_cmp_type(left_cmp_type,
+                                          (Item_cmp) args[i]->result_type());
   }
 }
 
@@ -3236,7 +3196,7 @@ void Item_func_case::cleanup()
   uint i;
   DBUG_ENTER("Item_func_case::cleanup");
   Item_func::cleanup();
-  for (i= 0; i <= (uint)TIME_RESULT; i++)
+  for (i= 0; i <= (uint) TIME_CMP; i++)
   {
     delete cmp_items[i];
     cmp_items[i]= 0;
@@ -3326,8 +3286,8 @@ my_decimal *Item_func_coalesce::decimal_op(my_decimal *decimal_value)
 void Item_func_coalesce::fix_length_and_dec()
 {
   cached_field_type= agg_field_type(args, arg_count);
-  agg_result_type(&cached_result_type, args, arg_count);
-  switch (cached_result_type) {
+  Result_type::aggregate(args, arg_count);
+  switch (Result_type::type()) {
   case STRING_RESULT:
     if (count_string_result_length(cached_field_type, args, arg_count))
       return;          
@@ -3344,7 +3304,6 @@ void Item_func_coalesce::fix_length_and_dec()
     break;
   case ROW_RESULT:
   case TIME_RESULT:
-  case IMPOSSIBLE_RESULT:
     DBUG_ASSERT(0);
   }
 }
@@ -3659,24 +3618,24 @@ uchar *in_decimal::get_value(Item *item)
 }
 
 
-cmp_item* cmp_item::get_comparator(Item_result type, Item *warn_item,
+cmp_item* cmp_item::get_comparator(Item_cmp type, Item *warn_item,
                                    CHARSET_INFO *cs)
 {
   switch (type) {
-  case STRING_RESULT:
+  case STRING_CMP:
     return new cmp_item_sort_string(cs);
-  case INT_RESULT:
+  case INT_CMP:
     return new cmp_item_int;
-  case REAL_RESULT:
+  case REAL_CMP:
     return new cmp_item_real;
-  case ROW_RESULT:
+  case ROW_CMP:
     return new cmp_item_row;
-  case DECIMAL_RESULT:
+  case DECIMAL_CMP:
     return new cmp_item_decimal;
-  case TIME_RESULT:
+  case TIME_CMP:
     DBUG_ASSERT(warn_item);
     return new cmp_item_datetime(warn_item);
-  case IMPOSSIBLE_RESULT:
+  case IMPOSSIBLE_CMP:
     DBUG_ASSERT(0);
     break;
   }
@@ -3741,9 +3700,9 @@ void cmp_item_row::store_value(Item *item)
     {
       if (!comparators[i])
       {
-        DBUG_ASSERT(item->element_index(i)->cmp_type() != TIME_RESULT);
+        DBUG_ASSERT(item->element_index(i)->cmp_type() != TIME_CMP);
         if (!(comparators[i]=
-              cmp_item::get_comparator(item->element_index(i)->result_type(), 0,
+              cmp_item::get_comparator((Item_cmp) item->element_index(i)->result_type(), 0,
                                        item->element_index(i)->collation.collation)))
 	  break;					// new failed
       }
@@ -3973,8 +3932,8 @@ 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;
-  left_result_type= args[0]->cmp_type();
+  Item_cmp cmp_type= STRING_CMP;
+  left_cmp_type= args[0]->cmp_type();
   if (!(found_types= collect_cmp_types(args, arg_count, true)))
     return;
   
@@ -3986,23 +3945,23 @@ void Item_func_in::fix_length_and_dec()
       break;
     }
   }
-  for (i= 0; i <= (uint)TIME_RESULT; i++)
+  for (i= 0; i <= (uint)TIME_CMP; i++)
   {
     if (found_types & (1U << i))
     {
       (type_cnt)++;
-      cmp_type= (Item_result) i;
+      cmp_type= (Item_cmp) i;
     }
   }
 
   if (type_cnt == 1)
   {
-    if (cmp_type == STRING_RESULT && 
+    if (cmp_type == STRING_CMP && 
         agg_arg_charsets_for_comparison(cmp_collation, args, arg_count))
       return;
     arg_types_compatible= TRUE;
 
-    if (cmp_type == ROW_RESULT)
+    if (cmp_type == ROW_CMP)
     {
       uint cols= args[0]->cols();
       cmp_item_row *cmp= 0;
@@ -4016,7 +3975,7 @@ void Item_func_in::fix_length_and_dec()
       {
         if (!(cmp= new cmp_item_row))
           return;
-        cmp_items[ROW_RESULT]= cmp;
+        cmp_items[ROW_CMP]= cmp;
       }
       cmp->n= cols;
       cmp->alloc_comparators();
@@ -4030,7 +3989,7 @@ void Item_func_in::fix_length_and_dec()
           if (array)
             cmp= ((in_row*)array)->tmp.comparators + col;
           else
-            cmp= ((cmp_item_row*)cmp_items[ROW_RESULT])->comparators + col;
+            cmp= ((cmp_item_row*)cmp_items[ROW_CMP])->comparators + col;
           *cmp= new cmp_item_datetime(date_arg);
         }
       }
@@ -4052,7 +4011,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() && cmp_type != INT_CMP)
     {
       Item_field *field_item= (Item_field*) (args[0]->real_item());
       if (field_item->field_type() ==  MYSQL_TYPE_LONGLONG ||
@@ -4065,21 +4024,21 @@ void Item_func_in::fix_length_and_dec()
             all_converted= FALSE;
         }
         if (all_converted)
-          cmp_type= INT_RESULT;
+          cmp_type= INT_CMP;
       }
     }
     switch (cmp_type) {
-    case STRING_RESULT:
+    case STRING_CMP:
       array=new in_string(arg_count-1,(qsort2_cmp) srtcmp_in, 
                           cmp_collation.collation);
       break;
-    case INT_RESULT:
+    case INT_CMP:
       array= new in_longlong(arg_count-1);
       break;
-    case REAL_RESULT:
+    case REAL_CMP:
       array= new in_double(arg_count-1);
       break;
-    case ROW_RESULT:
+    case ROW_CMP:
       /*
         The row comparator was created at the beginning but only DATETIME
         items comparators were initialized. Call store_value() to setup
@@ -4087,14 +4046,14 @@ void Item_func_in::fix_length_and_dec()
       */
       ((in_row*)array)->tmp.store_value(args[0]);
       break;
-    case DECIMAL_RESULT:
+    case DECIMAL_CMP:
       array= new in_decimal(arg_count - 1);
       break;
-    case TIME_RESULT:
+    case TIME_CMP:
       date_arg= find_date_time_item(args, arg_count, 0);
       array= new in_datetime(date_arg, arg_count - 1);
       break;
-    case IMPOSSIBLE_RESULT:
+    case IMPOSSIBLE_CMP:
       DBUG_ASSERT(0);
       break;
     }
@@ -4115,17 +4074,17 @@ void Item_func_in::fix_length_and_dec()
   }
   else
   {
-    if (found_types & (1U << TIME_RESULT))
+    if (found_types & (1U << TIME_CMP))
       date_arg= find_date_time_item(args, arg_count, 0);
-    if (found_types & (1U << STRING_RESULT) &&
+    if (found_types & (1U << STRING_CMP) &&
         agg_arg_charsets_for_comparison(cmp_collation, args, arg_count))
       return;
-    for (i= 0; i <= (uint) TIME_RESULT; i++)
+    for (i= 0; i <= (uint) TIME_CMP; i++)
     {
       if (found_types & (1U << i) && !cmp_items[i])
       {
         if (!cmp_items[i] && !(cmp_items[i]=
-            cmp_item::get_comparator((Item_result)i, date_arg,
+            cmp_item::get_comparator((Item_cmp) i, date_arg,
                                      cmp_collation.collation)))
           return;
       }
@@ -4139,7 +4098,8 @@ void Item_func_in::fix_length_and_dec()
    */
   for (arg= args + 1, arg_end= args + arg_count; arg != arg_end ; arg++)
   {
-    arg[0]->cmp_context= item_cmp_type(left_result_type, arg[0]->result_type());
+    arg[0]->cmp_context= item_cmp_type(left_cmp_type,
+                                       (Item_cmp) arg[0]->result_type());
   }
   max_length= 1;
 }
@@ -4205,7 +4165,7 @@ longlong Item_func_in::val_int()
       have_null= TRUE;
       continue;
     }
-    Item_result cmp_type= item_cmp_type(left_result_type, args[i]->cmp_type());
+    Item_cmp cmp_type= item_cmp_type(left_cmp_type, args[i]->cmp_type());
     in_item= cmp_items[(uint)cmp_type];
     DBUG_ASSERT(in_item);
     if (!(value_added_map & (1U << (uint)cmp_type)))
@@ -5704,7 +5664,7 @@ Item *Item_bool_rowready_func2::negated_item()
   with_const= with_const_item;
   equal_items.push_back(f1);
   equal_items.push_back(f2);
-  compare_as_dates= with_const_item && f2->cmp_type() == TIME_RESULT;
+  compare_as_dates= with_const_item && f2->cmp_type() == TIME_CMP;
   upper_levels= NULL;
   sargable= TRUE; 
 }
@@ -5766,7 +5726,7 @@ void Item_equal::add_const(Item *c, Item *f)
   {
     with_const= TRUE;
     if (f)
-      compare_as_dates= f->cmp_type() == TIME_RESULT;
+      compare_as_dates= f->cmp_type() == TIME_CMP;
     equal_items.push_front(c);
     return;
   }
diff --git a/sql/item_cmpfunc.h b/sql/item_cmpfunc.h
index 2482f02..8ada18e 100644
--- a/sql/item_cmpfunc.h
+++ b/sql/item_cmpfunc.h
@@ -28,7 +28,7 @@
 #define PCRE_STATIC 1             /* Important on Windows */
 #include "pcre.h"                 /* pcre header file */
 
-extern Item_result item_cmp_type(Item_result a,Item_result b);
+extern Item_cmp item_cmp_type(Item_cmp a,Item_cmp b);
 class Item_bool_func2;
 class Arg_comparator;
 
@@ -58,15 +58,16 @@ class Arg_comparator: public Sql_alloc
   Arg_comparator(Item **a1, Item **a2): a(a1), b(a2),  set_null(TRUE),
     comparators(0), thd(0), a_cache(0), b_cache(0) {};
 
-  int set_compare_func(Item_result_field *owner, Item_result type);
+  int set_compare_func(Item_result_field *owner, Item_cmp type);
   inline int set_compare_func(Item_result_field *owner_arg)
   {
-    return set_compare_func(owner_arg, item_cmp_type((*a)->result_type(),
-                                                     (*b)->result_type()));
+    return set_compare_func(owner_arg,
+                            item_cmp_type((Item_cmp) (*a)->result_type(),
+                                          (Item_cmp) (*b)->result_type()));
   }
   int set_cmp_func(Item_result_field *owner_arg,
 			  Item **a1, Item **a2,
-			  Item_result type);
+			  Item_cmp type);
 
   inline int set_cmp_func(Item_result_field *owner_arg,
 			  Item **a1, Item **a2, bool set_null_arg)
@@ -100,7 +101,7 @@ class Arg_comparator: public Sql_alloc
   int compare_e_datetime();
 
   Item** cache_converted_constant(THD *thd, Item **value, Item **cache,
-                                  Item_result type);
+                                  Item_cmp type);
   void set_datetime_cmp_func(Item_result_field *owner_arg, Item **a1, Item **b1);
   static arg_cmp_func comparator_matrix [6][2];
   inline bool is_owner_equal_func()
@@ -673,7 +674,7 @@ class Item_func_between :public Item_func_opt_neg
 {
   DTCollation cmp_collation;
 public:
-  Item_result cmp_type;
+  Item_cmp cmp_type;
   String value0,value1,value2;
   /* TRUE <=> arguments will be compared as dates. */
   Item *compare_as_dates;
@@ -1074,7 +1075,7 @@ class cmp_item :public Sql_alloc
   virtual int cmp(Item *item)= 0;
   // for optimized IN with row
   virtual int compare(cmp_item *item)= 0;
-  static cmp_item* get_comparator(Item_result type, Item * warn_item,
+  static cmp_item* get_comparator(Item_cmp type, Item * warn_item,
                                   CHARSET_INFO *cs);
   virtual cmp_item *make_same()= 0;
   virtual void store_value_by_template(cmp_item *tmpl, Item *item)
@@ -1265,17 +1266,17 @@ class cmp_item_sort_string_in_static :public cmp_item_string
 class Item_func_case :public Item_func_hybrid_field_type
 {
   int first_expr_num, else_expr_num;
-  enum Item_result left_result_type;
+  enum Item_cmp left_cmp_type;
   String tmp_value;
   uint ncases;
-  Item_result cmp_type;
+  Item_cmp cmp_type;
   DTCollation cmp_collation;
   cmp_item *cmp_items[6]; /* For all result types */
   cmp_item *case_item;
 public:
   Item_func_case(List<Item> &list, Item *first_expr_arg, Item *else_expr_arg)
     :Item_func_hybrid_field_type(), first_expr_num(-1), else_expr_num(-1),
-    left_result_type(INT_RESULT), case_item(0)
+    left_cmp_type(INT_CMP), case_item(0)
   {
     ncases= list.elements;
     if (first_expr_arg)
@@ -1337,7 +1338,7 @@ class Item_func_in :public Item_func_opt_neg
     and can be used safely as comparisons for key conditions
   */
   bool arg_types_compatible;
-  Item_result left_result_type;
+  Item_cmp left_cmp_type;
   cmp_item *cmp_items[6]; /* One cmp_item for each result type */
   DTCollation cmp_collation;
 
@@ -1359,7 +1360,7 @@ class Item_func_in :public Item_func_opt_neg
     Item_int_func::cleanup();
     delete array;
     array= 0;
-    for (i= 0; i <= (uint)TIME_RESULT; i++)
+    for (i= 0; i <= (uint)TIME_CMP; i++)
     {
       delete cmp_items[i];
       cmp_items[i]= 0;
@@ -1721,7 +1722,6 @@ class Item_cond :public Item_bool_func
   Item *transform(Item_transformer transformer, uchar *arg);
   void traverse_cond(Cond_traverser, void *arg, traverse_order order);
   void neg_arguments(THD *thd);
-  enum_field_types field_type() const { return MYSQL_TYPE_LONGLONG; }
   bool subst_argument_checker(uchar **arg) { return TRUE; }
   Item *compile(Item_analyzer analyzer, uchar **arg_p,
                 Item_transformer transformer, uchar *arg_t);
diff --git a/sql/item_func.cc b/sql/item_func.cc
index 25c0197..4001ac2 100644
--- a/sql/item_func.cc
+++ b/sql/item_func.cc
@@ -542,7 +542,6 @@ Field *Item_func::tmp_table_field(TABLE *table)
     break;
   case ROW_RESULT:
   case TIME_RESULT:
-  case IMPOSSIBLE_RESULT:
     // This case should never be chosen
     DBUG_ASSERT(0);
     field= 0;
@@ -803,36 +802,36 @@ void Item_num_op::fix_length_and_dec(void)
   DBUG_ENTER("Item_num_op::fix_length_and_dec");
   DBUG_PRINT("info", ("name %s", func_name()));
   DBUG_ASSERT(arg_count == 2);
-  Item_result r0= args[0]->cast_to_int_type();
-  Item_result r1= args[1]->cast_to_int_type();
+  Item_cmp r0= args[0]->cast_to_int_type();
+  Item_cmp r1= args[1]->cast_to_int_type();
 
-  if (r0 == REAL_RESULT || r1 == REAL_RESULT ||
-      r0 == STRING_RESULT || r1 ==STRING_RESULT)
+  if (r0 == REAL_CMP || r1 == REAL_CMP ||
+      r0 == STRING_CMP || r1 ==STRING_CMP)
   {
     count_real_length();
     max_length= float_length(decimals);
-    cached_result_type= REAL_RESULT;
+    Result_type::set(REAL_RESULT);
   }
-  else if (r0 == DECIMAL_RESULT || r1 == DECIMAL_RESULT ||
-           r0 == TIME_RESULT || r1 == TIME_RESULT)
+  else if (r0 == DECIMAL_CMP || r1 == DECIMAL_CMP ||
+           r0 == TIME_CMP || r1 == TIME_CMP)
   {
-    cached_result_type= DECIMAL_RESULT;
+    Result_type::set(DECIMAL_RESULT);
     result_precision();
     fix_decimals();
-    if ((r0 == TIME_RESULT || r1 == TIME_RESULT) && decimals == 0)
-      cached_result_type= INT_RESULT;
+    if ((r0 == TIME_CMP || r1 == TIME_CMP) && decimals == 0)
+      Result_type::set(INT_RESULT);
   }
   else
   {
-    DBUG_ASSERT(r0 == INT_RESULT && r1 == INT_RESULT);
-    cached_result_type=INT_RESULT;
+    DBUG_ASSERT(r0 == INT_CMP && r1 == INT_CMP);
+    Result_type::set(INT_RESULT);
     result_precision();
     decimals= 0;
   }
   DBUG_PRINT("info", ("Type: %s",
-             (cached_result_type == REAL_RESULT ? "REAL_RESULT" :
-              cached_result_type == DECIMAL_RESULT ? "DECIMAL_RESULT" :
-              cached_result_type == INT_RESULT ? "INT_RESULT" :
+             (result_type() == REAL_RESULT ? "REAL_RESULT" :
+              result_type() == DECIMAL_RESULT ? "DECIMAL_RESULT" :
+              result_type() == INT_RESULT ? "INT_RESULT" :
               "--ILLEGAL!!!--")));
   DBUG_VOID_RETURN;
 }
@@ -848,31 +847,33 @@ void Item_func_num1::fix_length_and_dec()
 {
   DBUG_ENTER("Item_func_num1::fix_length_and_dec");
   DBUG_PRINT("info", ("name %s", func_name()));
-  switch (cached_result_type= args[0]->cast_to_int_type()) {
-  case INT_RESULT:
+  switch (args[0]->cast_to_int_type()) {
+  case INT_CMP:
     max_length= args[0]->max_length;
     unsigned_flag= args[0]->unsigned_flag;
+    Result_type::set(INT_RESULT);
     break;
-  case STRING_RESULT:
-  case REAL_RESULT:
-    cached_result_type= REAL_RESULT;
+  case STRING_CMP:
+  case REAL_CMP:
+    Result_type::set(REAL_RESULT);
     decimals= args[0]->decimals; // Preserve NOT_FIXED_DEC
     max_length= float_length(decimals);
     break;
-  case TIME_RESULT:
-    cached_result_type= DECIMAL_RESULT;
-  case DECIMAL_RESULT:
+  case TIME_CMP:
+  case DECIMAL_CMP:
+    Result_type::set(DECIMAL_RESULT);
     decimals= args[0]->decimal_scale(); // Do not preserve NOT_FIXED_DEC
     max_length= args[0]->max_length;
     break;
-  case ROW_RESULT:
-  case IMPOSSIBLE_RESULT:
+  case ROW_CMP:
+  case IMPOSSIBLE_CMP:
+    Result_type::set(REAL_RESULT);
     DBUG_ASSERT(0);
   }
   DBUG_PRINT("info", ("Type: %s",
-                      (cached_result_type == REAL_RESULT ? "REAL_RESULT" :
-                       cached_result_type == DECIMAL_RESULT ? "DECIMAL_RESULT" :
-                       cached_result_type == INT_RESULT ? "INT_RESULT" :
+                      (result_type() == REAL_RESULT ? "REAL_RESULT" :
+                       result_type() == DECIMAL_RESULT ? "DECIMAL_RESULT" :
+                       result_type() == INT_RESULT ? "INT_RESULT" :
                        "--ILLEGAL!!!--")));
   DBUG_VOID_RETURN;
 }
@@ -881,7 +882,7 @@ void Item_func_num1::fix_length_and_dec()
 String *Item_func_hybrid_result_type::val_str(String *str)
 {
   DBUG_ASSERT(fixed == 1);
-  switch (cached_result_type) {
+  switch (Result_type::type()) {
   case DECIMAL_RESULT:
   {
     my_decimal decimal_value, *val;
@@ -927,7 +928,6 @@ String *Item_func_hybrid_result_type::val_str(String *str)
     return str_op(&str_value);
   case TIME_RESULT:
   case ROW_RESULT:
-  case IMPOSSIBLE_RESULT:
     DBUG_ASSERT(0);
   }
   return str;
@@ -937,7 +937,7 @@ String *Item_func_hybrid_result_type::val_str(String *str)
 double Item_func_hybrid_result_type::val_real()
 {
   DBUG_ASSERT(fixed == 1);
-  switch (cached_result_type) {
+  switch (Result_type::type()) {
   case DECIMAL_RESULT:
   {
     my_decimal decimal_value, *val;
@@ -976,7 +976,6 @@ double Item_func_hybrid_result_type::val_real()
   }
   case TIME_RESULT:
   case ROW_RESULT:
-  case IMPOSSIBLE_RESULT:
     DBUG_ASSERT(0);
   }
   return 0.0;
@@ -986,7 +985,7 @@ double Item_func_hybrid_result_type::val_real()
 longlong Item_func_hybrid_result_type::val_int()
 {
   DBUG_ASSERT(fixed == 1);
-  switch (cached_result_type) {
+  switch (Result_type::type()) {
   case DECIMAL_RESULT:
   {
     my_decimal decimal_value, *val;
@@ -1025,7 +1024,6 @@ longlong Item_func_hybrid_result_type::val_int()
   }
   case TIME_RESULT:
   case ROW_RESULT:
-  case IMPOSSIBLE_RESULT:
     DBUG_ASSERT(0);
   }
   return 0;
@@ -1036,7 +1034,7 @@ my_decimal *Item_func_hybrid_result_type::val_decimal(my_decimal *decimal_value)
 {
   my_decimal *val= decimal_value;
   DBUG_ASSERT(fixed == 1);
-  switch (cached_result_type) {
+  switch (Result_type::type()) {
   case DECIMAL_RESULT:
     val= decimal_op(decimal_value);
     break;
@@ -1077,7 +1075,6 @@ my_decimal *Item_func_hybrid_result_type::val_decimal(my_decimal *decimal_value)
   }  
   case ROW_RESULT:
   case TIME_RESULT:
-  case IMPOSSIBLE_RESULT:
     DBUG_ASSERT(0);
   }
   return val;
@@ -1088,7 +1085,7 @@ bool Item_func_hybrid_result_type::get_date(MYSQL_TIME *ltime,
                                             ulonglong fuzzydate)
 {
   DBUG_ASSERT(fixed == 1);
-  switch (cached_result_type) {
+  switch (Result_type::type()) {
   case DECIMAL_RESULT:
   {
     my_decimal value, *res;
@@ -1131,7 +1128,6 @@ bool Item_func_hybrid_result_type::get_date(MYSQL_TIME *ltime,
   }
   case ROW_RESULT:
   case TIME_RESULT:
-  case IMPOSSIBLE_RESULT:
     DBUG_ASSERT(0);
   }
 
@@ -1195,7 +1191,7 @@ longlong Item_func_signed::val_int()
   longlong value;
   int error;
 
-  if (args[0]->cast_to_int_type() != STRING_RESULT)
+  if (args[0]->cast_to_int_type() != STRING_CMP)
   {
     value= args[0]->val_int();
     null_value= args[0]->null_value; 
@@ -1239,7 +1235,7 @@ longlong Item_func_unsigned::val_int()
   longlong value;
   int error;
 
-  if (args[0]->cast_to_int_type() == DECIMAL_RESULT)
+  if (args[0]->cast_to_int_type() == DECIMAL_CMP)
   {
     my_decimal tmp, *dec= args[0]->val_decimal(&tmp);
     if (!(null_value= args[0]->null_value))
@@ -1258,7 +1254,7 @@ longlong Item_func_unsigned::val_int()
       goto err;                                 // Warn about overflow
     return value;
   }
-  else if (args[0]->cast_to_int_type() != STRING_RESULT)
+  else if (args[0]->cast_to_int_type() != STRING_CMP)
   {
     value= args[0]->val_int();
     null_value= args[0]->null_value; 
@@ -1853,7 +1849,7 @@ void Item_func_div::fix_length_and_dec()
   DBUG_ENTER("Item_func_div::fix_length_and_dec");
   prec_increment= current_thd->variables.div_precincrement;
   Item_num_op::fix_length_and_dec();
-  switch (cached_result_type) {
+  switch (Result_type::type()) {
   case REAL_RESULT:
   {
     decimals=MY_MAX(args[0]->decimals,args[1]->decimals)+prec_increment;
@@ -1869,7 +1865,7 @@ void Item_func_div::fix_length_and_dec()
     break;
   }
   case INT_RESULT:
-    cached_result_type= DECIMAL_RESULT;
+    Result_type::set(DECIMAL_RESULT);
     DBUG_PRINT("info", ("Type changed: DECIMAL_RESULT"));
     result_precision();
     break;
@@ -1880,7 +1876,6 @@ void Item_func_div::fix_length_and_dec()
   case STRING_RESULT:
   case ROW_RESULT:
   case TIME_RESULT:
-  case IMPOSSIBLE_RESULT:
     DBUG_ASSERT(0);
   }
   maybe_null= 1; // devision by zero
@@ -2117,7 +2112,7 @@ void Item_func_neg::fix_length_and_dec()
     Use val() to get value as arg_type doesn't mean that item is
     Item_int or Item_real due to existence of Item_param.
   */
-  if (cached_result_type == INT_RESULT && args[0]->const_item())
+  if (Result_type::type() == INT_RESULT && args[0]->const_item())
   {
     longlong val= args[0]->val_int();
     if ((ulonglong) val >= (ulonglong) LONGLONG_MIN &&
@@ -2128,7 +2123,7 @@ void Item_func_neg::fix_length_and_dec()
         Ensure that result is converted to DECIMAL, as longlong can't hold
         the negated number
       */
-      cached_result_type= DECIMAL_RESULT;
+      Result_type::set(DECIMAL_RESULT);
       DBUG_PRINT("info", ("Type changed: DECIMAL_RESULT"));
     }
   }
@@ -2430,15 +2425,15 @@ void Item_func_int_val::fix_length_and_dec()
   set_if_smaller(max_length,tmp);
   decimals= 0;
 
-  switch (cached_result_type= args[0]->cast_to_int_type())
+  switch (args[0]->cast_to_int_type())
   {
-  case STRING_RESULT:
-  case REAL_RESULT:
-    cached_result_type= REAL_RESULT;
+  case STRING_CMP:
+  case REAL_CMP:
+    Result_type::set(REAL_RESULT);
     max_length= float_length(decimals);
     break;
-  case INT_RESULT:
-  case TIME_RESULT:
+  case INT_CMP:
+  case TIME_CMP:
   case DECIMAL_RESULT:
     /*
       -2 because in most high position can't be used any digit for longlong
@@ -2447,22 +2442,23 @@ void Item_func_int_val::fix_length_and_dec()
     if ((args[0]->max_length - args[0]->decimals) >=
         (DECIMAL_LONGLONG_DIGITS - 2))
     {
-      cached_result_type= DECIMAL_RESULT;
+      Result_type::set(DECIMAL_RESULT);
     }
     else
     {
       unsigned_flag= args[0]->unsigned_flag;
-      cached_result_type= INT_RESULT;
+      Result_type::set(INT_RESULT);
     }
     break;
-  case ROW_RESULT:
-  case IMPOSSIBLE_RESULT:
+  case ROW_CMP:
+  case IMPOSSIBLE_CMP:
+    Result_type::set(REAL_RESULT);
     DBUG_ASSERT(0);
   }
   DBUG_PRINT("info", ("Type: %s",
-                      (cached_result_type == REAL_RESULT ? "REAL_RESULT" :
-                       cached_result_type == DECIMAL_RESULT ? "DECIMAL_RESULT" :
-                       cached_result_type == INT_RESULT ? "INT_RESULT" :
+                      (result_type() == REAL_RESULT ? "REAL_RESULT" :
+                       result_type() == DECIMAL_RESULT ? "DECIMAL_RESULT" :
+                       result_type() == INT_RESULT ? "INT_RESULT" :
                        "--ILLEGAL!!!--")));
 
   DBUG_VOID_RETURN;
@@ -2577,10 +2573,10 @@ void Item_func_round::fix_length_and_dec()
     if (args[0]->result_type() == DECIMAL_RESULT)
     {
       max_length++;
-      cached_result_type= DECIMAL_RESULT;
+      Result_type::set(DECIMAL_RESULT);
     }
     else
-      cached_result_type= REAL_RESULT;
+      Result_type::set(REAL_RESULT);
     return;
   }
 
@@ -2598,14 +2594,14 @@ void Item_func_round::fix_length_and_dec()
   {
     decimals= MY_MIN(decimals_to_set, NOT_FIXED_DEC);
     max_length= float_length(decimals);
-    cached_result_type= REAL_RESULT;
+    Result_type::set(REAL_RESULT);
     return;
   }
   
   switch (args[0]->result_type()) {
   case REAL_RESULT:
   case STRING_RESULT:
-    cached_result_type= REAL_RESULT;
+    Result_type::set(REAL_RESULT);
     decimals= MY_MIN(decimals_to_set, NOT_FIXED_DEC);
     max_length= float_length(decimals);
     break;
@@ -2616,14 +2612,14 @@ void Item_func_round::fix_length_and_dec()
                                        !val1_unsigned);
       max_length= args[0]->max_length + length_can_increase;
       /* Here we can keep INT_RESULT */
-      cached_result_type= INT_RESULT;
+      Result_type::set(INT_RESULT);
       decimals= 0;
       break;
     }
     /* fall through */
   case DECIMAL_RESULT:
   {
-    cached_result_type= DECIMAL_RESULT;
+    Result_type::set(DECIMAL_RESULT);
     decimals_to_set= MY_MIN(DECIMAL_MAX_SCALE, decimals_to_set);
     int decimals_delta= args[0]->decimals - decimals_to_set;
     int precision= args[0]->decimal_precision();
@@ -2638,7 +2634,6 @@ void Item_func_round::fix_length_and_dec()
   }
   case ROW_RESULT:
   case TIME_RESULT:
-  case IMPOSSIBLE_RESULT:
     DBUG_ASSERT(0); /* This result type isn't handled */
   }
 }
@@ -2872,7 +2867,7 @@ void Item_func_min_max::fix_length_and_dec()
   max_length=0;
   maybe_null=0;
   thd= current_thd;
-  cmp_type=args[0]->result_type();
+  m_cmp_type= (Item_cmp) args[0]->result_type();
 
   for (uint i=0 ; i < arg_count ; i++)
   {
@@ -2881,12 +2876,13 @@ void Item_func_min_max::fix_length_and_dec()
     set_if_bigger(max_int_part, args[i]->decimal_int_part());
     if (args[i]->maybe_null)
       maybe_null= 1;
-    cmp_type= item_cmp_type(cmp_type,args[i]->result_type());
+    m_cmp_type= item_cmp_type((Item_cmp) m_cmp_type,
+                              (Item_cmp) args[i]->result_type());
   }
-  if (cmp_type == STRING_RESULT)
+  if (m_cmp_type == STRING_CMP)
     agg_arg_charsets_for_string_result_with_comparison(collation,
                                                        args, arg_count);
-  else if ((cmp_type == DECIMAL_RESULT) || (cmp_type == INT_RESULT))
+  else if ((m_cmp_type == DECIMAL_CMP) || (m_cmp_type == INT_CMP))
   {
     collation.set_numeric();
     fix_char_length(my_decimal_precision_to_length_no_truncation(max_int_part +
@@ -2894,7 +2890,7 @@ void Item_func_min_max::fix_length_and_dec()
                                                                  decimals,
                                                                  unsigned_flag));
   }
-  else if (cmp_type == REAL_RESULT)
+  else if (m_cmp_type == REAL_CMP)
     fix_char_length(float_length(decimals));
 
   compare_as_dates= find_date_time_item(args, arg_count, 0);
@@ -2986,14 +2982,14 @@ String *Item_func_min_max::val_str(String *str)
   DBUG_ASSERT(fixed == 1);
   if (compare_as_dates)
     return val_string_from_date(str);
-  switch (cmp_type) {
-  case INT_RESULT:
+  switch (m_cmp_type) {
+  case INT_CMP:
     return val_string_from_int(str);
-  case DECIMAL_RESULT:
+  case DECIMAL_CMP:
     return val_string_from_decimal(str);
-  case REAL_RESULT:
+  case REAL_CMP:
     return val_string_from_real(str);
-  case STRING_RESULT:
+  case STRING_CMP:
   {
     String *UNINIT_VAR(res);
     for (uint i=0; i < arg_count ; i++)
@@ -3017,9 +3013,9 @@ String *Item_func_min_max::val_str(String *str)
     res->set_charset(collation.collation);
     return res;
   }
-  case ROW_RESULT:
-  case TIME_RESULT:
-  case IMPOSSIBLE_RESULT:
+  case ROW_CMP:
+  case TIME_CMP:
+  case IMPOSSIBLE_CMP:
     DBUG_ASSERT(0);                // This case should never be chosen
     return 0;
   }
@@ -3232,7 +3228,7 @@ longlong Item_func_field::val_int()
 {
   DBUG_ASSERT(fixed == 1);
 
-  if (cmp_type == STRING_RESULT)
+  if (cmp_type == STRING_CMP)
   {
     String *field;
     if (!(field= args[0]->val_str(&value)))
@@ -3244,7 +3240,7 @@ longlong Item_func_field::val_int()
         return (longlong) (i);
     }
   }
-  else if (cmp_type == INT_RESULT)
+  else if (cmp_type == INT_CMP)
   {
     longlong val= args[0]->val_int();
     if (args[0]->null_value)
@@ -3255,7 +3251,7 @@ longlong Item_func_field::val_int()
         return (longlong) (i);
     }
   }
-  else if (cmp_type == DECIMAL_RESULT)
+  else if (cmp_type == DECIMAL_CMP)
   {
     my_decimal dec_arg_buf, *dec_arg,
                dec_buf, *dec= args[0]->val_decimal(&dec_buf);
@@ -3286,10 +3282,10 @@ longlong Item_func_field::val_int()
 void Item_func_field::fix_length_and_dec()
 {
   maybe_null=0; max_length=3;
-  cmp_type= args[0]->result_type();
+  cmp_type= args[0]->cmp_type();
   for (uint i=1; i < arg_count ; i++)
-    cmp_type= item_cmp_type(cmp_type, args[i]->result_type());
-  if (cmp_type == STRING_RESULT)
+    cmp_type= item_cmp_type(cmp_type, (Item_cmp) args[i]->result_type());
+  if (cmp_type == STRING_CMP)
     agg_arg_charsets_for_comparison(cmp_collation, args, arg_count);
 }
 
@@ -3618,7 +3614,6 @@ void udf_handler::cleanup()
           break;
         case ROW_RESULT:
         case TIME_RESULT:
-        case IMPOSSIBLE_RESULT:
           DBUG_ASSERT(0);          // This case should never be chosen
           break;
         }
@@ -3693,7 +3688,6 @@ bool udf_handler::get_arguments()
       break;
     case ROW_RESULT:
     case TIME_RESULT:
-    case IMPOSSIBLE_RESULT:
       DBUG_ASSERT(0);              // This case should never be chosen
       break;
     }
@@ -4510,7 +4504,6 @@ longlong Item_func_benchmark::val_int()
       break;
     case ROW_RESULT:
     case TIME_RESULT:
-    case IMPOSSIBLE_RESULT:
       DBUG_ASSERT(0);              // This case should never be chosen
       return 0;
     }
@@ -4670,7 +4663,7 @@ user_var_entry *get_variable(HASH *hash, LEX_STRING &name,
       by Item_func_get_user_var (because that's not necessary).
     */
     entry->used_query_id=current_thd->query_id;
-    entry->type=STRING_RESULT;
+    entry->Result_type::set(STRING_RESULT);
     memcpy(entry->name.str, name.str, name.length+1);
     if (my_hash_insert(hash,(uchar*) entry))
     {
@@ -4742,7 +4735,7 @@ bool Item_func_set_user_var::fix_fields(THD *thd, Item **ref)
                          default_charset() : args[0]->collation.collation,
                          DERIVATION_IMPLICIT);
   collation.set(entry->collation.collation, DERIVATION_IMPLICIT);
-  cached_result_type= args[0]->result_type();
+  Result_type::set(args[0]->result_type());
   if (thd->lex->current_select)
   {
     /*
@@ -4895,7 +4888,7 @@ bool Item_func_set_user_var::register_field_in_bitmap(uchar *arg)
     entry->collation.set(cs, dv);
     entry->unsigned_flag= unsigned_arg;
   }
-  entry->type=type;
+  entry->Result_type::set(type);
   return 0;
 }
 
@@ -4911,7 +4904,7 @@ bool Item_func_set_user_var::register_field_in_bitmap(uchar *arg)
     result type of the variable
   */
   if ((null_value= args[0]->null_value) && null_item)
-    res_type= entry->type;                      // Don't change type of item
+    res_type= entry->type();              // Don't change type of item
   if (::update_hash(entry, (null_value= args[0]->null_value),
                     ptr, length, res_type, cs, dv, unsigned_arg))
   {
@@ -4929,7 +4922,7 @@ double user_var_entry::val_real(bool *null_value)
   if ((*null_value= (value == 0)))
     return 0.0;
 
-  switch (type) {
+  switch (type()) {
   case REAL_RESULT:
     return *(double*) value;
   case INT_RESULT:
@@ -4944,7 +4937,6 @@ double user_var_entry::val_real(bool *null_value)
     return my_atof(value);                      // This is null terminated
   case ROW_RESULT:
   case TIME_RESULT:
-  case IMPOSSIBLE_RESULT:
     DBUG_ASSERT(0);				// Impossible
     break;
   }
@@ -4959,7 +4951,7 @@ longlong user_var_entry::val_int(bool *null_value) const
   if ((*null_value= (value == 0)))
     return 0;
 
-  switch (type) {
+  switch (type()) {
   case REAL_RESULT:
     return (longlong) *(double*) value;
   case INT_RESULT:
@@ -4977,7 +4969,6 @@ longlong user_var_entry::val_int(bool *null_value) const
   }
   case ROW_RESULT:
   case TIME_RESULT:
-  case IMPOSSIBLE_RESULT:
     DBUG_ASSERT(0);				// Impossible
     break;
   }
@@ -4993,7 +4984,7 @@ String *user_var_entry::val_str(bool *null_value, String *str,
   if ((*null_value= (value == 0)))
     return (String*) 0;
 
-  switch (type) {
+  switch (type()) {
   case REAL_RESULT:
     str->set_real(*(double*) value, decimals, collation.collation);
     break;
@@ -5012,7 +5003,6 @@ String *user_var_entry::val_str(bool *null_value, String *str,
     break;
   case ROW_RESULT:
   case TIME_RESULT:
-  case IMPOSSIBLE_RESULT:
     DBUG_ASSERT(0);				// Impossible
     break;
   }
@@ -5026,7 +5016,7 @@ my_decimal *user_var_entry::val_decimal(bool *null_value, my_decimal *val)
   if ((*null_value= (value == 0)))
     return 0;
 
-  switch (type) {
+  switch (type()) {
   case REAL_RESULT:
     double2my_decimal(E_DEC_FATAL_ERROR, *(double*) value, val);
     break;
@@ -5041,7 +5031,6 @@ my_decimal *user_var_entry::val_decimal(bool *null_value, my_decimal *val)
     break;
   case ROW_RESULT:
   case TIME_RESULT:
-  case IMPOSSIBLE_RESULT:
     DBUG_ASSERT(0);				// Impossible
     break;
   }
@@ -5069,7 +5058,7 @@ my_decimal *user_var_entry::val_decimal(bool *null_value, my_decimal *val)
   if (use_result_field && !result_field)
     use_result_field= FALSE;
 
-  switch (cached_result_type) {
+  switch (Result_type::type()) {
   case REAL_RESULT:
   {
     save_result.vreal= use_result_field ? result_field->val_real() :
@@ -5100,7 +5089,6 @@ my_decimal *user_var_entry::val_decimal(bool *null_value, my_decimal *val)
   }
   case ROW_RESULT:
   case TIME_RESULT:
-  case IMPOSSIBLE_RESULT:
     DBUG_ASSERT(0);                // This case should never be chosen
     break;
   }
@@ -5135,7 +5123,6 @@ void Item_func_set_user_var::save_item_result(Item *item)
     break;
   case ROW_RESULT:
   case TIME_RESULT:
-  case IMPOSSIBLE_RESULT:
     DBUG_ASSERT(0);                // This case should never be chosen
     break;
   }
@@ -5164,7 +5151,7 @@ void Item_func_set_user_var::save_item_result(Item *item)
   bool res= 0;
   DBUG_ENTER("Item_func_set_user_var::update");
 
-  switch (cached_result_type) {
+  switch (Result_type::type()) {
   case REAL_RESULT:
   {
     res= update_hash((void*) &save_result.vreal,sizeof(save_result.vreal),
@@ -5203,7 +5190,6 @@ void Item_func_set_user_var::save_item_result(Item *item)
   }
   case ROW_RESULT:
   case TIME_RESULT:
-  case IMPOSSIBLE_RESULT:
     DBUG_ASSERT(0);                // This case should never be chosen
     break;
   }
@@ -5589,7 +5575,7 @@ longlong Item_func_get_user_var::val_int()
   user_var_event->value= (char*) user_var_event +
     ALIGN_SIZE(sizeof(BINLOG_USER_VAR_EVENT));
   user_var_event->user_var_event= var_entry;
-  user_var_event->type= var_entry->type;
+  user_var_event->type= var_entry->type();
   user_var_event->charset_number= var_entry->collation.collation->number;
   user_var_event->unsigned_flag= var_entry->unsigned_flag;
   if (!var_entry->value)
@@ -5634,12 +5620,12 @@ void Item_func_get_user_var::fix_length_and_dec()
   */
   if (!error && var_entry)
   {
-    m_cached_result_type= var_entry->type;
+    Result_type::set(var_entry);
     unsigned_flag= var_entry->unsigned_flag;
     max_length= var_entry->length;
 
     collation.set(var_entry->collation);
-    switch (m_cached_result_type) {
+    switch (Result_type::type()) {
     case REAL_RESULT:
       fix_char_length(DBL_DIG + 8);
       break;
@@ -5656,7 +5642,6 @@ void Item_func_get_user_var::fix_length_and_dec()
       break;
     case ROW_RESULT:                            // Keep compiler happy
     case TIME_RESULT:
-    case IMPOSSIBLE_RESULT:
       DBUG_ASSERT(0);                // This case should never be chosen
       break;
     }
@@ -5665,7 +5650,7 @@ void Item_func_get_user_var::fix_length_and_dec()
   {
     collation.set(&my_charset_bin, DERIVATION_IMPLICIT);
     null_value= 1;
-    m_cached_result_type= STRING_RESULT;
+    Result_type::set(STRING_RESULT);
     max_length= MAX_BLOB_WIDTH;
   }
 }
@@ -5677,12 +5662,6 @@ bool Item_func_get_user_var::const_item() const
 }
 
 
-enum Item_result Item_func_get_user_var::result_type() const
-{
-  return m_cached_result_type;
-}
-
-
 void Item_func_get_user_var::print(String *str, enum_query_type query_type)
 {
   str->append(STRING_WITH_LEN("(@"));
@@ -5725,7 +5704,7 @@ bool Item_user_var_as_out_param::fix_fields(THD *thd, Item **ref)
   if (Item::fix_fields(thd, ref) ||
       !(entry= get_variable(&thd->user_vars, name, 1)))
     return TRUE;
-  entry->type= STRING_RESULT;
+  entry->Result_type::set(STRING_RESULT);
   /*
     Let us set the same collation which is used for loading
     of fields in LOAD DATA INFILE.
diff --git a/sql/item_func.h b/sql/item_func.h
index 6c9d7e8..1a98cc5 100644
--- a/sql/item_func.h
+++ b/sql/item_func.h
@@ -400,7 +400,7 @@ class Item_func :public Item_result_field
 };
 
 
-class Item_real_func :public Item_func
+class Item_real_func :public Item_func, public Type_handler_double
 {
 public:
   Item_real_func() :Item_func() { collation.set_numeric(); }
@@ -411,33 +411,33 @@ class Item_real_func :public Item_func
   my_decimal *val_decimal(my_decimal *decimal_value);
   longlong val_int()
     { DBUG_ASSERT(fixed == 1); return (longlong) rint(val_real()); }
-  enum Item_result result_type () const { return REAL_RESULT; }
   void fix_length_and_dec()
   { decimals= NOT_FIXED_DEC; max_length= float_length(decimals); }
 };
 
 
-class Item_func_hybrid_result_type: public Item_func
+class Item_func_hybrid_result_type: public Item_func,
+                                    public Result_type
 {
-protected:
-  Item_result cached_result_type;
-
 public:
-  Item_func_hybrid_result_type() :Item_func(), cached_result_type(REAL_RESULT)
+  Item_func_hybrid_result_type() :Item_func(), Result_type()
   { collation.set_numeric(); }
-  Item_func_hybrid_result_type(Item *a) :Item_func(a), cached_result_type(REAL_RESULT)
+  Item_func_hybrid_result_type(Item *a) :Item_func(a), Result_type()
   { collation.set_numeric(); }
   Item_func_hybrid_result_type(Item *a,Item *b)
-    :Item_func(a,b), cached_result_type(REAL_RESULT)
+    :Item_func(a,b), Result_type()
   { collation.set_numeric(); }
   Item_func_hybrid_result_type(Item *a,Item *b,Item *c)
-    :Item_func(a,b,c), cached_result_type(REAL_RESULT)
+    :Item_func(a,b,c), Result_type()
   { collation.set_numeric(); }
   Item_func_hybrid_result_type(List<Item> &list)
-    :Item_func(list), cached_result_type(REAL_RESULT)
+    :Item_func(list), Result_type()
   { collation.set_numeric(); }
 
-  enum Item_result result_type () const { return cached_result_type; }
+  enum Item_result result_type () const { return Result_type::type(); }
+  enum_field_types field_type() const
+  { return Result_type(result_type()).field_type(*this); }
+  Item_cmp cmp_type() const { return cmp_type_by_field_type(); }
 
   double val_real();
   longlong val_int();
@@ -572,7 +572,7 @@ class Item_num_op :public Item_func_numhybrid
 };
 
 
-class Item_int_func :public Item_func
+class Item_int_func :public Item_func, public Type_handler_longlong
 {
 protected:
   bool sargable;
@@ -593,7 +593,6 @@ class Item_int_func :public Item_func
   { collation.set_numeric(); sargable= false; }
   double val_real();
   String *val_str(String*str);
-  enum Item_result result_type () const { return INT_RESULT; }
   void fix_length_and_dec() {}
   bool count_sargable_conds(uchar *arg);
 };
@@ -646,7 +645,8 @@ class Item_func_unsigned :public Item_func_signed
 };
 
 
-class Item_decimal_typecast :public Item_func
+class Item_decimal_typecast :public Item_func,
+                             public Type_handler_newdecimal
 {
   my_decimal decimal_value;
 public:
@@ -661,8 +661,6 @@ class Item_decimal_typecast :public Item_func
   double val_real();
   longlong val_int();
   my_decimal *val_decimal(my_decimal*);
-  enum Item_result result_type () const { return DECIMAL_RESULT; }
-  enum_field_types field_type() const { return MYSQL_TYPE_NEWDECIMAL; }
   void fix_length_and_dec() {}
   const char *func_name() const { return "decimal_typecast"; }
   virtual void print(String *str, enum_query_type query_type);
@@ -678,7 +676,6 @@ class Item_double_typecast :public Item_real_func
     max_length= (uint32) len;
   }
   double val_real();
-  enum_field_types field_type() const { return MYSQL_TYPE_DOUBLE; }
   void fix_length_and_dec() { maybe_null= 1; }
   const char *func_name() const { return "double_typecast"; }
   virtual void print(String *str, enum_query_type query_type);
@@ -1048,7 +1045,7 @@ class Item_func_units :public Item_real_func
 
 class Item_func_min_max :public Item_func
 {
-  Item_result cmp_type;
+  Item_cmp m_cmp_type;
   String tmp_value;
   int cmp_sign;
   /* An item used for issuing warnings while string to DATETIME conversion. */
@@ -1058,15 +1055,21 @@ class Item_func_min_max :public Item_func
   enum_field_types cached_field_type;
 public:
   Item_func_min_max(List<Item> &list,int cmp_sign_arg) :Item_func(list),
-    cmp_type(INT_RESULT), cmp_sign(cmp_sign_arg), compare_as_dates(0) {}
+    m_cmp_type(INT_CMP), cmp_sign(cmp_sign_arg), compare_as_dates(0) {}
   double val_real();
   longlong val_int();
   String *val_str(String *);
   my_decimal *val_decimal(my_decimal *);
   bool get_date(MYSQL_TIME *res, ulonglong fuzzy_date);
   void fix_length_and_dec();
-  enum Item_result result_type () const { return cmp_type; }
+  enum Item_result result_type () const
+  {
+    DBUG_ASSERT(m_cmp_type != TIME_CMP);
+    DBUG_ASSERT(m_cmp_type != IMPOSSIBLE_CMP);
+    return (Item_result) m_cmp_type;
+  }
   enum_field_types field_type() const { return cached_field_type; }
+  Item_cmp cmp_type() const { return cmp_type_by_field_type(); }
 };
 
 class Item_func_min :public Item_func_min_max
@@ -1104,6 +1107,10 @@ class Item_func_rollup_const :public Item_func
   const char *func_name() const { return "rollup_const"; }
   bool const_item() const { return 0; }
   Item_result result_type() const { return args[0]->result_type(); }
+  enum_field_types field_type() const
+  { return Result_type(result_type()).field_type(*this); }
+  Item_cmp cmp_type() const { return cmp_type_by_field_type(); }
+
   void fix_length_and_dec()
   {
     collation= args[0]->collation;
@@ -1171,7 +1178,7 @@ class Item_func_locate :public Item_int_func
 class Item_func_field :public Item_int_func
 {
   String value,tmp;
-  Item_result cmp_type;
+  Item_cmp cmp_type;
   DTCollation cmp_collation;
 public:
   Item_func_field(List<Item> &list) :Item_int_func(list) {}
@@ -1421,14 +1428,14 @@ class Item_udf_func :public Item_func
     }
   }
   void cleanup();
-  Item_result result_type () const { return udf.result_type(); }
   table_map not_null_tables() const { return 0; }
   bool is_expensive() { return 1; }
   virtual void print(String *str, enum_query_type query_type);
 };
 
 
-class Item_func_udf_float :public Item_udf_func
+class Item_func_udf_float :public Item_udf_func,
+                           public Type_handler_double
 {
  public:
   Item_func_udf_float(udf_func *udf_arg)
@@ -1455,7 +1462,8 @@ class Item_func_udf_float :public Item_udf_func
 };
 
 
-class Item_func_udf_int :public Item_udf_func
+class Item_func_udf_int :public Item_udf_func,
+                         public Type_handler_longlong
 {
 public:
   Item_func_udf_int(udf_func *udf_arg)
@@ -1466,12 +1474,12 @@ class Item_func_udf_int :public Item_udf_func
   longlong val_int();
   double val_real() { return (double) Item_func_udf_int::val_int(); }
   String *val_str(String *str);
-  enum Item_result result_type () const { return INT_RESULT; }
   void fix_length_and_dec() { decimals= 0; max_length= 21; }
 };
 
 
-class Item_func_udf_decimal :public Item_udf_func
+class Item_func_udf_decimal :public Item_udf_func,
+                             public Type_handler_newdecimal
 {
 public:
   Item_func_udf_decimal(udf_func *udf_arg)
@@ -1482,12 +1490,12 @@ class Item_func_udf_decimal :public Item_udf_func
   double val_real();
   my_decimal *val_decimal(my_decimal *);
   String *val_str(String *str);
-  enum Item_result result_type () const { return DECIMAL_RESULT; }
   void fix_length_and_dec() { fix_num_length_and_dec(); }
 };
 
 
-class Item_func_udf_str :public Item_udf_func
+class Item_func_udf_str :public Item_udf_func,
+                         public Type_handler_string_hybrid
 {
 public:
   Item_func_udf_str(udf_func *udf_arg)
@@ -1519,7 +1527,7 @@ class Item_func_udf_str :public Item_udf_func
     string2my_decimal(E_DEC_FATAL_ERROR, res, dec_buf);
     return dec_buf;
   }
-  enum Item_result result_type () const { return STRING_RESULT; }
+  enum_field_types field_type() const { return string_field_type(); }
   void fix_length_and_dec();
 };
 
@@ -1657,9 +1665,9 @@ class Item_master_gtid_wait :public Item_int_func
 
 class user_var_entry;
 
-class Item_func_set_user_var :public Item_func
+class Item_func_set_user_var :public Item_func,
+                              public Type_handler_hybrid_result
 {
-  enum Item_result cached_result_type;
   user_var_entry *entry;
   /*
     The entry_thread_id variable is used:
@@ -1687,11 +1695,13 @@ class Item_func_set_user_var :public Item_func
 public:
   LEX_STRING name; // keep it public
   Item_func_set_user_var(LEX_STRING a,Item *b)
-    :Item_func(b), cached_result_type(INT_RESULT),
+    :Item_func(b),
+     Type_handler_hybrid_result(INT_RESULT),
      entry(NULL), entry_thread_id(0), name(a)
   {}
   Item_func_set_user_var(THD *thd, Item_func_set_user_var *item)
-    :Item_func(thd, item), cached_result_type(item->cached_result_type),
+    :Item_func(thd, item),
+    Type_handler_hybrid_result(item->result_type()),
     entry(item->entry), entry_thread_id(item->entry_thread_id),
     value(item->value), decimal_buff(item->decimal_buff),
     null_item(item->null_item), save_result(item->save_result),
@@ -1716,7 +1726,9 @@ class Item_func_set_user_var :public Item_func
   bool check(bool use_result_field);
   void save_item_result(Item *item);
   bool update();
-  enum Item_result result_type () const { return cached_result_type; }
+  enum_field_types field_type() const
+  { return Type_handler_hybrid_result::field_type(*this); }
+
   bool fix_fields(THD *thd, Item **ref);
   void fix_length_and_dec();
   table_map used_tables() const
@@ -1745,15 +1757,14 @@ class Item_func_set_user_var :public Item_func
 
 
 class Item_func_get_user_var :public Item_func,
-                              private Settable_routine_parameter
+                              private Settable_routine_parameter,
+                              public Type_handler_hybrid_result
 {
   user_var_entry *var_entry;
-  Item_result m_cached_result_type;
-
 public:
   LEX_STRING name; // keep it public
-  Item_func_get_user_var(LEX_STRING a):
-    Item_func(), m_cached_result_type(STRING_RESULT), name(a) {}
+  Item_func_get_user_var(LEX_STRING a)
+   :Item_func(), Type_handler_hybrid_result(STRING_RESULT), name(a) {}
   enum Functype functype() const { return GUSERVAR_FUNC; }
   LEX_STRING get_name() { return name; }
   double val_real();
@@ -1762,7 +1773,8 @@ class Item_func_get_user_var :public Item_func,
   String *val_str(String* str);
   void fix_length_and_dec();
   virtual void print(String *str, enum_query_type query_type);
-  enum Item_result result_type() const;
+  enum_field_types field_type() const
+  { return Type_handler_hybrid_result::field_type(*this); }
   /*
     We must always return variables as strings to guard against selects of type
     select @t1:=1,@t1,@t:="hello",@t from foo where (@t1:= t2.b)
@@ -1792,7 +1804,8 @@ class Item_func_get_user_var :public Item_func,
   in List<Item> and desire to place this code somewhere near other functions
   working with user variables.
 */
-class Item_user_var_as_out_param :public Item
+class Item_user_var_as_out_param :public Item,
+                                  public Type_handler_string_hybrid
 {
   LEX_STRING name;
   user_var_entry *entry;
@@ -1801,6 +1814,7 @@ class Item_user_var_as_out_param :public Item
   { set_name(a.str, 0, system_charset_info); }
   /* We should return something different from FIELD_ITEM here */
   enum Type type() const { return STRING_ITEM;}
+  enum_field_types field_type() const { return string_field_type(); }
   double val_real();
   longlong val_int();
   String *val_str(String *str);
@@ -1843,6 +1857,7 @@ class Item_func_get_system_var :public Item_func
   table_map used_tables() const { return 0; }
   enum Item_result result_type() const;
   enum_field_types field_type() const;
+  Item_cmp cmp_type() const { return cmp_type_by_field_type(); }
   double val_real();
   longlong val_int();
   String* val_str(String*);
@@ -2064,6 +2079,7 @@ class Item_func_sp :public Item_func
   void make_field(Send_field *tmp_field);
 
   Item_result result_type() const;
+  Item_cmp cmp_type() const { return cmp_type_by_field_type(); }
 
   longlong val_int()
   {
@@ -2183,6 +2199,7 @@ class Item_func_last_value :public Item_func
   const char *func_name() const { return "last_value"; }
   table_map not_null_tables() const { return 0; }
   enum_field_types field_type() const { return last_value->field_type(); }
+  Item_cmp cmp_type() const { return cmp_type_by_field_type(); }
   bool const_item() const { return 0; }
   void evaluate_sideeffects();
   void update_used_tables()
diff --git a/sql/item_row.h b/sql/item_row.h
index aa56068..c033f60 100644
--- a/sql/item_row.h
+++ b/sql/item_row.h
@@ -17,7 +17,7 @@
    along with this program; if not, write to the Free Software
    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA */
 
-class Item_row: public Item
+class Item_row: public Item, public Type_handler_row
 {
   Item **items;
   table_map used_tables_cache, not_null_tables_cache;
@@ -69,8 +69,6 @@ class Item_row: public Item
   void split_sum_func(THD *thd, Item **ref_pointer_array, List<Item> &fields);
   table_map used_tables() const { return used_tables_cache; };
   bool const_item() const { return const_item_cache; };
-  enum Item_result result_type() const { return ROW_RESULT; }
-  Item_result cmp_type() const { return ROW_RESULT; }
   void update_used_tables();
   table_map not_null_tables() const { return not_null_tables_cache; }
   virtual void print(String *str, enum_query_type query_type);
diff --git a/sql/item_strfunc.h b/sql/item_strfunc.h
index 8377a20..9b1dccc 100644
--- a/sql/item_strfunc.h
+++ b/sql/item_strfunc.h
@@ -29,7 +29,8 @@
 
 class MY_LOCALE;
 
-class Item_str_func :public Item_func
+class Item_str_func :public Item_func,
+                     public Type_handler_string_hybrid
 {
 protected:
   /**
@@ -59,7 +60,7 @@ class Item_str_func :public Item_func
   longlong val_int();
   double val_real();
   my_decimal *val_decimal(my_decimal *);
-  enum Item_result result_type () const { return STRING_RESULT; }
+  enum_field_types field_type() const { return string_field_type(); }
   void left_right_max_length();
   bool fix_fields(THD *thd, Item **ref);
 };
diff --git a/sql/item_subselect.cc b/sql/item_subselect.cc
index a62bb17..8d7c4e1 100644
--- a/sql/item_subselect.cc
+++ b/sql/item_subselect.cc
@@ -1113,7 +1113,7 @@ enum Item_result Item_singlerow_subselect::result_type() const
   return engine->type();
 }
 
-enum Item_result Item_singlerow_subselect::cmp_type() const
+enum Item_cmp Item_singlerow_subselect::cmp_type() const
 {
   return engine->cmptype();
 }
@@ -3527,12 +3527,13 @@ void subselect_engine::set_row(List<Item> &item_list, Item_cache **row)
 {
   Item *sel_item;
   List_iterator_fast<Item> li(item_list);
-  cmp_type= res_type= STRING_RESULT;
+  cmp_type= STRING_CMP;
+  Result_type::set(STRING_RESULT);
   res_field_type= MYSQL_TYPE_VAR_STRING;
   for (uint i= 0; (sel_item= li++); i++)
   {
     item->max_length= sel_item->max_length;
-    res_type= sel_item->result_type();
+    Result_type::set(sel_item->result_type());
     cmp_type= sel_item->cmp_type();
     res_field_type= sel_item->field_type();
     item->decimals= sel_item->decimals;
@@ -3544,7 +3545,10 @@ void subselect_engine::set_row(List<Item> &item_list, Item_cache **row)
  //psergey-backport-timours:   row[i]->store(sel_item);
   }
   if (item_list.elements > 1)
-    cmp_type= res_type= ROW_RESULT;
+  {
+    cmp_type= ROW_CMP;
+    Result_type::set(ROW_RESULT);
+  }
 }
 
 void subselect_single_select_engine::fix_length_and_dec(Item_cache **row)
diff --git a/sql/item_subselect.h b/sql/item_subselect.h
index 92b269d..9efcb92 100644
--- a/sql/item_subselect.h
+++ b/sql/item_subselect.h
@@ -287,7 +287,7 @@ class Item_singlerow_subselect :public Item_subselect
   bool val_bool();
   bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate);
   enum Item_result result_type() const;
-  enum Item_result cmp_type() const;
+  enum Item_cmp cmp_type() const;
   enum_field_types field_type() const;
   void fix_length_and_dec();
 
@@ -337,7 +337,8 @@ class Item_maxmin_subselect :public Item_singlerow_subselect
 
 /* exists subselect */
 
-class Item_exists_subselect :public Item_subselect
+class Item_exists_subselect :public Item_subselect,
+                             public Type_handler_longlong
 {
 protected:
   Item_func_not *upper_not;
@@ -378,7 +379,6 @@ class Item_exists_subselect :public Item_subselect
   }
   void no_rows_in_result();
 
-  enum Item_result result_type() const { return INT_RESULT;}
   longlong val_int();
   double val_real();
   String *val_str(String*);
@@ -727,14 +727,14 @@ class Item_allany_subselect :public Item_in_subselect
 };
 
 
-class subselect_engine: public Sql_alloc
+class subselect_engine: public Sql_alloc,
+                        public Result_type /* type of results */
 {
 protected:
   select_result_interceptor *result; /* results storage class */
   THD *thd; /* pointer to current THD */
   Item_subselect *item; /* item, that use this engine */
-  enum Item_result res_type; /* type of results */
-  enum Item_result cmp_type; /* how to compare the results */
+  enum Item_cmp    cmp_type; /* how to compare the results */
   enum_field_types res_field_type; /* column type of the results */
   bool maybe_null; /* may be null (first item in select) */
 public:
@@ -746,10 +746,11 @@ class subselect_engine: public Sql_alloc
 
   subselect_engine(THD *thd_arg, Item_subselect *si,
                    select_result_interceptor *res)
+   :Result_type(STRING_RESULT)
   {
     result= res;
     item= si;
-    cmp_type= res_type= STRING_RESULT;
+    cmp_type= STRING_CMP;
     res_field_type= MYSQL_TYPE_VAR_STRING;
     maybe_null= 0;
     set_thd(thd_arg);
@@ -788,8 +789,8 @@ class subselect_engine: public Sql_alloc
   virtual int exec()= 0;
   virtual uint cols()= 0; /* return number of columns in select */
   virtual uint8 uncacheable()= 0; /* query is uncacheable */
-  enum Item_result type() { return res_type; }
-  enum Item_result cmptype() { return cmp_type; }
+  enum Item_result type() { return Result_type::type(); }
+  enum Item_cmp cmptype() { return cmp_type; }
   enum_field_types field_type() { return res_field_type; }
   virtual void exclude()= 0;
   virtual bool may_be_null() { return maybe_null; };
diff --git a/sql/item_sum.cc b/sql/item_sum.cc
index 2dadf8b..937588b 100644
--- a/sql/item_sum.cc
+++ b/sql/item_sum.cc
@@ -551,7 +551,6 @@ Field *Item_sum::create_tmp_field(bool group, TABLE *table,
     break;
   case ROW_RESULT:
   case TIME_RESULT:
-  case IMPOSSIBLE_RESULT:
     // This case should never be choosen
     DBUG_ASSERT(0);
     return 0;
@@ -1212,7 +1211,8 @@ my_decimal *Item_sum_int::val_decimal(my_decimal *decimal_value)
   decimals=item->decimals;
   with_subselect= args[0]->with_subselect;
 
-  switch (hybrid_type= item->result_type()) {
+  Result_type::set(item->result_type());
+  switch (Result_type::type()) {
   case INT_RESULT:
   case DECIMAL_RESULT:
   case STRING_RESULT:
@@ -1223,7 +1223,6 @@ my_decimal *Item_sum_int::val_decimal(my_decimal *decimal_value)
     break;
   case ROW_RESULT:
   case TIME_RESULT:
-  case IMPOSSIBLE_RESULT:
     DBUG_ASSERT(0);
   };
   setup_hybrid(args[0], NULL);
@@ -1237,7 +1236,7 @@ my_decimal *Item_sum_int::val_decimal(my_decimal *decimal_value)
   if (item->type() == Item::FIELD_ITEM)
     hybrid_field_type= ((Item_field*) item)->field->type();
   else
-    hybrid_field_type= Item::field_type();
+    hybrid_field_type= Result_type(result_type()).field_type(*this);
 
   if (check_sum_func(thd, ref))
     return TRUE;
@@ -1337,11 +1336,12 @@ Field *Item_sum_hybrid::create_tmp_field(bool group, TABLE *table,
   check if the following assignments are really needed
 */
 Item_sum_sum::Item_sum_sum(THD *thd, Item_sum_sum *item) 
-  :Item_sum_num(thd, item), hybrid_type(item->hybrid_type),
+  :Item_sum_num(thd, item),
+   Type_handler_hybrid_result(item), 
    curr_dec_buff(item->curr_dec_buff)
 {
   /* TODO: check if the following assignments are really needed */
-  if (hybrid_type == DECIMAL_RESULT)
+  if (Result_type::type() == DECIMAL_RESULT)
   {
     my_decimal2decimal(item->dec_buffs, dec_buffs);
     my_decimal2decimal(item->dec_buffs + 1, dec_buffs + 1);
@@ -1360,7 +1360,7 @@ void Item_sum_sum::clear()
 {
   DBUG_ENTER("Item_sum_sum::clear");
   null_value=1;
-  if (hybrid_type == DECIMAL_RESULT)
+  if (Result_type::type() == DECIMAL_RESULT)
   {
     curr_dec_buff= 0;
     my_decimal_set_zero(dec_buffs);
@@ -1377,14 +1377,14 @@ void Item_sum_sum::fix_length_and_dec()
   maybe_null=null_value=1;
   decimals= args[0]->decimals;
   switch (args[0]->cast_to_int_type()) {
-  case REAL_RESULT:
-  case STRING_RESULT:
-    hybrid_type= REAL_RESULT;
+  case REAL_CMP:
+  case STRING_CMP:
+    Result_type::set(REAL_RESULT);
     sum= 0.0;
     break;
-  case INT_RESULT:
-  case TIME_RESULT:
-  case DECIMAL_RESULT:
+  case INT_CMP:
+  case TIME_CMP:
+  case DECIMAL_CMP:
   {
     /* SUM result can't be longer than length(arg) + length(MAX_ROWS) */
     int precision= args[0]->decimal_precision() + DECIMAL_LONGLONG_DIGITS;
@@ -1392,18 +1392,18 @@ void Item_sum_sum::fix_length_and_dec()
                                                              decimals,
                                                              unsigned_flag);
     curr_dec_buff= 0;
-    hybrid_type= DECIMAL_RESULT;
+    Result_type::set(DECIMAL_RESULT);
     my_decimal_set_zero(dec_buffs);
     break;
   }
-  case ROW_RESULT:
-  case IMPOSSIBLE_RESULT:
+  case ROW_CMP:
+  case IMPOSSIBLE_CMP:
     DBUG_ASSERT(0);
   }
   DBUG_PRINT("info", ("Type: %s (%d, %d)",
-                      (hybrid_type == REAL_RESULT ? "REAL_RESULT" :
-                       hybrid_type == DECIMAL_RESULT ? "DECIMAL_RESULT" :
-                       hybrid_type == INT_RESULT ? "INT_RESULT" :
+                      (result_type() == REAL_RESULT ? "REAL_RESULT" :
+                       result_type() == DECIMAL_RESULT ? "DECIMAL_RESULT" :
+                       result_type() == INT_RESULT ? "INT_RESULT" :
                        "--ILLEGAL!!!--"),
                       max_length,
                       (int)decimals));
@@ -1414,7 +1414,7 @@ void Item_sum_sum::fix_length_and_dec()
 bool Item_sum_sum::add()
 {
   DBUG_ENTER("Item_sum_sum::add");
-  if (hybrid_type == DECIMAL_RESULT)
+  if (Result_type::type() == DECIMAL_RESULT)
   {
     my_decimal value;
     const my_decimal *val= aggr->arg_val_decimal(&value);
@@ -1441,7 +1441,7 @@ longlong Item_sum_sum::val_int()
   DBUG_ASSERT(fixed == 1);
   if (aggr)
     aggr->endup();
-  if (hybrid_type == DECIMAL_RESULT)
+  if (Result_type::type() == DECIMAL_RESULT)
   {
     longlong result;
     my_decimal2int(E_DEC_FATAL_ERROR, dec_buffs + curr_dec_buff, unsigned_flag,
@@ -1457,7 +1457,7 @@ double Item_sum_sum::val_real()
   DBUG_ASSERT(fixed == 1);
   if (aggr)
     aggr->endup();
-  if (hybrid_type == DECIMAL_RESULT)
+  if (Result_type::type() == DECIMAL_RESULT)
     my_decimal2double(E_DEC_FATAL_ERROR, dec_buffs + curr_dec_buff, &sum);
   return sum;
 }
@@ -1467,7 +1467,7 @@ String *Item_sum_sum::val_str(String *str)
 {
   if (aggr)
     aggr->endup();
-  if (hybrid_type == DECIMAL_RESULT)
+  if (Result_type::type() == DECIMAL_RESULT)
     return val_string_from_decimal(str);
   return val_string_from_real(str);
 }
@@ -1477,7 +1477,7 @@ my_decimal *Item_sum_sum::val_decimal(my_decimal *val)
 {
   if (aggr)
     aggr->endup();
-  if (hybrid_type == DECIMAL_RESULT)
+  if (Result_type::type() == DECIMAL_RESULT)
     return (dec_buffs + curr_dec_buff);
   return val_decimal_from_real(val);
 }
@@ -1653,7 +1653,7 @@ void Item_sum_avg::fix_length_and_dec()
   Item_sum_sum::fix_length_and_dec();
   maybe_null=null_value=1;
   prec_increment= current_thd->variables.div_precincrement;
-  if (hybrid_type == DECIMAL_RESULT)
+  if (Result_type::type() == DECIMAL_RESULT)
   {
     int precision= args[0]->decimal_precision() + prec_increment;
     decimals= MY_MIN(args[0]->decimals + prec_increment, DECIMAL_MAX_SCALE);
@@ -1689,11 +1689,11 @@ Field *Item_sum_avg::create_tmp_field(bool group, TABLE *table,
       The easiest way is to do this is to store both value in a string
       and unpack on access.
     */
-    field= new Field_string(((hybrid_type == DECIMAL_RESULT) ?
+    field= new Field_string(((Result_type::type() == DECIMAL_RESULT) ?
                              dec_bin_size : sizeof(double)) + sizeof(longlong),
                             0, name, &my_charset_bin);
   }
-  else if (hybrid_type == DECIMAL_RESULT)
+  else if (Result_type::type() == DECIMAL_RESULT)
     field= Field_new_decimal::create_from_item(this);
   else
     field= new Field_double(max_length, maybe_null, name, decimals, TRUE);
@@ -1747,10 +1747,10 @@ my_decimal *Item_sum_avg::val_decimal(my_decimal *val)
   }
 
   /*
-    For non-DECIMAL hybrid_type the division will be done in
+    For non-DECIMAL result_type the division will be done in
     Item_sum_avg::val_real().
   */
-  if (hybrid_type != DECIMAL_RESULT)
+  if (Result_type::type() != DECIMAL_RESULT)
     return val_decimal_from_real(val);
 
   sum_dec= dec_buffs + curr_dec_buff;
@@ -1764,7 +1764,7 @@ String *Item_sum_avg::val_str(String *str)
 {
   if (aggr)
     aggr->endup();
-  if (hybrid_type == DECIMAL_RESULT)
+  if (Result_type::type() == DECIMAL_RESULT)
     return val_string_from_decimal(str);
   return val_string_from_real(str);
 }
@@ -1837,7 +1837,7 @@ static double variance_fp_recurrence_result(double s, ulonglong count, bool is_s
 
 
 Item_sum_variance::Item_sum_variance(THD *thd, Item_sum_variance *item):
-  Item_sum_num(thd, item), hybrid_type(item->hybrid_type),
+  Item_sum_num(thd, item),
     count(item->count), sample(item->sample),
     prec_increment(item->prec_increment)
 {
@@ -1852,14 +1852,6 @@ void Item_sum_variance::fix_length_and_dec()
   maybe_null= null_value= 1;
   prec_increment= current_thd->variables.div_precincrement;
 
-  /*
-    According to the SQL2003 standard (Part 2, Foundations; sec 10.9,
-    aggregate function; paragraph 7h of Syntax Rules), "the declared 
-    type of the result is an implementation-defined aproximate numeric
-    type.
-  */
-  hybrid_type= REAL_RESULT;
-
   switch (args[0]->result_type()) {
   case REAL_RESULT:
   case STRING_RESULT:
@@ -1878,7 +1870,6 @@ void Item_sum_variance::fix_length_and_dec()
   }
   case ROW_RESULT:
   case TIME_RESULT:
-  case IMPOSSIBLE_RESULT:
     DBUG_ASSERT(0);
   }
   DBUG_PRINT("info", ("Type: REAL_RESULT (%d, %d)", max_length, (int)decimals));
@@ -2241,7 +2232,7 @@ void Item_sum_num::reset_field()
 
 void Item_sum_hybrid::reset_field()
 {
-  switch(hybrid_type) {
+  switch(Result_type::type()) {
   case STRING_RESULT:
   {
     char buff[MAX_FIELD_WIDTH];
@@ -2316,7 +2307,6 @@ void Item_sum_hybrid::reset_field()
   }
   case ROW_RESULT:
   case TIME_RESULT:
-  case IMPOSSIBLE_RESULT:
     DBUG_ASSERT(0);
   }
 }
@@ -2325,7 +2315,7 @@ void Item_sum_hybrid::reset_field()
 void Item_sum_sum::reset_field()
 {
   DBUG_ASSERT (aggr->Aggrtype() != Aggregator::DISTINCT_AGGREGATOR);
-  if (hybrid_type == DECIMAL_RESULT)
+  if (Result_type::type() == DECIMAL_RESULT)
   {
     my_decimal value, *arg_val= args[0]->val_decimal(&value);
     if (!arg_val)                               // Null
@@ -2334,7 +2324,7 @@ void Item_sum_sum::reset_field()
   }
   else
   {
-    DBUG_ASSERT(hybrid_type == REAL_RESULT);
+    DBUG_ASSERT(Result_type::type() == REAL_RESULT);
     double nr= args[0]->val_real();			// Nulls also return 0
     float8store(result_field->ptr, nr);
   }
@@ -2361,7 +2351,7 @@ void Item_sum_avg::reset_field()
 {
   uchar *res=result_field->ptr;
   DBUG_ASSERT (aggr->Aggrtype() != Aggregator::DISTINCT_AGGREGATOR);
-  if (hybrid_type == DECIMAL_RESULT)
+  if (Result_type::type() == DECIMAL_RESULT)
   {
     longlong tmp;
     my_decimal value, *arg_dec= args[0]->val_decimal(&value);
@@ -2415,7 +2405,7 @@ void Item_sum_bit::update_field()
 void Item_sum_sum::update_field()
 {
   DBUG_ASSERT (aggr->Aggrtype() != Aggregator::DISTINCT_AGGREGATOR);
-  if (hybrid_type == DECIMAL_RESULT)
+  if (Result_type::type() == DECIMAL_RESULT)
   {
     my_decimal value, *arg_val= args[0]->val_decimal(&value);
     if (!args[0]->null_value)
@@ -2470,7 +2460,7 @@ void Item_sum_avg::update_field()
 
   DBUG_ASSERT (aggr->Aggrtype() != Aggregator::DISTINCT_AGGREGATOR);
 
-  if (hybrid_type == DECIMAL_RESULT)
+  if (Result_type::type() == DECIMAL_RESULT)
   {
     my_decimal value, *arg_val= args[0]->val_decimal(&value);
     if (!args[0]->null_value)
@@ -2508,7 +2498,7 @@ void Item_sum_avg::update_field()
 
 void Item_sum_hybrid::update_field()
 {
-  switch (hybrid_type) {
+  switch (Result_type::type()) {
   case STRING_RESULT:
     min_max_update_str_field();
     break;
@@ -2620,7 +2610,7 @@ void Item_sum_hybrid::update_field()
 }
 
 
-Item_avg_field::Item_avg_field(Item_result res_type, Item_sum_avg *item)
+Item_avg_field::Item_avg_field(Item_sum_avg *item)
 {
   name=item->name;
   decimals=item->decimals;
@@ -2628,26 +2618,26 @@ void Item_sum_hybrid::update_field()
   unsigned_flag= item->unsigned_flag;
   field=item->result_field;
   maybe_null=1;
-  hybrid_type= res_type;
   prec_increment= item->prec_increment;
-  if (hybrid_type == DECIMAL_RESULT)
-  {
-    f_scale= item->f_scale;
-    f_precision= item->f_precision;
-    dec_bin_size= item->dec_bin_size;
-  }
 }
 
-double Item_avg_field::val_real()
+
+Item_avg_field_newdecimal::Item_avg_field_newdecimal(Item_sum_avg *item)
+ :Item_avg_field(item)
+{
+  f_scale= item->f_scale;
+  f_precision= item->f_precision;
+  dec_bin_size= item->dec_bin_size;
+}
+
+
+double Item_avg_field_double::val_real()
 {
   // fix_fields() never calls for this Item
   double nr;
   longlong count;
   uchar *res;
 
-  if (hybrid_type == DECIMAL_RESULT)
-    return val_real_from_decimal();
-
   float8get(nr,field->ptr);
   res= (field->ptr+sizeof(double));
   count= sint8korr(res);
@@ -2658,18 +2648,9 @@ double Item_avg_field::val_real()
 }
 
 
-longlong Item_avg_field::val_int()
-{
-  return (longlong) rint(val_real());
-}
-
-
-my_decimal *Item_avg_field::val_decimal(my_decimal *dec_buf)
+my_decimal *Item_avg_field_newdecimal::val_decimal(my_decimal *dec_buf)
 {
   // fix_fields() never calls for this Item
-  if (hybrid_type == REAL_RESULT)
-    return val_decimal_from_real(dec_buf);
-
   longlong count= sint8korr(field->ptr + dec_bin_size);
   if ((null_value= !count))
     return 0;
@@ -2684,15 +2665,6 @@ my_decimal *Item_avg_field::val_decimal(my_decimal *dec_buf)
 }
 
 
-String *Item_avg_field::val_str(String *str)
-{
-  // fix_fields() never calls for this Item
-  if (hybrid_type == DECIMAL_RESULT)
-    return val_string_from_decimal(str);
-  return val_string_from_real(str);
-}
-
-
 Item_std_field::Item_std_field(Item_sum_std *item)
   : Item_variance_field(item)
 {
@@ -2709,29 +2681,6 @@ double Item_std_field::val_real()
 }
 
 
-my_decimal *Item_std_field::val_decimal(my_decimal *dec_buf)
-{
-  /*
-    We can't call val_decimal_from_real() for DECIMAL_RESULT as
-    Item_variance_field::val_real() would cause an infinite loop
-  */
-  my_decimal tmp_dec, *dec;
-  double nr;
-  if (hybrid_type == REAL_RESULT)
-    return val_decimal_from_real(dec_buf);
-
-  dec= Item_variance_field::val_decimal(dec_buf);
-  if (!dec)
-    return 0;
-  my_decimal2double(E_DEC_FATAL_ERROR, dec, &nr);
-  DBUG_ASSERT(nr >= 0.0);
-  nr= sqrt(nr);
-  double2my_decimal(E_DEC_FATAL_ERROR, nr, &tmp_dec);
-  my_decimal_round(E_DEC_FATAL_ERROR, &tmp_dec, decimals, FALSE, dec_buf);
-  return dec_buf;
-}
-
-
 Item_variance_field::Item_variance_field(Item_sum_variance *item)
 {
   name=item->name;
@@ -2742,24 +2691,12 @@ my_decimal *Item_std_field::val_decimal(my_decimal *dec_buf)
   maybe_null=1;
   sample= item->sample;
   prec_increment= item->prec_increment;
-  if ((hybrid_type= item->hybrid_type) == DECIMAL_RESULT)
-  {
-    f_scale0= item->f_scale0;
-    f_precision0= item->f_precision0;
-    dec_bin_size0= item->dec_bin_size0;
-    f_scale1= item->f_scale1;
-    f_precision1= item->f_precision1;
-    dec_bin_size1= item->dec_bin_size1;
-  }
 }
 
 
 double Item_variance_field::val_real()
 {
   // fix_fields() never calls for this Item
-  if (hybrid_type == DECIMAL_RESULT)
-    return val_real_from_decimal();
-
   double recurrence_s;
   ulonglong count;
   float8get(recurrence_s, (field->ptr + sizeof(double)));
diff --git a/sql/item_sum.h b/sql/item_sum.h
index d28c654..ee13a87 100644
--- a/sql/item_sum.h
+++ b/sql/item_sum.h
@@ -706,7 +706,7 @@ class Item_sum_num :public Item_sum
 };
 
 
-class Item_sum_int :public Item_sum_num
+class Item_sum_int :public Item_sum_num, public Type_handler_longlong
 {
 public:
   Item_sum_int(Item *item_par) :Item_sum_num(item_par) {}
@@ -715,16 +715,14 @@ class Item_sum_int :public Item_sum_num
   double val_real() { DBUG_ASSERT(fixed == 1); return (double) val_int(); }
   String *val_str(String*str);
   my_decimal *val_decimal(my_decimal *);
-  enum Item_result result_type () const { return INT_RESULT; }
   void fix_length_and_dec()
   { decimals=0; max_length=21; maybe_null=null_value=0; }
 };
 
 
-class Item_sum_sum :public Item_sum_num
+class Item_sum_sum :public Item_sum_num, public Type_handler_hybrid_result
 {
 protected:
-  Item_result hybrid_type;
   double sum;
   my_decimal dec_buffs[2];
   uint curr_dec_buff;
@@ -746,7 +744,8 @@ class Item_sum_sum :public Item_sum_num
   longlong val_int();
   String *val_str(String*str);
   my_decimal *val_decimal(my_decimal *);
-  enum Item_result result_type () const { return hybrid_type; }
+  enum_field_types field_type() const
+  { return Type_handler_hybrid_result::field_type(*this); }
   void reset_field();
   void update_field();
   void no_rows_in_result() {}
@@ -818,23 +817,12 @@ class Item_avg_field :public Item_result_field
 {
 public:
   Field *field;
-  Item_result hybrid_type;
   uint f_precision, f_scale, dec_bin_size;
   uint prec_increment;
-  Item_avg_field(Item_result res_type, Item_sum_avg *item);
+  Item_avg_field(Item_sum_avg *item);
   enum Type type() const { return FIELD_AVG_ITEM; }
-  double val_real();
-  longlong val_int();
-  my_decimal *val_decimal(my_decimal *);
   bool is_null() { update_null_value(); return null_value; }
-  String *val_str(String*);
-  enum_field_types field_type() const
-  {
-    return hybrid_type == DECIMAL_RESULT ?
-      MYSQL_TYPE_NEWDECIMAL : MYSQL_TYPE_DOUBLE;
-  }
   void fix_length_and_dec() {}
-  enum Item_result result_type () const { return hybrid_type; }
   bool check_vcol_func_processor(uchar *int_arg) 
   {
     return trace_unsupported_by_check_vcol_func_processor("avg_field");
@@ -843,6 +831,32 @@ class Item_avg_field :public Item_result_field
 };
 
 
+class Item_avg_field_double :public Item_avg_field, public Type_handler_double
+{
+public:
+  // fix_fields() is never called for this Item
+  Item_avg_field_double(Item_sum_avg *item) :Item_avg_field(item)
+  { }
+  double val_real();
+  my_decimal *val_decimal(my_decimal *d) { return val_decimal_from_real(d); }
+  String *val_str(String *str) { return val_string_from_real(str); }
+  longlong val_int() { return (longlong) rint(val_real()); }
+};
+
+
+class Item_avg_field_newdecimal :public Item_avg_field,
+                                 public Type_handler_newdecimal
+{
+public:
+  // fix_fields() is never called for this Item
+  Item_avg_field_newdecimal(Item_sum_avg *item);
+  double val_real() { return val_real_from_decimal(); }
+  my_decimal *val_decimal(my_decimal *);
+  String *val_str(String *str) { return val_string_from_decimal(str); }
+  longlong val_int() { return val_int_from_decimal(); }
+};
+
+
 class Item_sum_avg :public Item_sum_sum
 {
 public:
@@ -872,7 +886,11 @@ class Item_sum_avg :public Item_sum_sum
   void reset_field();
   void update_field();
   Item *result_item(Field *field)
-  { return new Item_avg_field(hybrid_type, this); }
+  {
+    return Result_type::type() == DECIMAL_RESULT ?
+           (Item_avg_field *) new Item_avg_field_newdecimal(this) :
+           (Item_avg_field *) new Item_avg_field_double(this);
+  }
   void no_rows_in_result() {}
   const char *func_name() const 
   { 
@@ -889,11 +907,10 @@ class Item_sum_avg :public Item_sum_sum
 
 class Item_sum_variance;
 
-class Item_variance_field :public Item_result_field
+class Item_variance_field :public Item_result_field, public Type_handler_double
 {
 public:
   Field *field;
-  Item_result hybrid_type;
   uint f_precision0, f_scale0;
   uint f_precision1, f_scale1;
   uint dec_bin_size0, dec_bin_size1;
@@ -909,13 +926,7 @@ class Item_variance_field :public Item_result_field
   my_decimal *val_decimal(my_decimal *dec_buf)
   { return val_decimal_from_real(dec_buf); }
   bool is_null() { update_null_value(); return null_value; }
-  enum_field_types field_type() const
-  {
-    return hybrid_type == DECIMAL_RESULT ?
-      MYSQL_TYPE_NEWDECIMAL : MYSQL_TYPE_DOUBLE;
-  }
   void fix_length_and_dec() {}
-  enum Item_result result_type () const { return hybrid_type; }
   bool check_vcol_func_processor(uchar *int_arg) 
   {
     return trace_unsupported_by_check_vcol_func_processor("var_field");
@@ -942,14 +953,17 @@ class Item_variance_field :public Item_result_field
   for 2 <= k <= n newline
   ital variance = S_{n} / (n-1)
 
+  According to the SQL2003 standard (Part 2, Foundations; sec 10.9,
+  aggregate function; paragraph 7h of Syntax Rules), "the declared 
+  type of the result is an implementation-defined aproximate numeric
+  type.
 */
 
-class Item_sum_variance : public Item_sum_num
+class Item_sum_variance : public Item_sum_num, public Type_handler_double
 {
   void fix_length_and_dec();
 
 public:
-  Item_result hybrid_type;
   int cur_dec;
   double recurrence_m, recurrence_s;    /* Used in recurrence relation. */
   ulonglong count;
@@ -960,7 +974,7 @@ class Item_sum_variance : public Item_sum_num
   uint prec_increment;
 
   Item_sum_variance(Item *item_par, uint sample_arg) :Item_sum_num(item_par),
-    hybrid_type(REAL_RESULT), count(0), sample(sample_arg)
+    count(0), sample(sample_arg)
     {}
   Item_sum_variance(THD *thd, Item_sum_variance *item);
   enum Sumfunctype sum_func () const { return VARIANCE_FUNC; }
@@ -977,7 +991,6 @@ class Item_sum_variance : public Item_sum_num
     { return sample ? "var_samp(" : "variance("; }
   Item *copy_or_same(THD* thd);
   Field *create_tmp_field(bool group, TABLE *table, uint convert_blob_length);
-  enum Item_result result_type () const { return REAL_RESULT; }
   void cleanup()
   {
     count= 0;
@@ -993,9 +1006,6 @@ class Item_std_field :public Item_variance_field
   Item_std_field(Item_sum_std *item);
   enum Type type() const { return FIELD_STD_ITEM; }
   double val_real();
-  my_decimal *val_decimal(my_decimal *);
-  enum Item_result result_type () const { return REAL_RESULT; }
-  enum_field_types field_type() const { return MYSQL_TYPE_DOUBLE;}
   const char *func_name() const { DBUG_ASSERT(0); return "std_field"; }
 };
 
@@ -1017,19 +1027,16 @@ class Item_sum_std :public Item_sum_variance
     { return new Item_std_field(this); }
   const char *func_name() const { return "std("; }
   Item *copy_or_same(THD* thd);
-  enum Item_result result_type () const { return REAL_RESULT; }
-  enum_field_types field_type() const { return MYSQL_TYPE_DOUBLE;}
 };
 
 // This class is a string or number function depending on num_func
 class Arg_comparator;
 class Item_cache;
-class Item_sum_hybrid :public Item_sum
+class Item_sum_hybrid :public Item_sum, public Result_type
 {
 protected:
   Item_cache *value, *arg_cache;
   Arg_comparator *cmp;
-  Item_result hybrid_type;
   enum_field_types hybrid_field_type;
   int cmp_sign;
   bool was_values;  // Set if we have found at least one row (for max/min only)
@@ -1037,13 +1044,15 @@ class Item_sum_hybrid :public Item_sum
 
   public:
   Item_sum_hybrid(Item *item_par,int sign)
-    :Item_sum(item_par), value(0), arg_cache(0), cmp(0),
-    hybrid_type(INT_RESULT), hybrid_field_type(MYSQL_TYPE_LONGLONG),
+    :Item_sum(item_par), Result_type(INT_RESULT),
+    value(0), arg_cache(0), cmp(0),
+    hybrid_field_type(MYSQL_TYPE_LONGLONG),
     cmp_sign(sign), was_values(TRUE)
   { collation.set(&my_charset_bin); }
   Item_sum_hybrid(THD *thd, Item_sum_hybrid *item)
-    :Item_sum(thd, item), value(item->value), arg_cache(0),
-    hybrid_type(item->hybrid_type), hybrid_field_type(item->hybrid_field_type),
+    :Item_sum(thd, item), Result_type(item->result_type()),
+    value(item->value), arg_cache(0),
+    hybrid_field_type(item->hybrid_field_type),
     cmp_sign(item->cmp_sign), was_values(item->was_values)
   { }
   bool fix_fields(THD *, Item **);
@@ -1055,8 +1064,9 @@ class Item_sum_hybrid :public Item_sum
   void reset_field();
   String *val_str(String *);
   bool keep_field_type(void) const { return 1; }
-  enum Item_result result_type () const { return hybrid_type; }
+  enum Item_result result_type () const { return Result_type::type(); }
   enum enum_field_types field_type() const { return hybrid_field_type; }
+  Item_cmp cmp_type() const { return cmp_type_by_field_type(); }
   void update_field();
   void min_max_update_str_field();
   void min_max_update_real_field();
@@ -1202,7 +1212,7 @@ class Item_udf_sum : public Item_sum
 };
 
 
-class Item_sum_udf_float :public Item_udf_sum
+class Item_sum_udf_float :public Item_udf_sum, public Type_handler_double
 {
  public:
   Item_sum_udf_float(udf_func *udf_arg)
@@ -1224,7 +1234,7 @@ class Item_sum_udf_float :public Item_udf_sum
 };
 
 
-class Item_sum_udf_int :public Item_udf_sum
+class Item_sum_udf_int :public Item_udf_sum, public Type_handler_longlong
 {
 public:
   Item_sum_udf_int(udf_func *udf_arg)
@@ -1238,13 +1248,13 @@ class Item_sum_udf_int :public Item_udf_sum
     { DBUG_ASSERT(fixed == 1); return (double) Item_sum_udf_int::val_int(); }
   String *val_str(String*str);
   my_decimal *val_decimal(my_decimal *);
-  enum Item_result result_type () const { return INT_RESULT; }
   void fix_length_and_dec() { decimals=0; max_length=21; }
   Item *copy_or_same(THD* thd);
 };
 
 
-class Item_sum_udf_str :public Item_udf_sum
+class Item_sum_udf_str :public Item_udf_sum,
+                        public Type_handler_string_hybrid
 {
 public:
   Item_sum_udf_str(udf_func *udf_arg)
@@ -1277,13 +1287,13 @@ class Item_sum_udf_str :public Item_udf_sum
     return cs->cset->strtoll10(cs, res->ptr(), &end, &err_not_used);
   }
   my_decimal *val_decimal(my_decimal *dec);
-  enum Item_result result_type () const { return STRING_RESULT; }
+  enum_field_types field_type() const { return string_field_type(); }
   void fix_length_and_dec();
   Item *copy_or_same(THD* thd);
 };
 
 
-class Item_sum_udf_decimal :public Item_udf_sum
+class Item_sum_udf_decimal :public Item_udf_sum, public Type_handler_newdecimal
 {
 public:
   Item_sum_udf_decimal(udf_func *udf_arg)
@@ -1296,7 +1306,6 @@ class Item_sum_udf_decimal :public Item_udf_sum
   double val_real();
   longlong val_int();
   my_decimal *val_decimal(my_decimal *);
-  enum Item_result result_type () const { return DECIMAL_RESULT; }
   void fix_length_and_dec() { fix_num_length_and_dec(); }
   Item *copy_or_same(THD* thd);
 };
@@ -1387,7 +1396,8 @@ int dump_leaf_key(void* key_arg,
                   void* item_arg);
 C_MODE_END
 
-class Item_func_group_concat : public Item_sum
+class Item_func_group_concat : public Item_sum,
+                               public Type_handler_string_hybrid
 {
   TMP_TABLE_PARAM *tmp_table_param;
   String result;
@@ -1441,7 +1451,6 @@ class Item_func_group_concat : public Item_sum
 
   enum Sumfunctype sum_func () const {return GROUP_CONCAT_FUNC;}
   const char *func_name() const { return "group_concat"; }
-  virtual Item_result result_type () const { return STRING_RESULT; }
   virtual Field *make_string_field(TABLE *table);
   enum_field_types field_type() const
   {
diff --git a/sql/item_timefunc.cc b/sql/item_timefunc.cc
index 4a8bb4c..fce6ac3 100644
--- a/sql/item_timefunc.cc
+++ b/sql/item_timefunc.cc
@@ -1067,7 +1067,7 @@ longlong Item_func_yearweek::val_int()
 }
 
 
-longlong Item_func_weekday::val_int()
+longlong Item_func_weekday_common::weekday(bool odbc_type)
 {
   DBUG_ASSERT(fixed == 1);
   MYSQL_TIME ltime;
diff --git a/sql/item_timefunc.h b/sql/item_timefunc.h
index cb8b595..df4b41f 100644
--- a/sql/item_timefunc.h
+++ b/sql/item_timefunc.h
@@ -147,23 +147,12 @@ class Item_func_dayofmonth :public Item_int_func
 };
 
 
-class Item_func_month :public Item_func
+class Item_func_month :public Item_int_func
 {
 public:
-  Item_func_month(Item *a) :Item_func(a) { collation.set_numeric(); }
+  Item_func_month(Item *a) :Item_int_func(a) { }
   longlong val_int();
-  double val_real()
-  { DBUG_ASSERT(fixed == 1); return (double) Item_func_month::val_int(); }
-  String *val_str(String *str) 
-  {
-    longlong nr= val_int();
-    if (null_value)
-      return 0;
-    str->set(nr, collation.collation);
-    return str;
-  }
   const char *func_name() const { return "month"; }
-  enum Item_result result_type () const { return INT_RESULT; }
   void fix_length_and_dec() 
   { 
     decimals= 0;
@@ -359,13 +348,23 @@ class Item_func_year :public Item_int_func
 };
 
 
-class Item_func_weekday :public Item_func
+class Item_func_weekday_common :public Item_func
+{
+public:
+  Item_func_weekday_common(Item *a): Item_func(a) { }
+  longlong weekday(bool odbc_type);
+};
+
+
+class Item_func_weekday :public Item_func_weekday_common,
+                         public Type_handler_longlong
 {
   bool odbc_type;
 public:
   Item_func_weekday(Item *a,bool type_arg)
-    :Item_func(a), odbc_type(type_arg) { collation.set_numeric(); }
-  longlong val_int();
+    :Item_func_weekday_common(a),
+     odbc_type(type_arg) { collation.set_numeric(); }
+  longlong val_int() { return weekday(odbc_type); }
   double val_real() { DBUG_ASSERT(fixed == 1); return (double) val_int(); }
   String *val_str(String *str)
   {
@@ -377,7 +376,6 @@ class Item_func_weekday :public Item_func
   {
      return (odbc_type ? "dayofweek" : "weekday");
   }
-  enum Item_result result_type () const { return INT_RESULT; }
   void fix_length_and_dec()
   {
     decimals= 0;
@@ -392,14 +390,16 @@ class Item_func_weekday :public Item_func
   }
 };
 
-class Item_func_dayname :public Item_func_weekday
+class Item_func_dayname :public Item_func_weekday_common,
+                         public Type_handler_varstring
 {
   MY_LOCALE *locale;
  public:
-  Item_func_dayname(Item *a) :Item_func_weekday(a,0) {}
+  Item_func_dayname(Item *a) :Item_func_weekday_common(a) {}
   const char *func_name() const { return "dayname"; }
+  longlong val_int() { return weekday(false); }
+  double val_real() { return (double) val_int(); }
   String *val_str(String *str);
-  enum Item_result result_type () const { return STRING_RESULT; }
   void fix_length_and_dec();
   bool check_partition_func_processor(uchar *int_arg) {return TRUE;}
   bool check_vcol_func_processor(uchar *int_arg) { return FALSE;}
@@ -420,7 +420,7 @@ class Item_func_seconds_hybrid: public Item_func_numhybrid
     set_if_smaller(decimals, TIME_SECOND_PART_DIGITS);
     max_length=17 + (decimals ? decimals + 1 : 0);
     maybe_null= true;
-    cached_result_type= decimals ? DECIMAL_RESULT : INT_RESULT;
+    Result_type::set(decimals ? DECIMAL_RESULT : INT_RESULT);
   }
   double real_op() { DBUG_ASSERT(0); return 0; }
   String *str_op(String *str) { DBUG_ASSERT(0); return 0; }
@@ -488,9 +488,6 @@ class Item_temporal_func: public Item_func
   Item_temporal_func(Item *a) :Item_func(a) {}
   Item_temporal_func(Item *a, Item *b) :Item_func(a,b) {}
   Item_temporal_func(Item *a, Item *b, Item *c) :Item_func(a,b,c) {}
-  enum Item_result result_type () const { return STRING_RESULT; }
-  enum_field_types field_type() const { return MYSQL_TYPE_DATETIME; }
-  Item_result cmp_type() const { return TIME_RESULT; }
   String *val_str(String *str);
   longlong val_int() { return val_int_from_date(); }
   double val_real() { return val_real_from_date(); }
@@ -518,10 +515,11 @@ class Item_temporal_hybrid_func: public Item_temporal_func
   Item_temporal_hybrid_func(Item *a,Item *b)
     :Item_temporal_func(a,b) {}
   enum_field_types field_type() const { return cached_field_type; }
-  Item_result cmp_type() const
+  enum Item_result result_type () const { return STRING_RESULT; }
+  Item_cmp cmp_type() const
   {
     return cached_field_type == MYSQL_TYPE_STRING ?
-           STRING_RESULT : TIME_RESULT;
+           STRING_CMP : TIME_CMP;
   }
   const CHARSET_INFO *charset_for_protocol() const
   {
@@ -554,23 +552,35 @@ class Item_temporal_hybrid_func: public Item_temporal_func
 };
 
 
-class Item_datefunc :public Item_temporal_func
+class Item_datetimefunc: public Item_temporal_func,
+                         public Type_handler_datetime
+{
+public:
+  Item_datetimefunc() :Item_temporal_func() { }
+  Item_datetimefunc(Item *a) :Item_temporal_func(a) { }
+  Item_datetimefunc(Item *a, Item *b) :Item_temporal_func(a, b) {}
+  Item_datetimefunc(Item *a, Item *b, Item *c) :Item_temporal_func(a, b, c) {}
+};
+
+
+class Item_datefunc :public Item_temporal_func,
+                     public Type_handler_date
 {
 public:
   Item_datefunc() :Item_temporal_func() { }
   Item_datefunc(Item *a) :Item_temporal_func(a) { }
-  enum_field_types field_type() const { return MYSQL_TYPE_DATE; }
+  Item_datefunc(Item *a, Item *b) :Item_temporal_func(a, b) { }
 };
 
 
-class Item_timefunc :public Item_temporal_func
+class Item_timefunc :public Item_temporal_func,
+                     public Type_handler_time
 {
 public:
   Item_timefunc() :Item_temporal_func() {}
   Item_timefunc(Item *a) :Item_temporal_func(a) {}
   Item_timefunc(Item *a,Item *b) :Item_temporal_func(a,b) {}
   Item_timefunc(Item *a, Item *b, Item *c) :Item_temporal_func(a, b ,c) {}
-  enum_field_types field_type() const { return MYSQL_TYPE_TIME; }
 };
 
 
@@ -658,16 +668,16 @@ class Item_func_curdate_utc :public Item_func_curdate
 /* Abstract CURRENT_TIMESTAMP function. See also Item_func_curtime */
 
 
-class Item_func_now :public Item_temporal_func
+class Item_func_now :public Item_datetimefunc
 {
   MYSQL_TIME ltime;
 public:
-  Item_func_now(uint dec) :Item_temporal_func() { decimals= dec; }
+  Item_func_now(uint dec) :Item_datetimefunc() { decimals= dec; }
   bool fix_fields(THD *, Item **);
   void fix_length_and_dec()
   {
     store_now_in_TIME(&ltime);
-    Item_temporal_func::fix_length_and_dec();
+    Item_datetimefunc::fix_length_and_dec();
     maybe_null= false;
   }
   bool get_date(MYSQL_TIME *res, ulonglong fuzzy_date);
@@ -751,11 +761,11 @@ class Item_func_date_format :public Item_str_func
 };
 
 
-class Item_func_from_unixtime :public Item_temporal_func
+class Item_func_from_unixtime :public Item_datetimefunc
 {
   THD *thd;
  public:
-  Item_func_from_unixtime(Item *a) :Item_temporal_func(a) {}
+  Item_func_from_unixtime(Item *a) :Item_datetimefunc(a) {}
   const char *func_name() const { return "from_unixtime"; }
   void fix_length_and_dec();
   bool get_date(MYSQL_TIME *res, ulonglong fuzzy_date);
@@ -776,7 +786,7 @@ class Item_func_from_unixtime :public Item_temporal_func
   tables can be used during this function calculation for loading time zone
   descriptions.
 */
-class Item_func_convert_tz :public Item_temporal_func
+class Item_func_convert_tz :public Item_datetimefunc
 {
   /*
     If time zone parameters are constants we are caching objects that
@@ -788,7 +798,7 @@ class Item_func_convert_tz :public Item_temporal_func
   Time_zone *from_tz, *to_tz;
  public:
   Item_func_convert_tz(Item *a, Item *b, Item *c):
-    Item_temporal_func(a, b, c), from_tz_cached(0), to_tz_cached(0) {}
+    Item_datetimefunc(a, b, c), from_tz_cached(0), to_tz_cached(0) {}
   const char *func_name() const { return "convert_tz"; }
   void fix_length_and_dec();
   bool get_date(MYSQL_TIME *res, ulonglong fuzzy_date);
@@ -911,18 +921,19 @@ class Item_temporal_typecast: public Item_temporal_func
   }
 };
 
-class Item_date_typecast :public Item_temporal_typecast
+class Item_date_typecast :public Item_temporal_typecast,
+                          public Type_handler_date
 {
 public:
   Item_date_typecast(Item *a) :Item_temporal_typecast(a) {}
   const char *func_name() const { return "cast_as_date"; }
   bool get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date);
   const char *cast_type() const { return "date"; }
-  enum_field_types field_type() const { return MYSQL_TYPE_DATE; }
 };
 
 
-class Item_time_typecast :public Item_temporal_typecast
+class Item_time_typecast :public Item_temporal_typecast,
+                          public Type_handler_time
 {
 public:
   Item_time_typecast(Item *a, uint dec_arg)
@@ -930,28 +941,25 @@ class Item_time_typecast :public Item_temporal_typecast
   const char *func_name() const { return "cast_as_time"; }
   bool get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date);
   const char *cast_type() const { return "time"; }
-  enum_field_types field_type() const { return MYSQL_TYPE_TIME; }
 };
 
 
-class Item_datetime_typecast :public Item_temporal_typecast
+class Item_datetime_typecast :public Item_temporal_typecast,
+                              public Type_handler_datetime
 {
 public:
   Item_datetime_typecast(Item *a, uint dec_arg)
     :Item_temporal_typecast(a) { decimals= dec_arg; }
   const char *func_name() const { return "cast_as_datetime"; }
   const char *cast_type() const { return "datetime"; }
-  enum_field_types field_type() const { return MYSQL_TYPE_DATETIME; }
   bool get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date);
 };
 
-
-class Item_func_makedate :public Item_temporal_func
+class Item_func_makedate :public Item_datefunc
 {
 public:
-  Item_func_makedate(Item *a,Item *b) :Item_temporal_func(a,b) {}
+  Item_func_makedate(Item *a,Item *b) :Item_datefunc(a,b) {}
   const char *func_name() const { return "makedate"; }
-  enum_field_types field_type() const { return MYSQL_TYPE_DATE; }
   bool get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date);
 };
 
diff --git a/sql/opt_range.cc b/sql/opt_range.cc
index 1cea800..9edf3d4 100644
--- a/sql/opt_range.cc
+++ b/sql/opt_range.cc
@@ -946,7 +946,7 @@ class PARAM : public RANGE_OPT_PARAM
 
 static SEL_TREE * get_mm_parts(RANGE_OPT_PARAM *param,COND *cond_func,Field *field,
 			       Item_func::Functype type,Item *value,
-			       Item_result cmp_type);
+			       Item_cmp cmp_type);
 static SEL_ARG *get_mm_leaf(RANGE_OPT_PARAM *param,COND *cond_func,Field *field,
 			    KEY_PART *key_part,
 			    Item_func::Functype type,Item *value);
@@ -7510,7 +7510,7 @@ QUICK_SELECT_I *TRP_ROR_UNION::make_quick(PARAM *param,
 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)
+                                Item_cmp cmp_type)
 {
   SEL_TREE *tree;
   tree= get_mm_parts(param, cond_func, field, Item_func::LT_FUNC,
@@ -7544,7 +7544,7 @@ static SEL_TREE *get_ne_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)
+                                  Item_cmp cmp_type, bool inv)
 {
   SEL_TREE *tree= 0;
   DBUG_ENTER("get_func_mm_tree");
@@ -7892,7 +7892,7 @@ static SEL_TREE *get_full_func_mm_tree(RANGE_OPT_PARAM *param,
       ref_tables|= arg->used_tables();
   }
   Field *field= field_item->field;
-  Item_result cmp_type= field->cmp_type();
+  Item_cmp cmp_type= field->cmp_type();
   if (!((ref_tables | field->table->map) & param_comp))
     ftree= get_func_mm_tree(param, cond_func, field, value, cmp_type, inv);
   Item_equal *item_equal= field_item->item_equal;
@@ -8122,7 +8122,7 @@ static SEL_TREE *get_mm_tree(RANGE_OPT_PARAM *param, Item **cond_ptr)
     while (it++)
     {
       Field *field= it.get_curr_field();
-      Item_result cmp_type= field->cmp_type();
+      Item_cmp cmp_type= field->cmp_type();
       if (!((ref_tables | field->table->map) & param_comp))
       {
         tree= get_mm_parts(param, cond, field, Item_func::EQ_FUNC,
@@ -8180,7 +8180,7 @@ static SEL_TREE *get_mm_tree(RANGE_OPT_PARAM *param, Item **cond_ptr)
 static SEL_TREE *
 get_mm_parts(RANGE_OPT_PARAM *param, COND *cond_func, Field *field,
 	     Item_func::Functype type,
-	     Item *value, Item_result cmp_type)
+	     Item *value, Item_cmp cmp_type)
 {
   DBUG_ENTER("get_mm_parts");
   if (field->table != param->table)
@@ -8354,7 +8354,7 @@ static SEL_TREE *get_mm_tree(RANGE_OPT_PARAM *param, Item **cond_ptr)
       tmp.copy(*res);				// Get own copy
       res= &tmp;
     }
-    if (field->cmp_type() != STRING_RESULT)
+    if (field->cmp_type() != STRING_CMP)
       goto end;                                 // Can only optimize strings
 
     offset=maybe_null;
@@ -8416,10 +8416,10 @@ static SEL_TREE *get_mm_tree(RANGE_OPT_PARAM *param, Item **cond_ptr)
     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)
+  if (field->cmp_type() == STRING_CMP && value->cmp_type() != STRING_CMP)
     goto end;
   err= value->save_in_field_no_warnings(field, 1);
-  if (err == 2 && field->cmp_type() == STRING_RESULT)
+  if (err == 2 && field->cmp_type() == STRING_CMP)
   {
     if (type == Item_func::EQ_FUNC)
     {
@@ -8432,11 +8432,12 @@ static SEL_TREE *get_mm_tree(RANGE_OPT_PARAM *param, Item **cond_ptr)
   }
   if (err > 0)
   {
-    if (field->cmp_type() != value->result_type())
+    if (field->cmp_type() != (Item_cmp) value->result_type())
     {
       if ((type == Item_func::EQ_FUNC || type == Item_func::EQUAL_FUNC) &&
-          value->result_type() == item_cmp_type(field->result_type(),
-                                                value->result_type()))
+          value->result_type() ==
+                 (Item_result) item_cmp_type((Item_cmp) field->result_type(),
+                                             (Item_cmp) value->result_type()))
       {
         tree= new (alloc) SEL_ARG(field, 0, 0);
         tree->type= SEL_ARG::IMPOSSIBLE;
@@ -13372,7 +13373,8 @@ static bool get_constant_key_infix(KEY *index_info, SEL_ARG *index_range_tree,
                number.
              */
              (args[1]->result_type() != STRING_RESULT &&
-              min_max_arg_item->field->cmp_type() != args[1]->result_type())))
+              min_max_arg_item->field->cmp_type() !=
+              (Item_cmp) args[1]->result_type())))
           DBUG_RETURN(FALSE);
       }
       else
diff --git a/sql/opt_subselect.cc b/sql/opt_subselect.cc
index 66b1ba1..8fb1469 100644
--- a/sql/opt_subselect.cc
+++ b/sql/opt_subselect.cc
@@ -843,7 +843,7 @@ bool subquery_types_allow_materialization(Item_in_subselect *in_subs)
     if (outer->cmp_type() != inner->cmp_type())
       DBUG_RETURN(FALSE);
     switch (outer->cmp_type()) {
-    case STRING_RESULT:
+    case STRING_CMP:
       if (!(outer->collation.collation == inner->collation.collation))
         DBUG_RETURN(FALSE);
       // Materialization does not work with BLOB columns
@@ -860,7 +860,7 @@ bool subquery_types_allow_materialization(Item_in_subselect *in_subs)
         DBUG_RETURN(FALSE);
       }
       break;
-    case TIME_RESULT:
+    case TIME_CMP:
       if (mysql_type_to_time_type(outer->field_type()) !=
           mysql_type_to_time_type(inner->field_type()))
         DBUG_RETURN(FALSE);
diff --git a/sql/opt_sum.cc b/sql/opt_sum.cc
index 4e8fcef..8f299f9 100644
--- a/sql/opt_sum.cc
+++ b/sql/opt_sum.cc
@@ -486,8 +486,8 @@ int opt_sum_query(THD *thd,
 
 static bool check_item1_shorter_item2(Item *item1, Item *item2)
 {
-  if (item1->cmp_type() == STRING_RESULT &&
-      item2->cmp_type() == STRING_RESULT)
+  if (item1->cmp_type() == STRING_CMP &&
+      item2->cmp_type() == STRING_CMP)
   {
     int len1= item1->max_length / item1->collation.collation->mbmaxlen;
     int len2= item2->max_length / item2->collation.collation->mbmaxlen;
diff --git a/sql/opt_table_elimination.cc b/sql/opt_table_elimination.cc
index 2ef5655..5b3681e 100644
--- a/sql/opt_table_elimination.cc
+++ b/sql/opt_table_elimination.cc
@@ -1490,7 +1490,7 @@ void check_equality(Dep_analysis_context *ctx, Dep_module_expr **eq_mod,
     {
       if (right->result_type() != STRING_RESULT)
       {
-        if (field->cmp_type() != right->result_type())
+        if (field->cmp_type() != (Item_cmp) right->result_type())
           return;
       }
       else
@@ -1499,7 +1499,7 @@ void check_equality(Dep_analysis_context *ctx, Dep_module_expr **eq_mod,
           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 &&
+        if (field->cmp_type() == STRING_CMP &&
             field->charset() != cond->compare_collation())
           return;
       }
diff --git a/sql/procedure.h b/sql/procedure.h
index 6870b97..8be6039 100644
--- a/sql/procedure.h
+++ b/sql/procedure.h
@@ -46,7 +46,6 @@ class Item_proc :public Item
   virtual void set(double nr)=0;
   virtual void set(const char *str,uint length,CHARSET_INFO *cs)=0;
   virtual void set(longlong nr)=0;
-  virtual enum_field_types field_type() const=0;
   void set(const char *str) { set(str,(uint) strlen(str), default_charset()); }
   void make_field(Send_field *tmp_field)
   {
@@ -59,7 +58,7 @@ class Item_proc :public Item
   }
 };
 
-class Item_proc_real :public Item_proc
+class Item_proc_real :public Item_proc, public Type_handler_double
 {
   double value;
 public:
@@ -67,8 +66,6 @@ class Item_proc_real :public Item_proc
   {
      decimals=dec; max_length=float_length(dec);
   }
-  enum Item_result result_type () const { return REAL_RESULT; }
-  enum_field_types field_type() const { return MYSQL_TYPE_DOUBLE; }
   void set(double nr) { value=nr; }
   void set(longlong nr) { value=(double) nr; }
   void set(const char *str,uint length,CHARSET_INFO *cs)
@@ -88,14 +85,12 @@ class Item_proc_real :public Item_proc
   unsigned int size_of() { return sizeof(*this);}
 };
 
-class Item_proc_int :public Item_proc
+class Item_proc_int :public Item_proc, public Type_handler_longlong
 {
   longlong value;
 public:
   Item_proc_int(const char *name_par) :Item_proc(name_par)
   { max_length=11; }
-  enum Item_result result_type () const { return INT_RESULT; }
-  enum_field_types field_type() const { return MYSQL_TYPE_LONGLONG; }
   void set(double nr) { value=(longlong) nr; }
   void set(longlong nr) { value=nr; }
   void set(const char *str,uint length, CHARSET_INFO *cs)
@@ -108,13 +103,11 @@ class Item_proc_int :public Item_proc
 };
 
 
-class Item_proc_string :public Item_proc
+class Item_proc_string :public Item_proc, public Type_handler_varchar
 {
 public:
   Item_proc_string(const char *name_par,uint length) :Item_proc(name_par)
     { this->max_length=length; }
-  enum Item_result result_type () const { return STRING_RESULT; }
-  enum_field_types field_type() const { return MYSQL_TYPE_VARCHAR; }
   void set(double nr) { str_value.set_real(nr, 2, default_charset()); }
   void set(longlong nr) { str_value.set(nr, default_charset()); }
   void set(const char *str, uint length, CHARSET_INFO *cs)
diff --git a/sql/sql_class.cc b/sql/sql_class.cc
index 90c550d..50f5da7 100644
--- a/sql/sql_class.cc
+++ b/sql/sql_class.cc
@@ -3293,7 +3293,6 @@ int select_max_min_finder_subselect::send_data(List<Item> &items)
         break;
       case ROW_RESULT:
       case TIME_RESULT:
-      case IMPOSSIBLE_RESULT:
         // This case should never be choosen
 	DBUG_ASSERT(0);
 	op= 0;
diff --git a/sql/sql_class.h b/sql/sql_class.h
index 7a34960..09c5db7 100644
--- a/sql/sql_class.h
+++ b/sql/sql_class.h
@@ -25,6 +25,7 @@
 #endif
 #include <waiting_threads.h>
 #include "sql_const.h"
+#include "sql_type.h"
 #include <mysql/plugin_audit.h>
 #include "log.h"
 #include "rpl_tblmap.h"
@@ -4678,7 +4679,7 @@ class SJ_MATERIALIZATION_INFO : public Sql_alloc
   Item	*item;				/* Item if not sorting fields */
   uint	 length;			/* Length of sort field */
   uint   suffix_length;                 /* Length suffix (0-4) */
-  Item_result result_type;		/* Type of item */
+  Item_cmp cmp_type;                    /* Type of item */
   bool reverse;				/* if descending sort */
   bool need_strxnfrm;			/* If we have to use strxnfrm() */
 } SORT_FIELD;
@@ -4736,7 +4737,7 @@ class Table_ident :public Sql_alloc
 };
 
 // this is needed for user_vars hash
-class user_var_entry
+class user_var_entry: public Result_type
 {
  public:
   user_var_entry() {}                         /* Remove gcc warning */
@@ -4744,7 +4745,6 @@ class user_var_entry
   char *value;
   ulong length;
   query_id_t update_query_id, used_query_id;
-  Item_result type;
   bool unsigned_flag;
 
   double val_real(bool *null_value);
diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc
index 5c67a4c..59343ff 100644
--- a/sql/sql_prepare.cc
+++ b/sql/sql_prepare.cc
@@ -718,54 +718,54 @@ static void setup_one_conversion_function(THD *thd, Item_param *param,
   case MYSQL_TYPE_TINY:
     param->set_param_func= set_param_tiny;
     param->item_type= Item::INT_ITEM;
-    param->item_result_type= INT_RESULT;
+    param->Result_type::set(INT_RESULT);
     break;
   case MYSQL_TYPE_SHORT:
     param->set_param_func= set_param_short;
     param->item_type= Item::INT_ITEM;
-    param->item_result_type= INT_RESULT;
+    param->Result_type::set(INT_RESULT);
     break;
   case MYSQL_TYPE_LONG:
     param->set_param_func= set_param_int32;
     param->item_type= Item::INT_ITEM;
-    param->item_result_type= INT_RESULT;
+    param->Result_type::set(INT_RESULT);
     break;
   case MYSQL_TYPE_LONGLONG:
     param->set_param_func= set_param_int64;
     param->item_type= Item::INT_ITEM;
-    param->item_result_type= INT_RESULT;
+    param->Result_type::set(INT_RESULT);
     break;
   case MYSQL_TYPE_FLOAT:
     param->set_param_func= set_param_float;
     param->item_type= Item::REAL_ITEM;
-    param->item_result_type= REAL_RESULT;
+    param->Result_type::set(REAL_RESULT);
     break;
   case MYSQL_TYPE_DOUBLE:
     param->set_param_func= set_param_double;
     param->item_type= Item::REAL_ITEM;
-    param->item_result_type= REAL_RESULT;
+    param->Result_type::set(REAL_RESULT);
     break;
   case MYSQL_TYPE_DECIMAL:
   case MYSQL_TYPE_NEWDECIMAL:
     param->set_param_func= set_param_decimal;
     param->item_type= Item::DECIMAL_ITEM;
-    param->item_result_type= DECIMAL_RESULT;
+    param->Result_type::set(DECIMAL_RESULT);
     break;
   case MYSQL_TYPE_TIME:
     param->set_param_func= set_param_time;
     param->item_type= Item::STRING_ITEM;
-    param->item_result_type= STRING_RESULT;
+    param->Result_type::set(STRING_RESULT);
     break;
   case MYSQL_TYPE_DATE:
     param->set_param_func= set_param_date;
     param->item_type= Item::STRING_ITEM;
-    param->item_result_type= STRING_RESULT;
+    param->Result_type::set(STRING_RESULT);
     break;
   case MYSQL_TYPE_DATETIME:
   case MYSQL_TYPE_TIMESTAMP:
     param->set_param_func= set_param_datetime;
     param->item_type= Item::STRING_ITEM;
-    param->item_result_type= STRING_RESULT;
+    param->Result_type::set(STRING_RESULT);
     break;
   case MYSQL_TYPE_TINY_BLOB:
   case MYSQL_TYPE_MEDIUM_BLOB:
@@ -778,7 +778,7 @@ static void setup_one_conversion_function(THD *thd, Item_param *param,
     DBUG_ASSERT(thd->variables.character_set_client);
     param->value.cs_info.final_character_set_of_str_value= &my_charset_bin;
     param->item_type= Item::STRING_ITEM;
-    param->item_result_type= STRING_RESULT;
+    param->Result_type::set(STRING_RESULT);
     break;
   default:
     /*
@@ -808,7 +808,7 @@ static void setup_one_conversion_function(THD *thd, Item_param *param,
         charset of connection, so we have to set it later.
       */
       param->item_type= Item::STRING_ITEM;
-      param->item_result_type= STRING_RESULT;
+      param->Result_type::set(STRING_RESULT);
     }
   }
   param->param_type= (enum enum_field_types) param_type;
diff --git a/sql/sql_repl.cc b/sql/sql_repl.cc
index d9c8898..8adbdc6 100644
--- a/sql/sql_repl.cc
+++ b/sql/sql_repl.cc
@@ -405,7 +405,7 @@ static uint8 get_binlog_checksum_value_at_connect(THD * thd)
   }
   else
   {
-    DBUG_ASSERT(entry->type == STRING_RESULT);
+    DBUG_ASSERT(entry->type() == STRING_RESULT);
     String str;
     uint dummy_errors;
     str.copy(entry->value, entry->length, &my_charset_bin, &my_charset_bin,
diff --git a/sql/sql_select.cc b/sql/sql_select.cc
index 64d2e91..6dc7c2d 100644
--- a/sql/sql_select.cc
+++ b/sql/sql_select.cc
@@ -4449,9 +4449,9 @@ static uint get_semi_join_select_list_index(Field *field)
         of the operation differ from the field collation.
        */
 
-      if (field->cmp_type() == STRING_RESULT)
+      if (field->cmp_type() == STRING_CMP)
       {
-        if ((*value)->cmp_type() != STRING_RESULT)
+        if ((*value)->cmp_type() != STRING_CMP)
             return;
         if (field->charset() != cond->compare_collation())
           return;
@@ -12451,7 +12451,7 @@ static bool check_simple_equality(Item *left_item, Item *right_item,
     {
       bool copyfl;
 
-      if (field_item->cmp_type() == STRING_RESULT)
+      if (field_item->cmp_type() == STRING_CMP)
       {
         CHARSET_INFO *cs= field_item->field->charset();
         if (!item)
@@ -15104,7 +15104,7 @@ bool cond_is_datetime_is_null(Item *cond)
 {
   return (r->const_item() || !(r->used_tables() & ~OUTER_REF_TABLE_BIT)) &&
     item_cmp_type(l->cmp_type(), r->cmp_type()) == l->cmp_type() &&
-    (l->cmp_type() != STRING_RESULT ||
+    (l->cmp_type() != STRING_CMP ||
      l->collation.collation == r->collation.collation);
 }
 
@@ -15334,7 +15334,7 @@ static Field *create_tmp_field_from_item(THD *thd, Item *item, TABLE *table,
       DATE/TIME and GEOMETRY fields have STRING_RESULT result type. 
       To preserve type they needed to be handled separately.
     */
-    if (item->cmp_type() == TIME_RESULT ||
+    if (item->cmp_type() == TIME_CMP ||
         item->field_type() == MYSQL_TYPE_GEOMETRY)
       new_field= item->tmp_table_field_from_field_type(table, 1);
     /* 
diff --git a/sql/sql_type.cc b/sql/sql_type.cc
new file mode 100644
index 0000000..e7bdaa1
--- /dev/null
+++ b/sql/sql_type.cc
@@ -0,0 +1,92 @@
+/* Copyright (c) 2014, MariaDB foundation.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; version 2 of the License.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA */
+
+/* Data type related definitions */
+
+#include "my_global.h"
+#include "sql_type.h"
+#include "item.h"
+
+
+Type_handler_longlong       type_handler_longlong;
+Type_handler_double         type_handler_double;
+Type_handler_newdecimal     type_handler_newdecimal;
+Type_handler_varchar        type_handler_varchar;
+Type_handler_row            type_handler_row;
+
+
+Result_type::Result_type()
+ :m_handler(&type_handler_double)
+{ }
+
+
+const Type_handler *
+Result_type::handler_by_result_type(Item_result type)
+{
+  switch (type)
+  {
+  case INT_RESULT:     return &type_handler_longlong;
+  case REAL_RESULT:    return &type_handler_double;
+  case DECIMAL_RESULT: return &type_handler_newdecimal;
+  case STRING_RESULT:  return &type_handler_varchar;
+  case ROW_RESULT:     return &type_handler_row;
+  case TIME_RESULT:    break; // Not a valid result type (yet)
+  }
+  return &type_handler_varchar;
+  DBUG_ASSERT(0);
+}
+
+
+static Item_result item_store_type(Item_result a, Item *item,
+                                   my_bool unsigned_flag)
+{
+  Item_result b= item->result_type();
+
+  if (a == STRING_RESULT || b == STRING_RESULT)
+    return STRING_RESULT;
+  else if (a == REAL_RESULT || b == REAL_RESULT)
+    return REAL_RESULT;
+  else if (a == DECIMAL_RESULT || b == DECIMAL_RESULT ||
+           unsigned_flag != item->unsigned_flag)
+    return DECIMAL_RESULT;
+  else
+    return INT_RESULT;
+}
+
+
+void Result_type::aggregate(Item **items, uint nitems)
+{
+  Item **item, **item_end;
+  my_bool unsigned_flag= 0;
+
+  m_handler= &type_handler_varchar;
+  /* Skip beginning NULL items */
+  for (item= items, item_end= item + nitems; item < item_end; item++)
+  {
+    if ((*item)->type() != Item::NULL_ITEM)
+    {
+      set((*item)->result_type());
+      unsigned_flag= (*item)->unsigned_flag;
+      item++;
+      break;
+    }
+  }
+  /* Combine result types. Note: NULL items don't affect the result */
+  for (; item < item_end; item++)
+  {
+    if ((*item)->type() != Item::NULL_ITEM)
+      set(item_store_type(type(), *item, unsigned_flag));
+  }
+}
diff --git a/sql/sql_type.h b/sql/sql_type.h
new file mode 100644
index 0000000..5982aea
--- /dev/null
+++ b/sql/sql_type.h
@@ -0,0 +1,395 @@
+#ifndef SQL_TYPE_INCLUDED
+#define SQL_TYPE_INCLUDED
+/* Copyright (c) 2014, MariaDB foundation.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; version 2 of the License.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA */
+
+/* Data type related definitions */
+
+#include "mysql_com.h"
+
+
+/*
+  Comparison type.
+  Define XXX_CMP values to be equal to the corresponding XXX_RESULT,
+  to we can safely cast like this:
+    Item_cmp cmp_type= (Item_cmp) result_type;
+  Note, casting the other way around (Item_cmp -> Item_result) is not safe!
+*/
+enum Item_cmp
+{
+  STRING_CMP=     STRING_RESULT,
+  REAL_CMP=       REAL_RESULT,
+  INT_CMP=        INT_RESULT,
+  ROW_CMP=        ROW_RESULT,
+  DECIMAL_CMP=    DECIMAL_RESULT,
+  TIME_CMP=       TIME_RESULT,
+  IMPOSSIBLE_CMP
+};
+
+
+/**
+  Item data type attributes which are need in the Type_handler API.
+  Currently only max_length is needed to choose between VARCHAR and BLOB
+  variants properly when detecting field_type() from result_type().
+
+  More attributes will move from Item to here later,
+  when we add more methods in Type_handler.
+
+  It would be nice to encapsulate members into methods eventually.
+  For example, max_length could be calculated for temporal data types
+  using type code and fractional precision. There's no really a need to assign
+  max_length in fix_length_and_dec() for temporal items.
+*/
+class Type_attributes_for_item
+{
+public:
+  /*
+    The maximum value length in characters multiplied by collation->mbmaxlen.
+    Almost always it's the maximum value length in bytes.
+  */
+  uint32 max_length;
+  Type_attributes_for_item()
+   :max_length(0)
+  { }
+  Type_attributes_for_item(uint32 max_length_arg)
+   :max_length(max_length_arg)
+  { }
+  Type_attributes_for_item(const Type_attributes_for_item *other)
+   :max_length(other->max_length)
+  { }
+  enum_field_types string_field_type() const
+  {
+    enum_field_types f_type= MYSQL_TYPE_VAR_STRING;
+    if (max_length >= 16777216)
+      f_type= MYSQL_TYPE_LONG_BLOB;
+    else if (max_length >= 65536)
+      f_type= MYSQL_TYPE_MEDIUM_BLOB;
+    return f_type;
+  }
+};
+
+
+class Type_handler
+{
+public:
+  virtual Item_result result_type() const = 0;
+  virtual enum_field_types field_type() const = 0;
+  virtual Item_cmp cmp_type() const = 0;
+};
+
+
+class Type_handler_longlong: public virtual Type_handler
+{
+public:
+  Item_result result_type() const { return INT_RESULT; }
+  enum_field_types field_type() const { return MYSQL_TYPE_LONGLONG; }
+  Item_cmp cmp_type() const { return INT_CMP; }
+};
+
+
+class Type_handler_tiny: public virtual Type_handler
+{
+public:
+  Item_result result_type() const { return INT_RESULT; }
+  enum_field_types field_type() const { return MYSQL_TYPE_TINY; }
+  Item_cmp cmp_type() const { return INT_CMP; }
+};
+
+
+class Type_handler_year: public virtual Type_handler
+{
+public:
+  Item_result result_type() const { return INT_RESULT; }
+  enum_field_types field_type() const { return MYSQL_TYPE_YEAR; }
+  Item_cmp cmp_type() const { return INT_CMP; }
+};
+
+
+class Type_handler_short: public virtual Type_handler
+{
+public:
+  Item_result result_type() const { return INT_RESULT; }
+  enum_field_types field_type() const { return MYSQL_TYPE_SHORT; }
+  Item_cmp cmp_type() const { return INT_CMP; }
+};
+
+
+class Type_handler_medium: public virtual Type_handler
+{
+public:
+  Item_result result_type() const { return INT_RESULT; }
+  enum_field_types field_type() const { return MYSQL_TYPE_INT24; }
+  Item_cmp cmp_type() const { return INT_CMP; }
+};
+
+
+class Type_handler_long: public virtual Type_handler
+{
+public:
+  Item_result result_type() const { return INT_RESULT; }
+  enum_field_types field_type() const { return MYSQL_TYPE_LONG; }
+  Item_cmp cmp_type() const { return INT_CMP; }
+};
+
+
+class Type_handler_bit: public virtual Type_handler
+{
+public:
+  Item_result result_type() const { return INT_RESULT; }
+  enum_field_types field_type() const { return MYSQL_TYPE_BIT; }
+  Item_cmp cmp_type() const { return INT_CMP; }
+};
+class Type_handler_double: public virtual Type_handler
+{
+public:
+  Item_result result_type() const { return REAL_RESULT; }
+  enum_field_types field_type() const { return MYSQL_TYPE_DOUBLE; }
+  Item_cmp cmp_type() const { return REAL_CMP; }
+};
+
+
+class Type_handler_float: public virtual Type_handler
+{
+public:
+  Item_result result_type() const { return REAL_RESULT; }
+  enum_field_types field_type() const { return MYSQL_TYPE_FLOAT; }
+  Item_cmp cmp_type() const { return REAL_CMP; }
+};
+
+
+class Type_handler_newdecimal: public virtual Type_handler
+{
+public:
+  Item_result result_type() const { return DECIMAL_RESULT; }
+  enum_field_types field_type() const { return MYSQL_TYPE_NEWDECIMAL; }
+  Item_cmp cmp_type() const { return DECIMAL_CMP; }
+};
+
+
+// Old decimal
+class Type_handler_decimal: public virtual Type_handler
+{
+public:
+  Item_result result_type() const { return REAL_RESULT; }
+  enum_field_types field_type() const { return MYSQL_TYPE_DECIMAL; }
+  Item_cmp cmp_type() const { return REAL_CMP; }
+};
+
+
+class Type_handler_varstring: public virtual Type_handler
+{
+public:
+  Item_result result_type() const { return STRING_RESULT; }
+  enum_field_types field_type() const { return MYSQL_TYPE_VAR_STRING; }
+  Item_cmp cmp_type() const { return STRING_CMP; }
+};
+
+
+class Type_handler_varchar: public virtual Type_handler
+{
+public:
+  Item_result result_type() const { return STRING_RESULT; }
+  enum_field_types field_type() const { return MYSQL_TYPE_VARCHAR; }
+  Item_cmp cmp_type() const { return STRING_CMP; }
+};
+
+
+class Type_handler_char: public virtual Type_handler
+{
+public:
+  Item_result result_type() const { return STRING_RESULT; }
+  enum_field_types field_type() const { return MYSQL_TYPE_STRING; }
+  Item_cmp cmp_type() const { return STRING_CMP; }
+};
+
+
+class Type_handler_blob: public virtual Type_handler
+{
+public:
+  Item_result result_type() const { return STRING_RESULT; }
+  enum_field_types field_type() const { return MYSQL_TYPE_BLOB; }
+  Item_cmp cmp_type() const { return STRING_CMP; }
+};
+
+
+#ifdef HAVE_SPATIAL
+class Type_handler_geometry: public virtual Type_handler
+{
+public:
+  Item_result result_type() const { return STRING_RESULT; }
+  enum_field_types field_type() const { return MYSQL_TYPE_GEOMETRY; }
+  Item_cmp cmp_type() const { return STRING_CMP; }
+};
+#endif
+
+class Type_handler_enum: public virtual Type_handler
+{
+public:
+  Item_result result_type() const { return STRING_RESULT; }
+  enum_field_types field_type() const { return MYSQL_TYPE_STRING; }
+  Item_cmp cmp_type() const { return INT_CMP; }
+};
+
+
+class Type_handler_time: public virtual Type_handler
+{
+public:
+  Item_result result_type() const { return STRING_RESULT; }
+  enum_field_types field_type() const { return MYSQL_TYPE_TIME; }
+  Item_cmp cmp_type() const { return TIME_CMP; }
+};
+
+
+class Type_handler_date: public virtual Type_handler
+{
+public:
+  Item_result result_type() const { return STRING_RESULT; }
+  enum_field_types field_type() const { return MYSQL_TYPE_DATE; }
+  Item_cmp cmp_type() const { return TIME_CMP; }
+};
+
+
+class Type_handler_datetime: public virtual Type_handler
+{
+public:
+  Item_result result_type() const { return STRING_RESULT; }
+  enum_field_types field_type() const { return MYSQL_TYPE_DATETIME; }
+  Item_cmp cmp_type() const { return TIME_CMP; }
+};
+
+
+class Type_handler_timestamp: public virtual Type_handler
+{
+public:
+  Item_result result_type() const { return STRING_RESULT; }
+  enum_field_types field_type() const { return MYSQL_TYPE_TIMESTAMP; }
+  Item_cmp cmp_type() const { return TIME_CMP; }
+};
+
+
+class Type_handler_null: public virtual Type_handler
+{
+public:
+  Item_result result_type() const { return STRING_RESULT; }
+  Item_cmp cmp_type() const { return STRING_CMP; }
+  enum_field_types field_type() const { return MYSQL_TYPE_NULL; }
+};
+
+
+class Type_handler_row: public virtual Type_handler
+{
+public:
+  Item_result result_type() const { return ROW_RESULT; }
+  enum_field_types field_type() const
+  { DBUG_ASSERT(0); return MYSQL_TYPE_DOUBLE; }
+  Item_cmp cmp_type() const { return ROW_CMP; }
+};
+
+
+/**
+  Classes to handler Item_result
+*/
+
+
+/**
+  A class to store cached result type, for hybrid result items.
+*/
+class Result_type
+{
+  const Type_handler *m_handler;
+  /**
+    Make sure that Item_result is a valid value and
+    return a Type_handler pointer for it.
+    For STRING_RESULT items, a pointer to Type_handler_varchar is returned.
+  */
+  const Type_handler *handler_by_result_type(Item_result type);
+public:
+  Result_type();
+  Result_type(Item_result type)
+   :m_handler(handler_by_result_type(type))
+  { }
+  Result_type(const Result_type *other)
+    :m_handler(other->m_handler)
+  { }
+  void set(Item_result type)
+  {
+    m_handler= handler_by_result_type(type);
+  }
+  void set(const Result_type *other)
+  {
+    m_handler= other->m_handler;
+  }
+  Item_result type() const { return m_handler->result_type(); }
+  enum_field_types field_type(const Type_attributes_for_item &attr) const
+  {
+    switch (type())
+    {
+    case INT_RESULT:     return MYSQL_TYPE_LONGLONG;
+    case REAL_RESULT:    return MYSQL_TYPE_DOUBLE;
+    case DECIMAL_RESULT: return MYSQL_TYPE_NEWDECIMAL;
+    case STRING_RESULT:  return attr.string_field_type();
+    case TIME_RESULT:    break; // Not a valid result_type (yet)
+    case ROW_RESULT:     break; // Does not have a field type
+    }
+    DBUG_ASSERT(0); // Invalid value
+    return MYSQL_TYPE_DOUBLE;
+  }
+  Item_cmp cmp_type() const { return m_handler->cmp_type(); }
+  const Type_handler *type_handler() const { return m_handler; }
+  void aggregate(Item **items, uint nitems);
+};
+
+
+/**
+  A Type_handler wrapper for Result_type.
+  It does not define field_type() without parameters,
+  as we don't have valid attributes at time of creation.
+  Attributes are needed, for example, to choose between VARCHAR and BLOB
+  properly. Attributes get fixed later, in fix_length_and_dec().
+  Descendants of Type_handler_hybrid_result are responsible to define
+  field_type() without attributes like this:
+  enum_field_types field_type() const
+  { return Type_handler_hybrid_result::field_type(*this); }
+  to pass its own attributes.
+*/
+class Type_handler_hybrid_result: public virtual Type_handler,
+                                  public Result_type
+{
+public:
+  Type_handler_hybrid_result() :Result_type() { }
+  Type_handler_hybrid_result(Item_result type) :Result_type(type) { }
+  Type_handler_hybrid_result(const Result_type *other) :Result_type(other) { }
+
+  Item_result result_type() const { return Result_type::type(); }
+  Item_cmp cmp_type() const { return Result_type::cmp_type(); }
+  enum_field_types field_type(const Type_attributes_for_item &attr) const
+  { return Result_type::field_type(attr); }
+};
+
+
+/**
+  VARCHAR / MEDIUM BLOB / BLOB, depending on max_length
+  It does not define field_type() without arguments.
+  See comment in Type_handler_hybrid_result.
+*/
+class Type_handler_string_hybrid: public virtual Type_handler
+{
+public:
+  Item_result result_type() const { return STRING_RESULT; }
+  Item_cmp cmp_type() const { return STRING_CMP; }
+};
+ 
+
+#endif /* SQL_TYPE_INCLUDED */