commit 913dd04e6348e883bba18b6b1dcba4e45fc598bf
Author: Alexander Barkov <bar@mariadb.org>
Date:   Sat Nov 26 21:19:48 2016 +0400

    MDEV-11357 Split Item_cache::get_cache() into virtual methods in Type_handler
    
    This patch:
    - Adds a new virtual method Type_handler::Item_get_cache
    - Splits moves Item_cache::get_cache() into the new method, every
      "case XXX_RESULT" to the corresponding Type_handler_xxx::Item_get_cache.
    - Adds Item::get_cache as a convenience wrapper, to make the caller code
      shorter.
    - Changes the last argument of Arg_comparator::cache_converted_constant()
      from Item_result to "const Type_handler *".
    - Removes subselect_engine::cmp_type, subselect_engine::res_type,
      subselect_engine::res_field_type and derives subselect_engine
      from Type_handler_hybrid_field_type instead.
    - Makes Type_handler_varchar public, as it's now needed as the
      default data type handler for subselect_engine.

diff --git a/sql/item.cc b/sql/item.cc
index ee453fd..c97f41f 100644
--- a/sql/item.cc
+++ b/sql/item.cc
@@ -6811,7 +6811,7 @@ Item* Item::cache_const_expr_transformer(THD *thd, uchar *arg)
   if (*(bool*)arg)
   {
     *((bool*)arg)= FALSE;
-    Item_cache *cache= Item_cache::get_cache(thd, this);
+    Item_cache *cache= get_cache(thd);
     if (!cache)
       return NULL;
     cache->setup(thd, this);
@@ -7851,7 +7851,7 @@ Item_cache_wrapper::Item_cache_wrapper(THD *thd, Item *item_arg):
   name_length= item_arg->name_length;
   with_subselect=  orig_item->with_subselect;
 
-  if ((expr_value= Item_cache::get_cache(thd, orig_item)))
+  if ((expr_value= orig_item->get_cache(thd)))
     expr_value->setup(thd, orig_item);
 
   fixed= 1;
@@ -9105,41 +9105,6 @@ int stored_field_cmp_to_item(THD *thd, Field *field, Item *item)
   return 0;
 }
 
-Item_cache* Item_cache::get_cache(THD *thd, const Item *item)
-{
-  return get_cache(thd, item, item->cmp_type());
-}
-
-
-/**
-  Get a cache item of given type.
-
-  @param item         value to be cached
-  @param type         required type of cache
-
-  @return cache item
-*/
-
-Item_cache* Item_cache::get_cache(THD *thd, const Item *item,
-                                  const Item_result type)
-{
-  MEM_ROOT *mem_root= thd->mem_root;
-  switch (type) {
-  case INT_RESULT:
-    return new (mem_root) Item_cache_int(thd, item->field_type());
-  case REAL_RESULT:
-    return new (mem_root) Item_cache_real(thd);
-  case DECIMAL_RESULT:
-    return new (mem_root) Item_cache_decimal(thd);
-  case STRING_RESULT:
-    return new (mem_root) Item_cache_str(thd, item);
-  case ROW_RESULT:
-    return new (mem_root) Item_cache_row(thd);
-  case TIME_RESULT:
-    return new (mem_root) Item_cache_temporal(thd, item->field_type());
-  }
-  return 0;                                     // Impossible
-}
 
 void Item_cache::store(Item *item)
 {
@@ -9580,7 +9545,7 @@ bool Item_cache_row::setup(THD *thd, Item *item)
   {
     Item *el= item->element_index(i);
     Item_cache *tmp;
-    if (!(tmp= values[i]= Item_cache::get_cache(thd, el)))
+    if (!(tmp= values[i]= el->get_cache(thd)))
       return 1;
     tmp->setup(thd, el);
   }
diff --git a/sql/item.h b/sql/item.h
index 3b21176..a665bc6 100644
--- a/sql/item.h
+++ b/sql/item.h
@@ -695,6 +695,10 @@ class Item: public Value_source,
   {
     return Type_handler::string_type_handler(max_length)->field_type();
   }
+  Item_cache* get_cache(THD *thd) const
+  {
+    return type_handler()->Item_get_cache(thd, this);
+  }
   virtual enum Type type() const =0;
   /*
     real_type() is the type of base item.  This is same as type() for
@@ -5186,8 +5190,6 @@ class Item_cache: public Item_basic_constant,
   enum Item_result cmp_type () const
   { return Type_handler_hybrid_field_type::cmp_type(); }
 
-  static Item_cache* get_cache(THD *thd, const Item *item);
-  static Item_cache* get_cache(THD *thd, const Item* item, const Item_result type);
   virtual void keep_array() {}
   virtual void print(String *str, enum_query_type query_type);
   bool eq_def(const Field *field) 
diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc
index 7313332..98b179b 100644
--- a/sql/item_cmpfunc.cc
+++ b/sql/item_cmpfunc.cc
@@ -608,8 +608,8 @@ bool Arg_comparator::set_cmp_func_string()
     if (owner->agg_arg_charsets_for_comparison(&m_compare_collation, a, b))
       return true;
   }
-  a= cache_converted_constant(thd, a, &a_cache, compare_type());
-  b= cache_converted_constant(thd, b, &b_cache, compare_type());
+  a= cache_converted_constant(thd, a, &a_cache, compare_type_handler());
+  b= cache_converted_constant(thd, b, &b_cache, compare_type_handler());
   return false;
 }
 
@@ -656,8 +656,8 @@ bool Arg_comparator::set_cmp_func_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());
+  a= cache_converted_constant(thd, a, &a_cache, compare_type_handler());
+  b= cache_converted_constant(thd, b, &b_cache, compare_type_handler());
   return false;
 }
 
@@ -674,8 +674,8 @@ bool Arg_comparator::set_cmp_func_real()
     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());
+  a= cache_converted_constant(thd, a, &a_cache, compare_type_handler());
+  b= cache_converted_constant(thd, b, &b_cache, compare_type_handler());
   return false;
 }
 
@@ -684,8 +684,8 @@ 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());
+  a= cache_converted_constant(thd, a, &a_cache, compare_type_handler());
+  b= cache_converted_constant(thd, b, &b_cache, compare_type_handler());
   return false;
 }
 
@@ -709,18 +709,22 @@ bool Arg_comparator::set_cmp_func_decimal()
 
 Item** Arg_comparator::cache_converted_constant(THD *thd_arg, Item **value,
                                                 Item **cache_item,
-                                                Item_result type)
+                                                const Type_handler *handler)
 {
   /*
+    get_datetime_value creates Item_cache internally when comparing
+    values for the first row.
+    Arg_comparator::cache_converted_constant() is never called for TIME_RESULT.
+  */
+  DBUG_ASSERT(handler->cmp_type() != TIME_RESULT);
+  /*
     Don't need cache if doing context analysis only.
-    Also, get_datetime_value creates Item_cache internally.
-    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() &&
+      handler->cmp_type() != (*value)->cmp_type())
   {
-    Item_cache *cache= Item_cache::get_cache(thd_arg, *value, type);
+    Item_cache *cache= handler->Item_get_cache(thd_arg, *value);
     cache->setup(thd_arg, *value);
     *cache_item= cache;
     return cache_item;
@@ -1285,7 +1289,7 @@ bool Item_in_optimizer::fix_left(THD *thd)
     args[0]= ((Item_in_subselect *)args[1])->left_expr;
   }
   if ((!(*ref0)->fixed && (*ref0)->fix_fields(thd, ref0)) ||
-      (!cache && !(cache= Item_cache::get_cache(thd, *ref0))))
+      (!cache && !(cache= (*ref0)->get_cache(thd))))
     DBUG_RETURN(1);
   /*
     During fix_field() expression could be substituted.
@@ -2710,7 +2714,7 @@ Item_func_nullif::fix_length_and_dec()
     */
     m_cache= args[0]->cmp_type() == STRING_RESULT ?
              new (thd->mem_root) Item_cache_str_for_nullif(thd, args[0]) :
-             Item_cache::get_cache(thd, args[0]);
+             args[0]->get_cache(thd);
     m_cache->setup(thd, args[0]);
     m_cache->store(args[0]);
     m_cache->set_used_tables(args[0]->used_tables());
diff --git a/sql/item_cmpfunc.h b/sql/item_cmpfunc.h
index 2c69896..98bcb2d 100644
--- a/sql/item_cmpfunc.h
+++ b/sql/item_cmpfunc.h
@@ -118,7 +118,7 @@ class Arg_comparator: public Sql_alloc
   int compare_e_time()     { return compare_e_temporal(MYSQL_TYPE_TIME); }
 
   Item** cache_converted_constant(THD *thd, Item **value, Item **cache,
-                                  Item_result type);
+                                  const Type_handler *type);
   inline bool is_owner_equal_func()
   {
     return (owner->type() == Item::FUNC_ITEM &&
diff --git a/sql/item_subselect.cc b/sql/item_subselect.cc
index 21c6333..8c88281 100644
--- a/sql/item_subselect.cc
+++ b/sql/item_subselect.cc
@@ -1057,7 +1057,7 @@ void Item_maxmin_subselect::no_rows_in_result()
   */
   if (parsing_place != SELECT_LIST || const_item())
     return;
-  value= Item_cache::get_cache(thd, new (thd->mem_root) Item_null(thd));
+  value= (new (thd->mem_root) Item_null(thd))->get_cache(thd);
   null_value= 0;
   was_values= 0;
   make_const();
@@ -1075,7 +1075,7 @@ void Item_singlerow_subselect::no_rows_in_result()
   */
   if (parsing_place != SELECT_LIST || const_item())
     return;
-  value= Item_cache::get_cache(thd, new (thd->mem_root) Item_null(thd));
+  value= (new (thd->mem_root) Item_null(thd))->get_cache(thd);
   reset();
   make_const();
 }
@@ -1165,12 +1165,17 @@ void Item_singlerow_subselect::store(uint i, Item *item)
 
 enum Item_result Item_singlerow_subselect::result_type() const
 {
-  return engine->type();
+  return engine->result_type();
 }
 
 enum Item_result Item_singlerow_subselect::cmp_type() const
 {
-  return engine->cmptype();
+  return engine->cmp_type();
+}
+
+const Type_handler *Item_singlerow_subselect::type_handler() const
+{
+  return engine->type_handler();
 }
 
 /* 
@@ -3635,24 +3640,21 @@ 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;
-  res_field_type= MYSQL_TYPE_VAR_STRING;
+  set_handler(&type_handler_varchar);
   for (uint i= 0; (sel_item= li++); i++)
   {
     item->max_length= sel_item->max_length;
-    res_type= sel_item->result_type();
-    cmp_type= sel_item->cmp_type();
-    res_field_type= sel_item->field_type();
+    set_handler(sel_item->type_handler());
     item->decimals= sel_item->decimals;
     item->unsigned_flag= sel_item->unsigned_flag;
     maybe_null= sel_item->maybe_null;
-    if (!(row[i]= Item_cache::get_cache(thd, sel_item, sel_item->cmp_type())))
+    if (!(row[i]= sel_item->get_cache(thd)))
       return;
     row[i]->setup(thd, sel_item);
  //psergey-backport-timours:   row[i]->store(sel_item);
   }
   if (item_list.elements > 1)
-    cmp_type= res_type= ROW_RESULT;
+    set_handler(&type_handler_row);
 }
 
 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 8334057..8928648 100644
--- a/sql/item_subselect.h
+++ b/sql/item_subselect.h
@@ -301,6 +301,7 @@ class Item_singlerow_subselect :public Item_subselect
   enum Item_result result_type() const;
   enum Item_result cmp_type() const;
   enum_field_types field_type() const;
+  const Type_handler *type_handler() const;
   void fix_length_and_dec();
 
   uint cols();
@@ -747,15 +748,13 @@ class Item_allany_subselect :public Item_in_subselect
 };
 
 
-class subselect_engine: public Sql_alloc
+class subselect_engine: public Sql_alloc,
+                        public Type_handler_hybrid_field_type
 {
 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_field_types res_field_type; /* column type of the results */
   bool maybe_null; /* may be null (first item in select) */
 public:
 
@@ -766,12 +765,11 @@ class subselect_engine: public Sql_alloc
 
   subselect_engine(Item_subselect *si,
                    select_result_interceptor *res):
+    Type_handler_hybrid_field_type(&type_handler_varchar),
     thd(NULL)
   {
     result= res;
     item= si;
-    cmp_type= res_type= STRING_RESULT;
-    res_field_type= MYSQL_TYPE_VAR_STRING;
     maybe_null= 0;
   }
   virtual ~subselect_engine() {}; // to satisfy compiler
@@ -808,9 +806,6 @@ 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_field_types field_type() { return res_field_type; }
   virtual void exclude()= 0;
   virtual bool may_be_null() { return maybe_null; };
   virtual table_map upper_select_const_tables()= 0;
diff --git a/sql/item_sum.cc b/sql/item_sum.cc
index 3754bc1..9ffb056 100644
--- a/sql/item_sum.cc
+++ b/sql/item_sum.cc
@@ -1185,14 +1185,14 @@ Item_sum_hybrid::fix_fields(THD *thd, Item **ref)
 
 void Item_sum_hybrid::setup_hybrid(THD *thd, Item *item, Item *value_arg)
 {
-  if (!(value= Item_cache::get_cache(thd, item, item->cmp_type())))
+  if (!(value= item->get_cache(thd)))
     return;
   value->setup(thd, item);
   value->store(value_arg);
   /* Don't cache value, as it will change */
   if (!item->const_item())
     value->set_used_tables(RAND_TABLE_BIT);
-  if (!(arg_cache= Item_cache::get_cache(thd, item, item->cmp_type())))
+  if (!(arg_cache= item->get_cache(thd)))
     return;
   arg_cache->setup(thd, item);
   /* Don't cache value, as it will change */
diff --git a/sql/item_windowfunc.cc b/sql/item_windowfunc.cc
index a13967e..27eeeff 100644
--- a/sql/item_windowfunc.cc
+++ b/sql/item_windowfunc.cc
@@ -301,7 +301,7 @@ bool Item_sum_hybrid_simple::add()
 
 void Item_sum_hybrid_simple::setup_hybrid(THD *thd, Item *item)
 {
-  if (!(value= Item_cache::get_cache(thd, item, item->cmp_type())))
+  if (!(value= item->get_cache(thd)))
     return;
   value->setup(thd, item);
   value->store(item);
diff --git a/sql/opt_subselect.cc b/sql/opt_subselect.cc
index a09826a..39df7b8 100644
--- a/sql/opt_subselect.cc
+++ b/sql/opt_subselect.cc
@@ -5228,7 +5228,7 @@ int select_value_catcher::setup(List<Item> *items)
   List_iterator<Item> li(*items);
   for (uint i= 0; (sel_item= li++); i++)
   {
-    if (!(row[i]= Item_cache::get_cache(thd, sel_item)))
+    if (!(row[i]= sel_item->get_cache(thd)))
       return TRUE;
     row[i]->setup(thd, sel_item);
   }
diff --git a/sql/sp_rcontext.cc b/sql/sp_rcontext.cc
index 6e72446..8873b87 100644
--- a/sql/sp_rcontext.cc
+++ b/sql/sp_rcontext.cc
@@ -387,7 +387,7 @@ Item_cache *sp_rcontext::create_case_expr_holder(THD *thd,
 
   thd->set_n_backup_active_arena(thd->spcont->callers_arena, &current_arena);
 
-  holder= Item_cache::get_cache(thd, item);
+  holder= item->get_cache(thd);
 
   thd->restore_active_arena(thd->spcont->callers_arena, &current_arena);
 
diff --git a/sql/sql_class.cc b/sql/sql_class.cc
index 8fabc8f..80faca6 100644
--- a/sql/sql_class.cc
+++ b/sql/sql_class.cc
@@ -3514,7 +3514,7 @@ int select_max_min_finder_subselect::send_data(List<Item> &items)
   {
     if (!cache)
     {
-      cache= Item_cache::get_cache(thd, val_item);
+      cache= val_item->get_cache(thd);
       switch (val_item->result_type()) {
       case REAL_RESULT:
 	op= &select_max_min_finder_subselect::cmp_real;
diff --git a/sql/sql_type.cc b/sql/sql_type.cc
index e78a3a6..f94e18d 100644
--- a/sql/sql_type.cc
+++ b/sql/sql_type.cc
@@ -40,7 +40,6 @@ 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_varchar     type_handler_varchar;
 static Type_handler_tiny_blob   type_handler_tiny_blob;
 static Type_handler_medium_blob type_handler_medium_blob;
 static Type_handler_long_blob   type_handler_long_blob;
@@ -54,6 +53,7 @@ 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;
 
 
 /**
@@ -722,3 +722,42 @@ bool Type_handler_temporal_result::set_comparator_func(Arg_comparator *cmp) cons
 
 
 /*************************************************************************/
+
+Item_cache *
+Type_handler_row::Item_get_cache(THD *thd, const Item *item) const
+{
+  return new (thd->mem_root) Item_cache_row(thd);
+}
+
+Item_cache *
+Type_handler_int_result::Item_get_cache(THD *thd, const Item *item) const
+{
+  return new (thd->mem_root) Item_cache_int(thd, item->field_type());
+}
+
+Item_cache *
+Type_handler_real_result::Item_get_cache(THD *thd, const Item *item) const
+{
+  return new (thd->mem_root) Item_cache_real(thd);
+}
+
+Item_cache *
+Type_handler_decimal_result::Item_get_cache(THD *thd, const Item *item) const
+{
+  return new (thd->mem_root) Item_cache_decimal(thd);
+}
+
+Item_cache *
+Type_handler_string_result::Item_get_cache(THD *thd, const Item *item) const
+{
+  return new (thd->mem_root) Item_cache_str(thd, item);
+}
+
+Item_cache *
+Type_handler_temporal_result::Item_get_cache(THD *thd, const Item *item) const
+{
+  return new (thd->mem_root) Item_cache_temporal(thd, item->field_type());
+}
+
+/*************************************************************************/
+
diff --git a/sql/sql_type.h b/sql/sql_type.h
index f34be07..87cd5c4 100644
--- a/sql/sql_type.h
+++ b/sql/sql_type.h
@@ -25,6 +25,7 @@
 
 class Field;
 class Item;
+class Item_cache;
 class Type_std_attributes;
 class Sort_param;
 class Arg_comparator;
@@ -286,6 +287,7 @@ class Type_handler
 
   virtual int Item_save_in_field(Item *item, Field *field,
                                  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;
 };
 
@@ -338,6 +340,7 @@ class Type_handler_row: public Type_handler
     DBUG_ASSERT(0);
     return 1;
   }
+  Item_cache *Item_get_cache(THD *thd, const Item *item) const;
   bool set_comparator_func(Arg_comparator *cmp) const;
 };
 
@@ -356,6 +359,7 @@ class Type_handler_real_result: public Type_handler
                   const Type_std_attributes *item,
                   SORT_FIELD_ATTR *attr) const;
   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;
 };
 
@@ -373,6 +377,7 @@ class Type_handler_decimal_result: public Type_handler
                   const Type_std_attributes *item,
                   SORT_FIELD_ATTR *attr) const;
   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;
 };
 
@@ -390,6 +395,7 @@ class Type_handler_int_result: public Type_handler
                   const Type_std_attributes *item,
                   SORT_FIELD_ATTR *attr) const;
   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;
 };
 
@@ -405,6 +411,7 @@ class Type_handler_temporal_result: public Type_handler
   void sortlength(THD *thd,
                   const Type_std_attributes *item,
                   SORT_FIELD_ATTR *attr) const;
+  Item_cache *Item_get_cache(THD *thd, const Item *item) const;
   bool set_comparator_func(Arg_comparator *cmp) const;
 };
 
@@ -424,6 +431,7 @@ class Type_handler_string_result: public Type_handler
                   const Type_std_attributes *item,
                   SORT_FIELD_ATTR *attr) const;
   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;
 };
 
@@ -836,5 +844,6 @@ 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;
 
 #endif /* SQL_TYPE_H_INCLUDED */
