commit ee91200893adaec59ad7543711a230f6417815f0
Author: Alexander Barkov <bar@mariadb.org>
Date:   Mon Dec 5 20:15:36 2016 +0400

    MDEV-11485 Split Item_func_between::val_int() into virtual methods in Type_handler
    
    - Removes "Item_result Item_func_opt_neg::m_compare_type" and introduces
      "Type_handler_hybrid_field_type Item_func_opt_neg::m_comparator" instead.
    
    - Removes Item_func_between::compare_as_dates, because
      the new member m_comparator now contains the precise information
      about the data type that is used for comparison, which is important
      for TIME vs DATETIME.
    
    - Adds a new method:
      Type_handler_hybrid_field_type::aggregate_for_comparison(const Type_handler*),
      as a better replacement for item_cmp_type(), which additionally can handle
      TIME vs DATE/DATETIME/TIMESTAMP correctly. Additionally, it correctly
      handles TIMESTAMP which fixes the problem reported in MDEV-11482.
      The old compare_as_dates/find_date_time_item() based code didn't handle
      comparison between TIME and TIMESTAMP correctly and erroneously used TIME
      comparison instead of DATETIME comparison.
    
    - Adds a new method:
      Type_handler_hybrid_field_type::aggregate_for_comparison(Item **, uint nitems),
      as a better replacement for agg_cmp_type(), which can handle TIME.
    - Splits Item_func_between::val_int() into pieces val_int_cmp_xxx(),
      one new method per XXX_RESULT.
    - Adds a new virtual method Type_handler::Item_func_between_val_int()
      whose implementations use Item_func_between::val_int_cmp_xxx().
    - Makes type_handler_longlong and type_handler_newdecimal public,
      as they are now needed in item_cmpfunc.cc.
    
    Note:
    This patch does not change Item_func_in to use the new aggregation methods,
    so it still uses collect_cmp_type()/item_cmp_type() based aggregation.
    Item_func_in will be changed in a separate patch and item_cmp_type() will be
    removed.

diff --git a/mysql-test/r/type_timestamp.result b/mysql-test/r/type_timestamp.result
index d4afed8..577ae5a 100644
--- a/mysql-test/r/type_timestamp.result
+++ b/mysql-test/r/type_timestamp.result
@@ -980,5 +980,17 @@ Warnings:
 Warning	1441	Datetime function: datetime field overflow
 DROP TABLE t1;
 #
+# MDEV-11482 Incorrect result for (time_expr BETWEEN timestamp_exp1 AND timestamp_expr2)
+#
+SET @@sql_mode=DEFAULT;
+SET @@timestamp=UNIX_TIMESTAMP('2001-01-01 10:20:30');
+CREATE TABLE t1 (a TIMESTAMP,b TIMESTAMP);
+INSERT INTO t1 VALUES ('2001-01-01 00:00:00','2001-01-01 23:59:59');
+SELECT * FROM t1 WHERE TIME'10:20:30' BETWEEN a and b;
+a	b
+2001-01-01 00:00:00	2001-01-01 23:59:59
+DROP TABLE t1;
+SET @@timestamp=DEFAULT;
+#
 # End of 10.3 tests
 #
diff --git a/mysql-test/t/type_timestamp.test b/mysql-test/t/type_timestamp.test
index 2dad92b..7f32f7a 100644
--- a/mysql-test/t/type_timestamp.test
+++ b/mysql-test/t/type_timestamp.test
@@ -577,5 +577,16 @@ EXPLAIN SELECT * FROM t1 WHERE a >= COALESCE(DATE_ADD(TIMESTAMP'9999-01-01 00:00
 DROP TABLE t1;
 
 --echo #
+--echo # MDEV-11482 Incorrect result for (time_expr BETWEEN timestamp_exp1 AND timestamp_expr2)
+--echo #
+SET @@sql_mode=DEFAULT;
+SET @@timestamp=UNIX_TIMESTAMP('2001-01-01 10:20:30');
+CREATE TABLE t1 (a TIMESTAMP,b TIMESTAMP);
+INSERT INTO t1 VALUES ('2001-01-01 00:00:00','2001-01-01 23:59:59');
+SELECT * FROM t1 WHERE TIME'10:20:30' BETWEEN a and b;
+DROP TABLE t1;
+SET @@timestamp=DEFAULT;
+
+--echo #
 --echo # End of 10.3 tests
 --echo #
diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc
index 98b179b..65754d2 100644
--- a/sql/item_cmpfunc.cc
+++ b/sql/item_cmpfunc.cc
@@ -118,14 +118,15 @@ static int cmp_row_type(Item* item1, Item* item2)
     0  otherwise
 */
 
-static int agg_cmp_type(Item_result *type, Item **items, uint nitems)
+bool Type_handler_hybrid_field_type::aggregate_for_comparison(Item **items,
+                                                              uint nitems)
 {
   uint unsigned_count= items[0]->unsigned_flag;
-  type[0]= items[0]->cmp_type();
+  set_handler(items[0]->type_handler());
   for (uint i= 1 ; i < nitems ; i++)
   {
     unsigned_count+= items[i]->unsigned_flag;
-    type[0]= item_cmp_type(type[0], items[i]);
+    aggregate_for_comparison(items[i]->type_handler());
     /*
       When aggregating types of two row expressions we have to check
       that they have the same cardinality and that each component
@@ -133,15 +134,16 @@ 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]))
-      return 1;     // error found: invalid usage of rows
+    if (cmp_type() == ROW_RESULT && cmp_row_type(items[0], items[i]))
+      return true;     // error found: invalid usage of rows
   }
   /**
     If all arguments are of INT type but have different unsigned_flag values,
     switch to DECIMAL_RESULT.
   */
-  if (type[0] == INT_RESULT && unsigned_count != nitems && unsigned_count != 0)
-    type[0]= DECIMAL_RESULT;
+  if (cmp_type() == INT_RESULT &&
+      unsigned_count != nitems && unsigned_count != 0)
+    set_handler(&type_handler_newdecimal);
   return 0;
 }
 
@@ -2081,7 +2083,6 @@ void Item_func_between::fix_length_and_dec()
 {
   THD *thd= current_thd;
   max_length= 1;
-  compare_as_dates= 0;
 
   /*
     As some compare functions are generated after sql_yacc,
@@ -2089,24 +2090,13 @@ void Item_func_between::fix_length_and_dec()
   */
   if (!args[0] || !args[1] || !args[2])
     return;
-  if (agg_cmp_type(&m_compare_type, args, 3))
+  if (m_comparator.aggregate_for_comparison(args, 3))
     return;
 
-  if (m_compare_type == STRING_RESULT &&
+  if (m_comparator.cmp_type() == STRING_RESULT &&
       agg_arg_charsets_for_comparison(cmp_collation, args, 3))
    return;
 
-  /*
-    When comparing as date/time, we need to convert non-temporal values
-    (e.g.  strings) to MYSQL_TIME. get_datetime_value() does it
-    automatically when one of the operands is a date/time.  But here we
-    may need to compare two strings as dates (str1 BETWEEN str2 AND date).
-    For this to work, we need to know what date/time type we compare
-    strings as.
-  */
-  if (m_compare_type ==  TIME_RESULT)
-    compare_as_dates= find_date_time_item(args, 3, 0);
-
   /* See the comment about the similar block in Item_bool_func2 */
   if (args[0]->real_item()->type() == FIELD_ITEM &&
       !thd->lex->is_ps_or_view_context_analysis())
@@ -2118,145 +2108,143 @@ 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)
-        m_compare_type= INT_RESULT;              // Works for all types.
+      {
+        // Works for all types
+        m_comparator.set_handler(&type_handler_longlong);
+      }
     }
   }
 }
 
 
-longlong Item_func_between::val_int()
+longlong Item_func_between::val_int_cmp_temporal()
 {
-  DBUG_ASSERT(fixed == 1);
-
-  switch (m_compare_type) {
-  case TIME_RESULT:
-  {
-    THD *thd= current_thd;
-    longlong value, a, b;
-    Item *cache, **ptr;
-    bool value_is_null, a_is_null, b_is_null;
+  THD *thd= current_thd;
+  longlong value, a, b;
+  Item *cache, **ptr;
+  bool value_is_null, a_is_null, b_is_null;
 
-    ptr= &args[0];
-    enum_field_types f_type= field_type_for_temporal_comparison(compare_as_dates);
-    value= get_datetime_value(thd, &ptr, &cache, f_type, &value_is_null);
-    if (ptr != &args[0])
-      thd->change_item_tree(&args[0], *ptr);
+  ptr= &args[0];
+  enum_field_types f_type= m_comparator.field_type();
+  value= get_datetime_value(thd, &ptr, &cache, f_type, &value_is_null);
+  if (ptr != &args[0])
+    thd->change_item_tree(&args[0], *ptr);
 
-    if ((null_value= value_is_null))
-      return 0;
+  if ((null_value= value_is_null))
+    return 0;
 
-    ptr= &args[1];
-    a= get_datetime_value(thd, &ptr, &cache, f_type, &a_is_null);
-    if (ptr != &args[1])
-      thd->change_item_tree(&args[1], *ptr);
+  ptr= &args[1];
+  a= get_datetime_value(thd, &ptr, &cache, f_type, &a_is_null);
+  if (ptr != &args[1])
+    thd->change_item_tree(&args[1], *ptr);
+
+  ptr= &args[2];
+  b= get_datetime_value(thd, &ptr, &cache, f_type, &b_is_null);
+  if (ptr != &args[2])
+    thd->change_item_tree(&args[2], *ptr);
+
+  if (!a_is_null && !b_is_null)
+    return (longlong) ((value >= a && value <= b) != negated);
+  if (a_is_null && b_is_null)
+    null_value= true;
+  else if (a_is_null)
+    null_value= value <= b;			// not null if false range.
+  else
+    null_value= value >= a;
+  return (longlong) (!null_value && negated);
+}
 
-    ptr= &args[2];
-    b= get_datetime_value(thd, &ptr, &cache, f_type, &b_is_null);
-    if (ptr != &args[2])
-      thd->change_item_tree(&args[2], *ptr);
 
-    if (!a_is_null && !b_is_null)
-      return (longlong) ((value >= a && value <= b) != negated);
-    if (a_is_null && b_is_null)
-      null_value=1;
-    else if (a_is_null)
-      null_value= value <= b;			// not null if false range.
-    else
-      null_value= value >= a;
-    break;
+longlong Item_func_between::val_int_cmp_string()
+{
+  String *value,*a,*b;
+  value=args[0]->val_str(&value0);
+  if ((null_value=args[0]->null_value))
+    return 0;
+  a= args[1]->val_str(&value1);
+  b= args[2]->val_str(&value2);
+  if (!args[1]->null_value && !args[2]->null_value)
+    return (longlong) ((sortcmp(value,a,cmp_collation.collation) >= 0 &&
+                        sortcmp(value,b,cmp_collation.collation) <= 0) !=
+                       negated);
+  if (args[1]->null_value && args[2]->null_value)
+    null_value= true;
+  else if (args[1]->null_value)
+  {
+    // Set to not null if false range.
+    null_value= sortcmp(value,b,cmp_collation.collation) <= 0;
   }
-
-  case STRING_RESULT:
+  else
   {
-    String *value,*a,*b;
-    value=args[0]->val_str(&value0);
-    if ((null_value=args[0]->null_value))
-      return 0;
-    a=args[1]->val_str(&value1);
-    b=args[2]->val_str(&value2);
-    if (!args[1]->null_value && !args[2]->null_value)
-      return (longlong) ((sortcmp(value,a,cmp_collation.collation) >= 0 &&
-                          sortcmp(value,b,cmp_collation.collation) <= 0) !=
-                         negated);
-    if (args[1]->null_value && args[2]->null_value)
-      null_value=1;
-    else if (args[1]->null_value)
-    {
-      // Set to not null if false range.
-      null_value= sortcmp(value,b,cmp_collation.collation) <= 0;
-    }
-    else
-    {
-      // Set to not null if false range.
-      null_value= sortcmp(value,a,cmp_collation.collation) >= 0;
-    }
-    break;
+    // Set to not null if false range.
+    null_value= sortcmp(value,a,cmp_collation.collation) >= 0;
   }
-  case INT_RESULT:
+  return (longlong) (!null_value && negated);
+}
+
+
+longlong Item_func_between::val_int_cmp_int()
+{
+  longlong value= args[0]->val_int(), a, b;
+  if ((null_value= args[0]->null_value))
+    return 0;					/* purecov: inspected */
+  a= args[1]->val_int();
+  b= args[2]->val_int();
+  if (!args[1]->null_value && !args[2]->null_value)
+    return (longlong) ((value >= a && value <= b) != negated);
+  if (args[1]->null_value && args[2]->null_value)
+    null_value= true;
+  else if (args[1]->null_value)
   {
-    longlong value=args[0]->val_int(), a, b;
-    if ((null_value=args[0]->null_value))
-      return 0;					/* purecov: inspected */
-    a=args[1]->val_int();
-    b=args[2]->val_int();
-    if (!args[1]->null_value && !args[2]->null_value)
-      return (longlong) ((value >= a && value <= b) != negated);
-    if (args[1]->null_value && args[2]->null_value)
-      null_value=1;
-    else if (args[1]->null_value)
-    {
-      null_value= value <= b;			// not null if false range.
-    }
-    else
-    {
-      null_value= value >= a;
-    }
-    break;
+    null_value= value <= b;			// not null if false range.
   }
-  case DECIMAL_RESULT:
+  else
   {
-    my_decimal dec_buf, *dec= args[0]->val_decimal(&dec_buf),
-               a_buf, *a_dec, b_buf, *b_dec;
-    if ((null_value=args[0]->null_value))
-      return 0;					/* purecov: inspected */
-    a_dec= args[1]->val_decimal(&a_buf);
-    b_dec= args[2]->val_decimal(&b_buf);
-    if (!args[1]->null_value && !args[2]->null_value)
-      return (longlong) ((my_decimal_cmp(dec, a_dec) >= 0 &&
-                          my_decimal_cmp(dec, b_dec) <= 0) != negated);
-    if (args[1]->null_value && args[2]->null_value)
-      null_value=1;
-    else if (args[1]->null_value)
-      null_value= (my_decimal_cmp(dec, b_dec) <= 0);
-    else
-      null_value= (my_decimal_cmp(dec, a_dec) >= 0);
-    break;
+    null_value= value >= a;
   }
-  case REAL_RESULT:
+  return (longlong) (!null_value && negated);
+}
+
+
+longlong Item_func_between::val_int_cmp_decimal()
+{
+  my_decimal dec_buf, *dec= args[0]->val_decimal(&dec_buf),
+             a_buf, *a_dec, b_buf, *b_dec;
+  if ((null_value=args[0]->null_value))
+    return 0;					/* purecov: inspected */
+  a_dec= args[1]->val_decimal(&a_buf);
+  b_dec= args[2]->val_decimal(&b_buf);
+  if (!args[1]->null_value && !args[2]->null_value)
+    return (longlong) ((my_decimal_cmp(dec, a_dec) >= 0 &&
+                        my_decimal_cmp(dec, b_dec) <= 0) != negated);
+  if (args[1]->null_value && args[2]->null_value)
+    null_value= true;
+  else if (args[1]->null_value)
+    null_value= (my_decimal_cmp(dec, b_dec) <= 0);
+  else
+    null_value= (my_decimal_cmp(dec, a_dec) >= 0);
+  return (longlong) (!null_value && negated);
+}
+
+
+longlong Item_func_between::val_int_cmp_real()
+{
+  double value= args[0]->val_real(),a,b;
+  if ((null_value=args[0]->null_value))
+    return 0;					/* purecov: inspected */
+  a= args[1]->val_real();
+  b= args[2]->val_real();
+  if (!args[1]->null_value && !args[2]->null_value)
+    return (longlong) ((value >= a && value <= b) != negated);
+  if (args[1]->null_value && args[2]->null_value)
+    null_value= true;
+  else if (args[1]->null_value)
   {
-    double value= args[0]->val_real(),a,b;
-    if ((null_value=args[0]->null_value))
-      return 0;					/* purecov: inspected */
-    a= args[1]->val_real();
-    b= args[2]->val_real();
-    if (!args[1]->null_value && !args[2]->null_value)
-      return (longlong) ((value >= a && value <= b) != negated);
-    if (args[1]->null_value && args[2]->null_value)
-      null_value=1;
-    else if (args[1]->null_value)
-    {
-      null_value= value <= b;			// not null if false range.
-    }
-    else
-    {
-      null_value= value >= a;
-    }
-    break;
+    null_value= value <= b;			// not null if false range.
   }
-  case ROW_RESULT:
-    DBUG_ASSERT(0);
-    null_value= 1;
-    return 0;
+  else
+  {
+    null_value= value >= a;
   }
   return (longlong) (!null_value && negated);
 }
@@ -4133,7 +4121,7 @@ void Item_func_in::fix_length_and_dec()
   Item *date_arg= 0;
   uint found_types= 0;
   uint type_cnt= 0, i;
-  m_compare_type= STRING_RESULT;
+  m_comparator.set_handler(&type_handler_varchar);
   left_cmp_type= args[0]->cmp_type();
   if (!(found_types= collect_cmp_types(args, arg_count, true)))
     return;
@@ -4151,7 +4139,7 @@ void Item_func_in::fix_length_and_dec()
     if (found_types & (1U << i))
     {
       (type_cnt)++;
-      m_compare_type= (Item_result) i;
+      m_comparator.set_handler_by_cmp_type((Item_result) i);
     }
   }
 
@@ -4175,7 +4163,7 @@ void Item_func_in::fix_length_and_dec()
         4. Neither left expression nor <in value list> contain any NULL value
       */
 
-    if (m_compare_type == ROW_RESULT &&
+    if (m_comparator.cmp_type() == ROW_RESULT &&
         ((!is_top_level_item() || negated) &&              // 3
          (list_contains_null() || args[0]->maybe_null)))   // 4
       bisection_possible= false;
@@ -4183,12 +4171,12 @@ void Item_func_in::fix_length_and_dec()
 
   if (type_cnt == 1)
   {
-    if (m_compare_type == STRING_RESULT &&
+    if (m_comparator.cmp_type() == STRING_RESULT &&
         agg_arg_charsets_for_comparison(cmp_collation, args, arg_count))
       return;
     arg_types_compatible= TRUE;
 
-    if (m_compare_type == ROW_RESULT)
+    if (m_comparator.cmp_type() == ROW_RESULT)
     {
       uint cols= args[0]->cols();
       cmp_item_row *cmp= 0;
@@ -4235,7 +4223,8 @@ 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() && m_compare_type != INT_RESULT)
+        !thd->lex->is_view_context_analysis() &&
+        m_comparator.cmp_type() != INT_RESULT)
     {
       Item_field *field_item= (Item_field*) (args[0]->real_item());
       if (field_item->field_type() ==  MYSQL_TYPE_LONGLONG ||
@@ -4248,10 +4237,10 @@ void Item_func_in::fix_length_and_dec()
             all_converted= FALSE;
         }
         if (all_converted)
-          m_compare_type= INT_RESULT;
+          m_comparator.set_handler(&type_handler_longlong);
       }
     }
-    switch (m_compare_type) {
+    switch (m_comparator.cmp_type()) {
     case STRING_RESULT:
       array=new (thd->mem_root) in_string(thd, arg_count - 1,
                                           (qsort2_cmp) srtcmp_in,
diff --git a/sql/item_cmpfunc.h b/sql/item_cmpfunc.h
index 98bcb2d..bf7f8fe 100644
--- a/sql/item_cmpfunc.h
+++ b/sql/item_cmpfunc.h
@@ -830,10 +830,10 @@ class Item_func_opt_neg :public Item_bool_func
 {
 protected:
   /*
-    The result type that will be used for comparison.
-    cmp_type() of all arguments are collected to here.
+    The data type handler that will be used for comparison.
+    Data type handlers of all arguments are mixed to here.
   */
-  Item_result m_compare_type;
+  Type_handler_hybrid_field_type m_comparator;
   /*
     The collation that will be used for comparison in case
     when m_compare_type is STRING_RESULT.
@@ -869,11 +869,13 @@ class Item_func_between :public Item_func_opt_neg
                              Field *field, Item *value);
 public:
   String value0,value1,value2;
-  /* TRUE <=> arguments will be compared as dates. */
-  Item *compare_as_dates;
   Item_func_between(THD *thd, Item *a, Item *b, Item *c):
-    Item_func_opt_neg(thd, a, b, c), compare_as_dates(FALSE) { }
-  longlong val_int();
+    Item_func_opt_neg(thd, a, b, c) { }
+  longlong val_int()
+  {
+    DBUG_ASSERT(fixed);
+    return m_comparator.type_handler()->Item_func_between_val_int(this);
+  }
   enum Functype functype() const   { return BETWEEN; }
   const char *func_name() const { return "between"; }
   void fix_length_and_dec();
@@ -889,13 +891,19 @@ class Item_func_between :public Item_func_opt_neg
   {
     Item_args::propagate_equal_fields(thd,
                                       Context(ANY_SUBST,
-                                              m_compare_type,
+                                              m_comparator.cmp_type(),
                                               compare_collation()),
                                       cond);
     return this;
   }
   Item *get_copy(THD *thd, MEM_ROOT *mem_root)
   { return get_item_copy<Item_func_between>(thd, mem_root, this); }
+
+  longlong val_int_cmp_string();
+  longlong val_int_cmp_temporal();
+  longlong val_int_cmp_int();
+  longlong val_int_cmp_real();
+  longlong val_int_cmp_decimal();
 };
 
 
@@ -1659,7 +1667,7 @@ class Item_func_in :public Item_func_opt_neg
       will be replaced to a zero-filled Item_string.
       Such a change would require rebuilding of cmp_items.
     */
-    Context cmpctx(ANY_SUBST, m_compare_type,
+    Context cmpctx(ANY_SUBST, m_comparator.cmp_type(),
                    Item_func_in::compare_collation());
     for (uint i= 0; i < arg_count; i++)
     {
diff --git a/sql/sql_type.cc b/sql/sql_type.cc
index 00d4da5..9538f02 100644
--- a/sql/sql_type.cc
+++ b/sql/sql_type.cc
@@ -23,7 +23,6 @@
 static Type_handler_tiny        type_handler_tiny;
 static Type_handler_short       type_handler_short;
 static Type_handler_long        type_handler_long;
-static Type_handler_longlong    type_handler_longlong;
 static Type_handler_int24       type_handler_int24;
 static Type_handler_year        type_handler_year;
 static Type_handler_bit         type_handler_bit;
@@ -38,7 +37,6 @@ static Type_handler_datetime2   type_handler_datetime2;
 static Type_handler_timestamp   type_handler_timestamp;
 static Type_handler_timestamp2  type_handler_timestamp2;
 static Type_handler_olddecimal  type_handler_olddecimal;
-static Type_handler_newdecimal  type_handler_newdecimal;
 static Type_handler_string      type_handler_string;
 static Type_handler_tiny_blob   type_handler_tiny_blob;
 static Type_handler_medium_blob type_handler_medium_blob;
@@ -54,6 +52,8 @@ static Type_handler_set         type_handler_set;
 Type_handler_null        type_handler_null;
 Type_handler_row         type_handler_row;
 Type_handler_varchar     type_handler_varchar;
+Type_handler_longlong    type_handler_longlong;
+Type_handler_newdecimal  type_handler_newdecimal;
 
 
 /**
@@ -123,6 +123,55 @@ Type_handler_hybrid_field_type::Type_handler_hybrid_field_type()
 }
 
 
+/*
+  Collect built-in data type handlers for comparison.
+  This method is very similar to item_cmp_type() defined in item.cc.
+  Now they coexist. Later item_cmp_type() will be removed.
+  In addition to item_cmp_type(), this method correctly aggregates
+  TIME with DATETIME/TIMESTAMP/DATE, so no additional find_date_time_item()
+  is needed after this call.
+*/
+void
+Type_handler_hybrid_field_type::aggregate_for_comparison(const Type_handler *h)
+{
+  Item_result a= cmp_type();
+  Item_result b= h->cmp_type();
+  if (a == STRING_RESULT && b == STRING_RESULT)
+    m_type_handler= &type_handler_long_blob;
+  else if (a == INT_RESULT && b == INT_RESULT)
+    m_type_handler= &type_handler_longlong;
+  else if (a == ROW_RESULT || b == ROW_RESULT)
+    m_type_handler= &type_handler_row;
+  else if (a == TIME_RESULT || b == TIME_RESULT)
+  {
+    if ((a == TIME_RESULT) + (b == TIME_RESULT) == 1)
+    {
+      /*
+        We're here if there's only one temporal data type:
+        either m_type_handler or h.
+      */
+      if (b == TIME_RESULT)
+        m_type_handler= h; // Temporal types bit non-temporal types
+    }
+    else
+    {
+      /*
+        We're here if both m_type_handler and h are temporal data types.
+      */
+      if (field_type() != MYSQL_TYPE_TIME || h->field_type() != MYSQL_TYPE_TIME)
+        m_type_handler= &type_handler_datetime; // DATETIME bits TIME
+    }
+  }
+  else if ((a == INT_RESULT || a == DECIMAL_RESULT) &&
+           (b == INT_RESULT || b == DECIMAL_RESULT))
+  {
+    m_type_handler= &type_handler_newdecimal;
+  }
+  else
+    m_type_handler= &type_handler_double;
+}
+
+
 const Type_handler *
 Type_handler::get_handler_by_field_type(enum_field_types type)
 {
@@ -1141,3 +1190,43 @@ Type_handler_string_result::Item_func_hybrid_field_type_get_date(
 }
 
 /***************************************************************************/
+
+longlong Type_handler_row::
+           Item_func_between_val_int(Item_func_between *func) const
+{
+  DBUG_ASSERT(0);
+  func->null_value= true;
+  return 0;
+}
+
+longlong Type_handler_string_result::
+           Item_func_between_val_int(Item_func_between *func) const
+{
+  return func->val_int_cmp_string();
+}
+
+longlong Type_handler_temporal_result::
+           Item_func_between_val_int(Item_func_between *func) const
+{
+  return func->val_int_cmp_temporal();
+}
+
+longlong Type_handler_int_result::
+           Item_func_between_val_int(Item_func_between *func) const
+{
+  return func->val_int_cmp_int();
+}
+
+longlong Type_handler_real_result::
+           Item_func_between_val_int(Item_func_between *func) const
+{
+  return func->val_int_cmp_real();
+}
+
+longlong Type_handler_decimal_result::
+           Item_func_between_val_int(Item_func_between *func) const
+{
+  return func->val_int_cmp_decimal();
+}
+
+/***************************************************************************/
diff --git a/sql/sql_type.h b/sql/sql_type.h
index ab04a4d..9086e60 100644
--- a/sql/sql_type.h
+++ b/sql/sql_type.h
@@ -29,6 +29,7 @@ class Item_cache;
 class Item_sum_hybrid;
 class Item_func_hex;
 class Item_func_hybrid_field_type;
+class Item_func_between;
 class Type_std_attributes;
 class Sort_param;
 class Arg_comparator;
@@ -314,6 +315,8 @@ class Type_handler
                                             MYSQL_TIME *,
                                             ulonglong fuzzydate) const= 0;
 
+  virtual longlong
+  Item_func_between_val_int(Item_func_between *func) const= 0;
 };
 
 
@@ -410,6 +413,7 @@ class Type_handler_row: public Type_handler
     return true;
   }
 
+  longlong Item_func_between_val_int(Item_func_between *func) const;
 };
 
 
@@ -457,6 +461,7 @@ class Type_handler_real_result: public Type_handler_numeric
   bool Item_func_hybrid_field_type_get_date(Item_func_hybrid_field_type *,
                                             MYSQL_TIME *,
                                             ulonglong fuzzydate) const;
+  longlong Item_func_between_val_int(Item_func_between *func) const;
 };
 
 
@@ -489,6 +494,7 @@ class Type_handler_decimal_result: public Type_handler_numeric
   bool Item_func_hybrid_field_type_get_date(Item_func_hybrid_field_type *,
                                             MYSQL_TIME *,
                                             ulonglong fuzzydate) const;
+  longlong Item_func_between_val_int(Item_func_between *func) const;
 };
 
 
@@ -521,6 +527,7 @@ class Type_handler_int_result: public Type_handler_numeric
   bool Item_func_hybrid_field_type_get_date(Item_func_hybrid_field_type *,
                                             MYSQL_TIME *,
                                             ulonglong fuzzydate) const;
+  longlong Item_func_between_val_int(Item_func_between *func) const;
 };
 
 
@@ -551,6 +558,7 @@ class Type_handler_temporal_result: public Type_handler
   bool Item_func_hybrid_field_type_get_date(Item_func_hybrid_field_type *,
                                             MYSQL_TIME *,
                                             ulonglong fuzzydate) const;
+  longlong Item_func_between_val_int(Item_func_between *func) const;
 };
 
 
@@ -585,6 +593,7 @@ class Type_handler_string_result: public Type_handler
   bool Item_func_hybrid_field_type_get_date(Item_func_hybrid_field_type *,
                                             MYSQL_TIME *,
                                             ulonglong fuzzydate) const;
+  longlong Item_func_between_val_int(Item_func_between *func) const;
 };
 
 
@@ -960,6 +969,10 @@ class Type_handler_hybrid_field_type
   {
     return (m_type_handler= Type_handler::get_handler_by_result_type(type));
   }
+  const Type_handler *set_handler_by_cmp_type(Item_result type)
+  {
+    return (m_type_handler= Type_handler::get_handler_by_cmp_type(type));
+  }
   const Type_handler *set_handler_by_result_type(Item_result type,
                                                  uint max_octet_length,
                                                  CHARSET_INFO *cs)
@@ -977,6 +990,8 @@ class Type_handler_hybrid_field_type
   {
     return (m_type_handler= Type_handler::get_handler_by_real_type(type));
   }
+  void aggregate_for_comparison(const Type_handler *other);
+  bool aggregate_for_comparison(Item **items, uint nitems);
 };
 
 
@@ -997,5 +1012,7 @@ class Type_handler_hybrid_real_field_type:
 extern Type_handler_row   type_handler_row;
 extern Type_handler_null  type_handler_null;
 extern Type_handler_varchar type_handler_varchar;
+extern Type_handler_longlong type_handler_longlong;
+extern Type_handler_newdecimal type_handler_newdecimal;
 
 #endif /* SQL_TYPE_H_INCLUDED */
