commit 1f76111b71a90e51251eb98f07f3a300b4b190de
Author: Alexander Barkov <bar@mariadb.org>
Date:   Thu Nov 24 16:26:30 2016 +0400

    MDEV-11344 Split Arg_comparator::set_compare_func() into virtual methods in Type_handler
    
    This patch:
    - Introduces a new virtuial method Type_handler::set_comparator_func
      and moves pieces of the code from the switch in
      Arg_comparator::set_compare_func into the corresponding
      Type_handler_xxx::set_comparator_func.
    - Adds Type_handler::get_handler_by_cmp_type()
    - Moves Type_handler_hybrid_field_type::get_handler_by_result_type() to
      a static method Type_handler::get_handler_by_result_type(),
      for symmetry with similar methods:
      * Type_handler::get_handler_by_field_type()
      * Type_handler::get_handler_by_real_type()
      * Type_handler::get_handler_by_cmp_type()
    - Introduces Type_handler_row, to unify the code for the scalar
      data types and the ROW data type (currently for comparison purposes only).
    - Adds public type_handler_row, as it's now needed in item_row.h
    - Makes type_handler_null public, as it's now needed in item_cmpfunc.h
      Note, other type_handler_xxx will become public as well later.
    - Removes the global variable Arg_comparator::comparator_matrix,
      as it's not needed any more.

diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc
index e10a4ba..7313332 100644
--- a/sql/item_cmpfunc.cc
+++ b/sql/item_cmpfunc.cc
@@ -532,78 +532,6 @@ void Item_bool_rowready_func2::fix_length_and_dec()
 }
 
 
-int Arg_comparator::set_compare_func(Item_func_or_sum *item, Item_result type)
-{
-  owner= item;
-  func= comparator_matrix[type]
-                         [is_owner_equal_func()];
-
-  switch (type) {
-  case TIME_RESULT:
-    m_compare_collation= &my_charset_numeric;
-    break;
-  case ROW_RESULT:
-  {
-    uint n= (*a)->cols();
-    if (n != (*b)->cols())
-    {
-      my_error(ER_OPERAND_COLUMNS, MYF(0), n);
-      comparators= 0;
-      return 1;
-    }
-    if (!(comparators= new Arg_comparator[n]))
-      return 1;
-    for (uint i=0; i < n; i++)
-    {
-      if ((*a)->element_index(i)->cols() != (*b)->element_index(i)->cols())
-      {
-	my_error(ER_OPERAND_COLUMNS, MYF(0), (*a)->element_index(i)->cols());
-	return 1;
-      }
-      if (comparators[i].set_cmp_func(owner, (*a)->addr(i),
-                                             (*b)->addr(i), set_null))
-        return 1;
-    }
-    break;
-  }
-  case INT_RESULT:
-  {
-    if (func == &Arg_comparator::compare_int_signed)
-    {
-      if ((*a)->unsigned_flag)
-        func= (((*b)->unsigned_flag)?
-               &Arg_comparator::compare_int_unsigned :
-               &Arg_comparator::compare_int_unsigned_signed);
-      else if ((*b)->unsigned_flag)
-        func= &Arg_comparator::compare_int_signed_unsigned;
-    }
-    else if (func== &Arg_comparator::compare_e_int)
-    {
-      if ((*a)->unsigned_flag ^ (*b)->unsigned_flag)
-        func= &Arg_comparator::compare_e_int_diff_signedness;
-    }
-    break;
-  }
-  case STRING_RESULT:
-  case DECIMAL_RESULT:
-    break;
-  case REAL_RESULT:
-  {
-    if ((*a)->decimals < NOT_FIXED_DEC && (*b)->decimals < NOT_FIXED_DEC)
-    {
-      precision= 5 / log_10[MY_MAX((*a)->decimals, (*b)->decimals) + 1];
-      if (func == &Arg_comparator::compare_real)
-        func= &Arg_comparator::compare_real_fixed;
-      else if (func == &Arg_comparator::compare_e_real)
-        func= &Arg_comparator::compare_e_real_fixed;
-    }
-    break;
-  }
-  }
-  return 0;
-}
-
-
 /**
   Prepare the comparator (set the comparison function) for comparing
   items *a1 and *a2 in the context of 'type'.
@@ -625,9 +553,51 @@ int Arg_comparator::set_cmp_func(Item_func_or_sum *owner_arg,
   set_null= set_null && owner_arg;
   a= a1;
   b= a2;
-  m_compare_type= item_cmp_type(*a1, *a2);
+  m_compare_handler= Type_handler::get_handler_by_cmp_type(item_cmp_type(*a1,
+                                                                         *a2));
+  return m_compare_handler->set_comparator_func(this);
+}
 
-  if (m_compare_type == STRING_RESULT &&
+
+bool Arg_comparator::set_cmp_func_for_row_arguments()
+{
+  uint n= (*a)->cols();
+  if (n != (*b)->cols())
+  {
+    my_error(ER_OPERAND_COLUMNS, MYF(0), n);
+    comparators= 0;
+    return true;
+  }
+  if (!(comparators= new Arg_comparator[n]))
+    return true;
+  for (uint i=0; i < n; i++)
+  {
+    if ((*a)->element_index(i)->cols() != (*b)->element_index(i)->cols())
+    {
+      my_error(ER_OPERAND_COLUMNS, MYF(0), (*a)->element_index(i)->cols());
+      return true;
+    }
+    if (comparators[i].set_cmp_func(owner, (*a)->addr(i),
+                                           (*b)->addr(i), set_null))
+      return true;
+  }
+  return false;
+}
+
+
+bool Arg_comparator::set_cmp_func_row()
+{
+  func= is_owner_equal_func() ? &Arg_comparator::compare_e_row :
+                                &Arg_comparator::compare_row;
+  return set_cmp_func_for_row_arguments();
+}
+
+
+bool Arg_comparator::set_cmp_func_string()
+{
+  func= is_owner_equal_func() ? &Arg_comparator::compare_e_string :
+                                &Arg_comparator::compare_string;
+  if (compare_type() == STRING_RESULT &&
       (*a)->result_type() == STRING_RESULT &&
       (*b)->result_type() == STRING_RESULT)
   {
@@ -636,37 +606,87 @@ int Arg_comparator::set_cmp_func(Item_func_or_sum *owner_arg,
       generated item, like in natural join
     */
     if (owner->agg_arg_charsets_for_comparison(&m_compare_collation, a, b))
-      return 1;
+      return true;
   }
+  a= cache_converted_constant(thd, a, &a_cache, compare_type());
+  b= cache_converted_constant(thd, b, &b_cache, compare_type());
+  return false;
+}
+
 
-  if (m_compare_type == TIME_RESULT)
+bool Arg_comparator::set_cmp_func_temporal()
+{
+  enum_field_types f_type= a[0]->field_type_for_temporal_comparison(b[0]);
+  m_compare_collation= &my_charset_numeric;
+  if (f_type == MYSQL_TYPE_TIME)
   {
-    enum_field_types f_type= a[0]->field_type_for_temporal_comparison(b[0]);
-    if (f_type == MYSQL_TYPE_TIME)
-    {
-      func= is_owner_equal_func() ? &Arg_comparator::compare_e_time :
-                                    &Arg_comparator::compare_time;
-    }
-    else
-    {
-      func= is_owner_equal_func() ? &Arg_comparator::compare_e_datetime :
-                                    &Arg_comparator::compare_datetime;
-    }
-    return 0;
+    func= is_owner_equal_func() ? &Arg_comparator::compare_e_time :
+                                  &Arg_comparator::compare_time;
   }
+  else
+  {
+    func= is_owner_equal_func() ? &Arg_comparator::compare_e_datetime :
+                                  &Arg_comparator::compare_datetime;
+  }
+  return false;
+}
+
 
-  if (m_compare_type == INT_RESULT &&
-      (*a)->field_type() == MYSQL_TYPE_YEAR &&
+bool Arg_comparator::set_cmp_func_int()
+{
+  func= is_owner_equal_func() ? &Arg_comparator::compare_e_int :
+                                &Arg_comparator::compare_int_signed;
+  if ((*a)->field_type() == MYSQL_TYPE_YEAR &&
       (*b)->field_type() == MYSQL_TYPE_YEAR)
   {
-    m_compare_type= TIME_RESULT;
     func= is_owner_equal_func() ? &Arg_comparator::compare_e_datetime :
                                   &Arg_comparator::compare_datetime;
   }
+  else if (func == &Arg_comparator::compare_int_signed)
+  {
+    if ((*a)->unsigned_flag)
+      func= (((*b)->unsigned_flag)?
+             &Arg_comparator::compare_int_unsigned :
+             &Arg_comparator::compare_int_unsigned_signed);
+    else if ((*b)->unsigned_flag)
+      func= &Arg_comparator::compare_int_signed_unsigned;
+  }
+  else if (func== &Arg_comparator::compare_e_int)
+  {
+    if ((*a)->unsigned_flag ^ (*b)->unsigned_flag)
+      func= &Arg_comparator::compare_e_int_diff_signedness;
+  }
+  a= cache_converted_constant(thd, a, &a_cache, compare_type());
+  b= cache_converted_constant(thd, b, &b_cache, compare_type());
+  return false;
+}
+
 
-  a= cache_converted_constant(thd, a, &a_cache, m_compare_type);
-  b= cache_converted_constant(thd, b, &b_cache, m_compare_type);
-  return set_compare_func(owner_arg, m_compare_type);
+bool Arg_comparator::set_cmp_func_real()
+{
+  func= is_owner_equal_func() ? &Arg_comparator::compare_e_real :
+                                &Arg_comparator::compare_real;
+  if ((*a)->decimals < NOT_FIXED_DEC && (*b)->decimals < NOT_FIXED_DEC)
+  {
+    precision= 5 / log_10[MY_MAX((*a)->decimals, (*b)->decimals) + 1];
+    if (func == &Arg_comparator::compare_real)
+      func= &Arg_comparator::compare_real_fixed;
+    else if (func == &Arg_comparator::compare_e_real)
+      func= &Arg_comparator::compare_e_real_fixed;
+  }
+  a= cache_converted_constant(thd, a, &a_cache, compare_type());
+  b= cache_converted_constant(thd, b, &b_cache, compare_type());
+  return false;
+}
+
+
+bool Arg_comparator::set_cmp_func_decimal()
+{
+  func= is_owner_equal_func() ? &Arg_comparator::compare_e_decimal :
+                                &Arg_comparator::compare_decimal;
+  a= cache_converted_constant(thd, a, &a_cache, compare_type());
+  b= cache_converted_constant(thd, b, &b_cache, compare_type());
+  return false;
 }
 
 
diff --git a/sql/item_cmpfunc.h b/sql/item_cmpfunc.h
index b0120dd..2c69896 100644
--- a/sql/item_cmpfunc.h
+++ b/sql/item_cmpfunc.h
@@ -47,7 +47,7 @@ typedef int (*Item_field_cmpfunc)(Item *f1, Item *f2, void *arg);
 class Arg_comparator: public Sql_alloc
 {
   Item **a, **b;
-  Item_result m_compare_type;
+  const Type_handler *m_compare_handler;
   CHARSET_INFO *m_compare_collation;
   arg_cmp_func func;
   Item_func_or_sum *owner;
@@ -58,7 +58,7 @@ class Arg_comparator: public Sql_alloc
   THD *thd;
   Item *a_cache, *b_cache;         // Cached values of a and b items
                                    //   when one of arguments is NULL.
-  int set_compare_func(Item_func_or_sum *owner, Item_result type);
+
   int set_cmp_func(Item_func_or_sum *owner_arg, Item **a1, Item **a2);
 
   int compare_temporal(enum_field_types type);
@@ -68,17 +68,26 @@ class Arg_comparator: public Sql_alloc
   /* Allow owner function to use string buffers. */
   String value1, value2;
 
-  Arg_comparator(): m_compare_type(STRING_RESULT),
+  Arg_comparator():
+    m_compare_handler(&type_handler_null),
     m_compare_collation(&my_charset_bin),
     set_null(TRUE), comparators(0), thd(0),
     a_cache(0), b_cache(0) {};
   Arg_comparator(Item **a1, Item **a2): a(a1), b(a2),
-    m_compare_type(STRING_RESULT),
+    m_compare_handler(&type_handler_null),
     m_compare_collation(&my_charset_bin),
     set_null(TRUE), comparators(0), thd(0),
     a_cache(0), b_cache(0) {};
 
 public:
+  bool set_cmp_func_for_row_arguments();
+  bool set_cmp_func_row();
+  bool set_cmp_func_string();
+  bool set_cmp_func_temporal();
+  bool set_cmp_func_int();
+  bool set_cmp_func_real();
+  bool set_cmp_func_decimal();
+
   inline int set_cmp_func(Item_func_or_sum *owner_arg,
 			  Item **a1, Item **a2, bool set_null_arg)
   {
@@ -110,13 +119,13 @@ class Arg_comparator: public Sql_alloc
 
   Item** cache_converted_constant(THD *thd, Item **value, Item **cache,
                                   Item_result type);
-  static arg_cmp_func comparator_matrix [6][2];
   inline bool is_owner_equal_func()
   {
     return (owner->type() == Item::FUNC_ITEM &&
            ((Item_func*)owner)->functype() == Item_func::EQUAL_FUNC);
   }
-  Item_result compare_type() const { return m_compare_type; }
+  const Type_handler *compare_type_handler() const { return m_compare_handler; }
+  Item_result compare_type() const { return m_compare_handler->cmp_type(); }
   CHARSET_INFO *compare_collation() const { return m_compare_collation; }
   Arg_comparator *subcomparators() const { return comparators; }
   void cleanup()
diff --git a/sql/item_row.h b/sql/item_row.h
index bbfebb5..f885873 100644
--- a/sql/item_row.h
+++ b/sql/item_row.h
@@ -56,6 +56,7 @@ class Item_row: public Item,
   {}
 
   enum Type type() const { return ROW_ITEM; };
+  const Type_handler *type_handler() const { return &type_handler_row; }
   void illegal_method_call(const char *);
   bool is_null() { return null_value; }
   void make_field(THD *thd, Send_field *)
diff --git a/sql/mysqld.cc b/sql/mysqld.cc
index e7fb7b7..845d2b6 100644
--- a/sql/mysqld.cc
+++ b/sql/mysqld.cc
@@ -309,14 +309,6 @@ static my_bool opt_autocommit; ///< for --autocommit command-line option
 */
 static my_bool opt_verbose= 0;
 
-arg_cmp_func Arg_comparator::comparator_matrix[6][2] =
-{{&Arg_comparator::compare_string,     &Arg_comparator::compare_e_string},
- {&Arg_comparator::compare_real,       &Arg_comparator::compare_e_real},
- {&Arg_comparator::compare_int_signed, &Arg_comparator::compare_e_int},
- {&Arg_comparator::compare_row,        &Arg_comparator::compare_e_row},
- {&Arg_comparator::compare_decimal,    &Arg_comparator::compare_e_decimal},
- {&Arg_comparator::compare_datetime,   &Arg_comparator::compare_e_datetime}};
-
 /* Timer info to be used by the SQL layer */
 MY_TIMER_INFO sys_timer_info;
 
diff --git a/sql/sql_type.cc b/sql/sql_type.cc
index a80417f..7cd549f 100644
--- a/sql/sql_type.cc
+++ b/sql/sql_type.cc
@@ -39,7 +39,6 @@ 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_null        type_handler_null;
 static Type_handler_string      type_handler_string;
 static Type_handler_varchar     type_handler_varchar;
 static Type_handler_tiny_blob   type_handler_tiny_blob;
@@ -53,6 +52,10 @@ static Type_handler_enum        type_handler_enum;
 static Type_handler_set         type_handler_set;
 
 
+Type_handler_null        type_handler_null;
+Type_handler_row         type_handler_row;
+
+
 /**
   This method is used by:
   - Item_user_var_as_out_param::field_type()
@@ -99,18 +102,17 @@ Type_handler_string_result::type_handler_adjusted_to_max_octet_length(
 
 
 const Type_handler *
-Type_handler_hybrid_field_type::get_handler_by_result_type(Item_result type)
-                                                           const
+Type_handler::get_handler_by_cmp_type(Item_result type)
 {
   switch (type) {
   case REAL_RESULT:       return &type_handler_double;
   case INT_RESULT:        return &type_handler_longlong;
   case DECIMAL_RESULT:    return &type_handler_newdecimal;
   case STRING_RESULT:     return &type_handler_long_blob;
-  case TIME_RESULT:
-  case ROW_RESULT:
-    DBUG_ASSERT(0);
+  case TIME_RESULT:       return &type_handler_datetime;
+  case ROW_RESULT:        return &type_handler_row;
   }
+  DBUG_ASSERT(0);
   return &type_handler_string;
 }
 
@@ -640,3 +642,40 @@ Field *Type_handler_set::make_conversion_table_field(TABLE *table,
                    metadata & 0x00ff/*pack_length()*/,
                    ((const Field_enum*) target)->typelib, target->charset());
 }
+
+
+/***********************************************************************/
+
+
+bool Type_handler_row::set_comparator_func(Arg_comparator *cmp) const
+{
+  return cmp->set_cmp_func_row();
+}
+
+bool Type_handler_int_result::set_comparator_func(Arg_comparator *cmp) const
+{
+  return cmp->set_cmp_func_int();
+}
+
+bool Type_handler_real_result::set_comparator_func(Arg_comparator *cmp) const
+{
+  return cmp->set_cmp_func_real();
+}
+
+bool Type_handler_decimal_result::set_comparator_func(Arg_comparator *cmp) const
+{
+  return cmp->set_cmp_func_decimal();
+}
+
+bool Type_handler_string_result::set_comparator_func(Arg_comparator *cmp) const
+{
+  return cmp->set_cmp_func_string();
+}
+
+bool Type_handler_temporal_result::set_comparator_func(Arg_comparator *cmp) const
+{
+  return cmp->set_cmp_func_temporal();
+}
+
+
+/*************************************************************************/
diff --git a/sql/sql_type.h b/sql/sql_type.h
index de5c31a..bbf732d 100644
--- a/sql/sql_type.h
+++ b/sql/sql_type.h
@@ -27,6 +27,7 @@ class Field;
 class Item;
 class Type_std_attributes;
 class Sort_param;
+class Arg_comparator;
 struct TABLE;
 struct SORT_FIELD_ATTR;
 
@@ -221,6 +222,16 @@ class Type_handler
   static const Type_handler *string_type_handler(uint max_octet_length);
   static const Type_handler *get_handler_by_field_type(enum_field_types type);
   static const Type_handler *get_handler_by_real_type(enum_field_types type);
+  static const Type_handler *get_handler_by_cmp_type(Item_result type);
+  static const Type_handler *get_handler_by_result_type(Item_result type)
+  {
+    /*
+      As result_type() returns STRING_RESULT for temporal Items,
+      type should never be equal to TIME_RESULT here.
+    */
+    DBUG_ASSERT(type != TIME_RESULT);
+    return get_handler_by_cmp_type(type);
+  }
   virtual enum_field_types field_type() const= 0;
   virtual enum_field_types real_field_type() const { return field_type(); }
   virtual Item_result result_type() const= 0;
@@ -272,6 +283,52 @@ class Type_handler
   virtual void sortlength(THD *thd,
                           const Type_std_attributes *item,
                           SORT_FIELD_ATTR *attr) const= 0;
+  virtual bool set_comparator_func(Arg_comparator *cmp) const= 0;
+};
+
+
+/*** Special handler for ROW ***/
+class Type_handler_row: public Type_handler
+{
+public:
+  virtual ~Type_handler_row() {}
+  enum_field_types field_type() const
+  {
+    DBUG_ASSERT(0);
+    return MYSQL_TYPE_NULL;
+  };
+  Item_result result_type() const
+  {
+    return ROW_RESULT;
+  }
+  Item_result cmp_type() const
+  {
+    return ROW_RESULT;
+  }
+  Field *make_num_distinct_aggregator_field(MEM_ROOT *, const Item *) const
+  {
+    DBUG_ASSERT(0);
+    return NULL;
+  }
+  Field *make_conversion_table_field(TABLE *TABLE,
+                                     uint metadata,
+                                     const Field *target) const
+  {
+    DBUG_ASSERT(0);
+    return NULL;
+  }
+  void make_sort_key(uchar *to, Item *item,
+                     const SORT_FIELD_ATTR *sort_field,
+                     Sort_param *param) const
+  {
+    DBUG_ASSERT(0);
+  }
+  void sortlength(THD *thd, const Type_std_attributes *item,
+                            SORT_FIELD_ATTR *attr) const
+  {
+    DBUG_ASSERT(0);
+  }
+  bool set_comparator_func(Arg_comparator *cmp) const;
 };
 
 
@@ -288,6 +345,7 @@ class Type_handler_real_result: public Type_handler
   void sortlength(THD *thd,
                   const Type_std_attributes *item,
                   SORT_FIELD_ATTR *attr) const;
+  bool set_comparator_func(Arg_comparator *cmp) const;
 };
 
 
@@ -303,6 +361,7 @@ class Type_handler_decimal_result: public Type_handler
   void sortlength(THD *thd,
                   const Type_std_attributes *item,
                   SORT_FIELD_ATTR *attr) const;
+  bool set_comparator_func(Arg_comparator *cmp) const;
 };
 
 
@@ -318,6 +377,7 @@ class Type_handler_int_result: public Type_handler
   void sortlength(THD *thd,
                   const Type_std_attributes *item,
                   SORT_FIELD_ATTR *attr) const;
+  bool set_comparator_func(Arg_comparator *cmp) const;
 };
 
 
@@ -332,6 +392,7 @@ class Type_handler_temporal_result: public Type_handler
   void sortlength(THD *thd,
                   const Type_std_attributes *item,
                   SORT_FIELD_ATTR *attr) const;
+  bool set_comparator_func(Arg_comparator *cmp) const;
 };
 
 
@@ -349,6 +410,7 @@ class Type_handler_string_result: public Type_handler
   void sortlength(THD *thd,
                   const Type_std_attributes *item,
                   SORT_FIELD_ATTR *attr) const;
+  bool set_comparator_func(Arg_comparator *cmp) const;
 };
 
 
@@ -682,7 +744,6 @@ class Type_handler_set: public Type_handler_string_result
 class Type_handler_hybrid_field_type
 {
   const Type_handler *m_type_handler;
-  const Type_handler *get_handler_by_result_type(Item_result type) const;
 public:
   Type_handler_hybrid_field_type();
   Type_handler_hybrid_field_type(const Type_handler *handler)
@@ -708,13 +769,13 @@ class Type_handler_hybrid_field_type
   }
   const Type_handler *set_handler_by_result_type(Item_result type)
   {
-    return (m_type_handler= get_handler_by_result_type(type));
+    return (m_type_handler= Type_handler::get_handler_by_result_type(type));
   }
   const Type_handler *set_handler_by_result_type(Item_result type,
                                                  uint max_octet_length,
                                                  CHARSET_INFO *cs)
   {
-    m_type_handler= get_handler_by_result_type(type);
+    m_type_handler= Type_handler::get_handler_by_result_type(type);
     return m_type_handler=
       m_type_handler->type_handler_adjusted_to_max_octet_length(max_octet_length,
                                                                 cs);
@@ -744,4 +805,7 @@ class Type_handler_hybrid_real_field_type:
 };
 
 
+extern Type_handler_row   type_handler_row;
+extern Type_handler_null  type_handler_null;
+
 #endif /* SQL_TYPE_H_INCLUDED */
