commit 595f33775d49c6a77511d5f4b92f54a9ea4bda1c
Author: Alexander Barkov <bar@mariadb.org>
Date:   Tue Apr 25 19:05:33 2017 +0400

    MDEV-12588 Add Type_handler::type_handler_for_tmp_table() and Type_handler::type_handler_for_union()
    
    1. Implementing the task according to the description:
    
    a. Adding Type_handler::type_handler_for_tmp_table().
    b. Adding Type_handler::type_handler_for_union_table.
    c. Adding helper methods Type_handler::varstring_type_handler(const Item*),
       Type_handler::blob_type_handler(const Item*)
    d. Removing Item::make_string_field() and
       Item_func_group_concat::make_string_field().
       They are not needed any more.
    e. Simplifying Item::tmp_table_field_from_field_type() to just two lines.
    f. Renaming Item_type_holder::make_field_by_type() and implementing
       virtual Item_type_holder::create_tmp_field() instead.
       The new implementation is also as simple as two lines.
    g. Adding a new virtual method Type_all_attributes::get_typelib(),
       to access to TYPELIB definitions for ENUM and SET columns.
    h. Simplifying the code branch for TIME_RESULT, DECIMAL_RESULT, STRING_RESULT
       in Item::create_tmp_field(). It's now just one line.
    i. Implementing Type_handler_enum::make_table_field() and
       Type_handler_set::make_table_field().
    
    2. Code simplification in Field_str constructor calls.
    
    a. Changing the "CHARSET_INFO *cs" argument in constuctors for Field_str
       and its descendants to "const DTCollation &collation". This is to
       avoid two step initialization:
       - setting Field_str::derivation and Field_str::repertoire to the
         default values first
       - then resetting them using:
         set_derivation(item->derivation, item->repertoire).
    
    b. Removing Field::set_derivation()
    
    c. Adding a new constructor DTCollation(CHARSET_INFO *cs),
       for the old code compatibility.
    
    3. Changes in test results
    
    As a side effect some test results have changed, because
    in the old version Item::make_string_field() converted
    TINYBLOB to VARCHAR(255). Now TINYBLOB is preserved.
    
    a. sp-row.result
       This query:
         CREATE TABLE t1 AS SELECT tinyblob_sp_variable;
       Now preserves TINYBLOB as the data type.
       Before the patch a VARCHAR(255) was created.
    
    b. gis-debug.result
       This is a debug test, to make sure that + and - operators
       are commutative and non-commutative correspondingly.
       The exact data type is not really important.
       (But anyway, it now chooses a better data type that fits the result)

diff --git a/mysql-test/r/gis-debug.result b/mysql-test/r/gis-debug.result
index 889ee5c..1516c3d 100644
--- a/mysql-test/r/gis-debug.result
+++ b/mysql-test/r/gis-debug.result
@@ -381,11 +381,11 @@ POINT(0,0) MOD '0' LIMIT 0;
 SHOW CREATE TABLE t1;
 Table	Create Table
 t1	CREATE TABLE `t1` (
-  `POINT(0,0)+'0'` longtext DEFAULT NULL,
-  `POINT(0,0)-'0'` longtext DEFAULT NULL,
-  `POINT(0,0)*'0'` longtext DEFAULT NULL,
-  `POINT(0,0)/'0'` longtext DEFAULT NULL,
-  `POINT(0,0) MOD '0'` longtext DEFAULT NULL
+  `POINT(0,0)+'0'` tinytext DEFAULT NULL,
+  `POINT(0,0)-'0'` tinytext DEFAULT NULL,
+  `POINT(0,0)*'0'` tinytext DEFAULT NULL,
+  `POINT(0,0)/'0'` tinytext DEFAULT NULL,
+  `POINT(0,0) MOD '0'` tinytext DEFAULT NULL
 ) ENGINE=MyISAM DEFAULT CHARSET=latin1
 DROP TABLE t1;
 CREATE TABLE t1 AS SELECT
@@ -394,8 +394,8 @@ CREATE TABLE t1 AS SELECT
 SHOW CREATE TABLE t1;
 Table	Create Table
 t1	CREATE TABLE `t1` (
-  `'0'+POINT(0,0)` longtext DEFAULT NULL,
-  `'0'*POINT(0,0)` longtext DEFAULT NULL
+  `'0'+POINT(0,0)` tinytext DEFAULT NULL,
+  `'0'*POINT(0,0)` tinytext DEFAULT NULL
 ) ENGINE=MyISAM DEFAULT CHARSET=latin1
 DROP TABLE t1;
 CREATE TABLE t1 AS SELECT '0'-POINT(0,0) LIMIT 0;
diff --git a/mysql-test/r/sp-row.result b/mysql-test/r/sp-row.result
index 687e662..1f33f11 100644
--- a/mysql-test/r/sp-row.result
+++ b/mysql-test/r/sp-row.result
@@ -2052,8 +2052,8 @@ CALL p1();
 SHOW CREATE TABLE t1;
 Table	Create Table
 t1	CREATE TABLE `t1` (
-  `var` varchar(255) DEFAULT NULL,
-  `rec.var` varchar(255) DEFAULT NULL
+  `var` tinytext DEFAULT NULL,
+  `rec.var` tinytext DEFAULT NULL
 ) ENGINE=MyISAM DEFAULT CHARSET=latin1
 DROP TABLE t1;
 DROP PROCEDURE p1;
@@ -2092,8 +2092,8 @@ CALL p1();
 SHOW CREATE TABLE t1;
 Table	Create Table
 t1	CREATE TABLE `t1` (
-  `var` varchar(255) CHARACTER SET utf8 DEFAULT NULL,
-  `rec.var` varchar(255) CHARACTER SET utf8 DEFAULT NULL
+  `var` text CHARACTER SET utf8 DEFAULT NULL,
+  `rec.var` text CHARACTER SET utf8 DEFAULT NULL
 ) ENGINE=MyISAM DEFAULT CHARSET=latin1
 DROP TABLE t1;
 DROP PROCEDURE p1;
diff --git a/mysql-test/suite/compat/oracle/r/sp-row.result b/mysql-test/suite/compat/oracle/r/sp-row.result
index 5712bce..6526b3e 100644
--- a/mysql-test/suite/compat/oracle/r/sp-row.result
+++ b/mysql-test/suite/compat/oracle/r/sp-row.result
@@ -2129,8 +2129,8 @@ CALL p1();
 SHOW CREATE TABLE t1;
 Table	Create Table
 t1	CREATE TABLE "t1" (
-  "var" varchar(255) DEFAULT NULL,
-  "rec.var" varchar(255) DEFAULT NULL
+  "var" tinytext DEFAULT NULL,
+  "rec.var" tinytext DEFAULT NULL
 )
 DROP TABLE t1;
 DROP PROCEDURE p1;
@@ -2169,8 +2169,8 @@ CALL p1();
 SHOW CREATE TABLE t1;
 Table	Create Table
 t1	CREATE TABLE "t1" (
-  "var" varchar(255) CHARACTER SET utf8 DEFAULT NULL,
-  "rec.var" varchar(255) CHARACTER SET utf8 DEFAULT NULL
+  "var" text CHARACTER SET utf8 DEFAULT NULL,
+  "rec.var" text CHARACTER SET utf8 DEFAULT NULL
 )
 DROP TABLE t1;
 DROP PROCEDURE p1;
diff --git a/sql/field.cc b/sql/field.cc
index 13bef70..2b602e1 100644
--- a/sql/field.cc
+++ b/sql/field.cc
@@ -2003,15 +2003,15 @@ bool Field_num::get_date(MYSQL_TIME *ltime,ulonglong fuzzydate)
 Field_str::Field_str(uchar *ptr_arg,uint32 len_arg, uchar *null_ptr_arg,
                      uchar null_bit_arg, utype unireg_check_arg,
                      const LEX_CSTRING *field_name_arg,
-                     CHARSET_INFO *charset_arg)
+                     const DTCollation &collation)
   :Field(ptr_arg, len_arg, null_ptr_arg, null_bit_arg,
          unireg_check_arg, field_name_arg)
 {
-  field_charset= charset_arg;
-  if (charset_arg->state & MY_CS_BINSORT)
+  field_charset= collation.collation;
+  if (collation.collation->state & MY_CS_BINSORT)
     flags|=BINARY_FLAG;
-  field_derivation= DERIVATION_IMPLICIT;
-  field_repertoire= my_charset_repertoire(charset_arg);
+  field_derivation= collation.derivation;
+  field_repertoire= collation.repertoire;
 }
 
 
@@ -7756,10 +7756,10 @@ Field_blob::Field_blob(uchar *ptr_arg, uchar *null_ptr_arg, uchar null_bit_arg,
 		       enum utype unireg_check_arg,
                        const LEX_CSTRING *field_name_arg,
                        TABLE_SHARE *share, uint blob_pack_length,
-		       CHARSET_INFO *cs)
+		       const DTCollation &collation)
   :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),
+                 collation),
    packlength(blob_pack_length)
 {
   DBUG_ASSERT(blob_pack_length <= 4); // Only pack lengths 1-4 supported currently
diff --git a/sql/field.h b/sql/field.h
index 30a379a..73c725f 100644
--- a/sql/field.h
+++ b/sql/field.h
@@ -1306,9 +1306,6 @@ class Field: public Value_source
   virtual enum Derivation derivation(void) const
   { return DERIVATION_IMPLICIT; }
   virtual uint repertoire(void) const { return MY_REPERTOIRE_UNICODE30; }
-  virtual void set_derivation(enum Derivation derivation_arg,
-                              uint repertoire_arg)
-  { }
   virtual int set_time() { return 1; }
   bool set_warning(Sql_condition::enum_warning_level, unsigned int code,
                    int cuted_increment) const;
@@ -1659,7 +1656,8 @@ class Field_str :public Field {
                                         const Item_equal *item_equal);
   Field_str(uchar *ptr_arg,uint32 len_arg, uchar *null_ptr_arg,
 	    uchar null_bit_arg, utype unireg_check_arg,
-	    const LEX_CSTRING *field_name_arg, CHARSET_INFO *charset);
+	    const LEX_CSTRING *field_name_arg,
+	    const DTCollation &collation);
   uint decimals() const { return NOT_FIXED_DEC; }
   int  save_in_field(Field *to) { return save_in_field_str(to); }
   bool memcpy_field_possible(const Field *from) const
@@ -1679,12 +1677,6 @@ class Field_str :public Field {
   uint repertoire(void) const { return field_repertoire; }
   CHARSET_INFO *charset(void) const { return field_charset; }
   enum Derivation derivation(void) const { return field_derivation; }
-  void set_derivation(enum Derivation derivation_arg,
-                      uint repertoire_arg)
-  {
-    field_derivation= derivation_arg;
-    field_repertoire= repertoire_arg;
-  }
   bool binary() const { return field_charset == &my_charset_bin; }
   uint32 max_display_length() { return field_length; }
   friend class Create_field;
@@ -1726,9 +1718,10 @@ class Field_longstr :public Field_str
 public:
   Field_longstr(uchar *ptr_arg, uint32 len_arg, uchar *null_ptr_arg,
                 uchar null_bit_arg, utype unireg_check_arg,
-                const LEX_CSTRING *field_name_arg, CHARSET_INFO *charset_arg)
+                const LEX_CSTRING *field_name_arg,
+                const DTCollation &collation)
     :Field_str(ptr_arg, len_arg, null_ptr_arg, null_bit_arg, unireg_check_arg,
-               field_name_arg, charset_arg)
+               field_name_arg, collation)
     {}
 
   int store_decimal(const my_decimal *d);
@@ -2222,9 +2215,9 @@ class Field_null :public Field_str {
 public:
   Field_null(uchar *ptr_arg, uint32 len_arg,
 	     enum utype unireg_check_arg, const LEX_CSTRING *field_name_arg,
-	     CHARSET_INFO *cs)
+	     const DTCollation &collation)
     :Field_str(ptr_arg, len_arg, null, 1,
-	       unireg_check_arg, field_name_arg, cs)
+	       unireg_check_arg, field_name_arg, collation)
     {}
   const Type_handler *type_handler() const { return &type_handler_null; }
   Copy_func *get_copy_func(const Field *from) const
@@ -3023,15 +3016,15 @@ class Field_string :public Field_longstr {
   Field_string(uchar *ptr_arg, uint32 len_arg,uchar *null_ptr_arg,
 	       uchar null_bit_arg,
 	       enum utype unireg_check_arg, const LEX_CSTRING *field_name_arg,
-	       CHARSET_INFO *cs)
+	       const DTCollation &collation)
     :Field_longstr(ptr_arg, len_arg, null_ptr_arg, null_bit_arg,
-                   unireg_check_arg, field_name_arg, cs),
+                   unireg_check_arg, field_name_arg, collation),
      can_alter_field_type(1) {};
   Field_string(uint32 len_arg,bool maybe_null_arg,
                const LEX_CSTRING *field_name_arg,
-               CHARSET_INFO *cs)
+               const DTCollation &collation)
     :Field_longstr((uchar*) 0, len_arg, maybe_null_arg ? (uchar*) "": 0, 0,
-                   NONE, field_name_arg, cs),
+                   NONE, field_name_arg, collation),
      can_alter_field_type(1) {};
 
   const Type_handler *type_handler() const
@@ -3110,18 +3103,18 @@ class Field_varstring :public Field_longstr {
                   uint32 len_arg, uint length_bytes_arg,
                   uchar *null_ptr_arg, uchar null_bit_arg,
 		  enum utype unireg_check_arg, const LEX_CSTRING *field_name_arg,
-		  TABLE_SHARE *share, CHARSET_INFO *cs)
+		  TABLE_SHARE *share, const DTCollation &collation)
     :Field_longstr(ptr_arg, len_arg, null_ptr_arg, null_bit_arg,
-                   unireg_check_arg, field_name_arg, cs),
+                   unireg_check_arg, field_name_arg, collation),
      length_bytes(length_bytes_arg)
   {
     share->varchar_fields++;
   }
   Field_varstring(uint32 len_arg,bool maybe_null_arg,
                   const LEX_CSTRING *field_name_arg,
-                  TABLE_SHARE *share, CHARSET_INFO *cs)
+                  TABLE_SHARE *share, const DTCollation &collation)
     :Field_longstr((uchar*) 0,len_arg, maybe_null_arg ? (uchar*) "": 0, 0,
-                   NONE, field_name_arg, cs),
+                   NONE, field_name_arg, collation),
      length_bytes(len_arg < 256 ? 1 :2)
   {
     share->varchar_fields++;
@@ -3210,20 +3203,21 @@ class Field_blob :public Field_longstr {
 public:
   Field_blob(uchar *ptr_arg, uchar *null_ptr_arg, uchar null_bit_arg,
 	     enum utype unireg_check_arg, const LEX_CSTRING *field_name_arg,
-	     TABLE_SHARE *share, uint blob_pack_length, CHARSET_INFO *cs);
+	     TABLE_SHARE *share, uint blob_pack_length,
+	     const DTCollation &collation);
   Field_blob(uint32 len_arg,bool maybe_null_arg, const LEX_CSTRING *field_name_arg,
-             CHARSET_INFO *cs)
+             const DTCollation &collation)
     :Field_longstr((uchar*) 0, len_arg, maybe_null_arg ? (uchar*) "": 0, 0,
-                   NONE, field_name_arg, cs),
+                   NONE, field_name_arg, collation),
     packlength(4)
   {
     flags|= BLOB_FLAG;
   }
   Field_blob(uint32 len_arg,bool maybe_null_arg,
              const LEX_CSTRING *field_name_arg,
-	     CHARSET_INFO *cs, bool set_packlength)
+             const DTCollation &collation, bool set_packlength)
     :Field_longstr((uchar*) 0,len_arg, maybe_null_arg ? (uchar*) "": 0, 0,
-                   NONE, field_name_arg, cs)
+                   NONE, field_name_arg, collation)
   {
     flags|= BLOB_FLAG;
     packlength= 4;
@@ -3509,9 +3503,9 @@ class Field_enum :public Field_str {
              enum utype unireg_check_arg, const LEX_CSTRING *field_name_arg,
              uint packlength_arg,
              TYPELIB *typelib_arg,
-             CHARSET_INFO *charset_arg)
+             const DTCollation &collation)
     :Field_str(ptr_arg, len_arg, null_ptr_arg, null_bit_arg,
-	       unireg_check_arg, field_name_arg, charset_arg),
+	       unireg_check_arg, field_name_arg, collation),
     packlength(packlength_arg),typelib(typelib_arg)
   {
       flags|=ENUM_FLAG;
@@ -3602,12 +3596,12 @@ class Field_set :public Field_enum {
 	    uchar null_bit_arg,
 	    enum utype unireg_check_arg, const LEX_CSTRING *field_name_arg,
 	    uint32 packlength_arg,
-	    TYPELIB *typelib_arg, CHARSET_INFO *charset_arg)
+	    TYPELIB *typelib_arg, const DTCollation &collation)
     :Field_enum(ptr_arg, len_arg, null_ptr_arg, null_bit_arg,
 		    unireg_check_arg, field_name_arg,
                 packlength_arg,
-                typelib_arg,charset_arg),
-      empty_set_string("", 0, charset_arg)
+                typelib_arg, collation),
+      empty_set_string("", 0, collation.collation)
     {
       flags=(flags & ~ENUM_FLAG) | SET_FLAG;
     }
diff --git a/sql/item.cc b/sql/item.cc
index 6199aba..3ee8985 100644
--- a/sql/item.cc
+++ b/sql/item.cc
@@ -6205,82 +6205,6 @@ bool Item::eq_by_collation(Item *item, bool binary_cmp, CHARSET_INFO *cs)
 }  
 
 
-/**
-  Create a field to hold a string value from an item.
-
-  If too_big_for_varchar() create a blob @n
-  If max_length > 0 create a varchar @n
-  If max_length == 0 create a CHAR(0) 
-
-  @param table		Table for which the field is created
-*/
-
-Field *Item::make_string_field(TABLE *table)
-{
-  Field *field;
-  MEM_ROOT *mem_root= table->in_use->mem_root;
-  DBUG_ASSERT(collation.collation);
-  /* 
-    Note: the following check is repeated in 
-    subquery_types_allow_materialization():
-  */
-  if (too_big_for_varchar())
-    field= new (mem_root)
-      Field_blob(max_length, maybe_null, &name,
-                 collation.collation, TRUE);
-  /* Item_type_holder holds the exact type, do not change it */
-  else if (max_length > 0 &&
-      (type() != Item::TYPE_HOLDER || field_type() != MYSQL_TYPE_STRING))
-    field= new (mem_root)
-      Field_varstring(max_length, maybe_null, &name, table->s,
-                      collation.collation);
-  else
-    field= new (mem_root)
-      Field_string(max_length, maybe_null, &name, collation.collation);
-  if (field)
-    field->init(table);
-  return field;
-}
-
-
-/**
-  Create a field based on field_type of argument.
-
-  This is used to create a field for
-  - IFNULL(x,something)
-  - time functions
-  - prepared statement placeholders
-  - SP variables with data type references: DECLARE a t1.a%TYPE;
-
-  @retval
-    NULL  error
-  @retval
-    \#    Created field
-*/
-
-Field *Item::tmp_table_field_from_field_type(TABLE *table)
-{
-  const Type_handler *handler= type_handler();
-  Record_addr addr(maybe_null);
-
-  switch (handler->field_type()) {
-  case MYSQL_TYPE_DECIMAL:
-    handler= &type_handler_newdecimal;
-    break;
-  case MYSQL_TYPE_NULL:
-  case MYSQL_TYPE_STRING:
-  case MYSQL_TYPE_ENUM:
-  case MYSQL_TYPE_SET:
-  case MYSQL_TYPE_VAR_STRING:
-  case MYSQL_TYPE_VARCHAR:
-    return make_string_field(table);
-  default:
-    break;
-  }
-  return handler->make_and_init_table_field(&name, addr, *this, table);
-}
-
-
 /* ARGSUSED */
 void Item_field::make_field(THD *thd, Send_field *tmp_field)
 {
@@ -10256,6 +10180,7 @@ bool Item_type_holder::join_types(THD *thd, Item *item)
   };
   maybe_null|= item->maybe_null;
   get_full_info(item);
+  set_handler(Item_type_holder::type_handler()->type_handler_for_union(this));
 
   /* Remember decimal integer part to be used in DECIMAL_RESULT handleng */
   prev_decimal_int_part= decimal_int_part();
@@ -10267,62 +10192,6 @@ bool Item_type_holder::join_types(THD *thd, Item *item)
 
 
 /**
-  Make temporary table field according collected information about type
-  of UNION result.
-
-  @param table  temporary table for which we create fields
-
-  @return
-    created field
-*/
-
-Field *Item_type_holder::make_field_by_type(TABLE *table)
-{
-  /*
-    The field functions defines a field to be not null if null_ptr is not 0
-  */
-  uchar *null_ptr= maybe_null ? (uchar*) "" : 0;
-  Field *field;
-
-  switch (Item_type_holder::real_type_handler()->real_field_type()) {
-  case MYSQL_TYPE_ENUM:
-  {
-    DBUG_ASSERT(enum_set_typelib);
-    field= new Field_enum((uchar *) 0, max_length, null_ptr, 0,
-                          Field::NONE, &name,
-                          get_enum_pack_length(enum_set_typelib->count),
-                          enum_set_typelib, collation.collation);
-    if (field)
-      field->init(table);
-    return field;
-  }
-  case MYSQL_TYPE_SET:
-  {
-    DBUG_ASSERT(enum_set_typelib);
-    field= new Field_set((uchar *) 0, max_length, null_ptr, 0,
-                         Field::NONE, &name,
-                         get_set_pack_length(enum_set_typelib->count),
-                         enum_set_typelib, collation.collation);
-    if (field)
-      field->init(table);
-    return field;
-  }
-  case MYSQL_TYPE_NULL:
-    return make_string_field(table);
-  case MYSQL_TYPE_TINY_BLOB:
-  case MYSQL_TYPE_BLOB:
-  case MYSQL_TYPE_MEDIUM_BLOB:
-  case MYSQL_TYPE_LONG_BLOB:
-    set_handler(Type_handler::blob_type_handler(max_length));
-    break;
-  default:
-    break;
-  }
-  return tmp_table_field_from_field_type(table);
-}
-
-
-/**
   Get full information from Item about enum/set fields to be able to create
   them later.
 
diff --git a/sql/item.h b/sql/item.h
index 28bc028..575b8e1 100644
--- a/sql/item.h
+++ b/sql/item.h
@@ -536,8 +536,22 @@ class Item: public Value_source,
 
   SEL_TREE *get_mm_tree_for_const(RANGE_OPT_PARAM *param);
 
-  virtual Field *make_string_field(TABLE *table);
-  Field *tmp_table_field_from_field_type(TABLE *table);
+  /**
+    Create a field based on field_type of argument.
+    This is used to create a field for
+    - IFNULL(x,something)
+    - time functions
+    - prepared statement placeholders
+    - SP variables with data type references: DECLARE a t1.a%TYPE;
+    @retval  NULL  error
+    @retval  !NULL on success
+  */
+  Field *tmp_table_field_from_field_type(TABLE *table)
+  {
+    const Type_handler *h= type_handler()->type_handler_for_tmp_table(this);
+    return h->make_and_init_table_field(&name, Record_addr(maybe_null),
+                                        *this, table);
+  }
   Field *create_tmp_field(bool group, TABLE *table, uint convert_int_length);
 
   void push_note_converted_to_negative_complement(THD *thd);
@@ -5891,12 +5905,18 @@ class Item_type_holder: public Item,
   }
 
   enum Type type() const { return TYPE_HOLDER; }
+  TYPELIB *get_typelib() const { return enum_set_typelib; }
   double val_real();
   longlong val_int();
   my_decimal *val_decimal(my_decimal *);
   String *val_str(String*);
   bool join_types(THD *thd, Item *);
-  Field *make_field_by_type(TABLE *table);
+  Field *create_tmp_field(bool group, TABLE *table)
+  {
+    return Item_type_holder::type_handler()->
+           make_and_init_table_field(&name, Record_addr(maybe_null),
+                                     *this, table);
+  }
   Field::geometry_type get_geometry_type() const { return geometry_type; };
   Item* get_copy(THD *thd, MEM_ROOT *mem_root) { return 0; }
 };
diff --git a/sql/item_sum.cc b/sql/item_sum.cc
index 1eac5ed..6d51025 100644
--- a/sql/item_sum.cc
+++ b/sql/item_sum.cc
@@ -3318,24 +3318,6 @@ void Item_func_group_concat::cleanup()
 }
 
 
-Field *Item_func_group_concat::make_string_field(TABLE *table_arg)
-{
-  Field *field;
-  DBUG_ASSERT(collation.collation);
-
-  if (too_big_for_varchar())
-    field= new Field_blob(max_length,
-                          maybe_null, &name, collation.collation, TRUE);
-  else
-    field= new Field_varstring(max_length,
-                               maybe_null, &name, table_arg->s, collation.collation);
-
-  if (field)
-    field->init(table_arg);
-  return field;
-}
-
-
 Item *Item_func_group_concat::copy_or_same(THD* thd)
 {
   return new (thd->mem_root) Item_func_group_concat(thd, this);
diff --git a/sql/item_sum.h b/sql/item_sum.h
index 039ae0d..33ee83f 100644
--- a/sql/item_sum.h
+++ b/sql/item_sum.h
@@ -1636,9 +1636,6 @@ class Item_func_group_concat : public Item_sum
   friend int dump_leaf_key(void* key_arg,
                            element_count count __attribute__((unused)),
 			   void* item_arg);
-protected:
-  virtual Field *make_string_field(TABLE *table);
-
 public:
   Item_func_group_concat(THD *thd, Name_resolution_context *context_arg,
                          bool is_distinct, List<Item> *is_select,
diff --git a/sql/sql_select.cc b/sql/sql_select.cc
index 3eecd9b..b3c427b 100644
--- a/sql/sql_select.cc
+++ b/sql/sql_select.cc
@@ -15880,19 +15880,8 @@ Field *Item::create_tmp_field(bool group, TABLE *table, uint convert_int_length)
   }
   case TIME_RESULT:
   case DECIMAL_RESULT:
-    new_field= tmp_table_field_from_field_type(table);
-    break;
   case STRING_RESULT:
-    DBUG_ASSERT(collation.collation);
-    /*
-      GEOMETRY fields have STRING_RESULT result type.
-      To preserve type they needed to be handled separately.
-    */
-    if (field_type() == MYSQL_TYPE_GEOMETRY)
-      new_field= tmp_table_field_from_field_type(table);
-    else
-      new_field= make_string_field(table);
-    new_field->set_derivation(collation.derivation, collation.repertoire);
+    new_field= tmp_table_field_from_field_type(table);
     break;
   case ROW_RESULT:
     // This case should never be choosen
@@ -16029,6 +16018,7 @@ Field *create_tmp_field(THD *thd, TABLE *table,Item *item, Item::Type type,
   }
 
   switch (type) {
+  case Item::TYPE_HOLDER:
   case Item::SUM_FUNC_ITEM:
   {
     result= item->create_tmp_field(group, table);
@@ -16158,11 +16148,6 @@ Field *create_tmp_field(THD *thd, TABLE *table,Item *item, Item::Type type,
     return create_tmp_field_from_item(thd, item, table,
                                       (make_copy_field ? 0 : copy_func),
                                        modify_item);
-  case Item::TYPE_HOLDER:  
-    result= ((Item_type_holder *)item)->make_field_by_type(table);
-    result->set_derivation(item->collation.derivation,
-                           item->collation.repertoire);
-    return result;
   default:					// Dosen't have to be stored
     return 0;
   }
diff --git a/sql/sql_type.cc b/sql/sql_type.cc
index 6152105..9b4aa61 100644
--- a/sql/sql_type.cc
+++ b/sql/sql_type.cc
@@ -155,6 +155,17 @@ Type_handler::string_type_handler(uint max_octet_length)
 
 
 const Type_handler *
+Type_handler::varstring_type_handler(const Item *item)
+{
+  if (!item->max_length)
+    return &type_handler_string;
+  if (item->too_big_for_varchar())
+    return blob_type_handler(item->max_length);
+  return &type_handler_varchar;
+}
+
+
+const Type_handler *
 Type_handler::blob_type_handler(uint max_octet_length)
 {
   if (max_octet_length <= 255)
@@ -167,6 +178,12 @@ Type_handler::blob_type_handler(uint max_octet_length)
 }
 
 
+const Type_handler *
+Type_handler::blob_type_handler(const Item *item)
+{
+  return blob_type_handler(item->max_length);
+}
+
 /**
   This method is used by:
   - Item_sum_hybrid, e.g. MAX(item), MIN(item).
@@ -1514,7 +1531,7 @@ Field *Type_handler_string::make_table_field(const LEX_CSTRING *name,
 {
   return new (table->in_use->mem_root)
          Field_string(addr.ptr, attr.max_length, addr.null_ptr, addr.null_bit,
-                      Field::NONE, name, attr.collation.collation);
+                      Field::NONE, name, attr.collation);
 }
 
 
@@ -1529,7 +1546,7 @@ Field *Type_handler_varchar::make_table_field(const LEX_CSTRING *name,
                          HA_VARCHAR_PACKLENGTH(attr.max_length),
                          addr.null_ptr, addr.null_bit,
                          Field::NONE, name,
-                         table->s, attr.collation.collation);
+                         table->s, attr.collation);
 }
 
 
@@ -1542,7 +1559,7 @@ Field *Type_handler_tiny_blob::make_table_field(const LEX_CSTRING *name,
   return new (table->in_use->mem_root)
          Field_blob(addr.ptr, addr.null_ptr, addr.null_bit,
                     Field::NONE, name, table->s,
-                    1, attr.collation.collation);
+                    1, attr.collation);
 }
 
 
@@ -1555,7 +1572,7 @@ Field *Type_handler_blob::make_table_field(const LEX_CSTRING *name,
   return new (table->in_use->mem_root)
          Field_blob(addr.ptr, addr.null_ptr, addr.null_bit,
                     Field::NONE, name, table->s,
-                    2, attr.collation.collation);
+                    2, attr.collation);
 }
 
 
@@ -1569,7 +1586,7 @@ Type_handler_medium_blob::make_table_field(const LEX_CSTRING *name,
   return new (table->in_use->mem_root)
          Field_blob(addr.ptr, addr.null_ptr, addr.null_bit,
                     Field::NONE, name, table->s,
-                    3, attr.collation.collation);
+                    3, attr.collation);
 }
 
 
@@ -1582,7 +1599,7 @@ Field *Type_handler_long_blob::make_table_field(const LEX_CSTRING *name,
   return new (table->in_use->mem_root)
          Field_blob(addr.ptr, addr.null_ptr, addr.null_bit,
                     Field::NONE, name, table->s,
-                    4, attr.collation.collation);
+                    4, attr.collation);
 }
 
 
@@ -1607,12 +1624,13 @@ Field *Type_handler_enum::make_table_field(const LEX_CSTRING *name,
                                            const Type_all_attributes &attr,
                                            TABLE *table) const
 {
-  /*
-    Will be implemented when we split Item_type_holder::make_field_by_type()
-    and/or reuse Type_handler::make_table_field() in make_field() in field.cc
-  */
-  DBUG_ASSERT(0);
-  return 0;
+  TYPELIB *typelib= attr.get_typelib();
+  DBUG_ASSERT(typelib);
+  return new (table->in_use->mem_root)
+         Field_enum(addr.ptr, attr.max_length, addr.null_ptr, addr.null_bit,
+                    Field::NONE, name,
+                    get_enum_pack_length(typelib->count), typelib,
+                    attr.collation);
 }
 
 
@@ -1622,12 +1640,13 @@ Field *Type_handler_set::make_table_field(const LEX_CSTRING *name,
                                           TABLE *table) const
 
 {
-  /*
-    Will be implemented when we split Item_type_holder::make_field_by_type()
-    and/or reuse Type_handler::make_table_field() in make_field() in field.cc
-  */
-  DBUG_ASSERT(0);
-  return 0;
+  TYPELIB *typelib= attr.get_typelib();
+  DBUG_ASSERT(typelib);
+  return new (table->in_use->mem_root)
+         Field_set(addr.ptr, attr.max_length, addr.null_ptr, addr.null_bit,
+                   Field::NONE, name,
+                   get_enum_pack_length(typelib->count), typelib,
+                   attr.collation);
 }
 
 /*************************************************************************/
@@ -3714,7 +3733,7 @@ bool Type_handler_string_result::
          /*
            Materialization also is unable to work when create_tmp_table() will
            create a blob column because item->max_length is too big.
-           The following test is copied from Item::make_string_field():
+           The following test is copied from varstring_type_handler().
          */
          !inner->too_big_for_varchar();
 }
@@ -3730,3 +3749,33 @@ bool Type_handler_temporal_result::
 }
 
 /***************************************************************************/
+
+
+const Type_handler *
+Type_handler_null::type_handler_for_tmp_table(const Item *item) const
+{
+  return &type_handler_string;
+}
+
+
+const Type_handler *
+Type_handler_null::type_handler_for_union(const Item *item) const
+{
+  return &type_handler_string;
+}
+
+
+const Type_handler *
+Type_handler_olddecimal::type_handler_for_tmp_table(const Item *item) const
+{
+  return &type_handler_newdecimal;
+}
+
+const Type_handler *
+Type_handler_olddecimal::type_handler_for_union(const Item *item) const
+{
+  return &type_handler_newdecimal;
+}
+
+
+/***************************************************************************/
diff --git a/sql/sql_type.h b/sql/sql_type.h
index 0d42304..745c966 100644
--- a/sql/sql_type.h
+++ b/sql/sql_type.h
@@ -130,6 +130,12 @@ class DTCollation {
     derivation= DERIVATION_NONE;
     repertoire= MY_REPERTOIRE_UNICODE30;
   }
+  DTCollation(CHARSET_INFO *collation_arg)
+  {
+    collation= collation_arg;
+    derivation= DERIVATION_IMPLICIT;
+    repertoire= my_charset_repertoire(collation_arg);
+  }
   DTCollation(CHARSET_INFO *collation_arg, Derivation derivation_arg)
   {
     collation= collation_arg;
@@ -332,6 +338,7 @@ class Type_all_attributes: public Type_std_attributes
     datatype indepented method.
   */
   virtual uint uint_geometry_type() const= 0;
+  virtual TYPELIB *get_typelib() const { return NULL; }
 };
 
 
@@ -390,6 +397,15 @@ class Type_handler
 public:
   static const Type_handler *blob_type_handler(uint max_octet_length);
   static const Type_handler *string_type_handler(uint max_octet_length);
+  /**
+    Return a string type handler for Item
+    If too_big_for_varchar() returns an BLOB variant, according to length.
+    If max_length > 0 create a VARCHAR(n)
+    If max_length == 0 create a CHAR(0)
+    @param item - the Item to get the handler to.
+  */
+  static const Type_handler *varstring_type_handler(const Item *item);
+  static const Type_handler *blob_type_handler(const Item *item);
   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);
@@ -426,6 +442,14 @@ class Type_handler
   */
   virtual bool is_param_long_data_type() const { return false; }
   virtual const Type_handler *type_handler_for_comparison() const= 0;
+  virtual const Type_handler *type_handler_for_tmp_table(const Item *) const
+  {
+    return this;
+  }
+  virtual const Type_handler *type_handler_for_union(const Item *) const
+  {
+    return this;
+  }
   virtual const Type_handler *cast_to_int_type_handler() const
   {
     return this;
@@ -1594,6 +1618,8 @@ class Type_handler_olddecimal: public Type_handler_decimal_result
   virtual ~Type_handler_olddecimal() {}
   const Name name() const { return m_name_decimal; }
   enum_field_types field_type() const { return MYSQL_TYPE_DECIMAL; }
+  const Type_handler *type_handler_for_tmp_table(const Item *item) const;
+  const Type_handler *type_handler_for_union(const Item *item) const;
   Field *make_conversion_table_field(TABLE *, uint metadata,
                                      const Field *target) const;
   Field *make_table_field(const LEX_CSTRING *name,
@@ -1627,6 +1653,8 @@ class Type_handler_null: public Type_handler_string_result
   const Name name() const { return m_name_null; }
   enum_field_types field_type() const { return MYSQL_TYPE_NULL; }
   const Type_handler *type_handler_for_comparison() const;
+  const Type_handler *type_handler_for_tmp_table(const Item *item) const;
+  const Type_handler *type_handler_for_union(const Item *) const;
   uint32 max_display_length(const Item *item) const { return 0; }
   Field *make_conversion_table_field(TABLE *, uint metadata,
                                      const Field *target) const;
@@ -1645,6 +1673,10 @@ class Type_handler_string: public Type_handler_string_result
   const Name name() const { return m_name_char; }
   enum_field_types field_type() const { return MYSQL_TYPE_STRING; }
   bool is_param_long_data_type() const { return true; }
+  const Type_handler *type_handler_for_tmp_table(const Item *item) const
+  {
+    return varstring_type_handler(item);
+  }
   Field *make_conversion_table_field(TABLE *, uint metadata,
                                      const Field *target) const;
   Field *make_table_field(const LEX_CSTRING *name,
@@ -1663,6 +1695,14 @@ class Type_handler_var_string: public Type_handler_string
   const Name name() const { return m_name_var_string; }
   enum_field_types field_type() const { return MYSQL_TYPE_VAR_STRING; }
   enum_field_types real_field_type() const { return MYSQL_TYPE_STRING; }
+  const Type_handler *type_handler_for_tmp_table(const Item *item) const
+  {
+    return varstring_type_handler(item);
+  }
+  const Type_handler *type_handler_for_union(const Item *item) const
+  {
+    return varstring_type_handler(item);
+  }
 };
 
 
@@ -1673,6 +1713,14 @@ class Type_handler_varchar: public Type_handler_string_result
   virtual ~Type_handler_varchar() {}
   const Name name() const { return m_name_varchar; }
   enum_field_types field_type() const { return MYSQL_TYPE_VARCHAR; }
+  const Type_handler *type_handler_for_tmp_table(const Item *item) const
+  {
+    return varstring_type_handler(item);
+  }
+  const Type_handler *type_handler_for_union(const Item *item) const
+  {
+    return varstring_type_handler(item);
+  }
   bool is_param_long_data_type() const { return true; }
   Field *make_conversion_table_field(TABLE *, uint metadata,
                                      const Field *target) const;
@@ -1687,6 +1735,14 @@ class Type_handler_blob_common: public Type_handler_string_result
 {
 public:
   virtual ~Type_handler_blob_common() { }
+  const Type_handler *type_handler_for_tmp_table(const Item *item) const
+  {
+    return blob_type_handler(item);
+  }
+  const Type_handler *type_handler_for_union(const Item *item) const
+  {
+    return blob_type_handler(item);
+  }
   bool subquery_type_allows_materialization(const Item *inner,
                                             const Item *outer) const
   {
