commit 0fc326434740e8beb411ef5c71ee615581b91c77
Author: Alexander Barkov <bar@mariadb.org>
Date:   Tue Dec 20 18:19:47 2016 +0400

    MDEV-11615 Split Item_hybrid_func::fix_attributes into virtual methods in Type_handler

diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc
index a81dd46..f4bd211 100644
--- a/sql/item_cmpfunc.cc
+++ b/sql/item_cmpfunc.cc
@@ -3108,25 +3108,19 @@ void Item_func_case::fix_length_and_dec()
   
   set_handler_by_field_type(agg_field_type(agg, nagg, true));
 
-  if (Item_func_case::result_type() == STRING_RESULT)
-  {
-    if (count_string_result_length(Item_func_case::field_type(), agg, nagg))
-      return;
-    /*
-      Copy all THEN and ELSE items back to args[] array.
-      Some of the items might have been changed to Item_func_conv_charset.
-    */
-    for (nagg= 0 ; nagg < ncases / 2 ; nagg++)
-      change_item_tree_if_needed(thd, &args[nagg * 2 + 1], agg[nagg]);
+  if (fix_attributes(agg, nagg))
+    return;
+
+  /*
+    Copy all modified THEN and ELSE items back to args[] array.
+    Some of the items might have been changed to Item_func_conv_charset.
+  */
+  for (nagg= 0 ; nagg < ncases / 2 ; nagg++)
+    change_item_tree_if_needed(thd, &args[nagg * 2 + 1], agg[nagg]);
+
+  if (else_expr_num != -1)
+    change_item_tree_if_needed(thd, &args[else_expr_num], agg[nagg++]);
 
-    if (else_expr_num != -1)
-      change_item_tree_if_needed(thd, &args[else_expr_num], agg[nagg++]);
-  }
-  else
-  {
-    fix_attributes(agg, nagg);
-  }
-  
   /*
     Aggregate first expression and all WHEN expression types
     and collations when string comparison
@@ -3405,31 +3399,12 @@ my_decimal *Item_func_coalesce::decimal_op(my_decimal *decimal_value)
 }
 
 
-void Item_hybrid_func::fix_attributes(Item **items, uint nitems)
+bool Item_hybrid_func::fix_attributes(Item **items, uint nitems)
 {
-  switch (Item_hybrid_func::result_type()) {
-  case STRING_RESULT:
-    if (count_string_result_length(Item_hybrid_func::field_type(),
-                                   items, nitems))
-      return;          
-    break;
-  case DECIMAL_RESULT:
-    collation.set_numeric();
-    count_decimal_length(items, nitems);
-    break;
-  case REAL_RESULT:
-    collation.set_numeric();
-    count_real_length(items, nitems);
-    break;
-  case INT_RESULT:
-    collation.set_numeric();
-    count_only_length(items, nitems);
-    decimals= 0;
-    break;
-  case ROW_RESULT:
-  case TIME_RESULT:
-    DBUG_ASSERT(0);
-  }
+  bool rc= Item_hybrid_func::type_handler()->
+             Item_hybrid_func_fix_attributes(current_thd, this, items, nitems);
+  DBUG_ASSERT(!rc || current_thd->is_error());
+  return rc;
 }
 
 /****************************************************************************
diff --git a/sql/item_func.cc b/sql/item_func.cc
index 716c47e..d1c3737 100644
--- a/sql/item_func.cc
+++ b/sql/item_func.cc
@@ -568,30 +568,6 @@ void Item_udf_func::fix_num_length_and_dec()
 
 
 /**
-  Count max_length and decimals for temporal functions.
-
-  @param item    Argument array
-  @param nitems  Number of arguments in the array.
-
-  @retval        False on success, true on error.
-*/
-void Item_func::count_datetime_length(enum_field_types field_type_arg,
-                                      Item **item, uint nitems)
-{
-  unsigned_flag= 0;
-  decimals= 0;
-  if (field_type_arg != MYSQL_TYPE_DATE)
-  {
-    for (uint i= 0; i < nitems; i++)
-      set_if_bigger(decimals, item[i]->decimals);
-  }
-  set_if_smaller(decimals, TIME_SECOND_PART_DIGITS);
-  uint len= decimals ? (decimals + 1) : 0;
-  len+= mysql_temporal_int_part_length(field_type_arg);
-  fix_char_length(len);
-}
-
-/**
   Set max_length/decimals of function if function is fixed point and
   result length/precision depends on argument ones.
 */
@@ -665,7 +641,7 @@ void Item_func::count_real_length(Item **items, uint nitems)
 
 
 /**
-  Calculate max_length and decimals for STRING_RESULT functions.
+  Calculate max_length and decimals for string functions.
 
   @param field_type  Field type.
   @param items       Argument array.
@@ -673,18 +649,13 @@ void Item_func::count_real_length(Item **items, uint nitems)
 
   @retval            False on success, true on error.
 */
-bool Item_func::count_string_result_length(enum_field_types field_type_arg,
-                                           Item **items, uint nitems)
+bool Item_func::count_string_length(Item **items, uint nitems)
 {
+  DBUG_ASSERT(!is_temporal_type(field_type()));
   if (agg_arg_charsets_for_string_result(collation, items, nitems, 1))
     return true;
-  if (is_temporal_type(field_type_arg))
-    count_datetime_length(field_type_arg, items, nitems);
-  else
-  {
-    count_only_length(items, nitems);
-    decimals= max_length ? NOT_FIXED_DEC : 0;
-  }
+  count_only_length(items, nitems);
+  decimals= max_length ? NOT_FIXED_DEC : 0;
   return false;
 }
 
diff --git a/sql/item_func.h b/sql/item_func.h
index dbe23e3..458be99 100644
--- a/sql/item_func.h
+++ b/sql/item_func.h
@@ -45,11 +45,49 @@ class Item_func :public Item_func_or_sum
   void count_only_length(Item **item, uint nitems);
   void count_real_length(Item **item, uint nitems);
   void count_decimal_length(Item **item, uint nitems);
-  void count_datetime_length(enum_field_types field_type,
-                             Item **item, uint nitems);
-  bool count_string_result_length(enum_field_types field_type,
-                                  Item **item, uint nitems);
+  bool count_string_length(Item **item, uint nitems);
+  uint count_max_decimals(Item **item, uint nitems)
+  {
+    uint res= 0;
+    for (uint i= 0; i < nitems; i++)
+      set_if_bigger(res, item[i]->decimals);
+    return res;
+  }
 public:
+  void aggregate_attributes_int(Item **items, uint nitems)
+  {
+    collation.set_numeric();
+    count_only_length(items, nitems);
+    decimals= 0;
+  }
+  void aggregate_attributes_real(Item **items, uint nitems)
+  {
+    collation.set_numeric();
+    count_real_length(items, nitems);
+  }
+  void aggregate_attributes_decimal(Item **items, uint nitems)
+  {
+    collation.set_numeric();
+    count_decimal_length(items, nitems);
+  }
+  bool aggregate_attributes_string(Item **item, uint nitems)
+  {
+    return count_string_length(item, nitems);
+  }
+  void set_attributes_temporal(uint int_part_length, uint dec)
+  {
+    collation.set_numeric();
+    unsigned_flag= 0;
+    decimals= MY_MIN(dec, TIME_SECOND_PART_DIGITS);
+    uint length= decimals + int_part_length + (dec ? 1 : 0);
+    fix_char_length(length);
+  }
+  void aggregate_attributes_temporal(uint int_part_length,
+                                     Item **item, uint nitems)
+  {
+    set_attributes_temporal(int_part_length, count_max_decimals(item, nitems));
+  }
+
   table_map not_null_tables_cache;
 
   enum Functype { UNKNOWN_FUNC,EQ_FUNC,EQUAL_FUNC,NE_FUNC,LT_FUNC,LE_FUNC,
@@ -389,7 +427,7 @@ class Item_hybrid_func: public Item_func,
                         public Type_handler_hybrid_field_type
 {
 protected:
-  void fix_attributes(Item **item, uint nitems);
+  bool fix_attributes(Item **item, uint nitems);
 public:
   Item_hybrid_func(THD *thd): Item_func(thd) { }
   Item_hybrid_func(THD *thd, Item *a):  Item_func(thd, a) { }
diff --git a/sql/sql_type.cc b/sql/sql_type.cc
index b7dd4c2..0f6424e 100644
--- a/sql/sql_type.cc
+++ b/sql/sql_type.cc
@@ -890,6 +890,79 @@ Type_handler_temporal_result::Item_get_cache(THD *thd, const Item *item) const
 
 /*************************************************************************/
 
+bool Type_handler_int_result::
+       Item_hybrid_func_fix_attributes(THD *thd, Item_hybrid_func *func,
+                                       Item **items, uint nitems) const
+{
+  func->aggregate_attributes_int(items, nitems);
+  return false;
+}
+
+
+bool Type_handler_real_result::
+       Item_hybrid_func_fix_attributes(THD *thd, Item_hybrid_func *func,
+                                       Item **items, uint nitems) const
+{
+  func->aggregate_attributes_real(items, nitems);
+  return false;
+}
+
+
+bool Type_handler_decimal_result::
+       Item_hybrid_func_fix_attributes(THD *thd, Item_hybrid_func *func,
+                                       Item **items, uint nitems) const
+{
+  func->aggregate_attributes_decimal(items, nitems);
+  return false;
+}
+
+
+bool Type_handler_string_result::
+       Item_hybrid_func_fix_attributes(THD *thd, Item_hybrid_func *func,
+                                       Item **items, uint nitems) const
+{
+  return func->aggregate_attributes_string(items, nitems);
+}
+
+
+bool Type_handler_date_common::
+       Item_hybrid_func_fix_attributes(THD *thd, Item_hybrid_func *func,
+                                       Item **items, uint nitems) const
+{
+  func->set_attributes_temporal(MAX_DATE_WIDTH, 0);
+  return false;
+}
+
+
+bool Type_handler_time_common::
+       Item_hybrid_func_fix_attributes(THD *thd, Item_hybrid_func *func,
+                                       Item **items, uint nitems) const
+{
+  func->aggregate_attributes_temporal(MIN_TIME_WIDTH, items, nitems);
+  return false;
+}
+
+
+bool Type_handler_datetime_common::
+       Item_hybrid_func_fix_attributes(THD *thd, Item_hybrid_func *func,
+                                       Item **items, uint nitems) const
+{
+  func->aggregate_attributes_temporal(MAX_DATETIME_WIDTH, items, nitems);
+  return false;
+}
+
+
+bool Type_handler_timestamp_common::
+       Item_hybrid_func_fix_attributes(THD *thd, Item_hybrid_func *func,
+                                       Item **items, uint nitems) const
+{
+  func->aggregate_attributes_temporal(MAX_DATETIME_WIDTH, items, nitems);
+  return false;
+}
+
+
+/*************************************************************************/
+
 /**
   MAX/MIN for the traditional numeric types preserve the exact data type
   from Fields, but do not preserve the exact type from Items:
diff --git a/sql/sql_type.h b/sql/sql_type.h
index 1f82f5b..e1a5883 100644
--- a/sql/sql_type.h
+++ b/sql/sql_type.h
@@ -28,6 +28,7 @@ class Item;
 class Item_cache;
 class Item_sum_hybrid;
 class Item_func_hex;
+class Item_hybrid_func;
 class Item_func_hybrid_field_type;
 class Item_func_between;
 class Item_func_in;
@@ -298,6 +299,9 @@ class Type_handler
                                  bool no_conversions) const= 0;
   virtual Item_cache *Item_get_cache(THD *thd, const Item *item) const= 0;
   virtual bool set_comparator_func(Arg_comparator *cmp) const= 0;
+  virtual bool Item_hybrid_func_fix_attributes(THD *thd, Item_hybrid_func *func,
+                                               Item **items,
+                                               uint nitems) const= 0;
   virtual bool Item_sum_hybrid_fix_length_and_dec(Item_sum_hybrid *) const= 0;
   virtual String *Item_func_hex_val_str_ascii(Item_func_hex *item,
                                               String *str) const= 0;
@@ -391,6 +395,12 @@ class Type_handler_row: public Type_handler
   }
   Item_cache *Item_get_cache(THD *thd, const Item *item) const;
   bool set_comparator_func(Arg_comparator *cmp) const;
+  bool Item_hybrid_func_fix_attributes(THD *thd, Item_hybrid_func *func,
+                                       Item **items, uint nitems) const
+  {
+    DBUG_ASSERT(0);
+    return true;
+  }
   bool Item_sum_hybrid_fix_length_and_dec(Item_sum_hybrid *func) const
   {
     DBUG_ASSERT(0);
@@ -473,6 +483,8 @@ class Type_handler_real_result: public Type_handler_numeric
   int Item_save_in_field(Item *item, Field *field, bool no_conversions) const;
   Item_cache *Item_get_cache(THD *thd, const Item *item) const;
   bool set_comparator_func(Arg_comparator *cmp) const;
+  bool Item_hybrid_func_fix_attributes(THD *thd, Item_hybrid_func *func,
+                                       Item **items, uint nitems) const;
   bool Item_sum_hybrid_fix_length_and_dec(Item_sum_hybrid *func) const;
   String *Item_func_hex_val_str_ascii(Item_func_hex *item, String *str) const;
   String *Item_func_hybrid_field_type_val_str(Item_func_hybrid_field_type *,
@@ -513,6 +525,8 @@ class Type_handler_decimal_result: public Type_handler_numeric
   int Item_save_in_field(Item *item, Field *field, bool no_conversions) const;
   Item_cache *Item_get_cache(THD *thd, const Item *item) const;
   bool set_comparator_func(Arg_comparator *cmp) const;
+  bool Item_hybrid_func_fix_attributes(THD *thd, Item_hybrid_func *func,
+                                       Item **items, uint nitems) const;
   bool Item_sum_hybrid_fix_length_and_dec(Item_sum_hybrid *func) const;
   String *Item_func_hex_val_str_ascii(Item_func_hex *item, String *str) const;
   String *Item_func_hybrid_field_type_val_str(Item_func_hybrid_field_type *,
@@ -551,6 +565,8 @@ class Type_handler_int_result: public Type_handler_numeric
   int Item_save_in_field(Item *item, Field *field, bool no_conversions) const;
   Item_cache *Item_get_cache(THD *thd, const Item *item) const;
   bool set_comparator_func(Arg_comparator *cmp) const;
+  bool Item_hybrid_func_fix_attributes(THD *thd, Item_hybrid_func *func,
+                                       Item **items, uint nitems) const;
   bool Item_sum_hybrid_fix_length_and_dec(Item_sum_hybrid *func) const;
   String *Item_func_hex_val_str_ascii(Item_func_hex *item, String *str) const;
   String *Item_func_hybrid_field_type_val_str(Item_func_hybrid_field_type *,
@@ -626,6 +642,8 @@ class Type_handler_string_result: public Type_handler
   int Item_save_in_field(Item *item, Field *field, bool no_conversions) const;
   Item_cache *Item_get_cache(THD *thd, const Item *item) const;
   bool set_comparator_func(Arg_comparator *cmp) const;
+  bool Item_hybrid_func_fix_attributes(THD *thd, Item_hybrid_func *func,
+                                       Item **items, uint nitems) const;
   bool Item_sum_hybrid_fix_length_and_dec(Item_sum_hybrid *func) const;
   String *Item_func_hex_val_str_ascii(Item_func_hex *item, String *str) const;
   String *Item_func_hybrid_field_type_val_str(Item_func_hybrid_field_type *,
@@ -778,6 +796,8 @@ class Type_handler_time_common: public Type_handler_temporal_result
   enum_field_types field_type() const { return MYSQL_TYPE_TIME; }
   const Type_handler *type_handler_for_comparison() const;
   int Item_save_in_field(Item *item, Field *field, bool no_conversions) const;
+  bool Item_hybrid_func_fix_attributes(THD *thd, Item_hybrid_func *func,
+                                       Item **items, uint nitems) const;
   cmp_item *make_cmp_item(THD *thd, CHARSET_INFO *cs) const;
   in_vector *make_in_vector(THD *, const Item_func_in *, uint nargs) const;
 };
@@ -813,62 +833,85 @@ class Type_handler_temporal_with_date: public Type_handler_temporal_result
 };
 
 
-class Type_handler_date: public Type_handler_temporal_with_date
+class Type_handler_date_common: public Type_handler_temporal_with_date
 {
 public:
-  virtual ~Type_handler_date() {}
+  virtual ~Type_handler_date_common() {}
   enum_field_types field_type() const { return MYSQL_TYPE_DATE; }
+  bool Item_hybrid_func_fix_attributes(THD *thd, Item_hybrid_func *func,
+                                       Item **items, uint nitems) const;
+};
+
+class Type_handler_date: public Type_handler_date_common
+{
+public:
+  virtual ~Type_handler_date() {}
   Field *make_conversion_table_field(TABLE *, uint metadata,
                                      const Field *target) const;
 };
 
 
-class Type_handler_newdate: public Type_handler_temporal_with_date
+class Type_handler_newdate: public Type_handler_date_common
 {
 public:
   virtual ~Type_handler_newdate() {}
-  enum_field_types field_type() const { return MYSQL_TYPE_DATE; }
   Field *make_conversion_table_field(TABLE *, uint metadata,
                                      const Field *target) const;
 };
 
 
-class Type_handler_datetime: public Type_handler_temporal_with_date
+class Type_handler_datetime_common: public Type_handler_temporal_with_date
 {
 public:
-  virtual ~Type_handler_datetime() {}
+  virtual ~Type_handler_datetime_common() {}
   enum_field_types field_type() const { return MYSQL_TYPE_DATETIME; }
+  bool Item_hybrid_func_fix_attributes(THD *thd, Item_hybrid_func *func,
+                                       Item **items, uint nitems) const;
+};
+
+
+class Type_handler_datetime: public Type_handler_datetime_common
+{
+public:
+  virtual ~Type_handler_datetime() {}
   Field *make_conversion_table_field(TABLE *, uint metadata,
                                      const Field *target) const;
 };
 
 
-class Type_handler_datetime2: public Type_handler_temporal_with_date
+class Type_handler_datetime2: public Type_handler_datetime_common
 {
 public:
   virtual ~Type_handler_datetime2() {}
-  enum_field_types field_type() const { return MYSQL_TYPE_DATETIME; }
   enum_field_types real_field_type() const { return MYSQL_TYPE_DATETIME2; }
   Field *make_conversion_table_field(TABLE *, uint metadata,
                                      const Field *target) const;
 };
 
 
-class Type_handler_timestamp: public Type_handler_temporal_with_date
+class Type_handler_timestamp_common: public Type_handler_temporal_with_date
 {
 public:
-  virtual ~Type_handler_timestamp() {}
+  virtual ~Type_handler_timestamp_common() {}
   enum_field_types field_type() const { return MYSQL_TYPE_TIMESTAMP; }
+  bool Item_hybrid_func_fix_attributes(THD *thd, Item_hybrid_func *func,
+                                       Item **items, uint nitems) const;
+};
+
+
+class Type_handler_timestamp: public Type_handler_timestamp_common
+{
+public:
+  virtual ~Type_handler_timestamp() {}
   Field *make_conversion_table_field(TABLE *, uint metadata,
                                      const Field *target) const;
 };
 
 
-class Type_handler_timestamp2: public Type_handler_temporal_with_date
+class Type_handler_timestamp2: public Type_handler_timestamp_common
 {
 public:
   virtual ~Type_handler_timestamp2() {}
-  enum_field_types field_type() const { return MYSQL_TYPE_TIMESTAMP; }
   enum_field_types real_field_type() const { return MYSQL_TYPE_TIMESTAMP2; }
   Field *make_conversion_table_field(TABLE *, uint metadata,
                                      const Field *target) const;
