← Back to team overview

maria-developers team mailing list archive

Re: Please review MDEV-11298 also fixing GIS bugs MDEV-9405 and MDEV-9425

 

  Привет, Леха!

Че-то я запутался с номерами. Прошу прощения.


Вместо:

Please review MDEV-11298 also fixing GIS bugs MDEV-9405 and MDEV-9425

в этом письме должно было быть:

Please review MDEV-11302 also fixing GIS bugs MDEV-9405 and MDEV-9425

И имя патча было неверное.

А настоящий MDEV-11298 я оправлял другим письмом.



То есть всего осталось два патча из тех, что я отправлял тебе на ревью:


MDEV-11298 Split Item_func_hex::val_str_ascii() into virtual methods in Type_handler

MDEV-11302 Add class Type_ext_attributes and Type_handler::join_type_ext_attributes()



On 11/17/2016 05:57 PM, Alexander Barkov wrote:
Hello Alexey,

Please review a patch for:

MDEV-11302 Add class Type_ext_attributes and
Type_handler::join_type_ext_attributes()

A detailed description can be found in the task ticket:
https://jira.mariadb.org/browse/MDEV-11302


This patch also fixes the problems reported in:

MDEV-9405 Hybrid functions, SP do not preserve geometry type
MDEV-9425 Hybrid functions and UNION do not preserve spatial REF_SYSTEM_ID

Some calls for geometry_type() and/or srid() were forgotten in the old
code.


The new code replaces calls for geometry_type() and srid()
to a generic type_ext_attributes() and makes maintaining/adding
of similar data type specific attributes easier.

The new class Type_ext_attributes will be used in a few field creation
methods in Type_handler later.


Thanks!
diff --git a/mysql-test/r/gis.result b/mysql-test/r/gis.result
index d10cfec..fb7bbe0 100644
--- a/mysql-test/r/gis.result
+++ b/mysql-test/r/gis.result
@@ -2193,3 +2193,102 @@ DROP TABLE t1;
 #
 # End of 10.2 tests
 #
+#
+# Start of 10.3 tests
+#
+#
+#  MDEV-9405 Hybrid functions, SP do not preserve geometry type
+#
+CREATE TABLE t1 AS SELECT
+Point(0,0) AS p0,
+COALESCE(Point(0,0)) AS p1,
+CASE WHEN 0 THEN Point(0,0) ELSE Point(1,1) END AS p2,
+LEAST(Point(0,0), Point(0,0)) AS p3;
+SHOW CREATE TABLE t1;
+Table	Create Table
+t1	CREATE TABLE `t1` (
+  `p0` point DEFAULT NULL,
+  `p1` point DEFAULT NULL,
+  `p2` point DEFAULT NULL,
+  `p3` point DEFAULT NULL
+) ENGINE=MyISAM DEFAULT CHARSET=latin1
+DROP TABLE t1;
+CREATE FUNCTION f1() RETURNS POINT RETURN POINT(1,1);
+CREATE TABLE t1 SELECT f1() AS a;
+SHOW CREATE TABLE t1;
+Table	Create Table
+t1	CREATE TABLE `t1` (
+  `a` point DEFAULT NULL
+) ENGINE=MyISAM DEFAULT CHARSET=latin1
+DROP TABLE t1;
+DROP FUNCTION f1;
+CREATE TABLE t1 (a POINT REF_SYSTEM_ID=10, b POINT REF_SYSTEM_ID=11);
+CREATE TABLE t2 AS SELECT a FROM t1 UNION SELECT b FROM t1;
+SELECT G_TABLE_NAME,G_GEOMETRY_COLUMN,SRID FROM INFORMATION_SCHEMA.GEOMETRY_COLUMNS ORDER BY G_TABLE_NAME, G_GEOMETRY_COLUMN;
+G_TABLE_NAME	G_GEOMETRY_COLUMN	SRID
+t1	a	10
+t1	b	11
+t2	a	0
+DROP TABLE t2;
+CREATE TABLE t2 AS SELECT a FROM t1 UNION SELECT a FROM t1;
+SELECT G_TABLE_NAME,G_GEOMETRY_COLUMN,SRID FROM INFORMATION_SCHEMA.GEOMETRY_COLUMNS ORDER BY G_TABLE_NAME, G_GEOMETRY_COLUMN;
+G_TABLE_NAME	G_GEOMETRY_COLUMN	SRID
+t1	a	10
+t1	b	11
+t2	a	10
+DROP TABLE t2;
+DROP TABLE t1;
+CREATE TABLE t1 (p POINT REF_SYSTEM_ID=10);
+CREATE TABLE t2 AS SELECT MIN(p) AS p FROM t1;
+SELECT G_TABLE_NAME,G_GEOMETRY_COLUMN,SRID FROM INFORMATION_SCHEMA.GEOMETRY_COLUMNS ORDER BY G_TABLE_NAME, G_GEOMETRY_COLUMN;
+G_TABLE_NAME	G_GEOMETRY_COLUMN	SRID
+t1	p	10
+t2	p	10
+DROP TABLE t2;
+DROP TABLE t1;
+CREATE TABLE t1 (p POINT REF_SYSTEM_ID=10);
+CREATE TABLE t2 AS SELECT MIN(p) AS p FROM t1 UNION SELECT p FROM t1;
+SELECT G_TABLE_NAME,G_GEOMETRY_COLUMN,SRID FROM INFORMATION_SCHEMA.GEOMETRY_COLUMNS ORDER BY G_TABLE_NAME, G_GEOMETRY_COLUMN;
+G_TABLE_NAME	G_GEOMETRY_COLUMN	SRID
+t1	p	10
+t2	p	10
+DROP TABLE t2;
+DROP TABLE t1;
+CREATE TABLE t1 (p POINT REF_SYSTEM_ID=10);
+CREATE VIEW v1 AS SELECT p FROM t1;
+CREATE TABLE t2 AS SELECT MIN(p) AS p FROM t1 UNION SELECT p FROM v1;
+SHOW CREATE TABLE t2;
+Table	Create Table
+t2	CREATE TABLE `t2` (
+  `p` point DEFAULT NULL
+) ENGINE=MyISAM DEFAULT CHARSET=latin1
+SELECT G_TABLE_NAME,G_GEOMETRY_COLUMN,SRID FROM INFORMATION_SCHEMA.GEOMETRY_COLUMNS ORDER BY G_TABLE_NAME, G_GEOMETRY_COLUMN;
+G_TABLE_NAME	G_GEOMETRY_COLUMN	SRID
+t1	p	10
+t2	p	10
+v1	p	10
+DROP TABLE t2;
+DROP VIEW v1;
+DROP TABLE t1;
+#
+#  End of MDEV-9405 Hybrid functions, SP do not preserve geometry type
+#
+#
+# MDEV-9425 Hybrid functions and UNION do not preserve spatial REF_SYSTEM_ID
+#
+CREATE TABLE t1 (g GEOMETRY REF_SYSTEM_ID=101);
+CREATE TABLE t2 AS SELECT g AS g_column, COALESCE(g) AS g_coalesce FROM t1;
+CREATE TABLE t3 AS SELECT g AS g_unionn FROM t1 UNION SELECT g FROM t1;
+SELECT G_TABLE_NAME, G_GEOMETRY_COLUMN, SRID FROM INFORMATION_SCHEMA.GEOMETRY_COLUMNS ORDER BY G_TABLE_NAME, G_GEOMETRY_COLUMN;
+G_TABLE_NAME	G_GEOMETRY_COLUMN	SRID
+t1	g	101
+t2	g_coalesce	101
+t2	g_column	101
+t3	g_unionn	101
+DROP TABLE t3,t2,t1;
+#
+# End of MDEV-9425 Hybrid functions and UNION do not preserve spatial REF_SYSTEM_ID
+#
+#
+# End of 10.3 tests
+#
diff --git a/mysql-test/t/gis.test b/mysql-test/t/gis.test
index 12b3e60..aa721af 100644
--- a/mysql-test/t/gis.test
+++ b/mysql-test/t/gis.test
@@ -1711,3 +1711,84 @@ DROP TABLE t1;
 --echo #
 --echo # End of 10.2 tests
 --echo #
+
+--echo #
+--echo # Start of 10.3 tests
+--echo #
+
+--echo #
+--echo #  MDEV-9405 Hybrid functions, SP do not preserve geometry type
+--echo #
+
+CREATE TABLE t1 AS SELECT
+  Point(0,0) AS p0,
+  COALESCE(Point(0,0)) AS p1,
+  CASE WHEN 0 THEN Point(0,0) ELSE Point(1,1) END AS p2,
+  LEAST(Point(0,0), Point(0,0)) AS p3;
+SHOW CREATE TABLE t1;
+DROP TABLE t1;
+
+
+CREATE FUNCTION f1() RETURNS POINT RETURN POINT(1,1);
+CREATE TABLE t1 SELECT f1() AS a;
+SHOW CREATE TABLE t1;
+DROP TABLE t1;
+DROP FUNCTION f1;
+
+
+CREATE TABLE t1 (a POINT REF_SYSTEM_ID=10, b POINT REF_SYSTEM_ID=11);
+CREATE TABLE t2 AS SELECT a FROM t1 UNION SELECT b FROM t1;
+SELECT G_TABLE_NAME,G_GEOMETRY_COLUMN,SRID FROM INFORMATION_SCHEMA.GEOMETRY_COLUMNS ORDER BY G_TABLE_NAME, G_GEOMETRY_COLUMN;
+DROP TABLE t2;
+CREATE TABLE t2 AS SELECT a FROM t1 UNION SELECT a FROM t1;
+SELECT G_TABLE_NAME,G_GEOMETRY_COLUMN,SRID FROM INFORMATION_SCHEMA.GEOMETRY_COLUMNS ORDER BY G_TABLE_NAME, G_GEOMETRY_COLUMN;
+DROP TABLE t2;
+DROP TABLE t1;
+
+
+CREATE TABLE t1 (p POINT REF_SYSTEM_ID=10);
+CREATE TABLE t2 AS SELECT MIN(p) AS p FROM t1;
+SELECT G_TABLE_NAME,G_GEOMETRY_COLUMN,SRID FROM INFORMATION_SCHEMA.GEOMETRY_COLUMNS ORDER BY G_TABLE_NAME, G_GEOMETRY_COLUMN;
+DROP TABLE t2;
+DROP TABLE t1;
+
+
+CREATE TABLE t1 (p POINT REF_SYSTEM_ID=10);
+CREATE TABLE t2 AS SELECT MIN(p) AS p FROM t1 UNION SELECT p FROM t1;
+SELECT G_TABLE_NAME,G_GEOMETRY_COLUMN,SRID FROM INFORMATION_SCHEMA.GEOMETRY_COLUMNS ORDER BY G_TABLE_NAME, G_GEOMETRY_COLUMN;
+DROP TABLE t2;
+DROP TABLE t1;
+
+
+CREATE TABLE t1 (p POINT REF_SYSTEM_ID=10);
+CREATE VIEW v1 AS SELECT p FROM t1;
+CREATE TABLE t2 AS SELECT MIN(p) AS p FROM t1 UNION SELECT p FROM v1;
+SHOW CREATE TABLE t2;
+SELECT G_TABLE_NAME,G_GEOMETRY_COLUMN,SRID FROM INFORMATION_SCHEMA.GEOMETRY_COLUMNS ORDER BY G_TABLE_NAME, G_GEOMETRY_COLUMN;
+DROP TABLE t2;
+DROP VIEW v1;
+DROP TABLE t1;
+
+--echo #
+--echo #  End of MDEV-9405 Hybrid functions, SP do not preserve geometry type
+--echo #
+
+
+--echo #
+--echo # MDEV-9425 Hybrid functions and UNION do not preserve spatial REF_SYSTEM_ID
+--echo #
+
+CREATE TABLE t1 (g GEOMETRY REF_SYSTEM_ID=101);
+CREATE TABLE t2 AS SELECT g AS g_column, COALESCE(g) AS g_coalesce FROM t1;
+CREATE TABLE t3 AS SELECT g AS g_union FROM t1 UNION SELECT g FROM t1;
+SELECT G_TABLE_NAME, G_GEOMETRY_COLUMN, SRID FROM INFORMATION_SCHEMA.GEOMETRY_COLUMNS ORDER BY G_TABLE_NAME, G_GEOMETRY_COLUMN;
+DROP TABLE t3,t2,t1;
+
+--echo #
+--echo # End of MDEV-9425 Hybrid functions and UNION do not preserve spatial REF_SYSTEM_ID
+--echo #
+
+
+--echo #
+--echo # End of 10.3 tests
+--echo #
diff --git a/sql/field.cc b/sql/field.cc
index 6f273e6..b51fecf 100644
--- a/sql/field.cc
+++ b/sql/field.cc
@@ -8562,8 +8562,8 @@ int Field_geom::store(const char *from, uint length, CHARSET_INFO *cs)
 	wkb_type > (uint32) Geometry::wkb_last)
       goto err;
 
-    if (geom_type != Field::GEOM_GEOMETRY && 
-        geom_type != Field::GEOM_GEOMETRYCOLLECTION &&
+    if (geom_type != GEOM_GEOMETRY &&
+        geom_type != GEOM_GEOMETRYCOLLECTION &&
         (uint32) geom_type != wkb_type)
     {
       my_printf_error(ER_TRUNCATED_WRONG_VALUE_FOR_FIELD, 
@@ -8596,14 +8596,6 @@ int Field_geom::store(const char *from, uint length, CHARSET_INFO *cs)
   return -1;
 }
 
-Field::geometry_type Field_geom::geometry_type_merge(geometry_type a,
-                                                            geometry_type b)
-{
-  if (a == b)
-    return a;
-  return Field::GEOM_GEOMETRY;
-}
-
 
 uint Field_geom::is_equal(Create_field *new_field)
 {
@@ -9023,7 +9015,7 @@ bool Field_enum::eq_def(const Field *field) const
 
 uint Field_enum::is_equal(Create_field *new_field)
 {
-  TYPELIB *values= new_field->interval;
+  TYPELIB *values= new_field->typelib;
 
   /*
     The fields are compatible if they have the same flags,
@@ -9043,7 +9035,7 @@ uint Field_enum::is_equal(Create_field *new_field)
     return IS_EQUAL_NO;
 
   /* Check whether there are modification before the end. */
-  if (! compare_type_names(field_charset, typelib, new_field->interval))
+  if (! compare_type_names(field_charset, typelib, new_field->typelib))
     return IS_EQUAL_NO;
 
   return IS_EQUAL_YES;
@@ -9728,8 +9720,8 @@ bool Column_definition::create_interval_from_interval_list(MEM_ROOT *mem_root,
                                                bool reuse_interval_list_values)
 {
   DBUG_ENTER("Column_definition::create_interval_from_interval_list");
-  DBUG_ASSERT(!interval);
-  if (!(interval= (TYPELIB*) alloc_root(mem_root, sizeof(TYPELIB))))
+  DBUG_ASSERT(!typelib);
+  if (!(typelib= (TYPELIB*) alloc_root(mem_root, sizeof(TYPELIB))))
     DBUG_RETURN(true); // EOM
 
   List_iterator<String> it(interval_list);
@@ -9743,17 +9735,17 @@ bool Column_definition::create_interval_from_interval_list(MEM_ROOT *mem_root,
   DBUG_ASSERT(comma_length >= 0 && comma_length <= (int) sizeof(comma_buf));
 
   if (!multi_alloc_root(mem_root,
-                        &interval->type_names,
+                        &typelib->type_names,
                         sizeof(char*) * (interval_list.elements + 1),
-                        &interval->type_lengths,
+                        &typelib->type_lengths,
                         sizeof(uint) * (interval_list.elements + 1),
                         NullS))
     goto err; // EOM
 
-  interval->name= "";
-  interval->count= interval_list.elements;
+  typelib->name= "";
+  typelib->count= interval_list.elements;
 
-  for (uint i= 0; i < interval->count; i++)
+  for (uint i= 0; i < typelib->count; i++)
   {
     uint32 dummy;
     String *tmp= it++;
@@ -9791,15 +9783,15 @@ bool Column_definition::create_interval_from_interval_list(MEM_ROOT *mem_root,
         goto err;
       }
     }
-    interval->type_names[i]= value.str;
-    interval->type_lengths[i]= value.length;
+    typelib->type_names[i]= value.str;
+    typelib->type_lengths[i]= value.length;
   }
-  interval->type_names[interval->count]= 0;    // End marker
-  interval->type_lengths[interval->count]= 0;
+  typelib->type_names[typelib->count]= 0;    // End marker
+  typelib->type_lengths[typelib->count]= 0;
   interval_list.empty();  // Don't need interval_list anymore
   DBUG_RETURN(false);
 err:
-  interval= NULL;  // Avoid having both non-empty interval_list and interval
+  typelib= NULL;  // Avoid having both non-empty interval_list and interval
   DBUG_RETURN(true);
 }
 
@@ -10303,9 +10295,8 @@ Field *make_field(TABLE_SHARE *share,
 		  uint pack_flag,
 		  enum_field_types field_type,
 		  CHARSET_INFO *field_charset,
-		  Field::geometry_type geom_type, uint srid,
+		  const Type_ext_attributes &eattr,
 		  Field::utype unireg_check,
-		  TYPELIB *interval,
 		  const char *field_name)
 {
   uchar *UNINIT_VAR(bit_ptr);
@@ -10333,7 +10324,7 @@ Field *make_field(TABLE_SHARE *share,
   }
 
   DBUG_PRINT("debug", ("field_type: %d, field_length: %u, interval: %p, pack_flag: %s%s%s%s%s",
-                       field_type, field_length, interval,
+                       field_type, field_length, eattr.typelib,
                        FLAGSTR(pack_flag, FIELDFLAG_BINARY),
                        FLAGSTR(pack_flag, FIELDFLAG_INTERVAL),
                        FLAGSTR(pack_flag, FIELDFLAG_NUMBER),
@@ -10373,7 +10364,7 @@ Field *make_field(TABLE_SHARE *share,
       return new (mem_root)
         Field_geom(ptr,null_pos,null_bit,
                    unireg_check, field_name, share,
-                   pack_length, geom_type, srid);
+                   pack_length, eattr);
     }
 #endif
     if (f_is_blob(pack_flag))
@@ -10381,18 +10372,18 @@ Field *make_field(TABLE_SHARE *share,
         Field_blob(ptr,null_pos,null_bit,
                    unireg_check, field_name, share,
                    pack_length, field_charset);
-    if (interval)
+    if (eattr.typelib)
     {
       if (f_is_enum(pack_flag))
 	return new (mem_root)
           Field_enum(ptr,field_length,null_pos,null_bit,
                      unireg_check, field_name,
-                     pack_length, interval, field_charset);
+                     pack_length, eattr.typelib, field_charset);
       else
 	return new (mem_root)
           Field_set(ptr,field_length,null_pos,null_bit,
                     unireg_check, field_name,
-                    pack_length, interval, field_charset);
+                    pack_length, eattr.typelib, field_charset);
     }
   }
 
@@ -10588,8 +10579,7 @@ Column_definition::Column_definition(THD *thd, Field *old_field,
     break;
 #ifdef HAVE_SPATIAL
   case MYSQL_TYPE_GEOMETRY:
-    geom_type= ((Field_geom*)old_field)->geom_type;
-    srid= ((Field_geom*)old_field)->srid;
+    Type_geom_attributes::operator= (*((Field_geom*) old_field));
     break;
 #endif
   case MYSQL_TYPE_YEAR:
@@ -10617,9 +10607,9 @@ Column_definition::Column_definition(THD *thd, Field *old_field,
   }
 
   if (flags & (ENUM_FLAG | SET_FLAG))
-    interval= ((Field_enum*) old_field)->typelib;
+    typelib= ((Field_enum*) old_field)->typelib;
   else
-    interval=0;
+    typelib= 0;
 
   interval_list.empty(); // prepare_interval_field() needs this
 
diff --git a/sql/field.h b/sql/field.h
index cc8f758..4f18b3f 100644
--- a/sql/field.h
+++ b/sql/field.h
@@ -31,6 +31,7 @@
 #include "my_decimal.h"                         /* my_decimal */
 #include "sql_error.h"                          /* Sql_condition */
 #include "compat56.h"
+#include "sql_type.h"
 
 class Send_field;
 class Copy_field;
@@ -713,12 +714,6 @@ class Field: public Value_source
 		CHECK,EMPTY,UNKNOWN_FIELD,CASEDN,NEXT_NUMBER,INTERVAL_FIELD,
                 BIT_FIELD, TIMESTAMP_OLD_FIELD, CAPITALIZE, BLOB_FIELD,
                 TIMESTAMP_DN_FIELD, TIMESTAMP_UN_FIELD, TIMESTAMP_DNUN_FIELD};
-  enum geometry_type
-  {
-    GEOM_GEOMETRY = 0, GEOM_POINT = 1, GEOM_LINESTRING = 2, GEOM_POLYGON = 3,
-    GEOM_MULTIPOINT = 4, GEOM_MULTILINESTRING = 5, GEOM_MULTIPOLYGON = 6,
-    GEOM_GEOMETRYCOLLECTION = 7
-  };
   enum imagetype { itRAW, itMBR};
 
   utype		unireg_check;
@@ -1340,11 +1335,9 @@ class Field: public Value_source
     return field_length / charset()->mbmaxlen;
   }
 
-  virtual geometry_type get_geometry_type()
+  virtual Type_ext_attributes type_ext_attributes() const
   {
-    /* shouldn't get here. */
-    DBUG_ASSERT(0);
-    return GEOM_GEOMETRY;
+    return Type_ext_attributes();
   }
 
   ha_storage_media field_storage_type() const
@@ -3327,10 +3320,9 @@ class Field_blob :public Field_longstr {
 
 
 #ifdef HAVE_SPATIAL
-class Field_geom :public Field_blob {
+class Field_geom :public Field_blob,
+                  public Type_geom_attributes {
 public:
-  enum geometry_type geom_type;
-  uint srid;
   uint precision;
   enum storage_type { GEOM_STORAGE_WKB= 0, GEOM_STORAGE_BINARY= 1};
   enum storage_type storage;
@@ -3338,14 +3330,16 @@ class Field_geom :public Field_blob {
   Field_geom(uchar *ptr_arg, uchar *null_ptr_arg, uint null_bit_arg,
 	     enum utype unireg_check_arg, const char *field_name_arg,
 	     TABLE_SHARE *share, uint blob_pack_length,
-	     enum geometry_type geom_type_arg, uint field_srid)
+	     const Type_geom_attributes &type_geom_attrs)
      :Field_blob(ptr_arg, null_ptr_arg, null_bit_arg, unireg_check_arg, 
-                 field_name_arg, share, blob_pack_length, &my_charset_bin)
-  { geom_type= geom_type_arg; srid= field_srid; }
+                 field_name_arg, share, blob_pack_length, &my_charset_bin),
+      Type_geom_attributes(type_geom_attrs)
+  { }
   Field_geom(uint32 len_arg,bool maybe_null_arg, const char *field_name_arg,
-	     TABLE_SHARE *share, enum geometry_type geom_type_arg)
-    :Field_blob(len_arg, maybe_null_arg, field_name_arg, &my_charset_bin)
-  { geom_type= geom_type_arg; srid= 0; }
+	     TABLE_SHARE *share, const Type_geom_attributes &type_geom_attrs)
+    :Field_blob(len_arg, maybe_null_arg, field_name_arg, &my_charset_bin),
+     Type_geom_attributes(type_geom_attrs)
+  { }
   enum ha_base_keytype key_type() const { return HA_KEYTYPE_VARBINARY2; }
   enum_field_types type() const { return MYSQL_TYPE_GEOMETRY; }
   bool can_optimize_range(const Item_bool_func *cond,
@@ -3373,9 +3367,14 @@ class Field_geom :public Field_blob {
    */
   int reset(void) { return Field_blob::reset() || !maybe_null(); }
 
-  geometry_type get_geometry_type() { return geom_type; };
-  static geometry_type geometry_type_merge(geometry_type, geometry_type);
-  uint get_srid() { return srid; }
+  Type_geom_attributes type_geom_attributes() const
+  {
+    return *this;
+  }
+  Type_ext_attributes type_ext_attributes() const
+  {
+    return static_cast<const Type_geom_attributes>(*this);
+  }
 };
 
 uint gis_field_options_image(uchar *buff, List<Create_field> &create_fields);
@@ -3385,12 +3384,13 @@ uint gis_field_options_read(const uchar *buf, uint buf_len,
 #endif /*HAVE_SPATIAL*/
 
 
-class Field_enum :public Field_str {
+class Field_enum :public Field_str,
+                  public Type_typelib_attributes
+{
   static void do_field_enum(Copy_field *copy_field);
 protected:
   uint packlength;
 public:
-  TYPELIB *typelib;
   Field_enum(uchar *ptr_arg, uint32 len_arg, uchar *null_ptr_arg,
              uchar null_bit_arg,
              enum utype unireg_check_arg, const char *field_name_arg,
@@ -3399,7 +3399,8 @@ class Field_enum :public Field_str {
              CHARSET_INFO *charset_arg)
     :Field_str(ptr_arg, len_arg, null_ptr_arg, null_bit_arg,
 	       unireg_check_arg, field_name_arg, charset_arg),
-    packlength(packlength_arg),typelib(typelib_arg)
+     Type_typelib_attributes(typelib_arg),
+     packlength(packlength_arg)
   {
       flags|=ENUM_FLAG;
   }
@@ -3476,6 +3477,12 @@ class Field_enum :public Field_str {
     */
     return false;
   }
+
+  Type_ext_attributes type_ext_attributes() const
+  {
+    return static_cast<const Type_typelib_attributes>(*this);
+  }
+
 private:
   int do_save_field_metadata(uchar *first_byte);
   uint is_equal(Create_field *new_field);
@@ -3690,14 +3697,15 @@ Field *make_field(TABLE_SHARE *share, MEM_ROOT *mem_root,
                   uchar *null_pos, uchar null_bit,
                   uint pack_flag, enum_field_types field_type,
                   CHARSET_INFO *cs,
-                  Field::geometry_type geom_type, uint srid,
+                  const Type_ext_attributes &type_geom_attr,
                   Field::utype unireg_check,
-                  TYPELIB *interval, const char *field_name);
+                  const char *field_name);
 
 /*
   Create field class for CREATE TABLE
 */
-class Column_definition: public Sql_alloc
+class Column_definition: public Sql_alloc,
+                         public Type_ext_attributes
 {
   /**
     Create "interval" from "interval_list".
@@ -3729,7 +3737,7 @@ class Column_definition: public Sql_alloc
     const char **pos;
     uint *len;
     *max_length= *tot_length= 0;
-    for (pos= interval->type_names, len= interval->type_lengths;
+    for (pos= typelib->type_names, len= typelib->type_lengths;
          *pos ; pos++, len++)
     {
       size_t length= charset->cset->numchars(charset, *pos, *pos + *len);
@@ -3754,11 +3762,8 @@ class Column_definition: public Sql_alloc
   uint32 char_length;
   uint  decimals, flags, pack_length, key_length;
   Field::utype unireg_check;
-  TYPELIB *interval;			// Which interval to use
   List<String> interval_list;
   CHARSET_INFO *charset;
-  uint32 srid;
-  Field::geometry_type geom_type;
   engine_option_value *option_list;
 
   uint pack_flag;
@@ -3777,7 +3782,6 @@ class Column_definition: public Sql_alloc
     comment(null_lex_str),
     on_update(0), sql_type(MYSQL_TYPE_NULL),
     flags(0), pack_length(0), key_length(0), unireg_check(Field::NONE),
-    interval(0), srid(0), geom_type(Field::GEOM_GEOMETRY),
     option_list(NULL),
     vcol_info(0), default_value(0), check_constraint(0)
   {
@@ -3803,23 +3807,23 @@ class Column_definition: public Sql_alloc
     DBUG_ENTER("Column_definition::prepare_interval_field");
     DBUG_ASSERT(sql_type == MYSQL_TYPE_ENUM || sql_type == MYSQL_TYPE_SET);
     /*
-      Interval values are either in "interval" or in "interval_list",
+      Interval values are either in "typelib" or in "interval_list",
       but not in both at the same time, and are not empty at the same time.
       - Values are in "interval_list" when we're coming from the parser
         in CREATE TABLE or in CREATE {FUNCTION|PROCEDURE}.
-      - Values are in "interval" when we're in ALTER TABLE.
+      - Values are in "typelib" when we're in ALTER TABLE.
 
       In a corner case with an empty set like SET(''):
       - after the parser we have interval_list.elements==1
-      - in ALTER TABLE we have a non-NULL interval with interval->count==1,
-        with interval->type_names[0]=="" and interval->type_lengths[0]==0.
+      - in ALTER TABLE we have a non-NULL typelib with typelib->count==1,
+        with typelib->type_names[0]=="" and typelib->type_lengths[0]==0.
       So the assert is still valid for this corner case.
 
       ENUM and SET with no values at all (e.g. ENUM(), SET()) are not possible,
       as the parser requires at least one element, so for a ENUM or SET field it
-      should never happen that both internal_list.elements and interval are 0.
+      should never happen that both internal_list.elements and typelib are 0.
     */
-    DBUG_ASSERT((interval == NULL) == (interval_list.elements > 0));
+    DBUG_ASSERT((typelib == NULL) == (interval_list.elements > 0));
 
     /*
       Create typelib from interval_list, and if necessary
@@ -3835,7 +3839,7 @@ class Column_definition: public Sql_alloc
     if (sql_type == MYSQL_TYPE_SET)
     {
       calculate_interval_lengths(&dummy, &field_length);
-      length= field_length + (interval->count - 1);
+      length= field_length + (typelib->count - 1);
     }
     else /* MYSQL_TYPE_ENUM */
     {
@@ -3874,8 +3878,8 @@ class Column_definition: public Sql_alloc
     return ::make_field(share, mem_root, ptr,
                         (uint32)length, null_pos, null_bit,
                         pack_flag, sql_type, charset,
-                        geom_type, srid, unireg_check, interval,
-                        field_name_arg);
+                        static_cast<Type_ext_attributes>(*this),
+                        unireg_check, field_name_arg);
   }
   Field *make_field(TABLE_SHARE *share, MEM_ROOT *mem_root,
                     const char *field_name_arg)
@@ -3916,6 +3920,23 @@ class Create_field :public Column_definition
   { }
   /* Used to make a clone of this object for ALTER/CREATE TABLE */
   Create_field *clone(MEM_ROOT *mem_root) const;
+
+  Type_ext_attributes type_ext_attributes() const
+  {
+    /*
+      If save_interval is set, then we have a UCS2/UTF16/UTF32
+      ENUM or SET column. The current "interval" value is set to its
+      HEX-encoded representation and is used when writing the FRM file only,
+      it should not be used for a Field_enum creation.
+      The non-encoded TYPELIB is in save_typelib. See unireg.cc for details.
+
+      Otherwise, if save_interval is not set, then all attributes
+      normally reside in the "Type_ext_attributes" part of "this".
+    */
+    return save_interval ?
+           Type_typelib_attributes(save_interval) :
+           static_cast<Type_ext_attributes>(*this);
+  }
 };
 
 
diff --git a/sql/item.cc b/sql/item.cc
index 70b7383..e30bb2f 100644
--- a/sql/item.cc
+++ b/sql/item.cc
@@ -5983,7 +5983,7 @@ Field *Item::tmp_table_field_from_field_type(TABLE *table,
 #ifdef HAVE_SPATIAL
   case MYSQL_TYPE_GEOMETRY:
     field= new (mem_root)
-      Field_geom(max_length, maybe_null, name, table->s, get_geometry_type());
+      Field_geom(max_length, maybe_null, name, table->s, type_ext_attributes());
 #endif /* HAVE_SPATIAL */
   }
   if (field)
@@ -9678,12 +9678,11 @@ void Item_cache_row::set_null()
 Item_type_holder::Item_type_holder(THD *thd, Item *item)
   :Item(thd, item),
    Type_handler_hybrid_real_field_type(get_real_type(item)),
-   enum_set_typelib(0)
+   Type_ext_attributes(item->type_ext_attributes())
 {
   DBUG_ASSERT(item->fixed);
   maybe_null= item->maybe_null;
   collation.set(item->collation);
-  get_full_info(item);
   /**
     Field::result_merge_type(real_field_type()) should be equal to
     result_type(), with one exception when "this" is a Item_field for
@@ -9700,10 +9699,6 @@ Item_type_holder::Item_type_holder(THD *thd, Item *item)
   if (Field::result_merge_type(real_field_type()) == INT_RESULT)
     decimals= 0;
   prev_decimal_int_part= item->decimal_int_part();
-#ifdef HAVE_SPATIAL
-  if (item->field_type() == MYSQL_TYPE_GEOMETRY)
-    geometry_type= item->get_geometry_type();
-#endif /* HAVE_SPATIAL */
 }
 
 
@@ -9822,9 +9817,6 @@ bool Item_type_holder::join_types(THD *thd, Item *item)
     decimals= MY_MAX(decimals, item_decimals);
   }
 
-  if (Item_type_holder::field_type() == FIELD_TYPE_GEOMETRY)
-    geometry_type=
-      Field_geom::geometry_type_merge(geometry_type, item->get_geometry_type());
 
   if (Field::result_merge_type(real_field_type()) == DECIMAL_RESULT)
   {
@@ -9908,7 +9900,9 @@ bool Item_type_holder::join_types(THD *thd, Item *item)
     max_length= MY_MAX(max_length, display_length(item));
   };
   maybe_null|= item->maybe_null;
-  get_full_info(item);
+
+
+  Item_type_holder::type_handler()->join_type_ext_attributes(this, item);
 
   /* Remember decimal integer part to be used in DECIMAL_RESULT handleng */
   prev_decimal_int_part= decimal_int_part();
@@ -9996,20 +9990,20 @@ Field *Item_type_holder::make_field_by_type(TABLE *table)
 
   switch (Item_type_holder::real_field_type()) {
   case MYSQL_TYPE_ENUM:
-    DBUG_ASSERT(enum_set_typelib);
+    DBUG_ASSERT(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);
+                          get_enum_pack_length(typelib->count),
+                          typelib, collation.collation);
     if (field)
       field->init(table);
     return field;
   case MYSQL_TYPE_SET:
-    DBUG_ASSERT(enum_set_typelib);
+    DBUG_ASSERT(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);
+                         get_set_pack_length(typelib->count),
+                         typelib, collation.collation);
     if (field)
       field->init(table);
     return field;
@@ -10022,40 +10016,6 @@ Field *Item_type_holder::make_field_by_type(TABLE *table)
 }
 
 
-/**
-  Get full information from Item about enum/set fields to be able to create
-  them later.
-
-  @param item    Item for information collection
-*/
-void Item_type_holder::get_full_info(Item *item)
-{
-  if (Item_type_holder::real_field_type() == MYSQL_TYPE_ENUM ||
-      Item_type_holder::real_field_type() == MYSQL_TYPE_SET)
-  {
-    if (item->type() == Item::SUM_FUNC_ITEM &&
-        (((Item_sum*)item)->sum_func() == Item_sum::MAX_FUNC ||
-         ((Item_sum*)item)->sum_func() == Item_sum::MIN_FUNC))
-      item = ((Item_sum*)item)->get_arg(0);
-    /*
-      We can have enum/set type after merging only if we have one enum|set
-      field (or MIN|MAX(enum|set field)) and number of NULL fields
-    */
-    DBUG_ASSERT((enum_set_typelib &&
-                 get_real_type(item) == MYSQL_TYPE_NULL) ||
-                (!enum_set_typelib &&
-                 item->real_item()->type() == Item::FIELD_ITEM &&
-                 (get_real_type(item->real_item()) == MYSQL_TYPE_ENUM ||
-                  get_real_type(item->real_item()) == MYSQL_TYPE_SET) &&
-                 ((Field_enum*)((Item_field *) item->real_item())->field)->typelib));
-    if (!enum_set_typelib)
-    {
-      enum_set_typelib= ((Field_enum*)((Item_field *) item->real_item())->field)->typelib;
-    }
-  }
-}
-
-
 double Item_type_holder::val_real()
 {
   DBUG_ASSERT(0); // should never be called
diff --git a/sql/item.h b/sql/item.h
index 6c9307f..f2e462b 100644
--- a/sql/item.h
+++ b/sql/item.h
@@ -1840,8 +1840,8 @@ class Item: public Value_source,
       is_expensive_cache= walk(&Item::is_expensive_processor, 0, NULL);
     return MY_TEST(is_expensive_cache);
   }
-  virtual Field::geometry_type get_geometry_type() const
-    { return Field::GEOM_GEOMETRY; };
+  virtual Type_ext_attributes type_ext_attributes() const
+    { return Type_ext_attributes(); };
   String *check_well_formed_result(String *str, bool send_error= 0);
   bool eq_by_collation(Item *item, bool binary_cmp, CHARSET_INFO *cs); 
   uint32 max_char_length() const
@@ -2654,11 +2654,11 @@ class Item_field :public Item_ident
     DBUG_ASSERT(fixed);
     return field->table->pos_in_table_list->outer_join;
   }
-  Field::geometry_type get_geometry_type() const
+  Type_ext_attributes type_ext_attributes() const
   {
-    DBUG_ASSERT(field_type() == MYSQL_TYPE_GEOMETRY);
-    return field->get_geometry_type();
-  }
+    return field->type_ext_attributes();
+  };
+
   CHARSET_INFO *charset_for_protocol(void) const
   { return field->charset_for_protocol(); }
   friend class Item_default_value;
@@ -4183,6 +4183,10 @@ class Item_ref :public Item_ident
   {
     return ref ? (*ref)->real_item() : this;
   }
+  Type_ext_attributes type_ext_attributes() const
+  {
+    return ref ? (*ref)->type_ext_attributes() : Type_ext_attributes();
+  }
   bool walk(Item_processor processor, bool walk_subquery, void *arg)
   { 
     if (ref && *ref)
@@ -5608,14 +5612,10 @@ class Item_cache_row: public Item_cache
   single SP/PS execution.
 */
 class Item_type_holder: public Item,
-                        public Type_handler_hybrid_real_field_type
+                        public Type_handler_hybrid_real_field_type,
+                        public Type_ext_attributes
 {
 protected:
-  TYPELIB *enum_set_typelib;
-  Field::geometry_type geometry_type;
-
-  void get_full_info(Item *item);
-
   /* It is used to count decimal precision in join_types */
   int prev_decimal_int_part;
 public:
@@ -5653,7 +5653,11 @@ class Item_type_holder: public Item,
   Field *make_field_by_type(TABLE *table);
   static uint32 display_length(Item *item);
   static enum_field_types get_real_type(Item *);
-  Field::geometry_type get_geometry_type() const { return geometry_type; };
+  Type_ext_attributes type_ext_attributes() const
+  {
+    return *this;
+  };
+
   Item* get_copy(THD *thd, MEM_ROOT *mem_root) { return 0; }
 };
 
diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc
index 193af86..41728fb 100644
--- a/sql/item_cmpfunc.cc
+++ b/sql/item_cmpfunc.cc
@@ -3101,7 +3101,8 @@ void Item_func_case::fix_length_and_dec()
 
   if (Item_func_case::result_type() == STRING_RESULT)
   {
-    if (count_string_result_length(Item_func_case::field_type(), agg, nagg))
+    if (count_string_result_length(Item_func_case::field_type(), agg, nagg) ||
+        fix_type_ext_attributes(agg, nagg))
       return;
     /*
       Copy all THEN and ELSE items back to args[] array.
@@ -3417,10 +3418,13 @@ void 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;          
+                                   items, nitems) ||
+        fix_type_ext_attributes(items, nitems))
+      return;
     break;
+  }
   case DECIMAL_RESULT:
     collation.set_numeric();
     count_decimal_length(items, nitems);
diff --git a/sql/item_func.cc b/sql/item_func.cc
index 3b98dd0..36584c8 100644
--- a/sql/item_func.cc
+++ b/sql/item_func.cc
@@ -2832,6 +2832,7 @@ void Item_func_min_max::fix_length_and_dec()
     set_handler_by_field_type(MYSQL_TYPE_DOUBLE);
     break;
   }
+  (void) fix_type_ext_attributes(args, arg_count);
 }
 
 
diff --git a/sql/item_func.h b/sql/item_func.h
index 8d337be..33f353b 100644
--- a/sql/item_func.h
+++ b/sql/item_func.h
@@ -386,9 +386,21 @@ class Item_real_func :public Item_func
   Functions whose returned field type is determined at fix_fields() time.
 */
 class Item_hybrid_func: public Item_func,
-                        public Type_handler_hybrid_field_type
+                        public Type_handler_hybrid_field_type,
+                        public Type_ext_attributes
 {
 protected:
+  bool fix_type_ext_attributes(Item **item, uint nitems)
+  {
+    Type_geom_attributes::operator=(item[0]->type_ext_attributes());
+    for (uint i= 1; i < nitems; i++)
+    {
+      if (Type_handler_hybrid_field_type::type_handler()->
+           join_type_ext_attributes(this, item[i]))
+        return true;
+    }
+    return false;
+  }
   void fix_attributes(Item **item, uint nitems);
 public:
   Item_hybrid_func(THD *thd): Item_func(thd) { }
@@ -407,6 +419,8 @@ class Item_hybrid_func: public Item_func,
   { return Type_handler_hybrid_field_type::result_type(); }
   enum Item_result cmp_type () const
   { return Type_handler_hybrid_field_type::cmp_type(); }
+  Type_ext_attributes type_ext_attributes() const
+  { return *this; };
 };
 
 
@@ -2305,6 +2319,11 @@ class Item_func_sp :public Item_func
 
   Item_result result_type() const;
 
+  Type_ext_attributes type_ext_attributes() const
+  {
+    return sp_result_field->type_ext_attributes();
+  }
+
   longlong val_int()
   {
     if (execute())
diff --git a/sql/item_geofunc.cc b/sql/item_geofunc.cc
index c856aa9..3d03c9c 100644
--- a/sql/item_geofunc.cc
+++ b/sql/item_geofunc.cc
@@ -44,7 +44,7 @@ Field *Item_geometry_func::create_field_for_create_select(TABLE *t_arg)
 {
   Field *result;
   if ((result= new Field_geom(max_length, maybe_null, name, t_arg->s,
-                              get_geometry_type())))
+                              type_ext_attributes())))
     result->init(t_arg);
   return result;
 }
@@ -189,9 +189,9 @@ String *Item_func_geometry_type::val_str_ascii(String *str)
 }
 
 
-Field::geometry_type Item_func_envelope::get_geometry_type() const
+Type_ext_attributes Item_func_envelope::type_ext_attributes() const
 {
-  return Field::GEOM_POLYGON;
+  return Type_geom_attributes(Type_geom_attributes::GEOM_POLYGON, 0);
 }
 
 
@@ -343,9 +343,9 @@ String *Item_func_boundary::val_str(String *str_value)
 }
 
 
-Field::geometry_type Item_func_centroid::get_geometry_type() const
+Type_ext_attributes Item_func_centroid::type_ext_attributes() const
 {
-  return Field::GEOM_POINT;
+  return Type_geom_attributes(Type_geom_attributes::GEOM_POINT, 0);
 }
 
 
@@ -766,9 +766,9 @@ String *Item_func_spatial_decomp_n::val_str(String *str)
 */
 
 
-Field::geometry_type Item_func_point::get_geometry_type() const
+Type_ext_attributes Item_func_point::type_ext_attributes() const
 {
-  return Field::GEOM_POINT;
+  return Type_geom_attributes(Type_geom_attributes::GEOM_POINT, 0);
 }
 
 
@@ -2490,9 +2490,9 @@ String *Item_func_pointonsurface::val_str(String *str)
 }
 
 
-Field::geometry_type Item_func_pointonsurface::get_geometry_type() const
+Type_ext_attributes Item_func_pointonsurface::type_ext_attributes() const
 {
-  return Field::GEOM_POINT;
+  return Type_geom_attributes(Type_geom_attributes::GEOM_POINT, 0);
 }
 
 
diff --git a/sql/item_geofunc.h b/sql/item_geofunc.h
index f96e570..5449abe 100644
--- a/sql/item_geofunc.h
+++ b/sql/item_geofunc.h
@@ -146,7 +146,7 @@ class Item_func_centroid: public Item_geometry_func
   Item_func_centroid(THD *thd, Item *a): Item_geometry_func(thd, a) {}
   const char *func_name() const { return "st_centroid"; }
   String *val_str(String *);
-  Field::geometry_type get_geometry_type() const;
+  Type_ext_attributes type_ext_attributes() const;
   Item *get_copy(THD *thd, MEM_ROOT *mem_root)
   { return get_item_copy<Item_func_centroid>(thd, mem_root, this); }
 };
@@ -157,7 +157,7 @@ class Item_func_envelope: public Item_geometry_func
   Item_func_envelope(THD *thd, Item *a): Item_geometry_func(thd, a) {}
   const char *func_name() const { return "st_envelope"; }
   String *val_str(String *);
-  Field::geometry_type get_geometry_type() const;
+  Type_ext_attributes type_ext_attributes() const;
   Item *get_copy(THD *thd, MEM_ROOT *mem_root)
   { return get_item_copy<Item_func_envelope>(thd, mem_root, this); }
 };
@@ -204,7 +204,7 @@ class Item_func_point: public Item_geometry_func
     Item_geometry_func(thd, a, b, srid) {}
   const char *func_name() const { return "point"; }
   String *val_str(String *);
-  Field::geometry_type get_geometry_type() const;
+  Type_ext_attributes type_ext_attributes() const;
   Item *get_copy(THD *thd, MEM_ROOT *mem_root)
   { return get_item_copy<Item_func_point>(thd, mem_root, this); }
 };
@@ -669,7 +669,7 @@ class Item_func_pointonsurface: public Item_geometry_func
   Item_func_pointonsurface(THD *thd, Item *a): Item_geometry_func(thd, a) {}
   const char *func_name() const { return "st_pointonsurface"; }
   String *val_str(String *);
-  Field::geometry_type get_geometry_type() const;
+  Type_ext_attributes type_ext_attributes() const;
   Item *get_copy(THD *thd, MEM_ROOT *mem_root)
   { return get_item_copy<Item_func_pointonsurface>(thd, mem_root, this); }
 };
diff --git a/sql/item_sum.h b/sql/item_sum.h
index 76cc983..eac7cc0 100644
--- a/sql/item_sum.h
+++ b/sql/item_sum.h
@@ -1044,6 +1044,8 @@ class Item_sum_min :public Item_sum_hybrid
 
   bool add();
   const char *func_name() const { return "min("; }
+  Type_ext_attributes type_ext_attributes() const
+  { return args[0]->type_ext_attributes(); }
   Item *copy_or_same(THD* thd);
   Item *get_copy(THD *thd, MEM_ROOT *mem_root)
   { return get_item_copy<Item_sum_min>(thd, mem_root, this); }
@@ -1059,6 +1061,8 @@ class Item_sum_max :public Item_sum_hybrid
 
   bool add();
   const char *func_name() const { return "max("; }
+  Type_ext_attributes type_ext_attributes() const
+  { return args[0]->type_ext_attributes(); }
   Item *copy_or_same(THD* thd);
   Item *get_copy(THD *thd, MEM_ROOT *mem_root)
   { return get_item_copy<Item_sum_max>(thd, mem_root, this); }
diff --git a/sql/opt_range.cc b/sql/opt_range.cc
index 3ea9f4e..681b2ad 100644
--- a/sql/opt_range.cc
+++ b/sql/opt_range.cc
@@ -7299,7 +7299,8 @@ SEL_TREE *Item_bool_func::get_full_func_mm_tree(RANGE_OPT_PARAM *param,
   if (field_item->field->type() == MYSQL_TYPE_GEOMETRY)
   {
     /* We have to be able to store all sorts of spatial features here */
-    ((Field_geom*) field_item->field)->geom_type= Field::GEOM_GEOMETRY;
+    ((Field_geom*) field_item->field)->geom_type=
+      Type_geom_attributes::GEOM_GEOMETRY;
   }
 #endif /*HAVE_SPATIAL*/
 
diff --git a/sql/sql_show.cc b/sql/sql_show.cc
index de284cb..a1e5c2b 100644
--- a/sql/sql_show.cc
+++ b/sql/sql_show.cc
@@ -420,6 +420,7 @@ static int get_geometry_column_record(THD *thd, TABLE_LIST *tables,
     if (field->type() == MYSQL_TYPE_GEOMETRY)
     {
       Field_geom *fg= (Field_geom *) field;
+      Type_geom_attributes attrs= fg->type_geom_attributes();
 
       DEBUG_SYNC(thd, "get_schema_column");
 
@@ -443,13 +444,13 @@ static int get_geometry_column_record(THD *thd, TABLE_LIST *tables,
       /*STORAGE_TYPE*/
       table->field[8]->store(1LL, TRUE); /*Always 1 (binary implementation)*/
       /*GEOMETRY_TYPE*/
-      table->field[9]->store((longlong) (fg->get_geometry_type()), TRUE);
+      table->field[9]->store((longlong) attrs.geom_type, TRUE);
       /*COORD_DIMENSION*/
       table->field[10]->store(2LL, TRUE);
       /*MAX_PPR*/
       table->field[11]->set_null();
       /*SRID*/
-      table->field[12]->store((longlong) (fg->get_srid()), TRUE);
+      table->field[12]->store((longlong) attrs.srid, TRUE);
 
       if (schema_table_store_record(thd, table))
         DBUG_RETURN(1);
diff --git a/sql/sql_table.cc b/sql/sql_table.cc
index 2fd8408..6d6c8bd 100644
--- a/sql/sql_table.cc
+++ b/sql/sql_table.cc
@@ -2923,7 +2923,7 @@ int prepare_create_field(Column_definition *sql_field,
       sql_field->pack_flag|=FIELDFLAG_BINARY;
     sql_field->unireg_check=Field::INTERVAL_FIELD;
     if (check_duplicates_in_interval("ENUM",sql_field->field_name,
-                                     sql_field->interval,
+                                     sql_field->typelib,
                                      sql_field->charset, &dup_val_count))
       DBUG_RETURN(1);
     break;
@@ -2934,11 +2934,11 @@ int prepare_create_field(Column_definition *sql_field,
       sql_field->pack_flag|=FIELDFLAG_BINARY;
     sql_field->unireg_check=Field::BIT_FIELD;
     if (check_duplicates_in_interval("SET",sql_field->field_name,
-                                     sql_field->interval,
+                                     sql_field->typelib,
                                      sql_field->charset, &dup_val_count))
       DBUG_RETURN(1);
     /* Check that count of unique members is not more then 64 */
-    if (sql_field->interval->count -  dup_val_count > sizeof(longlong)*8)
+    if (sql_field->typelib->count -  dup_val_count > sizeof(longlong)*8)
     {
        my_error(ER_TOO_BIG_SET, MYF(0), sql_field->field_name);
        DBUG_RETURN(1);
@@ -3338,14 +3338,14 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info,
         {
           char *not_used;
           uint not_used2;
-          find_set(sql_field->interval, def->ptr(), def->length(),
+          find_set(sql_field->typelib, def->ptr(), def->length(),
                    sql_field->charset, &not_used, &not_used2, &not_found);
         }
         else /* MYSQL_TYPE_ENUM */
         {
           def->length(sql_field->charset->cset->lengthsp(sql_field->charset,
                                                   def->ptr(), def->length()));
-          not_found= !find_type2(sql_field->interval, def->ptr(),
+          not_found= !find_type2(sql_field->typelib, def->ptr(),
                                  def->length(), sql_field->charset);
         }
       }
@@ -3427,7 +3427,7 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info,
           if (!(sql_field->flags & NOT_NULL_FLAG))
             null_fields--;
 	  sql_field->flags=		dup_field->flags;
-          sql_field->interval=          dup_field->interval;
+          sql_field->typelib=           dup_field->typelib;
           sql_field->vcol_info=         dup_field->vcol_info;
 	  it2.remove();			// Remove first (create) definition
 	  select_field_pos--;
@@ -3809,7 +3809,7 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info,
 	    DBUG_RETURN(TRUE);
 	  }
           if (f_is_geom(sql_field->pack_flag) && sql_field->geom_type ==
-              Field::GEOM_POINT)
+              Type_geom_attributes::GEOM_POINT)
             column->length= MAX_LEN_GEOM_POINT_FIELD;
 	  if (!column->length)
 	  {
diff --git a/sql/sql_type.cc b/sql/sql_type.cc
index a80417f..2b26bd4 100644
--- a/sql/sql_type.cc
+++ b/sql/sql_type.cc
@@ -607,8 +607,7 @@ Field *Type_handler_geometry::make_conversion_table_field(TABLE *table,
   */
   return new(table->in_use->mem_root)
          Field_geom(NULL, (uchar *) "", 1, Field::NONE, TMPNAME, table->s, 4,
-                    ((const Field_geom*) target)->geom_type,
-                    ((const Field_geom*) target)->srid);
+                    *((const Field_geom*) target));
 }
 #endif
 
@@ -640,3 +639,45 @@ Field *Type_handler_set::make_conversion_table_field(TABLE *table,
                    metadata & 0x00ff/*pack_length()*/,
                    ((const Field_enum*) target)->typelib, target->charset());
 }
+
+/***********************************************************************/
+
+bool
+Type_handler_geometry::join_type_ext_attributes(Type_ext_attributes *attr,
+                                                Item *item) const
+{
+  attr->Type_geom_attributes::join(item->type_ext_attributes());
+  return false;
+}
+
+
+bool
+Type_handler_typelib::join_type_ext_attributes(Type_ext_attributes *attr,
+                                               Item *item) const
+{
+  /*
+    We can have enum/set type after merging only if we have one enum|set
+    field (or MIN|MAX(enum|set field)) and number of NULL fields.
+
+    Field::field_type_merge() makes sure about this. In case of other
+    combinations (e.g. two ENUM/SET items), the merged real field type would
+    be MYSQL_TYPE_VARCHAR, not ENUM/SET.
+
+    TODO: We should eventually remove
+      Item_type_holder::get_real_type(Item *item)
+    which returns real type for the parameter "item",
+    and add a virtual Item::get_real_type() instead,
+    which will return real type for "this".
+
+    TODO: we could allow joining ENUM/SET having exactly the same definitions.
+    Preserving ENUM/SET in this case sounds better than switching to VARCHAR.
+  */
+  DBUG_ASSERT((attr->typelib &&
+               Item_type_holder::get_real_type(item) == MYSQL_TYPE_NULL) ||
+              (!attr->typelib && item->real_item()->type_ext_attributes().typelib &&
+               (Item_type_holder::get_real_type(item->real_item()) == MYSQL_TYPE_ENUM ||
+                Item_type_holder::get_real_type(item->real_item()) == MYSQL_TYPE_SET)));
+  if (!attr->typelib)
+    attr->typelib= item->real_item()->type_ext_attributes().typelib;
+  return false;
+}
diff --git a/sql/sql_type.h b/sql/sql_type.h
index 4b05a46..04da0f4 100644
--- a/sql/sql_type.h
+++ b/sql/sql_type.h
@@ -30,6 +30,67 @@ class Sort_param;
 struct TABLE;
 struct SORT_FIELD_ATTR;
 
+
+class Type_geom_attributes
+{
+public:
+  enum geometry_type
+  {
+    GEOM_GEOMETRY = 0, GEOM_POINT = 1, GEOM_LINESTRING = 2, GEOM_POLYGON = 3,
+    GEOM_MULTIPOINT = 4, GEOM_MULTILINESTRING = 5, GEOM_MULTIPOLYGON = 6,
+    GEOM_GEOMETRYCOLLECTION = 7
+  };
+
+  void join(const Type_geom_attributes &other)
+  {
+    if (geom_type != other.geom_type)
+      geom_type= GEOM_GEOMETRY;
+    if (srid != other.srid)
+      srid= 0;
+  }
+
+  geometry_type geom_type;
+  uint srid;
+  Type_geom_attributes()
+   :geom_type(GEOM_GEOMETRY),
+    srid(0)
+  { }
+  Type_geom_attributes(geometry_type type_arg, uint srid_arg)
+   :geom_type(type_arg),
+    srid(srid_arg)
+  { }
+};
+
+
+class Type_typelib_attributes
+{
+public:
+  TYPELIB *typelib;
+  Type_typelib_attributes()
+   :typelib(NULL)
+  { }
+  Type_typelib_attributes(TYPELIB *typelib_arg)
+   :typelib(typelib_arg)
+  { }
+};
+
+
+class Type_ext_attributes: public Type_geom_attributes,
+                           public Type_typelib_attributes
+{
+public:
+  Type_ext_attributes()
+   :Type_geom_attributes()
+  { }
+  Type_ext_attributes(const Type_geom_attributes &geom)
+   :Type_geom_attributes(geom)
+  { }
+  Type_ext_attributes(const Type_typelib_attributes &typelib_attr)
+   :Type_typelib_attributes(typelib_attr)
+  { }
+};
+
+
 class Type_handler
 {
 protected:
@@ -92,6 +153,11 @@ class Type_handler
   virtual void sortlength(THD *thd,
                           const Type_std_attributes *item,
                           SORT_FIELD_ATTR *attr) const= 0;
+  virtual bool join_type_ext_attributes(Type_ext_attributes *attr,
+                                        Item *item) const
+  {
+    return false;
+  }
 };
 
 
@@ -464,11 +530,19 @@ class Type_handler_geometry: public Type_handler_string_result
   enum_field_types field_type() const { return MYSQL_TYPE_GEOMETRY; }
   Field *make_conversion_table_field(TABLE *, uint metadata,
                                      const Field *target) const;
+  bool join_type_ext_attributes(Type_ext_attributes *, Item *) const;
 };
 #endif
 
 
-class Type_handler_enum: public Type_handler_string_result
+class Type_handler_typelib: public Type_handler_string_result
+{
+public:
+  virtual ~Type_handler_typelib() {}
+  bool join_type_ext_attributes(Type_ext_attributes *, Item *) const;
+};
+
+class Type_handler_enum: public Type_handler_typelib
 {
 public:
   virtual ~Type_handler_enum() {}
@@ -479,7 +553,7 @@ class Type_handler_enum: public Type_handler_string_result
 };
 
 
-class Type_handler_set: public Type_handler_string_result
+class Type_handler_set: public Type_handler_typelib
 {
 public:
   virtual ~Type_handler_set() {}
diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy
index e17a514..15905f4 100644
--- a/sql/sql_yacc.yy
+++ b/sql/sql_yacc.yy
@@ -1001,7 +1001,7 @@ Virtual_column_info *add_virtual_expression(THD *thd, const char *txt,
   enum Condition_information_item::Name cond_info_item_name;
   enum enum_diag_condition_item_name diag_condition_item_name;
   enum Diagnostics_information::Which_area diag_area;
-  enum Field::geometry_type geom_type;
+  enum Type_geom_attributes::geometry_type geom_type;
   enum Foreign_key::fk_option m_fk_option;
   enum Item_udftype udf_type;
   enum Key::Keytype key_type;
@@ -6461,14 +6461,14 @@ field_type:
         ;
 
 spatial_type:
-          GEOMETRY_SYM        { $$= Field::GEOM_GEOMETRY; }
-        | GEOMETRYCOLLECTION  { $$= Field::GEOM_GEOMETRYCOLLECTION; }
-        | POINT_SYM           { $$= Field::GEOM_POINT; }
-        | MULTIPOINT          { $$= Field::GEOM_MULTIPOINT; }
-        | LINESTRING          { $$= Field::GEOM_LINESTRING; }
-        | MULTILINESTRING     { $$= Field::GEOM_MULTILINESTRING; }
-        | POLYGON             { $$= Field::GEOM_POLYGON; }
-        | MULTIPOLYGON        { $$= Field::GEOM_MULTIPOLYGON; }
+          GEOMETRY_SYM        { $$= Type_geom_attributes::GEOM_GEOMETRY; }
+        | GEOMETRYCOLLECTION  { $$= Type_geom_attributes::GEOM_GEOMETRYCOLLECTION; }
+        | POINT_SYM           { $$= Type_geom_attributes::GEOM_POINT; }
+        | MULTIPOINT          { $$= Type_geom_attributes::GEOM_MULTIPOINT; }
+        | LINESTRING          { $$= Type_geom_attributes::GEOM_LINESTRING; }
+        | MULTILINESTRING     { $$= Type_geom_attributes::GEOM_MULTILINESTRING; }
+        | POLYGON             { $$= Type_geom_attributes::GEOM_POLYGON; }
+        | MULTIPOLYGON        { $$= Type_geom_attributes::GEOM_MULTIPOLYGON; }
         ;
 
 char:
diff --git a/sql/table.cc b/sql/table.cc
index 82c6a89..970a51d 100644
--- a/sql/table.cc
+++ b/sql/table.cc
@@ -1548,11 +1548,11 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write,
     uint vcol_expr_length=0;
     enum_field_types field_type;
     CHARSET_INFO *charset=NULL;
-    Field::geometry_type geom_type= Field::GEOM_GEOMETRY;
     LEX_STRING comment;
     Virtual_column_info *vcol_info= 0;
-    uint gis_length, gis_decimals, srid= 0;
+    uint gis_length, gis_decimals;
     Field::utype unireg_check;
+    Type_ext_attributes eattr;
 
     if (new_frm_ver >= 3)
     {
@@ -1571,10 +1571,10 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write,
 #ifdef HAVE_SPATIAL
         uint gis_opt_read;
         Field_geom::storage_type st_type;
-	geom_type= (Field::geometry_type) strpos[14];
+	eattr.geom_type= (Type_geom_attributes::geometry_type) strpos[14];
 	charset= &my_charset_bin;
         gis_opt_read= gis_field_options_read(gis_options, gis_options_len,
-            &st_type, &gis_length, &gis_decimals, &srid);
+            &st_type, &gis_length, &gis_decimals, &eattr.srid);
         gis_options+= gis_opt_read;
         gis_options_len-= gis_opt_read;
 #else
@@ -1775,12 +1775,11 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write,
     if (unireg_check == Field::TIMESTAMP_DN_FIELD)
       unireg_check= Field::NONE;
 
+    eattr.typelib= interval_nr ? share->intervals+interval_nr-1 : NULL;
     *field_ptr= reg_field=
       make_field(share, &share->mem_root, record+recpos, (uint32) field_length,
 		 null_pos, null_bit_pos, pack_flag, field_type, charset,
-		 geom_type, srid, unireg_check,
-		 (interval_nr ? share->intervals+interval_nr-1 : NULL),
-		 share->fieldnames.type_names[i]);
+		 eattr, unireg_check, share->fieldnames.type_names[i]);
     if (!reg_field)				// Not supported field type
       goto err;
 
diff --git a/sql/unireg.cc b/sql/unireg.cc
index d3a9b83..42a9cd7 100644
--- a/sql/unireg.cc
+++ b/sql/unireg.cc
@@ -349,7 +349,7 @@ LEX_CUSTRING build_frm_image(THD *thd, const char *table,
     {
       if (field->save_interval)
       {
-        field->interval= field->save_interval;
+        field->typelib= field->save_interval;
         field->save_interval= 0;
       }
     }
@@ -663,7 +663,7 @@ static bool pack_header(THD *thd, uchar *forminfo,
     n_length+= (ulong) strlen(field->field_name)+1;
     field->interval_id=0;
     field->save_interval= 0;
-    if (field->interval)
+    if (field->typelib)
     {
       uint old_int_count=int_count;
 
@@ -677,25 +677,25 @@ static bool pack_header(THD *thd, uchar *forminfo,
           filled with default values it is saved in save_interval
           The HEX representation is created from this copy.
         */
-        field->save_interval= field->interval;
-        field->interval= (TYPELIB*) thd->alloc(sizeof(TYPELIB));
-        *field->interval= *field->save_interval; 
-        field->interval->type_names= 
+        field->save_interval= field->typelib;
+        field->typelib= (TYPELIB*) thd->alloc(sizeof(TYPELIB));
+        *field->typelib= *field->save_interval;
+        field->typelib->type_names=
           (const char **) thd->alloc(sizeof(char*) * 
-                                     (field->interval->count+1));
-        field->interval->type_names[field->interval->count]= 0;
-        field->interval->type_lengths=
-          (uint *) thd->alloc(sizeof(uint) * field->interval->count);
+                                     (field->typelib->count+1));
+        field->typelib->type_names[field->typelib->count]= 0;
+        field->typelib->type_lengths=
+          (uint *) thd->alloc(sizeof(uint) * field->typelib->count);
  
-        for (uint pos= 0; pos < field->interval->count; pos++)
+        for (uint pos= 0; pos < field->typelib->count; pos++)
         {
           char *dst;
           const char *src= field->save_interval->type_names[pos];
           uint hex_length;
           length= field->save_interval->type_lengths[pos];
           hex_length= length * 2;
-          field->interval->type_lengths[pos]= hex_length;
-          field->interval->type_names[pos]= dst=
+          field->typelib->type_lengths[pos]= hex_length;
+          field->typelib->type_names[pos]= dst=
             (char*) thd->alloc(hex_length + 1);
           octet2hex(dst, src, length);
         }
@@ -704,9 +704,9 @@ static bool pack_header(THD *thd, uchar *forminfo,
       field->interval_id=get_interval_id(&int_count,create_fields,field);
       if (old_int_count != int_count)
       {
-	for (const char **pos=field->interval->type_names ; *pos ; pos++)
+	for (const char **pos= field->typelib->type_names ; *pos ; pos++)
 	  int_length+=(uint) strlen(*pos)+1;	// field + suffix prefix
-	int_parts+=field->interval->count+1;
+	int_parts+=field->typelib->count+1;
       }
     }
     if (f_maybe_null(field->pack_flag))
@@ -769,14 +769,14 @@ static uint get_interval_id(uint *int_count,List<Create_field> &create_fields,
 {
   List_iterator<Create_field> it(create_fields);
   Create_field *field;
-  TYPELIB *interval=last_field->interval;
+  TYPELIB *interval= last_field->typelib;
 
   while ((field=it++) != last_field)
   {
-    if (field->interval_id && field->interval->count == interval->count)
+    if (field->interval_id && field->typelib->count == interval->count)
     {
       const char **a,**b;
-      for (a=field->interval->type_names, b=interval->type_names ;
+      for (a=field->typelib->type_names, b= interval->type_names ;
 	   *a && !strcmp(*a,*b);
 	   a++,b++) ;
 
@@ -804,9 +804,9 @@ static size_t packed_fields_length(List<Create_field> &create_fields)
     {
       int_count= field->interval_id;
       length++;
-      for (int i=0; field->interval->type_names[i]; i++)
+      for (int i=0; field->typelib->type_names[i]; i++)
       {
-        length+= field->interval->type_lengths[i];
+        length+= field->typelib->type_lengths[i];
         length++;
       }
       length++;
@@ -919,8 +919,8 @@ static bool pack_fields(uchar **buff_arg, List<Create_field> &create_fields,
 
         bzero(occ, sizeof(occ));
 
-        for (i=0; (val= (unsigned char*) field->interval->type_names[i]); i++)
-          for (uint j = 0; j < field->interval->type_lengths[i]; j++)
+        for (i=0; (val= (unsigned char*) field->typelib->type_names[i]); i++)
+          for (uint j = 0; j < field->typelib->type_lengths[i]; j++)
             occ[(unsigned int) (val[j])]= 1;
 
         if (!occ[(unsigned char)NAMES_SEP_CHAR])
@@ -950,10 +950,10 @@ static bool pack_fields(uchar **buff_arg, List<Create_field> &create_fields,
 
         int_count= field->interval_id;
         *buff++= sep;
-        for (int i=0; field->interval->type_names[i]; i++)
+        for (int i=0; field->typelib->type_names[i]; i++)
         {
-          memcpy(buff, field->interval->type_names[i], field->interval->type_lengths[i]);
-          buff+= field->interval->type_lengths[i];
+          memcpy(buff, field->typelib->type_names[i], field->typelib->type_lengths[i]);
+          buff+= field->typelib->type_lengths[i];
           *buff++= sep;
         }
         *buff++= 0;
@@ -1038,13 +1038,11 @@ static bool make_empty_rec(THD *thd, uchar *buff, uint table_options,
                                 field->pack_flag,
                                 field->sql_type,
                                 field->charset,
-                                field->geom_type, field->srid,
+                                field->type_ext_attributes(),
                           field->unireg_check == Field::TIMESTAMP_DNUN_FIELD
                           ? Field::TIMESTAMP_UN_FIELD
                           : field->unireg_check == Field::TIMESTAMP_DN_FIELD
                           ? Field::NONE : field->unireg_check,
-                                field->save_interval ? field->save_interval :
-                                field->interval, 
                                 field->field_name);
     if (!regfield)
     {

References