← Back to team overview

maria-developers team mailing list archive

MDEV-12506 Split Item_func_min_max::fix_length_and_dec() into methods in Type_handler

 

Hello Alexey,

Can you please review a patch for MDEV-12506?

Thanks.
commit bb6cc2b98402c0bc83150b8ad5c09622813a887c
Author: Alexander Barkov <bar@xxxxxxxxxxx>
Date:   Sat Apr 15 07:21:55 2017 +0400

    MDEV-12506 Split Item_func_min_max::fix_length_and_dec() into methods in Type_handler
    
    This patch does the following:
    
    1. Adds a new method Type_handler_hybrid_field_type::aggregate_for_min_max()
    
       - For non-traditional data types it uses
         type_handler_data->m_type_aggregator_for_result.find_handler()
         This allows pluggable data types to define in the future their
         own behavior of the result data type detection for LEAST/GREATEST.
         Also, this disallows expressions of the GEOMETRY data type
         (and its variants such as POINT) to be mixed in with
         numeric and temporal data types in LEAST/GREATEST.
    
       - For traditional data types it reproduces the old behavior of
         the result data type detection (but not attributes, see below).
    
    2. Adds a new virtual method Type_handler::Item_func_min_max_fix_attributes()
       and reuses as much as possible the code that calculates data type attributes
       for CASE-alike functions (e.g. CASE..THEN, COALESCE, IF).
    
       As the old code responsible for attributes calculation in the old
       implementation of Item_func_min_max::fix_length_and_dec()
       was not fully correct, this automatically fixes the following bugs:
    
       - MDEV-12497 Wrong data type for LEAST(latin1_expr, utf8_expr)
         The old fix_length_and_dec() calculated max_length before
         character set aggregation. Now max_length is calculated after, in
         Item_func::count_string_length() called from
         Item_func::aggregate_attributes_string() called from
         Type_handler_string_result::Item_hybrid_func_fix_attributes() called from
         Type_handler::Item_func_min_max_fix_attributes() called from
         Item_func_min_max::fix_length_and_dec().
    
       - MDEV-12504 Wrong data type for LEAST(date_expr,time_expr)
         The old fix_length_and_dec() simply used the maximum of max_length
         among all arguments to set its own max_length and did not take
         into account that a mixture of DATE and TIME becomes DATETIME.
         Now this is correctly handled by:
         Type_handler_datetime_common::Item_hybrid_func_fix_attributes() called from
         Type_handler::Item_func_min_max_fix_attributes() called from
         Item_func_min_max::fix_length_and_dec().
    
    3. Removes the old implementation of Item_func_min_max::fix_length_and_dec()
       and replaces it to calls of the new methods.
    
    4. Cleanup: moves the code related to unsigned_flag processing
       from Type_handler_hybrid_field_type::aggregate_for_result()
       to   Type_handler_int_result::Item_hybrid_func_fix_attributes().
       This is done:
       - to avoid code duplication in
         Type_handler_hybrid_field_type::aggregate_for_min_max()
       - to get rid of one more call for field_type(), which is unfriendly
         to the conceipt of pluggable data types.

diff --git a/mysql-test/r/ctype_binary.result b/mysql-test/r/ctype_binary.result
index 976c3df..6c16b10 100644
--- a/mysql-test/r/ctype_binary.result
+++ b/mysql-test/r/ctype_binary.result
@@ -608,7 +608,7 @@ create table t1 as select concat(least(1,2)) as c1;
 show create table t1;
 Table	Create Table
 t1	CREATE TABLE `t1` (
-  `c1` varbinary(2) DEFAULT NULL
+  `c1` varbinary(1) DEFAULT NULL
 ) ENGINE=MyISAM DEFAULT CHARSET=latin1
 drop table t1;
 select hex(concat(greatest(1,2)));
@@ -618,7 +618,7 @@ create table t1 as select concat(greatest(1,2)) as c1;
 show create table t1;
 Table	Create Table
 t1	CREATE TABLE `t1` (
-  `c1` varbinary(2) DEFAULT NULL
+  `c1` varbinary(1) DEFAULT NULL
 ) ENGINE=MyISAM DEFAULT CHARSET=latin1
 drop table t1;
 select hex(concat(case when 11 then 22 else 33 end));
diff --git a/mysql-test/r/ctype_cp1251.result b/mysql-test/r/ctype_cp1251.result
index 037f3cb..f4f54d8 100644
--- a/mysql-test/r/ctype_cp1251.result
+++ b/mysql-test/r/ctype_cp1251.result
@@ -1017,7 +1017,7 @@ create table t1 as select concat(least(1,2)) as c1;
 show create table t1;
 Table	Create Table
 t1	CREATE TABLE `t1` (
-  `c1` varchar(2) CHARACTER SET cp1251 DEFAULT NULL
+  `c1` varchar(1) CHARACTER SET cp1251 DEFAULT NULL
 ) ENGINE=MyISAM DEFAULT CHARSET=latin1
 drop table t1;
 select hex(concat(greatest(1,2)));
@@ -1027,7 +1027,7 @@ create table t1 as select concat(greatest(1,2)) as c1;
 show create table t1;
 Table	Create Table
 t1	CREATE TABLE `t1` (
-  `c1` varchar(2) CHARACTER SET cp1251 DEFAULT NULL
+  `c1` varchar(1) CHARACTER SET cp1251 DEFAULT NULL
 ) ENGINE=MyISAM DEFAULT CHARSET=latin1
 drop table t1;
 select hex(concat(case when 11 then 22 else 33 end));
diff --git a/mysql-test/r/ctype_latin1.result b/mysql-test/r/ctype_latin1.result
index fc37dcf..4b92c81 100644
--- a/mysql-test/r/ctype_latin1.result
+++ b/mysql-test/r/ctype_latin1.result
@@ -1314,7 +1314,7 @@ create table t1 as select concat(least(1,2)) as c1;
 show create table t1;
 Table	Create Table
 t1	CREATE TABLE `t1` (
-  `c1` varchar(2) DEFAULT NULL
+  `c1` varchar(1) DEFAULT NULL
 ) ENGINE=MyISAM DEFAULT CHARSET=latin1
 drop table t1;
 select hex(concat(greatest(1,2)));
@@ -1324,7 +1324,7 @@ create table t1 as select concat(greatest(1,2)) as c1;
 show create table t1;
 Table	Create Table
 t1	CREATE TABLE `t1` (
-  `c1` varchar(2) DEFAULT NULL
+  `c1` varchar(1) DEFAULT NULL
 ) ENGINE=MyISAM DEFAULT CHARSET=latin1
 drop table t1;
 select hex(concat(case when 11 then 22 else 33 end));
diff --git a/mysql-test/r/ctype_ucs.result b/mysql-test/r/ctype_ucs.result
index 389a5d3..5b2fadb 100644
--- a/mysql-test/r/ctype_ucs.result
+++ b/mysql-test/r/ctype_ucs.result
@@ -2193,7 +2193,7 @@ create table t1 as select concat(least(1,2)) as c1;
 show create table t1;
 Table	Create Table
 t1	CREATE TABLE `t1` (
-  `c1` varchar(2) CHARACTER SET ucs2 DEFAULT NULL
+  `c1` varchar(1) CHARACTER SET ucs2 DEFAULT NULL
 ) ENGINE=MyISAM DEFAULT CHARSET=latin1
 drop table t1;
 select hex(concat(greatest(1,2)));
@@ -2203,7 +2203,7 @@ create table t1 as select concat(greatest(1,2)) as c1;
 show create table t1;
 Table	Create Table
 t1	CREATE TABLE `t1` (
-  `c1` varchar(2) CHARACTER SET ucs2 DEFAULT NULL
+  `c1` varchar(1) CHARACTER SET ucs2 DEFAULT NULL
 ) ENGINE=MyISAM DEFAULT CHARSET=latin1
 drop table t1;
 select hex(concat(case when 11 then 22 else 33 end));
diff --git a/mysql-test/r/ctype_utf8.result b/mysql-test/r/ctype_utf8.result
index eb18bb5..573799f 100644
--- a/mysql-test/r/ctype_utf8.result
+++ b/mysql-test/r/ctype_utf8.result
@@ -3065,7 +3065,7 @@ create table t1 as select concat(least(1,2)) as c1;
 show create table t1;
 Table	Create Table
 t1	CREATE TABLE `t1` (
-  `c1` varchar(2) CHARACTER SET utf8 DEFAULT NULL
+  `c1` varchar(1) CHARACTER SET utf8 DEFAULT NULL
 ) ENGINE=MyISAM DEFAULT CHARSET=latin1
 drop table t1;
 select hex(concat(greatest(1,2)));
@@ -3075,7 +3075,7 @@ create table t1 as select concat(greatest(1,2)) as c1;
 show create table t1;
 Table	Create Table
 t1	CREATE TABLE `t1` (
-  `c1` varchar(2) CHARACTER SET utf8 DEFAULT NULL
+  `c1` varchar(1) CHARACTER SET utf8 DEFAULT NULL
 ) ENGINE=MyISAM DEFAULT CHARSET=latin1
 drop table t1;
 select hex(concat(case when 11 then 22 else 33 end));
diff --git a/mysql-test/r/func_hybrid_type.result b/mysql-test/r/func_hybrid_type.result
index 324db45..edb3a55 100644
--- a/mysql-test/r/func_hybrid_type.result
+++ b/mysql-test/r/func_hybrid_type.result
@@ -1615,8 +1615,8 @@ def					coalesce___b	254	1	1	Y	0	39	8
 def					coalesce_b_b	254	1	1	Y	0	39	8
 def					if_______b_b	254	1	1	Y	0	39	8
 def					ifnull___b_b	254	1	1	Y	0	39	8
-def					least____b_b	254	1	1	Y	0	0	8
-def					greatest_b_b	254	1	1	Y	0	0	8
+def					least____b_b	254	1	1	Y	0	39	8
+def					greatest_b_b	254	1	1	Y	0	39	8
 ___________a	a
 case_______a	a
 case_____a_a	a
@@ -2190,10 +2190,10 @@ def					if_______a_b	12	19	19	Y	128	0	63
 def					if_______b_a	12	19	19	Y	128	0	63
 def					ifnull___a_b	12	19	19	Y	128	0	63
 def					ifnull___b_a	12	19	19	Y	128	0	63
-def					least____a_b	12	10	19	Y	128	0	63
-def					least____b_a	12	10	19	Y	128	0	63
-def					greatest_a_b	12	10	19	Y	128	0	63
-def					greatest_b_a	12	10	19	Y	128	0	63
+def					least____a_b	12	19	19	Y	128	0	63
+def					least____b_a	12	19	19	Y	128	0	63
+def					greatest_a_b	12	19	19	Y	128	0	63
+def					greatest_b_a	12	19	19	Y	128	0	63
 case_____a_b	2010-01-01 00:00:00
 case_____b_a	2001-01-01 10:20:30
 coalesce_a_b	2010-01-01 00:00:00
@@ -2317,8 +2317,8 @@ def					coalesce___a	7	19	19	N	129	0	63
 def					coalesce_a_a	7	19	19	N	129	0	63
 def					if_______a_a	7	19	19	N	129	0	63
 def					ifnull___a_a	7	19	19	N	129	0	63
-def					least____a_a	7	19	19	N	161	0	63
-def					greatest_a_a	7	19	19	N	161	0	63
+def					least____a_a	7	19	19	N	129	0	63
+def					greatest_a_a	7	19	19	N	129	0	63
 def	test	t1	t1	b	___________b	11	10	8	Y	128	0	63
 def					case_______b	11	10	8	Y	128	0	63
 def					case_____b_b	11	10	8	Y	128	0	63
@@ -3050,7 +3050,7 @@ DROP TABLE t1;
 SET timestamp=UNIX_TIMESTAMP('2010-01-01 01:02:03');
 SELECT GREATEST(CURRENT_TIME, CURRENT_DATE), COALESCE(CURRENT_TIME, CURRENT_DATE);
 Catalog	Database	Table	Table_alias	Column	Column_alias	Type	Length	Max length	Is_null	Flags	Decimals	Charsetnr
-def					GREATEST(CURRENT_TIME, CURRENT_DATE)	12	10	19	N	129	0	63
+def					GREATEST(CURRENT_TIME, CURRENT_DATE)	12	19	19	N	129	0	63
 def					COALESCE(CURRENT_TIME, CURRENT_DATE)	12	19	19	N	129	0	63
 GREATEST(CURRENT_TIME, CURRENT_DATE)	COALESCE(CURRENT_TIME, CURRENT_DATE)
 2010-01-01 01:02:03	2010-01-01 01:02:03
@@ -3058,9 +3058,14 @@ CREATE TABLE t1 (a TIMESTAMP);
 INSERT INTO t1 VALUES ('2010-01-01 10:20:30');
 SELECT GREATEST(a,a) FROM t1;
 Catalog	Database	Table	Table_alias	Column	Column_alias	Type	Length	Max length	Is_null	Flags	Decimals	Charsetnr
-def					GREATEST(a,a)	7	19	19	N	161	0	63
+def					GREATEST(a,a)	7	19	19	N	129	0	63
 GREATEST(a,a)
 2010-01-01 10:20:30
+SELECT COALESCE(a,a) FROM t1;
+Catalog	Database	Table	Table_alias	Column	Column_alias	Type	Length	Max length	Is_null	Flags	Decimals	Charsetnr
+def					COALESCE(a,a)	7	19	19	N	129	0	63
+COALESCE(a,a)
+2010-01-01 10:20:30
 DROP TABLE t1;
 CREATE TABLE t1 (a TIMESTAMP, b DATETIME);
 CREATE TABLE t2 AS SELECT LEAST(a,a),LEAST(b,b),LEAST(a,b) FROM t1;
@@ -3445,3 +3450,55 @@ DROP TABLE t1;
 #
 # End of 10.1 tests
 #
+#
+# Start of 10.3 tests
+#
+#
+# MDEV-12497 Wrong data type for LEAST(latin1_expr, utf8_expr)
+#
+CREATE TABLE t1 AS SELECT
+LEAST(_latin1'aaa',_utf8 0xC39F) AS c1,
+COALESCE(_latin1'aaa',_utf8 0xC39F) AS c2;
+SHOW CREATE TABLE t1;
+Table	Create Table
+t1	CREATE TABLE `t1` (
+  `c1` varchar(3) CHARACTER SET utf8 NOT NULL,
+  `c2` varchar(3) CHARACTER SET utf8 NOT NULL
+) ENGINE=MyISAM DEFAULT CHARSET=latin1
+SELECT * FROM t1;
+c1	c2
+aaa	aaa
+DROP TABLE t1;
+#
+# MDEV-12504 Wrong data type for LEAST(date_expr,time_expr)
+#
+CREATE TABLE t1 AS SELECT
+LEAST(DATE'2001-01-01', TIME'10:20:30') AS c1,
+CONCAT(LEAST(DATE'2001-01-01', TIME'10:20:30')) AS c2;
+SELECT * FROM t1;
+c1	c2
+2001-01-01 00:00:00	2001-01-01 00:00:00
+DROP TABLE t1;
+#
+# MDEV-12505 Wrong data type for GREATEST(bit_column, int_column)
+#
+CREATE TABLE t1 (a BIT(64),b INT);
+INSERT INTO t1 VALUES (0xFFFFFFFFFFFFFFFF,-1);
+SELECT a>b, COALESCE(a,b), GREATEST(a,b) FROM t1;
+a>b	COALESCE(a,b)	GREATEST(a,b)
+1	18446744073709551615	18446744073709551615
+CREATE TABLE t2 AS SELECT COALESCE(a,b),GREATEST(a,b) FROM t1;
+SELECT * FROM t2;
+COALESCE(a,b)	GREATEST(a,b)
+18446744073709551615	18446744073709551615
+SHOW CREATE TABLE t2;
+Table	Create Table
+t2	CREATE TABLE `t2` (
+  `COALESCE(a,b)` decimal(64,0) DEFAULT NULL,
+  `GREATEST(a,b)` decimal(64,0) DEFAULT NULL
+) ENGINE=MyISAM DEFAULT CHARSET=latin1
+DROP TABLE t2;
+DROP TABLE t1;
+#
+# End of 10.3 tests
+#
diff --git a/mysql-test/r/gis.result b/mysql-test/r/gis.result
index e713233..86bdc5e 100644
--- a/mysql-test/r/gis.result
+++ b/mysql-test/r/gis.result
@@ -2804,7 +2804,7 @@ CREATE TABLE t1 AS SELECT NULL UNION SELECT Point(1,1);
 SHOW CREATE TABLE t1;
 Table	Create Table
 t1	CREATE TABLE `t1` (
-  `NULL` geometry DEFAULT NULL
+  `NULL` point DEFAULT NULL
 ) ENGINE=MyISAM DEFAULT CHARSET=latin1
 DROP TABLE t1;
 DROP PROCEDURE p1;
@@ -3711,6 +3711,195 @@ CASE a WHEN POINT(1,1) THEN "a" WHEN POINT(1,2) THEN "b" END
 DROP PROCEDURE p1;
 DROP PROCEDURE p2;
 #
+# MDEV-12506 Split Item_func_min_max::fix_length_and_dec() into methods in Type_handler
+#
+CREATE PROCEDURE p2(query TEXT)
+BEGIN
+DECLARE errcount INT DEFAULT 0;
+DECLARE CONTINUE HANDLER FOR SQLEXCEPTION
+BEGIN
+SET errcount = errcount+1;
+#SHOW WARNINGS;
+GET DIAGNOSTICS CONDITION 1 @p= MESSAGE_TEXT;
+SELECT @p AS `ERROR: `;
+END;
+SELECT query AS ``;
+EXECUTE IMMEDIATE query;
+IF errcount = 0
+THEN
+SHOW CREATE TABLE t2;
+DROP TABLE t2;
+END IF;
+END;
+$$
+CREATE PROCEDURE p1(query TEXT)
+BEGIN
+SELECT query AS `-------------------------------------`;
+EXECUTE IMMEDIATE query;
+CALL p2('CREATE TABLE t2 AS SELECT LEAST(a,b) FROM t1');
+DROP TABLE t1;
+END;
+$$
+-------------------------------------
+CREATE TABLE t1 (a CHAR(10), b Point)
+
+CREATE TABLE t2 AS SELECT LEAST(a,b) FROM t1
+Table	Create Table
+t2	CREATE TABLE `t2` (
+  `LEAST(a,b)` longblob DEFAULT NULL
+) ENGINE=MyISAM DEFAULT CHARSET=latin1
+-------------------------------------
+CREATE TABLE t1 (a VARCHAR(10), b Point)
+
+CREATE TABLE t2 AS SELECT LEAST(a,b) FROM t1
+Table	Create Table
+t2	CREATE TABLE `t2` (
+  `LEAST(a,b)` longblob DEFAULT NULL
+) ENGINE=MyISAM DEFAULT CHARSET=latin1
+-------------------------------------
+CREATE TABLE t1 (a TINYTEXT, b Point)
+
+CREATE TABLE t2 AS SELECT LEAST(a,b) FROM t1
+Table	Create Table
+t2	CREATE TABLE `t2` (
+  `LEAST(a,b)` longblob DEFAULT NULL
+) ENGINE=MyISAM DEFAULT CHARSET=latin1
+-------------------------------------
+CREATE TABLE t1 (a TEXT, b Point)
+
+CREATE TABLE t2 AS SELECT LEAST(a,b) FROM t1
+Table	Create Table
+t2	CREATE TABLE `t2` (
+  `LEAST(a,b)` longblob DEFAULT NULL
+) ENGINE=MyISAM DEFAULT CHARSET=latin1
+-------------------------------------
+CREATE TABLE t1 (a MEDIUMTEXT, b Point)
+
+CREATE TABLE t2 AS SELECT LEAST(a,b) FROM t1
+Table	Create Table
+t2	CREATE TABLE `t2` (
+  `LEAST(a,b)` longblob DEFAULT NULL
+) ENGINE=MyISAM DEFAULT CHARSET=latin1
+-------------------------------------
+CREATE TABLE t1 (a LONGTEXT, b Point)
+
+CREATE TABLE t2 AS SELECT LEAST(a,b) FROM t1
+Table	Create Table
+t2	CREATE TABLE `t2` (
+  `LEAST(a,b)` longblob DEFAULT NULL
+) ENGINE=MyISAM DEFAULT CHARSET=latin1
+-------------------------------------
+CREATE TABLE t1 (a TINYINT, b Point)
+
+CREATE TABLE t2 AS SELECT LEAST(a,b) FROM t1
+ERROR: 
+Illegal parameter data types tinyint and geometry for operation 'least'
+-------------------------------------
+CREATE TABLE t1 (a SMALLINT, b Point)
+
+CREATE TABLE t2 AS SELECT LEAST(a,b) FROM t1
+ERROR: 
+Illegal parameter data types smallint and geometry for operation 'least'
+-------------------------------------
+CREATE TABLE t1 (a MEDIUMINT, b Point)
+
+CREATE TABLE t2 AS SELECT LEAST(a,b) FROM t1
+ERROR: 
+Illegal parameter data types mediumint and geometry for operation 'least'
+-------------------------------------
+CREATE TABLE t1 (a INT, b Point)
+
+CREATE TABLE t2 AS SELECT LEAST(a,b) FROM t1
+ERROR: 
+Illegal parameter data types int and geometry for operation 'least'
+-------------------------------------
+CREATE TABLE t1 (a BIGINT, b Point)
+
+CREATE TABLE t2 AS SELECT LEAST(a,b) FROM t1
+ERROR: 
+Illegal parameter data types bigint and geometry for operation 'least'
+-------------------------------------
+CREATE TABLE t1 (a FLOAT, b Point)
+
+CREATE TABLE t2 AS SELECT LEAST(a,b) FROM t1
+ERROR: 
+Illegal parameter data types float and geometry for operation 'least'
+-------------------------------------
+CREATE TABLE t1 (a DOUBLE, b Point)
+
+CREATE TABLE t2 AS SELECT LEAST(a,b) FROM t1
+ERROR: 
+Illegal parameter data types double and geometry for operation 'least'
+-------------------------------------
+CREATE TABLE t1 (a DECIMAL(10,2), b Point)
+
+CREATE TABLE t2 AS SELECT LEAST(a,b) FROM t1
+ERROR: 
+Illegal parameter data types decimal and geometry for operation 'least'
+-------------------------------------
+CREATE TABLE t1 (a BIT(8), b Point)
+
+CREATE TABLE t2 AS SELECT LEAST(a,b) FROM t1
+ERROR: 
+Illegal parameter data types bit and geometry for operation 'least'
+-------------------------------------
+CREATE TABLE t1 (a TIME, b Point)
+
+CREATE TABLE t2 AS SELECT LEAST(a,b) FROM t1
+ERROR: 
+Illegal parameter data types time and geometry for operation 'least'
+-------------------------------------
+CREATE TABLE t1 (a DATE, b Point)
+
+CREATE TABLE t2 AS SELECT LEAST(a,b) FROM t1
+ERROR: 
+Illegal parameter data types date and geometry for operation 'least'
+-------------------------------------
+CREATE TABLE t1 (a DATETIME, b Point)
+
+CREATE TABLE t2 AS SELECT LEAST(a,b) FROM t1
+ERROR: 
+Illegal parameter data types datetime and geometry for operation 'least'
+-------------------------------------
+CREATE TABLE t1 (a TIMESTAMP, b Point)
+
+CREATE TABLE t2 AS SELECT LEAST(a,b) FROM t1
+ERROR: 
+Illegal parameter data types timestamp and geometry for operation 'least'
+-------------------------------------
+CREATE TABLE t1 (a YEAR, b Point)
+
+CREATE TABLE t2 AS SELECT LEAST(a,b) FROM t1
+ERROR: 
+Illegal parameter data types year and geometry for operation 'least'
+# This LEAST(ENUM,GEOMETRY) creates BLOB, but fails on error with UNION (see MDEV-12503)
+-------------------------------------
+CREATE TABLE t1 (a ENUM(0x61), b Point)
+
+CREATE TABLE t2 AS SELECT LEAST(a,b) FROM t1
+Table	Create Table
+t2	CREATE TABLE `t2` (
+  `LEAST(a,b)` longblob DEFAULT NULL
+) ENGINE=MyISAM DEFAULT CHARSET=latin1
+-------------------------------------
+CREATE TABLE t1 (a SET(0x61), b Point)
+
+CREATE TABLE t2 AS SELECT LEAST(a,b) FROM t1
+Table	Create Table
+t2	CREATE TABLE `t2` (
+  `LEAST(a,b)` longblob DEFAULT NULL
+) ENGINE=MyISAM DEFAULT CHARSET=latin1
+# This does not preserve geometry type (MDEV-9405)
+CREATE TABLE t1 AS SELECT LEAST(NULL, Point(1,1));
+SHOW CREATE TABLE t1;
+Table	Create Table
+t1	CREATE TABLE `t1` (
+  `LEAST(NULL, Point(1,1))` geometry DEFAULT NULL
+) ENGINE=MyISAM DEFAULT CHARSET=latin1
+DROP TABLE t1;
+DROP PROCEDURE p1;
+DROP PROCEDURE p2;
+#
 # MDEV-12001 Split Item_func_round::fix_length_and_dec to virtual methods in Type_handler
 #
 CREATE TABLE t1 (a GEOMETRY);
diff --git a/mysql-test/t/func_hybrid_type.test b/mysql-test/t/func_hybrid_type.test
index dd8a399..2efbdbe 100644
--- a/mysql-test/t/func_hybrid_type.test
+++ b/mysql-test/t/func_hybrid_type.test
@@ -155,6 +155,8 @@ CREATE TABLE t1 (a TIMESTAMP);
 INSERT INTO t1 VALUES ('2010-01-01 10:20:30');
 # Expect TIMESTAMP type (7) in metadata 
 SELECT GREATEST(a,a) FROM t1;
+# Similar to this one
+SELECT COALESCE(a,a) FROM t1;
 DROP TABLE t1;
 --disable_metadata
 --enable_ps_protocol
@@ -459,3 +461,47 @@ DROP TABLE t1;
 --echo #
 --echo # End of 10.1 tests
 --echo #
+
+--echo #
+--echo # Start of 10.3 tests
+--echo #
+
+--echo #
+--echo # MDEV-12497 Wrong data type for LEAST(latin1_expr, utf8_expr)
+--echo #
+
+CREATE TABLE t1 AS SELECT
+  LEAST(_latin1'aaa',_utf8 0xC39F) AS c1,
+  COALESCE(_latin1'aaa',_utf8 0xC39F) AS c2;
+SHOW CREATE TABLE t1;
+SELECT * FROM t1;
+DROP TABLE t1;
+
+--echo #
+--echo # MDEV-12504 Wrong data type for LEAST(date_expr,time_expr)
+--echo #
+
+CREATE TABLE t1 AS SELECT
+  LEAST(DATE'2001-01-01', TIME'10:20:30') AS c1,
+  CONCAT(LEAST(DATE'2001-01-01', TIME'10:20:30')) AS c2;
+SELECT * FROM t1;
+DROP TABLE t1;
+
+--echo #
+--echo # MDEV-12505 Wrong data type for GREATEST(bit_column, int_column)
+--echo #
+
+CREATE TABLE t1 (a BIT(64),b INT);
+INSERT INTO t1 VALUES (0xFFFFFFFFFFFFFFFF,-1);
+SELECT a>b, COALESCE(a,b), GREATEST(a,b) FROM t1;
+CREATE TABLE t2 AS SELECT COALESCE(a,b),GREATEST(a,b) FROM t1;
+SELECT * FROM t2;
+SHOW CREATE TABLE t2;
+DROP TABLE t2;
+DROP TABLE t1;
+
+
+--echo #
+--echo # End of 10.3 tests
+--echo #
+
diff --git a/mysql-test/t/gis.test b/mysql-test/t/gis.test
index a19de83..d64be95 100644
--- a/mysql-test/t/gis.test
+++ b/mysql-test/t/gis.test
@@ -1870,6 +1870,77 @@ DROP PROCEDURE p2;
 
 
 --echo #
+--echo # MDEV-12506 Split Item_func_min_max::fix_length_and_dec() into methods in Type_handler
+--echo #
+
+DELIMITER $$;
+CREATE PROCEDURE p2(query TEXT)
+BEGIN
+  DECLARE errcount INT DEFAULT 0;
+  DECLARE CONTINUE HANDLER FOR SQLEXCEPTION
+  BEGIN
+    SET errcount = errcount+1;
+    #SHOW WARNINGS;
+    GET DIAGNOSTICS CONDITION 1 @p= MESSAGE_TEXT;
+    SELECT @p AS `ERROR: `;
+  END;
+  SELECT query AS ``;
+  EXECUTE IMMEDIATE query;
+  IF errcount = 0
+  THEN
+    SHOW CREATE TABLE t2;
+    DROP TABLE t2;
+  END IF;
+END;
+$$
+CREATE PROCEDURE p1(query TEXT)
+BEGIN
+  SELECT query AS `-------------------------------------`;
+  EXECUTE IMMEDIATE query;
+  CALL p2('CREATE TABLE t2 AS SELECT LEAST(a,b) FROM t1');
+  DROP TABLE t1;
+END;
+$$
+DELIMITER ;$$
+
+--disable_query_log
+CALL p1('CREATE TABLE t1 (a CHAR(10), b Point)');
+CALL p1('CREATE TABLE t1 (a VARCHAR(10), b Point)');
+CALL p1('CREATE TABLE t1 (a TINYTEXT, b Point)');
+CALL p1('CREATE TABLE t1 (a TEXT, b Point)');
+CALL p1('CREATE TABLE t1 (a MEDIUMTEXT, b Point)');
+CALL p1('CREATE TABLE t1 (a LONGTEXT, b Point)');
+
+CALL p1('CREATE TABLE t1 (a TINYINT, b Point)');
+CALL p1('CREATE TABLE t1 (a SMALLINT, b Point)');
+CALL p1('CREATE TABLE t1 (a MEDIUMINT, b Point)');
+CALL p1('CREATE TABLE t1 (a INT, b Point)');
+CALL p1('CREATE TABLE t1 (a BIGINT, b Point)');
+CALL p1('CREATE TABLE t1 (a FLOAT, b Point)');
+CALL p1('CREATE TABLE t1 (a DOUBLE, b Point)');
+CALL p1('CREATE TABLE t1 (a DECIMAL(10,2), b Point)');
+CALL p1('CREATE TABLE t1 (a BIT(8), b Point)');
+
+CALL p1('CREATE TABLE t1 (a TIME, b Point)');
+CALL p1('CREATE TABLE t1 (a DATE, b Point)');
+CALL p1('CREATE TABLE t1 (a DATETIME, b Point)');
+CALL p1('CREATE TABLE t1 (a TIMESTAMP, b Point)');
+CALL p1('CREATE TABLE t1 (a YEAR, b Point)');
+
+--echo # This LEAST(ENUM,GEOMETRY) creates BLOB, but fails on error with UNION (see MDEV-12503)
+CALL p1('CREATE TABLE t1 (a ENUM(0x61), b Point)');
+CALL p1('CREATE TABLE t1 (a SET(0x61), b Point)');
+--enable_query_log
+
+--echo # This does not preserve geometry type (MDEV-9405)
+CREATE TABLE t1 AS SELECT LEAST(NULL, Point(1,1));
+SHOW CREATE TABLE t1;
+DROP TABLE t1;
+
+DROP PROCEDURE p1;
+DROP PROCEDURE p2;
+
+--echo #
 --echo # MDEV-12001 Split Item_func_round::fix_length_and_dec to virtual methods in Type_handler
 --echo #
 
diff --git a/sql/item_func.cc b/sql/item_func.cc
index a52c260..96057ec 100644
--- a/sql/item_func.cc
+++ b/sql/item_func.cc
@@ -2697,119 +2697,12 @@ double Item_func_units::val_real()
 }
 
 
-void Item_func_min_max::fix_length_and_dec()
+bool Item_func_min_max::fix_attributes(Item **items, uint nitems)
 {
-  uint unsigned_count= 0;
-  int max_int_part=0;
-  decimals=0;
-  max_length=0;
-  maybe_null=0;
-  Item_result tmp_cmp_type= args[0]->cmp_type();
-  uint string_type_count= 0;
-  uint temporal_type_count= 0;
-  enum_field_types temporal_field_type= MYSQL_TYPE_DATETIME;
-
-  for (uint i=0 ; i < arg_count ; i++)
-  {
-    set_if_bigger(max_length, args[i]->max_length);
-    set_if_bigger(decimals, args[i]->decimals);
-    set_if_bigger(max_int_part, args[i]->decimal_int_part());
-    unsigned_count+= args[i]->unsigned_flag;
-    if (args[i]->maybe_null)
-      maybe_null= 1;
-    tmp_cmp_type= item_cmp_type(tmp_cmp_type, args[i]->cmp_type());
-    string_type_count+= args[i]->cmp_type() == STRING_RESULT;
-    if (args[i]->cmp_type() == TIME_RESULT)
-    {
-      if (!temporal_type_count)
-        temporal_field_type= args[i]->field_type();
-      else
-        temporal_field_type= Field::field_type_merge(temporal_field_type,
-                                                     args[i]->field_type());
-      temporal_type_count++;
-    }
-  }
-  unsigned_flag= unsigned_count == arg_count; // if all args are unsigned
-
-  switch (tmp_cmp_type) {
-  case TIME_RESULT:
-    // At least one temporal argument was found.
-    collation.set_numeric();
-    set_handler_by_field_type(temporal_field_type);
-    if (is_temporal_type_with_time(temporal_field_type))
-      set_if_smaller(decimals, TIME_SECOND_PART_DIGITS);
-    else
-      decimals= 0;
-    break;
-
-  case STRING_RESULT:
-    if (aggregate_for_result(func_name(), args, arg_count, false))
-      return;
-    /*
-      All arguments are of string-alike types:
-        CHAR, VARCHAR, TEXT, BINARY, VARBINARY, BLOB, SET, ENUM
-      No numeric and no temporal types were found.
-    */
-    agg_arg_charsets_for_string_result_with_comparison(collation,
-                                                       args, arg_count);
-    break;
-
-  case INT_RESULT:
-    /*
-       All arguments have INT-alike types:
-       TINY, SHORT, LONG, LONGLONG, INT24, YEAR, BIT.
-    */
-    collation.set_numeric();
-    fix_char_length(my_decimal_precision_to_length_no_truncation(max_int_part +
-                                                                 decimals,
-                                                                 decimals,
-                                                                 unsigned_flag));
-    if (unsigned_count != 0 && unsigned_count != arg_count)
-    {
-      /*
-        If all args are of INT-alike type, but have different unsigned_flag,
-        then change type to DECIMAL.
-      */
-      set_handler_by_field_type(MYSQL_TYPE_NEWDECIMAL);
-    }
-    else
-    {
-      /*
-        There are only INT-alike arguments with equal unsigned_flag.
-        Aggregate types to get the best covering type.
-        Treat BIT as LONGLONG when aggregating to non-BIT types.
-        Possible final type: TINY, SHORT, LONG, LONGLONG, INT24, YEAR, BIT.
-      */
-      if (aggregate_for_result(func_name(), args, arg_count, true))
-        return;
-    }
-    break;
-
-  case DECIMAL_RESULT:
-    // All arguments are of DECIMAL type
-    collation.set_numeric();
-    fix_char_length(my_decimal_precision_to_length_no_truncation(max_int_part +
-                                                                 decimals,
-                                                                 decimals,
-                                                                 unsigned_flag));
-    set_handler_by_field_type(MYSQL_TYPE_NEWDECIMAL);
-    break;
-
-  case ROW_RESULT:
-    DBUG_ASSERT(0);
-    // Pass through
-  case REAL_RESULT:
-    collation.set_numeric();
-    fix_char_length(float_length(decimals));
-    /*
-      Set type to DOUBLE, as Item_func::create_tmp_field() does not
-      distinguish between DOUBLE and FLOAT and always creates Field_double.
-      Perhaps we should eventually change this to use aggregate_for_result()
-      and fix Item_func::create_tmp_field() to create Field_float when possible.
-    */
-    set_handler_by_field_type(MYSQL_TYPE_DOUBLE);
-    break;
-  }
+  bool rc= Item_func_min_max::type_handler()->
+             Item_func_min_max_fix_attributes(current_thd, 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 7f9321f..e5fa4d1 100644
--- a/sql/item_func.h
+++ b/sql/item_func.h
@@ -1442,6 +1442,8 @@ class Item_func_min_max :public Item_hybrid_func
 {
   String tmp_value;
   int cmp_sign;
+protected:
+  bool fix_attributes(Item **item, uint nitems);
 public:
   Item_func_min_max(THD *thd, List<Item> &list, int cmp_sign_arg):
     Item_hybrid_func(thd, list), cmp_sign(cmp_sign_arg)
@@ -1482,7 +1484,32 @@ class Item_func_min_max :public Item_hybrid_func
     return Item_func_min_max::type_handler()->
              Item_func_min_max_get_date(this, res, fuzzy_date);
   }
-  void fix_length_and_dec();
+  void aggregate_attributes_real(Item **items, uint nitems)
+  {
+    /*
+      Aggregating attributes for the double data type for LEAST/GREATEST
+      is almost the same with aggregating for CASE-alike hybrid functions,
+      (CASE..THEN, COALESCE, IF, etc).
+      There is one notable difference though, when a numeric argument is mixed
+      with a string argument:
+      - CASE-alike functions return a string data type in such cases
+        COALESCE(10,'x') -> VARCHAR(2) = '10'
+      - LEAST/GREATEST returns double:
+        GREATEST(10,'10e4') -> DOUBLE = 100000
+      As the string argument can represent a number in the scientific notation,
+      like in the example above, max_length of the result can be longer than
+      max_length of the arguments. To handle this properly, max_length is
+      additionally assigned to the result of float_length(decimals).
+    */
+    Item_func::aggregate_attributes_real(items, nitems);
+    max_length= float_length(decimals);
+  }
+  void fix_length_and_dec()
+  {
+    if (aggregate_for_min_max(func_name(), args, arg_count))
+      return;
+    fix_attributes(args, arg_count);
+  }
 };
 
 class Item_func_min :public Item_func_min_max
diff --git a/sql/sql_type.cc b/sql/sql_type.cc
index 337111f..0e7e41f 100644
--- a/sql/sql_type.cc
+++ b/sql/sql_type.cc
@@ -368,7 +368,6 @@ Type_handler_hybrid_field_type::aggregate_for_result(const char *funcname,
     return true;
   }
   set_handler(items[0]->type_handler());
-  uint unsigned_count= items[0]->unsigned_flag;
   for (uint i= 1 ; i < nitems ; i++)
   {
     const Type_handler *cur= items[i]->type_handler();
@@ -386,26 +385,6 @@ Type_handler_hybrid_field_type::aggregate_for_result(const char *funcname,
                type_handler()->name().ptr(), cur->name().ptr(), funcname);
       return true;
     }
-    unsigned_count+= items[i]->unsigned_flag;
-  }
-  switch (field_type()) {
-  case MYSQL_TYPE_TINY:
-  case MYSQL_TYPE_SHORT:
-  case MYSQL_TYPE_LONG:
-  case MYSQL_TYPE_LONGLONG:
-  case MYSQL_TYPE_INT24:
-  case MYSQL_TYPE_YEAR:
-  case MYSQL_TYPE_BIT:
-    if (unsigned_count != 0 && unsigned_count != nitems)
-    {
-      /*
-        If all arguments are of INT-alike type but have different
-        unsigned_flag, then convert to DECIMAL.
-      */
-      set_handler(&type_handler_newdecimal);
-    }
-  default:
-    break;
   }
   return false;
 }
@@ -477,6 +456,114 @@ Type_handler_hybrid_field_type::aggregate_for_comparison(const Type_handler *h)
 }
 
 
+/**
+  Aggregate data type handler for LEAST/GRATEST.
+  aggregate_for_min_max() is close to aggregate_for_comparison(),
+  but tries to preserve the exact type handler for string, int and temporal
+  data types (instead of converting to super-types).
+  FLOAT is not preserved and is converted to its super-type (DOUBLE).
+  This should probably fixed eventually, for symmetry.
+*/
+
+bool
+Type_handler_hybrid_field_type::aggregate_for_min_max(const Type_handler *h)
+{
+  if (!m_type_handler->is_traditional_type() ||
+      !h->is_traditional_type())
+  {
+    /*
+      If at least one data type is non-traditional,
+      do aggregation for result immediately.
+      For now we suppose that these two expressions:
+        - LEAST(type1, type2)
+        - COALESCE(type1, type2)
+      return the same data type (or both expressions return error)
+      if type1 and/or type2 are non-traditional.
+      This may change in the future.
+    */
+    h= type_handler_data->
+       m_type_aggregator_for_result.find_handler(m_type_handler, h);
+    if (!h)
+      return true;
+    m_type_handler= h;
+    return false;
+  }
+
+  Item_result a= cmp_type();
+  Item_result b= h->cmp_type();
+  DBUG_ASSERT(a != ROW_RESULT); // Disallowed by check_cols() in fix_fields()
+  DBUG_ASSERT(b != ROW_RESULT); // Disallowed by check_cols() in fix_fields()
+
+  if (a == STRING_RESULT && b == STRING_RESULT)
+    m_type_handler=
+      Type_handler::aggregate_for_result_traditional(m_type_handler, h);
+  else if (a == INT_RESULT && b == INT_RESULT)
+  {
+    // BIT aggregates with non-BIT as BIGINT
+    if (m_type_handler != h)
+    {
+      if (m_type_handler == &type_handler_bit)
+        m_type_handler= &type_handler_longlong;
+      else if (h == &type_handler_bit)
+        h= &type_handler_longlong;
+    }
+    m_type_handler=
+      Type_handler::aggregate_for_result_traditional(m_type_handler, h);
+  }
+  else if (a == TIME_RESULT || b == TIME_RESULT)
+  {
+    if ((a == TIME_RESULT) + (b == TIME_RESULT) == 1)
+    {
+      /*
+        We're here if there's only one temporal data type:
+        either m_type_handler or h.
+      */
+      if (b == TIME_RESULT)
+        m_type_handler= h; // Temporal types bit non-temporal types
+    }
+    else
+    {
+      /*
+        We're here if both m_type_handler and h are temporal data types.
+      */
+      m_type_handler=
+        Type_handler::aggregate_for_result_traditional(m_type_handler, h);
+    }
+  }
+  else if ((a == INT_RESULT || a == DECIMAL_RESULT) &&
+           (b == INT_RESULT || b == DECIMAL_RESULT))
+  {
+    m_type_handler= &type_handler_newdecimal;
+  }
+  else
+  {
+    m_type_handler= &type_handler_double;
+  }
+  return false;
+}
+
+
+bool
+Type_handler_hybrid_field_type::aggregate_for_min_max(const char *funcname,
+                                                      Item **items, uint nitems)
+{
+  // LEAST/GREATEST require at least two arguments
+  DBUG_ASSERT(nitems > 1);
+  set_handler(items[0]->type_handler());
+  for (uint i= 1; i < nitems;  i++)
+  {
+    const Type_handler *cur= items[i]->type_handler();
+    if (aggregate_for_min_max(cur))
+    {
+      my_error(ER_ILLEGAL_PARAMETER_DATA_TYPES2_FOR_OPERATION, MYF(0),
+               type_handler()->name().ptr(), cur->name().ptr(), funcname);
+      return true;
+    }
+  }
+  return false;
+}
+
+
 const Type_handler *
 Type_handler::aggregate_for_num_op_traditional(const Type_handler *h0,
                                                const Type_handler *h1)
@@ -1311,6 +1398,17 @@ bool Type_handler_int_result::
        Item_hybrid_func_fix_attributes(THD *thd, Item_hybrid_func *func,
                                        Item **items, uint nitems) const
 {
+  uint unsigned_flag= items[0]->unsigned_flag;
+  for (uint i= 1; i < nitems; i++)
+  {
+    if (unsigned_flag != items[i]->unsigned_flag)
+    {
+      // Convert a mixture of signed and unsigned int to decimal
+      func->set_handler(&type_handler_newdecimal);
+      func->aggregate_attributes_decimal(items, nitems);
+      return false;
+    }
+  }
   func->aggregate_attributes_int(items, nitems);
   return false;
 }
@@ -1377,6 +1475,33 @@ bool Type_handler_timestamp_common::
   return false;
 }
 
+/*************************************************************************/
+
+bool Type_handler::
+       Item_func_min_max_fix_attributes(THD *thd, Item_func_min_max *func,
+                                        Item **items, uint nitems) const
+{
+  /*
+    Aggregating attributes for LEAST/GREATES is exactly the same
+    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);
+}
+
+
+bool Type_handler_real_result::
+       Item_func_min_max_fix_attributes(THD *thd, Item_func_min_max *func,
+                                        Item **items, uint nitems) const
+{
+  /*
+    DOUBLE is an exception and aggregates attributes differently
+    for LEAST/GREATEST vs CASE-alike functions. See the comment in
+    Item_func_min_max::aggregate_attributes_real().
+  */
+  func->aggregate_attributes_real(items, nitems);
+  return false;
+}
 
 /*************************************************************************/
 
diff --git a/sql/sql_type.h b/sql/sql_type.h
index 891c785..5ac6d22 100644
--- a/sql/sql_type.h
+++ b/sql/sql_type.h
@@ -426,6 +426,10 @@ class Type_handler
   virtual bool Item_hybrid_func_fix_attributes(THD *thd, Item_hybrid_func *func,
                                                Item **items,
                                                uint nitems) const= 0;
+  virtual bool Item_func_min_max_fix_attributes(THD *thd,
+                                                Item_func_min_max *func,
+                                                Item **items,
+                                                uint nitems) const;
   virtual bool Item_sum_hybrid_fix_length_and_dec(Item_sum_hybrid *) const= 0;
   virtual bool Item_sum_sum_fix_length_and_dec(Item_sum_sum *) const= 0;
   virtual bool Item_sum_avg_fix_length_and_dec(Item_sum_avg *) const= 0;
@@ -781,6 +785,8 @@ class Type_handler_real_result: public Type_handler_numeric
   bool set_comparator_func(Arg_comparator *cmp) const;
   bool Item_hybrid_func_fix_attributes(THD *thd, Item_hybrid_func *func,
                                        Item **items, uint nitems) const;
+  bool Item_func_min_max_fix_attributes(THD *thd, Item_func_min_max *func,
+                                        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;
   bool Item_sum_avg_fix_length_and_dec(Item_sum_avg *) const;
@@ -1523,6 +1529,7 @@ class Type_handler_set: public Type_handler_string_result
 class Type_handler_hybrid_field_type
 {
   const Type_handler *m_type_handler;
+  bool aggregate_for_min_max(const Type_handler *other);
 public:
   Type_handler_hybrid_field_type();
   Type_handler_hybrid_field_type(const Type_handler *handler)
@@ -1578,6 +1585,8 @@ class Type_handler_hybrid_field_type
   bool aggregate_for_result(const Type_handler *other);
   bool aggregate_for_result(const char *funcname,
                             Item **item, uint nitems, bool treat_bit_as_number);
+  bool aggregate_for_min_max(const char *funcname, Item **item, uint nitems);
+
   bool aggregate_for_num_op(const class Type_aggregator *aggregator,
                             const Type_handler *h0, const Type_handler *h1);
 };