← Back to team overview

maria-developers team mailing list archive

MDEV-12775 Reuse data type aggregation code for hybrid functions and UNION

 

Hello Sanja,

Please review a patch for MDEV-12775, also fixing a number of bugs:

- MDEV-9495 Wrong field type for a UNION of a signed and an unsigned INT
expression
- MDEV-9497 UNION and COALESCE produce different field types for DECIMAL+INT
- MDEV-12594 UNION between fixed length double columns does not always
preserve scale
- MDEV-12595 UNION converts INT to BIGINT
- MDEV-12599 UNION is not symmetric when mixing INT and CHAR


Thanks!
commit 66ac73266b39443797940f09751a372d489ed8e8
Author: Alexander Barkov <bar@xxxxxxxxxxx>
Date:   Wed May 10 15:29:48 2017 +0400

    MDEV-12775 Reuse data type aggregation code for hybrid functions and UNION
    
    Introducing a new class Type_holder (used internally in sql_union.cc),
    to reuse exactly the same data type attribute aggregation Type_handler API
    for hybrid functions and UNION.
    
    This fixes a number of bugs in UNION:
    
    - MDEV-9495 Wrong field type for a UNION of a signed and an unsigned INT expression
    - MDEV-9497 UNION and COALESCE produce different field types for DECIMAL+INT
    - MDEV-12594 UNION between fixed length double columns does not always preserve scale
    - MDEV-12595 UNION converts INT to BIGINT
    - MDEV-12599 UNION is not symmetric when mixing INT and CHAR
    
    Details:
    
    - sql_union.cc: Reusing attribute aggregation for UNION.
      Adding new methods:
      * st_select_lex_unit::join_union_type_handlers()
      * st_select_lex_unit::join_union_type_attributes()
      * st_select_lex_unit::join_union_item_types()
      Removing the old join_types()-based code.
    
    - Changing Type_handler::Item_hybrid_func_fix_attributes()
      to accept "name", Type_handler_hybrid_field_type, Type_all_attributes
      as three separate parameters instead of a single Item_hybrid_func parameter,
      to make it possible to pass both Item_hybrid_func and Type_holder.
    
    - Moving the former special GEOMETRY and ENUM/SET attribute aggregation code
      from Item_type_holder::join_types() to
      * Type_handler_typelib::Item_hybrid_func_fix_attributes().
      * Type_handler_geometry::Item_hybrid_func_fix_attrubutes().
      This makes GEOMETRY/ENUM/SET symmetric with all other data types
      (from the UNION point of view).
      Removing Item_type_holder::join_types() and Item_type_holder::get_full_info().
    
    - Adding new methods into Type_all_attributes:
      * Type_all_attributes::set_geometry_type() and
        Item_hybrid_func::set_geometry_type().
      * Adding Type_all_attributes::get_typelib().
      * Adding Type_all_attributes::set_typelib().
    
    - Adding Type_handler_typelib as a common parent for
      Type_handler_enum and Type_handler_set, to avoid code duplication: they have
      already had two common methods, and we're adding one more shared method.
    
    - Adding Type_all_attributes::set_maybe_null(), as some type handlers
      may want to set maybe_null (e.g. Type_handler_geometry) during data type
      attribute aggregation.
    
    - Changing Type_geometry_attributes() to accept Type_handler
      and Type_all_attributes as two separate parameters, instead
      of a single Item parameter, to make it possible to pass Type_holder.
    
    - Adding Item_args::add_argument().
    
    - Moving Item_args::alloc_arguments() from "protected" to "public".
    
    - Moving Item_type_holder::Item_type_holder() from item.cc to item.h, as
      now it's very simple.
      Btw, this constructor should probably be eventually removed.
      It's now used only in sql_show.cc, which could be modified to use
      Item_return_decimal (for symmetry with Item_return_xxx created for all
      other data types). Or, another option: remove all Item_return_xxx and
      use Item_type_holder for all data types instead.
    
    - storage/tokudb/mysql-test/tokudb/r/type_float.result
      Recording new results (MDEV-12594).
    
    - mysql-test/r/cte_recursive.result
      Recording new results (MDEV-9497)
    
    - mysql-test/r/subselect*.result
      Recording new results (MDEV-12595)
    
    - mysql-test/r/metadata.result
      Recording new results (MDEV-9495)
    
    - mysql-test/r/temp_table.result
      Recording new results (MDEV-12594)
    
    - mysql-test/r/type_float.result
      Recording new results (MDEV-12594)

diff --git a/mysql-test/r/cte_recursive.result b/mysql-test/r/cte_recursive.result
index f691db2..946ba16 100644
--- a/mysql-test/r/cte_recursive.result
+++ b/mysql-test/r/cte_recursive.result
@@ -130,7 +130,7 @@ select t2.a+1 from t1,t2 where t1.a=t2.a
 select * from t1;
 show columns from v1;
 Field	Type	Null	Key	Default	Extra
-a	bigint(20)	YES		NULL	
+a	bigint(12)	YES		NULL	
 # WITH RECURSIVE : types of t1 columns are determined by anchor parts 
 create view v2 as
 with recursive
diff --git a/mysql-test/r/metadata.result b/mysql-test/r/metadata.result
index 3ff332e..cc249a7 100644
--- a/mysql-test/r/metadata.result
+++ b/mysql-test/r/metadata.result
@@ -147,7 +147,7 @@ id	data	data
 2	female	no
 select t1.id from t1 union select t2.id from t2;
 Catalog	Database	Table	Table_alias	Column	Column_alias	Type	Length	Max length	Is_null	Flags	Decimals	Charsetnr
-def				id	id	1	4	1	Y	32768	0	63
+def				id	id	246	4	1	Y	32768	0	63
 id
 1
 2
@@ -168,12 +168,12 @@ def			aaa	@arg00	@arg00	8	20	1	Y	32768	0	63
 1
 select 1 union select 1;
 Catalog	Database	Table	Table_alias	Column	Column_alias	Type	Length	Max length	Is_null	Flags	Decimals	Charsetnr
-def				1	1	3	11	1	N	32769	0	63
+def				1	1	3	1	1	N	32769	0	63
 1
 1
 select * from (select 1 union select 1) aaa;
 Catalog	Database	Table	Table_alias	Column	Column_alias	Type	Length	Max length	Is_null	Flags	Decimals	Charsetnr
-def			aaa	1	1	3	11	1	N	32769	0	63
+def			aaa	1	1	3	1	1	N	32769	0	63
 1
 1
 drop table t1;
diff --git a/mysql-test/r/subselect.result b/mysql-test/r/subselect.result
index b83ee20..95a872f 100644
--- a/mysql-test/r/subselect.result
+++ b/mysql-test/r/subselect.result
@@ -1260,7 +1260,7 @@ a
 SHOW CREATE TABLE t1;
 Table	Create Table
 t1	CREATE TABLE `t1` (
-  `a` bigint(20) NOT NULL
+  `a` int(3) NOT NULL
 ) ENGINE=MyISAM DEFAULT CHARSET=latin1
 drop table t1;
 create table t1 (a int);
diff --git a/mysql-test/r/subselect_no_exists_to_in.result b/mysql-test/r/subselect_no_exists_to_in.result
index 87f33d1..7f07b97 100644
--- a/mysql-test/r/subselect_no_exists_to_in.result
+++ b/mysql-test/r/subselect_no_exists_to_in.result
@@ -1264,7 +1264,7 @@ a
 SHOW CREATE TABLE t1;
 Table	Create Table
 t1	CREATE TABLE `t1` (
-  `a` bigint(20) NOT NULL
+  `a` int(3) NOT NULL
 ) ENGINE=MyISAM DEFAULT CHARSET=latin1
 drop table t1;
 create table t1 (a int);
diff --git a/mysql-test/r/subselect_no_mat.result b/mysql-test/r/subselect_no_mat.result
index 626990f..57c7a97 100644
--- a/mysql-test/r/subselect_no_mat.result
+++ b/mysql-test/r/subselect_no_mat.result
@@ -1267,7 +1267,7 @@ a
 SHOW CREATE TABLE t1;
 Table	Create Table
 t1	CREATE TABLE `t1` (
-  `a` bigint(20) NOT NULL
+  `a` int(3) NOT NULL
 ) ENGINE=MyISAM DEFAULT CHARSET=latin1
 drop table t1;
 create table t1 (a int);
diff --git a/mysql-test/r/subselect_no_opts.result b/mysql-test/r/subselect_no_opts.result
index 3637604..6e8be4a 100644
--- a/mysql-test/r/subselect_no_opts.result
+++ b/mysql-test/r/subselect_no_opts.result
@@ -1263,7 +1263,7 @@ a
 SHOW CREATE TABLE t1;
 Table	Create Table
 t1	CREATE TABLE `t1` (
-  `a` bigint(20) NOT NULL
+  `a` int(3) NOT NULL
 ) ENGINE=MyISAM DEFAULT CHARSET=latin1
 drop table t1;
 create table t1 (a int);
diff --git a/mysql-test/r/subselect_no_scache.result b/mysql-test/r/subselect_no_scache.result
index afebd27..1b437f6 100644
--- a/mysql-test/r/subselect_no_scache.result
+++ b/mysql-test/r/subselect_no_scache.result
@@ -1266,7 +1266,7 @@ a
 SHOW CREATE TABLE t1;
 Table	Create Table
 t1	CREATE TABLE `t1` (
-  `a` bigint(20) NOT NULL
+  `a` int(3) NOT NULL
 ) ENGINE=MyISAM DEFAULT CHARSET=latin1
 drop table t1;
 create table t1 (a int);
diff --git a/mysql-test/r/subselect_no_semijoin.result b/mysql-test/r/subselect_no_semijoin.result
index 0f0fc59..6094c7e 100644
--- a/mysql-test/r/subselect_no_semijoin.result
+++ b/mysql-test/r/subselect_no_semijoin.result
@@ -1263,7 +1263,7 @@ a
 SHOW CREATE TABLE t1;
 Table	Create Table
 t1	CREATE TABLE `t1` (
-  `a` bigint(20) NOT NULL
+  `a` int(3) NOT NULL
 ) ENGINE=MyISAM DEFAULT CHARSET=latin1
 drop table t1;
 create table t1 (a int);
diff --git a/mysql-test/r/temp_table.result b/mysql-test/r/temp_table.result
index b833f4f..d1bec4a 100644
--- a/mysql-test/r/temp_table.result
+++ b/mysql-test/r/temp_table.result
@@ -181,20 +181,20 @@ CREATE TABLE t2 ( c FLOAT(30,18) );
 INSERT INTO t2 VALUES( 123456 );
 SELECT AVG( c ) FROM t1 UNION SELECT 1;
 AVG( c )
-12139
-1
+12139.000000000000000000
+1.000000000000000000
 SELECT 1 UNION SELECT AVG( c ) FROM t1;
 1
-1
-12139
+1.000000000000000000
+12139.000000000000000000
 SELECT 1 UNION SELECT * FROM t2 UNION SELECT 1;
 1
-1
-123456
+1.000000000000000000
+123456.000000000000000000
 SELECT c/1 FROM t1 UNION SELECT 1;
 c/1
-12139
-1
+12139.000000000000000000
+1.000000000000000000
 DROP TABLE t1, t2;
 create temporary table t1 (a int);
 insert into t1 values (4711);
diff --git a/mysql-test/r/type_float.result b/mysql-test/r/type_float.result
index 43aed60..ed62c5c 100644
--- a/mysql-test/r/type_float.result
+++ b/mysql-test/r/type_float.result
@@ -232,12 +232,12 @@ insert into t2 values ("1.23456780");
 create table t3 select * from t2 union select * from t1;
 select * from t3;
 d
-1.2345678
-100000000
+1.234567800
+100000000.000000000
 show create table t3;
 Table	Create Table
 t3	CREATE TABLE `t3` (
-  `d` double DEFAULT NULL
+  `d` double(18,9) DEFAULT NULL
 ) ENGINE=MyISAM DEFAULT CHARSET=latin1
 drop table t1, t2, t3;
 create table t1 select  105213674794682365.00 + 0.0 x;
diff --git a/mysql-test/r/union.result b/mysql-test/r/union.result
index 9949def..e0aa93d 100644
--- a/mysql-test/r/union.result
+++ b/mysql-test/r/union.result
@@ -852,7 +852,7 @@ select * from t1;
 show create table t1;
 Table	Create Table
 t1	CREATE TABLE `t1` (
-  `1` int(11) NOT NULL DEFAULT 0
+  `1` int(2) NOT NULL DEFAULT 0
 ) ENGINE=MyISAM DEFAULT CHARSET=latin1
 drop table t1;
 create table t1 select _latin1"test" union select _latin2"testt" ;
@@ -1608,7 +1608,7 @@ NULL	binary(0)	YES		NULL
 CREATE TABLE t5 SELECT NULL UNION SELECT NULL;
 DESC t5;
 Field	Type	Null	Key	Default	Extra
-NULL	binary(0)	YES		NULL	
+NULL	null	YES		NULL	
 CREATE TABLE t6 
 SELECT * FROM (SELECT * FROM (SELECT NULL)a) b UNION SELECT a FROM t1;
 DESC t6;
@@ -2195,16 +2195,223 @@ CREATE OR REPLACE TABLE t1 AS SELECT 1 UNION SELECT 1;
 SHOW CREATE TABLE t1;
 Table	Create Table
 t1	CREATE TABLE `t1` (
-  `1` int(11) NOT NULL DEFAULT 0
+  `1` int(1) NOT NULL DEFAULT 0
 ) ENGINE=MyISAM DEFAULT CHARSET=latin1
 DROP TABLE t1;
 CREATE OR REPLACE TABLE t1 AS SELECT * FROM (SELECT 1 UNION SELECT 1) AS t0;
 SHOW CREATE TABLE t1;
 Table	Create Table
 t1	CREATE TABLE `t1` (
-  `1` int(11) NOT NULL DEFAULT 0
+  `1` int(1) NOT NULL DEFAULT 0
+) ENGINE=MyISAM DEFAULT CHARSET=latin1
+DROP TABLE t1;
+#
+# MDEV-9495 Wrong field type for a UNION of a signed and an unsigned INT expression
+#
+CREATE TABLE t1 (a INT, b INT UNSIGNED);
+INSERT INTO t1 VALUES (0x7FFFFFFF,0xFFFFFFFF);
+CREATE TABLE t2 AS SELECT a FROM t1 UNION SELECT b FROM t1;
+SHOW CREATE TABLE t2;
+Table	Create Table
+t2	CREATE TABLE `t2` (
+  `a` decimal(10,0) DEFAULT NULL
+) ENGINE=MyISAM DEFAULT CHARSET=latin1
+SELECT * FROM t2 ORDER BY a;
+a
+2147483647
+4294967295
+DROP TABLE t2;
+CREATE TABLE t2 AS SELECT COALESCE(a,b), COALESCE(b,a) FROM t1;
+SHOW CREATE TABLE t2;
+Table	Create Table
+t2	CREATE TABLE `t2` (
+  `COALESCE(a,b)` decimal(10,0) DEFAULT NULL,
+  `COALESCE(b,a)` decimal(10,0) DEFAULT NULL
+) ENGINE=MyISAM DEFAULT CHARSET=latin1
+SELECT * FROM t2;
+COALESCE(a,b)	COALESCE(b,a)
+2147483647	4294967295
+DROP TABLE t2;
+DROP TABLE t1;
+#
+# MDEV-9497 UNION and COALESCE produce different field types for DECIMAL+INT
+#
+CREATE TABLE t1 AS SELECT COALESCE(10.1,CAST(10 AS UNSIGNED)) AS a;
+SHOW CREATE TABLE t1;
+Table	Create Table
+t1	CREATE TABLE `t1` (
+  `a` decimal(3,1) NOT NULL
+) ENGINE=MyISAM DEFAULT CHARSET=latin1
+DROP TABLE t1;
+CREATE OR REPLACE TABLE t1 AS SELECT 10.1 AS a UNION SELECT CAST(10 AS UNSIGNED);
+SHOW CREATE TABLE t1;
+Table	Create Table
+t1	CREATE TABLE `t1` (
+  `a` decimal(3,1) NOT NULL DEFAULT 0.0
 ) ENGINE=MyISAM DEFAULT CHARSET=latin1
 DROP TABLE t1;
 #
+# MDEV-12594 UNION between fixed length double columns does not always preserve scale
+#
+CREATE TABLE t1 (a FLOAT(20,4), b FLOAT(20,3), c FLOAT(20,4));
+INSERT INTO t1 VALUES (1111,2222,3333);
+CREATE TABLE t2 AS SELECT a FROM t1 UNION SELECT a FROM t1;
+SHOW CREATE TABLE t2;
+Table	Create Table
+t2	CREATE TABLE `t2` (
+  `a` float(20,4) DEFAULT NULL
+) ENGINE=MyISAM DEFAULT CHARSET=latin1
+DROP TABLE t2;
+CREATE OR REPLACE TABLE t2 SELECT a FROM t1 UNION SELECT c FROM t1;
+SHOW CREATE TABLE t2;
+Table	Create Table
+t2	CREATE TABLE `t2` (
+  `a` float(20,4) DEFAULT NULL
+) ENGINE=MyISAM DEFAULT CHARSET=latin1
+DROP TABLE t2;
+CREATE OR REPLACE TABLE t2 SELECT b FROM t1 UNION SELECT b FROM t1;
+SHOW CREATE TABLE t2;
+Table	Create Table
+t2	CREATE TABLE `t2` (
+  `b` float(20,3) DEFAULT NULL
+) ENGINE=MyISAM DEFAULT CHARSET=latin1
+DROP TABLE t2;
+CREATE OR REPLACE TABLE t2 SELECT c FROM t1 UNION SELECT c FROM t1;
+SHOW CREATE TABLE t2;
+Table	Create Table
+t2	CREATE TABLE `t2` (
+  `c` float(20,4) DEFAULT NULL
+) ENGINE=MyISAM DEFAULT CHARSET=latin1
+DROP TABLE t2;
+CREATE OR REPLACE TABLE t2 SELECT c FROM t1 UNION SELECT a FROM t1;
+SHOW CREATE TABLE t2;
+Table	Create Table
+t2	CREATE TABLE `t2` (
+  `c` float(20,4) DEFAULT NULL
+) ENGINE=MyISAM DEFAULT CHARSET=latin1
+DROP TABLE t2;
+CREATE OR REPLACE TABLE t2 AS SELECT a FROM t1 UNION SELECT b FROM t1;
+SHOW CREATE TABLE t2;
+Table	Create Table
+t2	CREATE TABLE `t2` (
+  `a` float(21,4) DEFAULT NULL
+) ENGINE=MyISAM DEFAULT CHARSET=latin1
+DROP TABLE t2;
+CREATE OR REPLACE TABLE t2 AS SELECT b FROM t1 UNION SELECT a FROM t1;
+SHOW CREATE TABLE t2;
+Table	Create Table
+t2	CREATE TABLE `t2` (
+  `b` float(21,4) DEFAULT NULL
+) ENGINE=MyISAM DEFAULT CHARSET=latin1
+DROP TABLE t2;
+DROP TABLE t1;
+# Corner case
+CREATE TABLE t1 (a FLOAT(255,4), b FLOAT(255,3));
+INSERT INTO t1 VALUES (1111,2222);
+CREATE OR REPLACE TABLE t2 AS SELECT b FROM t1 UNION SELECT a FROM t1;
+SHOW CREATE TABLE t2;
+Table	Create Table
+t2	CREATE TABLE `t2` (
+  `b` float(255,4) DEFAULT NULL
+) ENGINE=MyISAM DEFAULT CHARSET=latin1
+DROP TABLE t2;
+DROP TABLE t1;
+#
+# MDEV-12595 UNION converts INT to BIGINT
+#
+CREATE TABLE t1 AS SELECT
+1,
+-1,
+COALESCE(1,1),
+COALESCE(-1,-1),
+COALESCE(1,-1),
+COALESCE(-1,1);
+SHOW CREATE TABLE t1;
+Table	Create Table
+t1	CREATE TABLE `t1` (
+  `1` int(1) NOT NULL,
+  `-1` int(2) NOT NULL,
+  `COALESCE(1,1)` int(1) NOT NULL,
+  `COALESCE(-1,-1)` int(2) NOT NULL,
+  `COALESCE(1,-1)` int(2) NOT NULL,
+  `COALESCE(-1,1)` int(2) NOT NULL
+) ENGINE=MyISAM DEFAULT CHARSET=latin1
+DROP TABLE t1;
+CREATE TABLE t1 AS SELECT 1 AS c1,1 AS c2,-1 AS c3,-1 AS c4 UNION SELECT 1,-1,1,-1;
+SHOW CREATE TABLE t1;
+Table	Create Table
+t1	CREATE TABLE `t1` (
+  `c1` int(1) NOT NULL DEFAULT 0,
+  `c2` int(2) NOT NULL DEFAULT 0,
+  `c3` int(2) NOT NULL DEFAULT 0,
+  `c4` int(2) NOT NULL DEFAULT 0
+) ENGINE=MyISAM DEFAULT CHARSET=latin1
+DROP TABLE t1;
+#
+# MDEV-12599 UNION is not symmetric when mixing INT and CHAR
+#
+CREATE OR REPLACE TABLE t1 AS SELECT 1 AS c1, 'a' AS c2 UNION SELECT 'a', 1;
+SHOW CREATE TABLE t1;
+Table	Create Table
+t1	CREATE TABLE `t1` (
+  `c1` varchar(1) NOT NULL DEFAULT '',
+  `c2` varchar(1) NOT NULL DEFAULT ''
+) ENGINE=MyISAM DEFAULT CHARSET=latin1
+DROP TABLE t1;
+CREATE OR REPLACE TABLE t1 AS SELECT 11112222 AS c1, 'a' AS c2 UNION SELECT 'a', 11112222;
+SHOW CREATE TABLE t1;
+Table	Create Table
+t1	CREATE TABLE `t1` (
+  `c1` varchar(8) NOT NULL DEFAULT '',
+  `c2` varchar(8) NOT NULL DEFAULT ''
+) ENGINE=MyISAM DEFAULT CHARSET=latin1
+DROP TABLE t1;
+CREATE OR REPLACE TABLE t1 AS SELECT 111122223333 AS c1, 'a' AS c2 UNION SELECT 'a', 111122223333;
+SHOW CREATE TABLE t1;
+Table	Create Table
+t1	CREATE TABLE `t1` (
+  `c1` varchar(12) NOT NULL DEFAULT '',
+  `c2` varchar(12) NOT NULL DEFAULT ''
+) ENGINE=MyISAM DEFAULT CHARSET=latin1
+DROP TABLE t1;
+CREATE OR REPLACE TABLE t1 AS SELECT 1111222233334444 AS c1, 'a' AS c2 UNION SELECT 'a', 1111222233334444;
+SHOW CREATE TABLE t1;
+Table	Create Table
+t1	CREATE TABLE `t1` (
+  `c1` varchar(16) NOT NULL DEFAULT '',
+  `c2` varchar(16) NOT NULL DEFAULT ''
+) ENGINE=MyISAM DEFAULT CHARSET=latin1
+DROP TABLE t1;
+CREATE TABLE t1 (a INT(3), b VARCHAR(1));
+CREATE TABLE t2 AS SELECT a,b FROM t1 UNION SELECT b,a FROM t1;
+SHOW CREATE TABLE t2;
+Table	Create Table
+t2	CREATE TABLE `t2` (
+  `a` varchar(11) DEFAULT NULL,
+  `b` varchar(11) DEFAULT NULL
+) ENGINE=MyISAM DEFAULT CHARSET=latin1
+DROP TABLE t2;
+DROP TABLE t1;
+CREATE TABLE t1 (a BIGINT(3), b VARCHAR(1));
+CREATE TABLE t2 AS SELECT a,b FROM t1 UNION SELECT b,a FROM t1;
+SHOW CREATE TABLE t2;
+Table	Create Table
+t2	CREATE TABLE `t2` (
+  `a` varchar(20) DEFAULT NULL,
+  `b` varchar(20) DEFAULT NULL
+) ENGINE=MyISAM DEFAULT CHARSET=latin1
+DROP TABLE t2;
+DROP TABLE t1;
+CREATE TABLE t1 (a BIGINT(12), b VARCHAR(1));
+CREATE TABLE t2 AS SELECT a,b FROM t1 UNION SELECT b,a FROM t1;
+SHOW CREATE TABLE t2;
+Table	Create Table
+t2	CREATE TABLE `t2` (
+  `a` varchar(20) DEFAULT NULL,
+  `b` varchar(20) DEFAULT NULL
+) ENGINE=MyISAM DEFAULT CHARSET=latin1
+DROP TABLE t2;
+DROP TABLE t1;
+#
 # End of 10.3 tests
 #
diff --git a/mysql-test/t/union.test b/mysql-test/t/union.test
index 04ab715..ce8b2bc 100644
--- a/mysql-test/t/union.test
+++ b/mysql-test/t/union.test
@@ -1545,5 +1545,132 @@ SHOW CREATE TABLE t1;
 DROP TABLE t1;
 
 --echo #
+--echo # MDEV-9495 Wrong field type for a UNION of a signed and an unsigned INT expression
+--echo #
+CREATE TABLE t1 (a INT, b INT UNSIGNED);
+INSERT INTO t1 VALUES (0x7FFFFFFF,0xFFFFFFFF);
+CREATE TABLE t2 AS SELECT a FROM t1 UNION SELECT b FROM t1;
+SHOW CREATE TABLE t2;
+SELECT * FROM t2 ORDER BY a;
+DROP TABLE t2;
+CREATE TABLE t2 AS SELECT COALESCE(a,b), COALESCE(b,a) FROM t1;
+SHOW CREATE TABLE t2;
+SELECT * FROM t2;
+DROP TABLE t2;
+DROP TABLE t1;
+
+--echo #
+--echo # MDEV-9497 UNION and COALESCE produce different field types for DECIMAL+INT
+--echo #
+CREATE TABLE t1 AS SELECT COALESCE(10.1,CAST(10 AS UNSIGNED)) AS a;
+SHOW CREATE TABLE t1;
+DROP TABLE t1;
+CREATE OR REPLACE TABLE t1 AS SELECT 10.1 AS a UNION SELECT CAST(10 AS UNSIGNED);
+SHOW CREATE TABLE t1;
+DROP TABLE t1;
+
+--echo #
+--echo # MDEV-12594 UNION between fixed length double columns does not always preserve scale
+--echo #
+CREATE TABLE t1 (a FLOAT(20,4), b FLOAT(20,3), c FLOAT(20,4));
+INSERT INTO t1 VALUES (1111,2222,3333);
+
+CREATE TABLE t2 AS SELECT a FROM t1 UNION SELECT a FROM t1;
+SHOW CREATE TABLE t2;
+DROP TABLE t2;
+
+CREATE OR REPLACE TABLE t2 SELECT a FROM t1 UNION SELECT c FROM t1;
+SHOW CREATE TABLE t2;
+DROP TABLE t2;
+
+CREATE OR REPLACE TABLE t2 SELECT b FROM t1 UNION SELECT b FROM t1;
+SHOW CREATE TABLE t2;
+DROP TABLE t2;
+
+CREATE OR REPLACE TABLE t2 SELECT c FROM t1 UNION SELECT c FROM t1;
+SHOW CREATE TABLE t2;
+DROP TABLE t2;
+
+CREATE OR REPLACE TABLE t2 SELECT c FROM t1 UNION SELECT a FROM t1;
+SHOW CREATE TABLE t2;
+DROP TABLE t2;
+
+CREATE OR REPLACE TABLE t2 AS SELECT a FROM t1 UNION SELECT b FROM t1;
+SHOW CREATE TABLE t2;
+DROP TABLE t2;
+
+CREATE OR REPLACE TABLE t2 AS SELECT b FROM t1 UNION SELECT a FROM t1;
+SHOW CREATE TABLE t2;
+DROP TABLE t2;
+
+DROP TABLE t1;
+
+--echo # Corner case
+CREATE TABLE t1 (a FLOAT(255,4), b FLOAT(255,3));
+INSERT INTO t1 VALUES (1111,2222);
+CREATE OR REPLACE TABLE t2 AS SELECT b FROM t1 UNION SELECT a FROM t1;
+SHOW CREATE TABLE t2;
+DROP TABLE t2;
+DROP TABLE t1;
+
+
+--echo #
+--echo # MDEV-12595 UNION converts INT to BIGINT
+--echo #
+CREATE TABLE t1 AS SELECT
+  1,
+  -1,
+  COALESCE(1,1),
+  COALESCE(-1,-1),
+  COALESCE(1,-1),
+  COALESCE(-1,1);
+SHOW CREATE TABLE t1;
+DROP TABLE t1;
+CREATE TABLE t1 AS SELECT 1 AS c1,1 AS c2,-1 AS c3,-1 AS c4 UNION SELECT 1,-1,1,-1;
+SHOW CREATE TABLE t1;
+DROP TABLE t1;
+
+
+--echo #
+--echo # MDEV-12599 UNION is not symmetric when mixing INT and CHAR
+--echo #
+
+CREATE OR REPLACE TABLE t1 AS SELECT 1 AS c1, 'a' AS c2 UNION SELECT 'a', 1;
+SHOW CREATE TABLE t1;
+DROP TABLE t1;
+
+CREATE OR REPLACE TABLE t1 AS SELECT 11112222 AS c1, 'a' AS c2 UNION SELECT 'a', 11112222;
+SHOW CREATE TABLE t1;
+DROP TABLE t1;
+
+
+CREATE OR REPLACE TABLE t1 AS SELECT 111122223333 AS c1, 'a' AS c2 UNION SELECT 'a', 111122223333;
+SHOW CREATE TABLE t1;
+DROP TABLE t1;
+
+CREATE OR REPLACE TABLE t1 AS SELECT 1111222233334444 AS c1, 'a' AS c2 UNION SELECT 'a', 1111222233334444;
+SHOW CREATE TABLE t1;
+DROP TABLE t1;
+
+CREATE TABLE t1 (a INT(3), b VARCHAR(1));
+CREATE TABLE t2 AS SELECT a,b FROM t1 UNION SELECT b,a FROM t1;
+SHOW CREATE TABLE t2;
+DROP TABLE t2;
+DROP TABLE t1;
+
+CREATE TABLE t1 (a BIGINT(3), b VARCHAR(1));
+CREATE TABLE t2 AS SELECT a,b FROM t1 UNION SELECT b,a FROM t1;
+SHOW CREATE TABLE t2;
+DROP TABLE t2;
+DROP TABLE t1;
+
+CREATE TABLE t1 (a BIGINT(12), b VARCHAR(1));
+CREATE TABLE t2 AS SELECT a,b FROM t1 UNION SELECT b,a FROM t1;
+SHOW CREATE TABLE t2;
+DROP TABLE t2;
+DROP TABLE t1;
+
+
+--echo #
 --echo # End of 10.3 tests
 --echo #
diff --git a/sql/item.cc b/sql/item.cc
index 2094502..b8f6655 100644
--- a/sql/item.cc
+++ b/sql/item.cc
@@ -9809,199 +9809,6 @@ void Item_cache_row::set_null()
 };
 
 
-Item_type_holder::Item_type_holder(THD *thd, Item *item)
-  :Item(thd, item),
-   Type_handler_hybrid_field_type(item->real_type_handler()),
-   Type_geometry_attributes(item),
-   enum_set_typelib(0)
-{
-  DBUG_ASSERT(item->fixed);
-  maybe_null= item->maybe_null;
-  get_full_info(item);
-  DBUG_ASSERT(!decimals || result_type() != INT_RESULT);
-  prev_decimal_int_part= item->decimal_int_part();
-}
-
-
-/**
-  Find field type which can carry current Item_type_holder type and
-  type of given Item.
-
-  @param thd     thread handler
-  @param item    given item to join its parameters with this item ones
-
-  @retval
-    TRUE   error - types are incompatible
-  @retval
-    FALSE  OK
-*/
-
-bool Item_type_holder::join_types(THD *thd, Item *item)
-{
-  uint max_length_orig= max_length;
-  uint decimals_orig= decimals;
-  DBUG_ENTER("Item_type_holder::join_types");
-  DBUG_PRINT("info:", ("was type %s len %d, dec %d name %s",
-                       real_type_handler()->name().ptr(), max_length, decimals,
-                       (name.str ? name.str : "<NULL>")));
-  DBUG_PRINT("info:", ("in type %s len %d, dec %d",
-                       item->real_type_handler()->name().ptr(),
-                       item->max_length, item->decimals));
-  const Type_handler *item_type_handler= item->real_type_handler();
-  if (aggregate_for_result(item_type_handler))
-  {
-    my_error(ER_ILLEGAL_PARAMETER_DATA_TYPES2_FOR_OPERATION, MYF(0),
-             Item_type_holder::real_type_handler()->name().ptr(),
-             item_type_handler->name().ptr(),
-             "UNION");
-    DBUG_RETURN(true);
-  }
-
-  /*
-    At this point non-zero decimals in combination with integer data types
-    is possible in some cases:
-      SELECT * FROM (SELECT NULL) a UNION SELECT 1;
-    In the constructor Item_type_holder::Item_type_holder() the data type
-    handler was set to type_handler_null with decimals==NOT_FIXED_DEC.
-    After the above call for aggregate_for_result() for the literal 1
-    which is on the right side of the UNION, the data type handler
-    changes to type_handler_longlong, while decimals is still NOT_FIXED_DEC.
-  */
-  if (result_type() == INT_RESULT)
-    decimals= 0;
-  else
-    decimals= MY_MAX(decimals, item->decimals);
-
-  Type_geometry_attributes::join(item);
-
-  if (result_type() == DECIMAL_RESULT)
-  {
-    decimals= MY_MIN(MY_MAX(decimals, item->decimals), DECIMAL_MAX_SCALE);
-    int item_int_part= item->decimal_int_part();
-    int item_prec = MY_MAX(prev_decimal_int_part, item_int_part) + decimals;
-    int precision= MY_MIN(item_prec, DECIMAL_MAX_PRECISION);
-    unsigned_flag&= item->unsigned_flag;
-    max_length= my_decimal_precision_to_length_no_truncation(precision,
-                                                             decimals,
-                                                             unsigned_flag);
-  }
-
-  switch (result_type())
-  {
-  case STRING_RESULT:
-  {
-    const char *old_cs, *old_derivation;
-    uint32 old_max_chars= max_length / collation.collation->mbmaxlen;
-    old_cs= collation.collation->name;
-    old_derivation= collation.derivation_name();
-    if (collation.aggregate(item->collation, MY_COLL_ALLOW_CONV))
-    {
-      my_error(ER_CANT_AGGREGATE_2COLLATIONS, MYF(0),
-	       old_cs, old_derivation,
-	       item->collation.collation->name,
-	       item->collation.derivation_name(),
-	       "UNION");
-      DBUG_RETURN(TRUE);
-    }
-    /*
-      To figure out max_length, we have to take into account possible
-      expansion of the size of the values because of character set
-      conversions.
-     */
-    if (collation.collation != &my_charset_bin)
-    {
-      max_length= MY_MAX(old_max_chars * collation.collation->mbmaxlen,
-                      item->max_display_length() /
-                      item->collation.collation->mbmaxlen *
-                      collation.collation->mbmaxlen);
-    }
-    else
-      set_if_bigger(max_length, item->max_display_length());
-    break;
-  }
-  case REAL_RESULT:
-  {
-    if (decimals != NOT_FIXED_DEC)
-    {
-      /*
-        For FLOAT(M,D)/DOUBLE(M,D) do not change precision
-         if both fields have the same M and D
-      */
-      if (item->max_length != max_length_orig ||
-          item->decimals != decimals_orig)
-      {
-        int delta1= max_length_orig - decimals_orig;
-        int delta2= item->max_length - item->decimals;
-        max_length= MY_MAX(delta1, delta2) + decimals;
-        if (Item_type_holder::real_type_handler() == &type_handler_float &&
-            max_length > FLT_DIG + 2)
-        {
-          max_length= MAX_FLOAT_STR_LENGTH;
-          decimals= NOT_FIXED_DEC;
-        } 
-        else if (Item_type_holder::real_type_handler() == &type_handler_double &&
-                 max_length > DBL_DIG + 2)
-        {
-          max_length= MAX_DOUBLE_STR_LENGTH;
-          decimals= NOT_FIXED_DEC;
-        }
-      }
-    }
-    else
-      max_length= (Item_type_holder::field_type() == MYSQL_TYPE_FLOAT) ?
-                  FLT_DIG+6 : DBL_DIG+7;
-    break;
-  }
-  default:
-    max_length= MY_MAX(max_length, item->max_display_length());
-  };
-  maybe_null|= item->maybe_null;
-  get_full_info(item);
-  /*
-    Adjust data type for union, e.g.:
-    - convert type_handler_null to type_handler_string
-    - convert type_handler_olddecimal to type_handler_newdecimal
-    - adjust varchar/blob according to max_length
-  */
-  set_handler(Item_type_holder::
-                real_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();
-  DBUG_PRINT("info", ("become type: %s  len: %u  dec: %u",
-                      real_type_handler()->name().ptr(),
-                      max_length, (uint) decimals));
-  DBUG_RETURN(FALSE);
-}
-
-
-/**
-  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_type_handler() == &type_handler_enum ||
-      Item_type_holder::real_type_handler() == &type_handler_set)
-  {
-    TYPELIB *item_typelib= item->get_typelib();
-    /*
-      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(item->real_type_handler() == &type_handler_null ||
-                (enum_set_typelib && !item_typelib) ||
-                (!enum_set_typelib && item_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 4ef2375..9a3775c 100644
--- a/sql/item.h
+++ b/sql/item.h
@@ -787,7 +787,13 @@ class Item: public Value_source,
   {
     return type_handler()->max_display_length(this);
   }
-  virtual TYPELIB *get_typelib() const { return NULL; }
+  TYPELIB *get_typelib() const { return NULL; }
+  void set_maybe_null(bool maybe_null_arg) { maybe_null= maybe_null_arg; }
+  void set_typelib(TYPELIB *typelib)
+  {
+    // Non-field Items (e.g. hybrid functions) never have ENUM/SET types yet.
+    DBUG_ASSERT(0);
+  }
   Item_cache* get_cache(THD *thd) const
   {
     return type_handler()->Item_get_cache(thd, this);
@@ -1740,6 +1746,10 @@ class Item: public Value_source,
     { return Field::GEOM_GEOMETRY; };
   uint uint_geometry_type() const
   { return get_geometry_type(); }
+  void set_geometry_type(uint type)
+  {
+    DBUG_ASSERT(0);
+  }
   String *check_well_formed_result(String *str, bool send_error= 0);
   bool eq_by_collation(Item *item, bool binary_cmp, CHARSET_INFO *cs); 
   bool too_big_for_varchar() const
@@ -1842,27 +1852,28 @@ class Type_geometry_attributes
 {
   uint m_geometry_type;
   static const uint m_geometry_type_unknown= Field::GEOM_GEOMETRYCOLLECTION + 1;
-  void copy(const Item *item)
+  void copy(const Type_handler *handler, const Type_all_attributes *gattr)
   {
     // Ignore implicit NULLs
-    m_geometry_type= item->type_handler() == &type_handler_geometry ?
-                     item->uint_geometry_type() :
+    m_geometry_type= handler == &type_handler_geometry ?
+                     gattr->uint_geometry_type() :
                      m_geometry_type_unknown;
   }
 public:
   Type_geometry_attributes()
    :m_geometry_type(m_geometry_type_unknown)
   { }
-  Type_geometry_attributes(const Item *item)
+  Type_geometry_attributes(const Type_handler *handler,
+                           const Type_all_attributes *gattr)
    :m_geometry_type(m_geometry_type_unknown)
   {
-    copy(item);
+    copy(handler, gattr);
   }
   void join(const Item *item)
   {
     // Ignore implicit NULLs
     if (m_geometry_type == m_geometry_type_unknown)
-      copy(item);
+      copy(item->type_handler(), item);
     else if (item->type_handler() == &type_handler_geometry)
     {
       m_geometry_type=
@@ -1902,7 +1913,6 @@ class Item_args
 protected:
   Item **args, *tmp_arg[2];
   uint arg_count;
-  bool alloc_arguments(THD *thd, uint count);
   void set_arguments(THD *thd, List<Item> &list);
   bool walk_args(Item_processor processor, bool walk_subquery, void *arg)
   {
@@ -1961,6 +1971,11 @@ class Item_args
     set_arguments(thd, list);
   }
   Item_args(THD *thd, const Item_args *other);
+  bool alloc_arguments(THD *thd, uint count);
+  void add_argument(Item *item)
+  {
+    args[arg_count++]= item;
+  }
   inline Item **arguments() const { return args; }
   inline uint argument_count() const { return arg_count; }
   inline void remove_arguments() { arg_count=0; }
@@ -5837,12 +5852,29 @@ class Item_type_holder: public Item,
 {
 protected:
   TYPELIB *enum_set_typelib;
-  void get_full_info(Item *item);
-
-  /* It is used to count decimal precision in join_types */
-  int prev_decimal_int_part;
 public:
-  Item_type_holder(THD*, Item*);
+  Item_type_holder(THD *thd, Item *item)
+   :Item(thd, item),
+    Type_handler_hybrid_field_type(item->real_type_handler()),
+    enum_set_typelib(0)
+  {
+    DBUG_ASSERT(item->fixed);
+    maybe_null= item->maybe_null;
+  }
+  Item_type_holder(THD *thd,
+                   const LEX_CSTRING *name_arg,
+                   const Type_handler *handler,
+                   const Type_all_attributes *attr,
+                   bool maybe_null_arg)
+   :Item(thd),
+    Type_handler_hybrid_field_type(handler),
+    Type_geometry_attributes(handler, attr),
+    enum_set_typelib(attr->get_typelib())
+  {
+    name= *name_arg;
+    Type_std_attributes::set(*attr);
+    maybe_null= maybe_null_arg;
+  }
 
   const Type_handler *type_handler() const
   {
@@ -5860,7 +5892,6 @@ class Item_type_holder: public Item,
   longlong val_int();
   my_decimal *val_decimal(my_decimal *);
   String *val_str(String*);
-  bool join_types(THD *thd, Item *);
   Field *create_tmp_field(bool group, TABLE *table)
   {
     return Item_type_holder::real_type_handler()->
@@ -5871,6 +5902,10 @@ class Item_type_holder: public Item,
   {
     return Type_geometry_attributes::get_geometry_type();
   }
+  void set_geometry_type(uint type)
+  {
+    Type_geometry_attributes::set_geometry_type(type);
+  }
   Item* get_copy(THD *thd, MEM_ROOT *mem_root) { return 0; }
 };
 
diff --git a/sql/item_func.cc b/sql/item_func.cc
index c3048a0..d552f23 100644
--- a/sql/item_func.cc
+++ b/sql/item_func.cc
@@ -546,7 +546,9 @@ my_decimal *Item_func::val_decimal(my_decimal *decimal_value)
 bool Item_hybrid_func::fix_attributes(Item **items, uint nitems)
 {
   bool rc= Item_hybrid_func::type_handler()->
-             Item_hybrid_func_fix_attributes(current_thd, this, items, nitems);
+             Item_hybrid_func_fix_attributes(current_thd,
+                                             func_name(), this, this,
+                                             items, nitems);
   DBUG_ASSERT(!rc || current_thd->is_error());
   return rc;
 }
diff --git a/sql/item_func.h b/sql/item_func.h
index 18612db..ca9830c 100644
--- a/sql/item_func.h
+++ b/sql/item_func.h
@@ -398,6 +398,10 @@ class Item_hybrid_func: public Item_func,
   { return Type_handler_hybrid_field_type::type_handler(); }
   Field::geometry_type get_geometry_type() const
   { return Type_geometry_attributes::get_geometry_type(); };
+  void set_geometry_type(uint type)
+  {
+    Type_geometry_attributes::set_geometry_type(type);
+  }
 };
 
 
diff --git a/sql/sql_lex.h b/sql/sql_lex.h
index 85ce07b..4ac407b 100644
--- a/sql/sql_lex.h
+++ b/sql/sql_lex.h
@@ -640,6 +640,14 @@ class st_select_lex_unit: public st_select_lex_node {
   ulonglong found_rows_for_union;
   bool saved_error;
 
+  bool prepare_join(THD *thd, SELECT_LEX *sl, select_result *result,
+                    ulong additional_options,
+                    bool is_union_select);
+  bool join_union_item_types(THD *thd, List<Item> &types, uint count);
+  bool join_union_type_handlers(THD *thd,
+                                class Type_holder *holders, uint count);
+  bool join_union_type_attributes(THD *thd,
+                                  class Type_holder *holders, uint count);
 public:
   // Ensures that at least all members used during cleanup() are initialized.
   st_select_lex_unit()
@@ -749,9 +757,6 @@ class st_select_lex_unit: public st_select_lex_node {
 
   /* UNION methods */
   bool prepare(THD *thd, select_result *result, ulong additional_options);
-  bool prepare_join(THD *thd, SELECT_LEX *sl, select_result *result,
-                    ulong additional_options,
-                    bool is_union_select);
   bool optimize();
   bool exec();
   bool exec_recursive();
diff --git a/sql/sql_type.cc b/sql/sql_type.cc
index f632b47..47e511a 100644
--- a/sql/sql_type.cc
+++ b/sql/sql_type.cc
@@ -482,29 +482,18 @@ const Type_handler *Type_handler_row::type_handler_for_comparison() const
 
 /***************************************************************************/
 
-const Type_handler *Type_handler_enum::type_handler_for_item_field() const
+const Type_handler *Type_handler_typelib::type_handler_for_item_field() const
 {
   return &type_handler_string;
 }
 
 
-const Type_handler *Type_handler_enum::cast_to_int_type_handler() const
+const Type_handler *Type_handler_typelib::cast_to_int_type_handler() const
 {
   return &type_handler_longlong;
 }
 
 
-const Type_handler *Type_handler_set::type_handler_for_item_field() const
-{
-  return &type_handler_string;
-}
-
-
-const Type_handler *Type_handler_set::cast_to_int_type_handler() const
-{
-  return &type_handler_longlong;
-}
-
 /***************************************************************************/
 
 bool
@@ -2080,7 +2069,10 @@ Type_handler_temporal_result::Item_get_cache(THD *thd, const Item *item) const
 /*************************************************************************/
 
 bool Type_handler_int_result::
-       Item_hybrid_func_fix_attributes(THD *thd, Item_hybrid_func *func,
+       Item_hybrid_func_fix_attributes(THD *thd,
+                                       const char *func_name,
+                                       Type_handler_hybrid_field_type *handler,
+                                       Type_all_attributes *func,
                                        Item **items, uint nitems) const
 {
   uint unsigned_flag= items[0]->unsigned_flag;
@@ -2089,7 +2081,7 @@ bool Type_handler_int_result::
     if (unsigned_flag != items[i]->unsigned_flag)
     {
       // Convert a mixture of signed and unsigned int to decimal
-      func->set_handler(&type_handler_newdecimal);
+      handler->set_handler(&type_handler_newdecimal);
       func->aggregate_attributes_decimal(items, nitems);
       return false;
     }
@@ -2100,7 +2092,10 @@ bool Type_handler_int_result::
 
 
 bool Type_handler_real_result::
-       Item_hybrid_func_fix_attributes(THD *thd, Item_hybrid_func *func,
+       Item_hybrid_func_fix_attributes(THD *thd,
+                                       const char *func_name,
+                                       Type_handler_hybrid_field_type *handler,
+                                       Type_all_attributes *func,
                                        Item **items, uint nitems) const
 {
   func->aggregate_attributes_real(items, nitems);
@@ -2109,7 +2104,10 @@ bool Type_handler_real_result::
 
 
 bool Type_handler_decimal_result::
-       Item_hybrid_func_fix_attributes(THD *thd, Item_hybrid_func *func,
+       Item_hybrid_func_fix_attributes(THD *thd,
+                                       const char *func_name,
+                                       Type_handler_hybrid_field_type *handler,
+                                       Type_all_attributes *func,
                                        Item **items, uint nitems) const
 {
   func->aggregate_attributes_decimal(items, nitems);
@@ -2118,26 +2116,59 @@ bool Type_handler_decimal_result::
 
 
 bool Type_handler_string_result::
-       Item_hybrid_func_fix_attributes(THD *thd, Item_hybrid_func *func,
+       Item_hybrid_func_fix_attributes(THD *thd,
+                                       const char *func_name,
+                                       Type_handler_hybrid_field_type *handler,
+                                       Type_all_attributes *func,
+                                       Item **items, uint nitems) const
+{
+  return func->aggregate_attributes_string(func_name, items, nitems);
+}
+
+
+
+/*
+  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
+*/
+bool Type_handler_typelib::
+       Item_hybrid_func_fix_attributes(THD *thd,
+                                       const char *func_name,
+                                       Type_handler_hybrid_field_type *handler,
+                                       Type_all_attributes *func,
                                        Item **items, uint nitems) const
 {
-  return func->aggregate_attributes_string(func->func_name(), items, nitems);
+  TYPELIB *typelib= NULL;
+  for (uint i= 0; i < nitems; i++)
+  {
+    if ((typelib= items[i]->get_typelib()))
+      break;
+  }
+  DBUG_ASSERT(typelib); // There must be at least one typelib
+  func->set_typelib(typelib);
+  return func->aggregate_attributes_string(func_name, items, nitems);
 }
 
 
 bool Type_handler_blob_common::
-       Item_hybrid_func_fix_attributes(THD *thd, Item_hybrid_func *func,
+       Item_hybrid_func_fix_attributes(THD *thd,
+                                       const char *func_name,
+                                       Type_handler_hybrid_field_type *handler,
+                                       Type_all_attributes *func,
                                        Item **items, uint nitems) const
 {
-  if (func->aggregate_attributes_string(func->func_name(), items, nitems))
+  if (func->aggregate_attributes_string(func_name, items, nitems))
     return true;
-  func->set_handler(blob_type_handler(func->max_length));
+  handler->set_handler(blob_type_handler(func->max_length));
   return false;
 }
 
 
 bool Type_handler_date_common::
-       Item_hybrid_func_fix_attributes(THD *thd, Item_hybrid_func *func,
+       Item_hybrid_func_fix_attributes(THD *thd,
+                                       const char *func_name,
+                                       Type_handler_hybrid_field_type *handler,
+                                       Type_all_attributes *func,
                                        Item **items, uint nitems) const
 {
   func->fix_attributes_date();
@@ -2146,7 +2177,10 @@ bool Type_handler_date_common::
 
 
 bool Type_handler_time_common::
-       Item_hybrid_func_fix_attributes(THD *thd, Item_hybrid_func *func,
+       Item_hybrid_func_fix_attributes(THD *thd,
+                                       const char *func_name,
+                                       Type_handler_hybrid_field_type *handler,
+                                       Type_all_attributes *func,
                                        Item **items, uint nitems) const
 {
   func->aggregate_attributes_temporal(MIN_TIME_WIDTH, items, nitems);
@@ -2155,7 +2189,10 @@ bool Type_handler_time_common::
 
 
 bool Type_handler_datetime_common::
-       Item_hybrid_func_fix_attributes(THD *thd, Item_hybrid_func *func,
+       Item_hybrid_func_fix_attributes(THD *thd,
+                                       const char *func_name,
+                                       Type_handler_hybrid_field_type *handler,
+                                       Type_all_attributes *func,
                                        Item **items, uint nitems) const
 {
   func->aggregate_attributes_temporal(MAX_DATETIME_WIDTH, items, nitems);
@@ -2164,7 +2201,10 @@ bool Type_handler_datetime_common::
 
 
 bool Type_handler_timestamp_common::
-       Item_hybrid_func_fix_attributes(THD *thd, Item_hybrid_func *func,
+       Item_hybrid_func_fix_attributes(THD *thd,
+                                       const char *func_name,
+                                       Type_handler_hybrid_field_type *handler,
+                                       Type_all_attributes *func,
                                        Item **items, uint nitems) const
 {
   func->aggregate_attributes_temporal(MAX_DATETIME_WIDTH, items, nitems);
@@ -2173,11 +2213,14 @@ bool Type_handler_timestamp_common::
 
 #ifdef HAVE_SPATIAL
 bool Type_handler_geometry::
-       Item_hybrid_func_fix_attributes(THD *thd, Item_hybrid_func *func,
+       Item_hybrid_func_fix_attributes(THD *thd,
+                                       const char *func_name,
+                                       Type_handler_hybrid_field_type *handler,
+                                       Type_all_attributes *func,
                                        Item **items, uint nitems) const
 {
   DBUG_ASSERT(nitems > 0);
-  Type_geometry_attributes gattr(items[0]);
+  Type_geometry_attributes gattr(items[0]->type_handler(), items[0]);
   for (uint i= 1; i < nitems; i++)
     gattr.join(items[i]);
   func->set_geometry_type(gattr.get_geometry_type());
@@ -2185,7 +2228,7 @@ bool Type_handler_geometry::
   func->unsigned_flag= false;
   func->decimals= 0;
   func->max_length= (uint32) UINT_MAX32;
-  func->maybe_null= true;
+  func->set_maybe_null(true);
   return false;
 }
 #endif
@@ -2202,7 +2245,8 @@ bool Type_handler::
     with aggregating for CASE-alike functions (e.g. COALESCE)
     for the majority of data type handlers.
   */
-  return Item_hybrid_func_fix_attributes(thd, func, items, nitems);
+  return Item_hybrid_func_fix_attributes(thd, func->func_name(),
+                                         func, func, items, nitems);
 }
 
 
diff --git a/sql/sql_type.h b/sql/sql_type.h
index 8abc8e6..92ffd43 100644
--- a/sql/sql_type.h
+++ b/sql/sql_type.h
@@ -60,6 +60,7 @@ class Item_func_div;
 class Item_func_mod;
 class cmp_item;
 class in_vector;
+class Type_handler_hybrid_field_type;
 class Sort_param;
 class Arg_comparator;
 struct st_value;
@@ -467,6 +468,7 @@ class Type_all_attributes: public Type_std_attributes
    :Type_std_attributes(other)
   { }
   virtual ~Type_all_attributes() {}
+  virtual void set_maybe_null(bool maybe_null_arg)= 0;
   // Returns total number of decimal digits
   virtual uint decimal_precision() const= 0;
   /*
@@ -476,7 +478,9 @@ 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; }
+  virtual void set_geometry_type(uint type)= 0;
+  virtual TYPELIB *get_typelib() const= 0;
+  virtual void set_typelib(TYPELIB *typelib)= 0;
 };
 
 
@@ -769,7 +773,10 @@ class Type_handler
                                                const Item *cmp) const= 0;
   virtual Item_cache *Item_get_cache(THD *thd, const Item *item) const= 0;
   virtual bool set_comparator_func(Arg_comparator *cmp) const= 0;
-  virtual bool Item_hybrid_func_fix_attributes(THD *thd, Item_hybrid_func *func,
+  virtual bool Item_hybrid_func_fix_attributes(THD *thd,
+                                               const char *name,
+                                               Type_handler_hybrid_field_type *,
+                                               Type_all_attributes *atrr,
                                                Item **items,
                                                uint nitems) const= 0;
   virtual bool Item_func_min_max_fix_attributes(THD *thd,
@@ -964,7 +971,10 @@ class Type_handler_row: public Type_handler
   Item *make_const_item_for_comparison(THD *, Item *src, const Item *cmp) const;
   Item_cache *Item_get_cache(THD *thd, const Item *item) const;
   bool set_comparator_func(Arg_comparator *cmp) const;
-  bool Item_hybrid_func_fix_attributes(THD *thd, Item_hybrid_func *func,
+  bool Item_hybrid_func_fix_attributes(THD *thd,
+                                       const char *name,
+                                       Type_handler_hybrid_field_type *,
+                                       Type_all_attributes *atrr,
                                        Item **items, uint nitems) const
   {
     DBUG_ASSERT(0);
@@ -1168,7 +1178,10 @@ class Type_handler_real_result: public Type_handler_numeric
   Item *make_const_item_for_comparison(THD *, Item *src, const Item *cmp) const;
   Item_cache *Item_get_cache(THD *thd, const Item *item) const;
   bool set_comparator_func(Arg_comparator *cmp) const;
-  bool Item_hybrid_func_fix_attributes(THD *thd, Item_hybrid_func *func,
+  bool Item_hybrid_func_fix_attributes(THD *thd,
+                                       const char *name,
+                                       Type_handler_hybrid_field_type *,
+                                       Type_all_attributes *atrr,
                                        Item **items, uint nitems) const;
   bool Item_func_min_max_fix_attributes(THD *thd, Item_func_min_max *func,
                                         Item **items, uint nitems) const;
@@ -1234,7 +1247,10 @@ class Type_handler_decimal_result: public Type_handler_numeric
   Item *make_const_item_for_comparison(THD *, Item *src, const Item *cmp) const;
   Item_cache *Item_get_cache(THD *thd, const Item *item) const;
   bool set_comparator_func(Arg_comparator *cmp) const;
-  bool Item_hybrid_func_fix_attributes(THD *thd, Item_hybrid_func *func,
+  bool Item_hybrid_func_fix_attributes(THD *thd,
+                                       const char *name,
+                                       Type_handler_hybrid_field_type *,
+                                       Type_all_attributes *atrr,
                                        Item **items, uint nitems) const;
   bool Item_sum_hybrid_fix_length_and_dec(Item_sum_hybrid *func) const;
   bool Item_sum_sum_fix_length_and_dec(Item_sum_sum *) const;
@@ -1292,7 +1308,10 @@ class Type_handler_int_result: public Type_handler_numeric
   Item *make_const_item_for_comparison(THD *, Item *src, const Item *cmp) const;
   Item_cache *Item_get_cache(THD *thd, const Item *item) const;
   bool set_comparator_func(Arg_comparator *cmp) const;
-  bool Item_hybrid_func_fix_attributes(THD *thd, Item_hybrid_func *func,
+  bool Item_hybrid_func_fix_attributes(THD *thd,
+                                       const char *name,
+                                       Type_handler_hybrid_field_type *,
+                                       Type_all_attributes *atrr,
                                        Item **items, uint nitems) const;
   bool Item_sum_hybrid_fix_length_and_dec(Item_sum_hybrid *func) const;
   bool Item_sum_sum_fix_length_and_dec(Item_sum_sum *) const;
@@ -1437,7 +1456,10 @@ class Type_handler_string_result: public Type_handler
   Item *make_const_item_for_comparison(THD *, Item *src, const Item *cmp) const;
   Item_cache *Item_get_cache(THD *thd, const Item *item) const;
   bool set_comparator_func(Arg_comparator *cmp) const;
-  bool Item_hybrid_func_fix_attributes(THD *thd, Item_hybrid_func *func,
+  bool Item_hybrid_func_fix_attributes(THD *thd,
+                                       const char *name,
+                                       Type_handler_hybrid_field_type *,
+                                       Type_all_attributes *atrr,
                                        Item **items, uint nitems) const;
   bool Item_sum_hybrid_fix_length_and_dec(Item_sum_hybrid *func) const;
   bool Item_sum_sum_fix_length_and_dec(Item_sum_sum *) const;
@@ -1726,7 +1748,10 @@ class Type_handler_time_common: public Type_handler_temporal_result
   }
   int Item_save_in_field(Item *item, Field *field, bool no_conversions) const;
   String *print_item_value(THD *thd, Item *item, String *str) const;
-  bool Item_hybrid_func_fix_attributes(THD *thd, Item_hybrid_func *func,
+  bool Item_hybrid_func_fix_attributes(THD *thd,
+                                       const char *name,
+                                       Type_handler_hybrid_field_type *,
+                                       Type_all_attributes *atrr,
                                        Item **items, uint nitems) const;
   Item *make_const_item_for_comparison(THD *, Item *src, const Item *cmp) const;
   bool set_comparator_func(Arg_comparator *cmp) const;
@@ -1793,7 +1818,10 @@ class Type_handler_date_common: public Type_handler_temporal_with_date
   }
   uint Item_decimal_precision(const Item *item) const;
   String *print_item_value(THD *thd, Item *item, String *str) const;
-  bool Item_hybrid_func_fix_attributes(THD *thd, Item_hybrid_func *func,
+  bool Item_hybrid_func_fix_attributes(THD *thd,
+                                       const char *name,
+                                       Type_handler_hybrid_field_type *,
+                                       Type_all_attributes *atrr,
                                        Item **items, uint nitems) const;
 };
 
@@ -1850,7 +1878,10 @@ class Type_handler_datetime_common: public Type_handler_temporal_with_date
     return Item_send_datetime(item, protocol, buf);
   }
   String *print_item_value(THD *thd, Item *item, String *str) const;
-  bool Item_hybrid_func_fix_attributes(THD *thd, Item_hybrid_func *func,
+  bool Item_hybrid_func_fix_attributes(THD *thd,
+                                       const char *name,
+                                       Type_handler_hybrid_field_type *,
+                                       Type_all_attributes *atrr,
                                        Item **items, uint nitems) const;
 };
 
@@ -1912,7 +1943,10 @@ class Type_handler_timestamp_common: public Type_handler_temporal_with_date
     return Item_send_datetime(item, protocol, buf);
   }
   String *print_item_value(THD *thd, Item *item, String *str) const;
-  bool Item_hybrid_func_fix_attributes(THD *thd, Item_hybrid_func *func,
+  bool Item_hybrid_func_fix_attributes(THD *thd,
+                                       const char *name,
+                                       Type_handler_hybrid_field_type *,
+                                       Type_all_attributes *atrr,
                                        Item **items, uint nitems) const;
 };
 
@@ -2094,7 +2128,10 @@ class Type_handler_blob_common: public Type_handler_longstr
     return false; // Materialization does not work with BLOB columns
   }
   bool is_param_long_data_type() const { return true; }
-  bool Item_hybrid_func_fix_attributes(THD *thd, Item_hybrid_func *func,
+  bool Item_hybrid_func_fix_attributes(THD *thd,
+                                       const char *name,
+                                       Type_handler_hybrid_field_type *,
+                                       Type_all_attributes *atrr,
                                        Item **items, uint nitems) const;
 };
 
@@ -2197,7 +2234,10 @@ class Type_handler_geometry: public Type_handler_string_result
   bool Item_func_int_val_fix_length_and_dec(Item_func_int_val *) const;
   bool Item_func_abs_fix_length_and_dec(Item_func_abs *) const;
   bool Item_func_neg_fix_length_and_dec(Item_func_neg *) const;
-  bool Item_hybrid_func_fix_attributes(THD *thd, Item_hybrid_func *func,
+  bool Item_hybrid_func_fix_attributes(THD *thd,
+                                       const char *name,
+                                       Type_handler_hybrid_field_type *h,
+                                       Type_all_attributes *attr,
                                        Item **items, uint nitems) const;
   bool Item_sum_sum_fix_length_and_dec(Item_sum_sum *) const;
   bool Item_sum_avg_fix_length_and_dec(Item_sum_avg *) const;
@@ -2217,16 +2257,28 @@ extern MYSQL_PLUGIN_IMPORT Type_handler_geometry type_handler_geometry;
 #endif
 
 
-class Type_handler_enum: public Type_handler_string_result
+class Type_handler_typelib: public Type_handler_string_result
+{
+public:
+  virtual ~Type_handler_typelib() { }
+  enum_field_types field_type() const { return MYSQL_TYPE_STRING; }
+  const Type_handler *type_handler_for_item_field() const;
+  const Type_handler *cast_to_int_type_handler() const;
+  bool Item_hybrid_func_fix_attributes(THD *thd,
+                                       const char *name,
+                                       Type_handler_hybrid_field_type *,
+                                       Type_all_attributes *atrr,
+                                       Item **items, uint nitems) const;
+};
+
+
+class Type_handler_enum: public Type_handler_typelib
 {
   static const Name m_name_enum;
 public:
   virtual ~Type_handler_enum() {}
   const Name name() const { return m_name_enum; }
-  enum_field_types field_type() const { return MYSQL_TYPE_STRING; }
   virtual enum_field_types real_field_type() const { return MYSQL_TYPE_ENUM; }
-  const Type_handler *type_handler_for_item_field() const;
-  const Type_handler *cast_to_int_type_handler() const;
   Field *make_conversion_table_field(TABLE *, uint metadata,
                                      const Field *target) const;
   Field *make_table_field(const LEX_CSTRING *name,
@@ -2236,16 +2288,13 @@ 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
 {
   static const Name m_name_set;
 public:
   virtual ~Type_handler_set() {}
   const Name name() const { return m_name_set; }
-  enum_field_types field_type() const { return MYSQL_TYPE_STRING; }
   virtual enum_field_types real_field_type() const { return MYSQL_TYPE_SET; }
-  const Type_handler *type_handler_for_item_field() const;
-  const Type_handler *cast_to_int_type_handler() const;
   Field *make_conversion_table_field(TABLE *, uint metadata,
                                      const Field *target) const;
   Field *make_table_field(const LEX_CSTRING *name,
diff --git a/sql/sql_union.cc b/sql/sql_union.cc
index bf25c0b..0eb9b7f 100644
--- a/sql/sql_union.cc
+++ b/sql/sql_union.cc
@@ -692,6 +692,179 @@ bool st_select_lex_unit::prepare_join(THD *thd_arg, SELECT_LEX *sl,
 }
 
 
+class Type_holder: public Sql_alloc,
+                   public Item_args,
+                   public Type_handler_hybrid_field_type,
+                   public Type_all_attributes,
+                   public Type_geometry_attributes
+{
+  TYPELIB *m_typelib;
+  bool maybe_null;
+public:
+  Type_holder()
+   :m_typelib(NULL),
+    maybe_null(false)
+  { }
+
+  void set_maybe_null(bool maybe_null_arg) { maybe_null= maybe_null_arg; }
+  bool get_maybe_null() const { return maybe_null; }
+
+  uint decimal_precision() const
+  {
+    /*
+      Type_holder is not used directly to create fields, so
+      its virtual decimal_precision() is never called.
+      We should eventually extend create_result_table() to accept
+      an array of Type_holders directly, without having to allocate
+      Item_type_holder's and put them into List<Item>.
+    */
+    DBUG_ASSERT(0);
+    return 0;
+  }
+  void set_geometry_type(uint type)
+  {
+    Type_geometry_attributes::set_geometry_type(type);
+  }
+  uint uint_geometry_type() const
+  {
+    return Type_geometry_attributes::get_geometry_type();
+  }
+  void set_typelib(TYPELIB *typelib)
+  {
+    m_typelib= typelib;
+  }
+  TYPELIB *get_typelib() const
+  {
+    return m_typelib;
+  }
+
+  bool aggregate_attributes(THD *thd)
+  {
+    for (uint i= 0; i < arg_count; i++)
+      maybe_null|= args[i]->maybe_null;
+    return
+       type_handler()->Item_hybrid_func_fix_attributes(thd,
+                                                       "UNION", this, this,
+                                                       args, arg_count);
+  }
+};
+
+
+/**
+  Aggregate data type handlers for the "count" leftmost UNION parts.
+*/
+bool st_select_lex_unit::join_union_type_handlers(THD *thd_arg,
+                                                  Type_holder *holders,
+                                                  uint count)
+{
+  DBUG_ENTER("st_select_lex_unit::join_union_type_handlers");
+  SELECT_LEX *first_sl= first_select(), *sl= first_sl;
+  for (uint i= 0; i < count ; sl= sl->next_select(), i++)
+  {
+    Item *item;
+    List_iterator_fast<Item> it(sl->item_list);
+    for (uint pos= 0; (item= it++); pos++)
+    {
+      const Type_handler *item_type_handler= item->real_type_handler();
+      if (sl == first_sl)
+        holders[pos].set_handler(item_type_handler);
+      else
+      {
+        if (first_sl->item_list.elements != sl->item_list.elements)
+        {
+          my_message(ER_WRONG_NUMBER_OF_COLUMNS_IN_SELECT,
+                     ER_THD(thd_arg, ER_WRONG_NUMBER_OF_COLUMNS_IN_SELECT),
+                     MYF(0));
+          DBUG_RETURN(true);
+        }
+        if (holders[pos].aggregate_for_result(item_type_handler))
+        {
+          my_error(ER_ILLEGAL_PARAMETER_DATA_TYPES2_FOR_OPERATION, MYF(0),
+                   holders[pos].type_handler()->name().ptr(),
+                   item_type_handler->name().ptr(),
+                   "UNION");
+          DBUG_RETURN(true);
+        }
+      }
+    }
+  }
+  DBUG_RETURN(false);
+}
+
+
+/**
+  Aggregate data type attributes for the "count" leftmost UNION parts.
+*/
+bool st_select_lex_unit::join_union_type_attributes(THD *thd_arg,
+                                                    Type_holder *holders,
+                                                    uint count)
+{
+  DBUG_ENTER("st_select_lex_unit::join_union_type_attributes");
+  SELECT_LEX *sl, *first_sl= first_select();
+  uint item_pos;
+  for (uint pos= 0; pos < first_sl->item_list.elements; pos++)
+  {
+    if (holders[pos].alloc_arguments(thd_arg, count))
+      DBUG_RETURN(true);
+  }
+  for (item_pos= 0, sl= first_sl ;
+       item_pos < count;
+       sl= sl->next_select(), item_pos++)
+  {
+    Item *item_tmp;
+    List_iterator_fast<Item> itx(sl->item_list);
+    for (uint holder_pos= 0 ; (item_tmp= itx++); holder_pos++)
+    {
+      DBUG_ASSERT(item_tmp->fixed);
+      holders[holder_pos].add_argument(item_tmp);
+    }
+  }
+  for (uint pos= 0; pos < first_sl->item_list.elements; pos++)
+  {
+    if (holders[pos].aggregate_attributes(thd_arg))
+      DBUG_RETURN(true);
+  }
+  DBUG_RETURN(false);
+}
+
+
+/**
+  Join data types for the leftmost "count" UNION parts
+  and store corresponding Item_type_holder's into "types".
+*/
+bool st_select_lex_unit::join_union_item_types(THD *thd_arg,
+                                               List<Item> &types,
+                                               uint count)
+{
+  DBUG_ENTER("st_select_lex_unit::join_union_select_list_types");
+  SELECT_LEX *first_sl= first_select();
+  Type_holder *holders;
+
+  if (!(holders= new (thd_arg->mem_root)
+                 Type_holder[first_sl->item_list.elements]) ||
+     join_union_type_handlers(thd_arg, holders, count) ||
+     join_union_type_attributes(thd_arg, holders, count))
+    DBUG_RETURN(true);
+
+  types.empty();
+  List_iterator_fast<Item> it(first_sl->item_list);
+  Item *item_tmp;
+  for (uint pos= 0; (item_tmp= it++); pos++)
+  {
+    /* Error's in 'new' will be detected after loop */
+    types.push_back(new (thd_arg->mem_root)
+                    Item_type_holder(thd_arg,
+                                     &item_tmp->name,
+                                     holders[pos].type_handler(),
+                                     &holders[pos]/*Type_all_attributes*/,
+                                     holders[pos].get_maybe_null()));
+  }
+  if (thd_arg->is_fatal_error)
+    DBUG_RETURN(true); // out of memory
+  DBUG_RETURN(false);
+}
+
+
 bool st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result,
                                  ulong additional_options)
 {
@@ -699,6 +872,7 @@ bool st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result,
   SELECT_LEX *sl, *first_sl= first_select();
   bool is_recursive= with_element && with_element->is_recursive;
   bool is_rec_result_table_created= false;
+  uint union_part_count= 0;
   select_result *tmp_result;
   bool is_union_select;
   bool have_except= FALSE, have_intersect= FALSE;
@@ -811,7 +985,7 @@ bool st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result,
     goto cont;
   }
  
-  for (;sl; sl= sl->next_select())
+  for (;sl; sl= sl->next_select(), union_part_count++)
   {
     if (prepare_join(thd_arg, sl, tmp_result, additional_options,
                      is_union_select))
@@ -834,43 +1008,10 @@ bool st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result,
       if (is_recursive)
       {
         if (derived->with->rename_columns_of_derived_unit(thd, this))
-	  goto err; 
+          goto err;
         if (check_duplicate_names(thd, sl->item_list, 0))
           goto err;
       }
-      types.empty();
-      List_iterator_fast<Item> it(sl->item_list);
-      Item *item_tmp;
-      while ((item_tmp= it++))
-      {
-	/* Error's in 'new' will be detected after loop */
-	types.push_back(new (thd_arg->mem_root)
-                        Item_type_holder(thd_arg, item_tmp));
-      }
-
-      if (thd_arg->is_fatal_error)
-	goto err; // out of memory
-    }
-    else
-    {
-      if (types.elements != sl->item_list.elements)
-      {
-	my_message(ER_WRONG_NUMBER_OF_COLUMNS_IN_SELECT,
-		   ER_THD(thd, ER_WRONG_NUMBER_OF_COLUMNS_IN_SELECT),MYF(0));
-	goto err;
-      }
-      if (!is_rec_result_table_created)
-      {
-        List_iterator_fast<Item> it(sl->item_list);
-        List_iterator_fast<Item> tp(types);	
-        Item *type, *item_tmp;
-        while ((type= tp++, item_tmp= it++))
-        {
-          DBUG_ASSERT(item_tmp->fixed);
-          if (((Item_type_holder*)type)->join_types(thd_arg, item_tmp))
-	    DBUG_RETURN(TRUE);
-        }
-      }
     }
     if (is_recursive)
     {
@@ -883,6 +1024,9 @@ bool st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result,
         ulonglong create_options;
         create_options= (first_sl->options | thd_arg->variables.option_bits |
                          TMP_TABLE_ALL_COLUMNS);
+        // Join data types for all non-recursive parts of a recursive UNION
+        if (join_union_item_types(thd, types, union_part_count + 1))
+          goto err;
         if (union_result->create_result_table(thd, &types,
                                               MY_TEST(union_distinct),
                                               create_options, derived->alias,
@@ -898,6 +1042,9 @@ bool st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result,
       }
     }      
   }
+  // In case of a non-recursive UNION, join data types for all UNION parts.
+  if (!is_recursive && join_union_item_types(thd, types, union_part_count))
+    goto err;
 
 cont:
   /*
diff --git a/storage/tokudb/mysql-test/tokudb/r/type_float.result b/storage/tokudb/mysql-test/tokudb/r/type_float.result
index 6387cea..f8ce24f 100644
--- a/storage/tokudb/mysql-test/tokudb/r/type_float.result
+++ b/storage/tokudb/mysql-test/tokudb/r/type_float.result
@@ -233,12 +233,12 @@ insert into t2 values ("1.23456780");
 create table t3 select * from t2 union select * from t1;
 select * from t3;
 d
-1.2345678
-100000000
+1.234567800
+100000000.000000000
 show create table t3;
 Table	Create Table
 t3	CREATE TABLE `t3` (
-  `d` double DEFAULT NULL
+  `d` double(18,9) DEFAULT NULL
 ) ENGINE=ENGINE DEFAULT CHARSET=latin1
 drop table t1, t2, t3;
 create table t1 select  105213674794682365.00 + 0.0 x;