← Back to team overview

maria-developers team mailing list archive

MDEV-12617 CASE and CASE-alike hybrid functions do not preserve exact data types

 

Hi Sanja,

can you please review a patch for MDEV-12617?

Thanks!
commit 4a27ffde43189c1394a0edf39aaa9994c5c4e3ca
Author: Alexander Barkov <bar@xxxxxxxxxxx>
Date:   Fri Apr 28 19:32:24 2017 +0400

    MDEV-12617 CASE and CASE-alike hybrid functions do not preserve exact data types
    
    1. Adding a new class Item_func_case_expression, as a common parent
       for CASE and its abbreviation functions COALESCE, IFNULL, IF, NULIF, NVL2.
       Overriding its create_tmp_field() and create_field_for_create_select()
       to call tmp_table_field_from_field_type(). This makes these functions
       preserve the exact data type without conversion to supertypes, which
       is done in the default implementation Item::create_tmp_field().
    
    2. The above change needed additional changes in
       - Type_handler_hybrid_field_type::aggregate_for_result() and
       - Type_handler_hybrid_field_type::aggregate_for_min_max().
       As the default Item::create_tmp_field() is not called any more during
       fix_length_and_dec() time, the proper type handler for a mixture of
       BIT+nonBIT or nonBIT+BIT (type_handler_long or type_handler_longlog)
       is now determined inside aggregate_for_result() and aggregate_for_min_max()
       rather than outside.
    
    3. Adding a helper method Type_handler::bit_and_non_bit_mixture_handler()
       and reusing it in aggregate_for_result() and aggregate_for_min_max().
    
    4. Removing Item_func_ifnull::create_field_for_create_select().
       It was the only hybrid function that preserved the exact data type.
       Now we don't need to override it, as the derived method from
       Item_func_case_expression does exactly the same thing.

diff --git a/mysql-test/r/func_hybrid_type.result b/mysql-test/r/func_hybrid_type.result
index dfc552c..a8fd8a2 100644
--- a/mysql-test/r/func_hybrid_type.result
+++ b/mysql-test/r/func_hybrid_type.result
@@ -1000,14 +1000,14 @@ t2	CREATE TABLE `t2` (
   `least____a_a` int(11) DEFAULT NULL,
   `greatest_a_a` int(11) DEFAULT NULL,
   `___________b` bit(8) DEFAULT NULL,
-  `case_______b` int(8) unsigned DEFAULT NULL,
-  `case_____b_b` int(8) unsigned DEFAULT NULL,
-  `coalesce___b` int(8) unsigned DEFAULT NULL,
-  `coalesce_b_b` int(8) unsigned DEFAULT NULL,
-  `if_______b_b` int(8) unsigned DEFAULT NULL,
+  `case_______b` bit(8) DEFAULT NULL,
+  `case_____b_b` bit(8) DEFAULT NULL,
+  `coalesce___b` bit(8) DEFAULT NULL,
+  `coalesce_b_b` bit(8) DEFAULT NULL,
+  `if_______b_b` bit(8) DEFAULT NULL,
   `ifnull___b_b` bit(8) DEFAULT NULL,
-  `least____b_b` int(8) unsigned DEFAULT NULL,
-  `greatest_b_b` int(8) unsigned DEFAULT NULL
+  `least____b_b` bit(8) DEFAULT NULL,
+  `greatest_b_b` bit(8) DEFAULT NULL
 ) ENGINE=MyISAM DEFAULT CHARSET=latin1
 DROP TABLE t2;
 SET STATEMENT sql_mode = 'NO_ENGINE_SUBSTITUTION' FOR
@@ -1119,18 +1119,18 @@ GREATEST(a, b)                            AS greatest_a_b,
 GREATEST(b, a)                            AS greatest_b_a
 FROM t1;
 Catalog	Database	Table	Table_alias	Column	Column_alias	Type	Length	Max length	Is_null	Flags	Decimals	Charsetnr
-def					case_____a_b	8	10	10	Y	32928	0	63
-def					case_____b_a	8	10	2	Y	32928	0	63
-def					coalesce_a_b	8	10	10	Y	32928	0	63
-def					coalesce_b_a	8	10	2	Y	32928	0	63
-def					if_______a_b	8	10	2	Y	32928	0	63
-def					if_______b_a	8	10	10	Y	32928	0	63
-def					ifnull___a_b	8	10	10	Y	32928	0	63
-def					ifnull___b_a	8	10	2	Y	32928	0	63
-def					least____a_b	8	10	2	Y	32928	0	63
-def					least____b_a	8	10	2	Y	32928	0	63
-def					greatest_a_b	8	10	10	Y	32928	0	63
-def					greatest_b_a	8	10	10	Y	32928	0	63
+def					case_____a_b	3	10	10	Y	32928	0	63
+def					case_____b_a	3	10	2	Y	32928	0	63
+def					coalesce_a_b	3	10	10	Y	32928	0	63
+def					coalesce_b_a	3	10	2	Y	32928	0	63
+def					if_______a_b	3	10	2	Y	32928	0	63
+def					if_______b_a	3	10	10	Y	32928	0	63
+def					ifnull___a_b	3	10	10	Y	32928	0	63
+def					ifnull___b_a	3	10	2	Y	32928	0	63
+def					least____a_b	3	10	2	Y	32928	0	63
+def					least____b_a	3	10	2	Y	32928	0	63
+def					greatest_a_b	3	10	10	Y	32928	0	63
+def					greatest_b_a	3	10	10	Y	32928	0	63
 case_____a_b	4294967295
 case_____b_a	50
 coalesce_a_b	4294967295
@@ -1177,14 +1177,14 @@ t2	CREATE TABLE `t2` (
   `least____a_a` int(10) unsigned DEFAULT NULL,
   `greatest_a_a` int(10) unsigned DEFAULT NULL,
   `___________b` bit(8) DEFAULT NULL,
-  `case_______b` int(8) unsigned DEFAULT NULL,
-  `case_____b_b` int(8) unsigned DEFAULT NULL,
-  `coalesce___b` int(8) unsigned DEFAULT NULL,
-  `coalesce_b_b` int(8) unsigned DEFAULT NULL,
-  `if_______b_b` int(8) unsigned DEFAULT NULL,
+  `case_______b` bit(8) DEFAULT NULL,
+  `case_____b_b` bit(8) DEFAULT NULL,
+  `coalesce___b` bit(8) DEFAULT NULL,
+  `coalesce_b_b` bit(8) DEFAULT NULL,
+  `if_______b_b` bit(8) DEFAULT NULL,
   `ifnull___b_b` bit(8) DEFAULT NULL,
-  `least____b_b` int(8) unsigned DEFAULT NULL,
-  `greatest_b_b` int(8) unsigned DEFAULT NULL
+  `least____b_b` bit(8) DEFAULT NULL,
+  `greatest_b_b` bit(8) DEFAULT NULL
 ) ENGINE=MyISAM DEFAULT CHARSET=latin1
 DROP TABLE t2;
 SET STATEMENT sql_mode = 'NO_ENGINE_SUBSTITUTION' FOR
@@ -1212,8 +1212,8 @@ t2	CREATE TABLE `t2` (
   `coalesce_b_a` int(10) unsigned DEFAULT NULL,
   `if_______a_b` int(10) unsigned DEFAULT NULL,
   `if_______b_a` int(10) unsigned DEFAULT NULL,
-  `ifnull___a_b` bigint(10) unsigned DEFAULT NULL,
-  `ifnull___b_a` bigint(10) unsigned DEFAULT NULL,
+  `ifnull___a_b` int(10) unsigned DEFAULT NULL,
+  `ifnull___b_a` int(10) unsigned DEFAULT NULL,
   `least____a_b` int(10) unsigned DEFAULT NULL,
   `least____b_a` int(10) unsigned DEFAULT NULL,
   `greatest_a_b` int(10) unsigned DEFAULT NULL,
@@ -1345,23 +1345,23 @@ SHOW CREATE TABLE t2;
 Table	Create Table
 t2	CREATE TABLE `t2` (
   `___________a` bit(7) DEFAULT NULL,
-  `case_______a` int(7) unsigned DEFAULT NULL,
-  `case_____a_a` int(7) unsigned DEFAULT NULL,
-  `coalesce___a` int(7) unsigned DEFAULT NULL,
-  `coalesce_a_a` int(7) unsigned DEFAULT NULL,
-  `if_______a_a` int(7) unsigned DEFAULT NULL,
+  `case_______a` bit(7) DEFAULT NULL,
+  `case_____a_a` bit(7) DEFAULT NULL,
+  `coalesce___a` bit(7) DEFAULT NULL,
+  `coalesce_a_a` bit(7) DEFAULT NULL,
+  `if_______a_a` bit(7) DEFAULT NULL,
   `ifnull___a_a` bit(7) DEFAULT NULL,
-  `least____a_a` int(7) unsigned DEFAULT NULL,
-  `greatest_a_a` int(7) unsigned DEFAULT NULL,
+  `least____a_a` bit(7) DEFAULT NULL,
+  `greatest_a_a` bit(7) DEFAULT NULL,
   `___________b` bit(8) DEFAULT NULL,
-  `case_______b` int(8) unsigned DEFAULT NULL,
-  `case_____b_b` int(8) unsigned DEFAULT NULL,
-  `coalesce___b` int(8) unsigned DEFAULT NULL,
-  `coalesce_b_b` int(8) unsigned DEFAULT NULL,
-  `if_______b_b` int(8) unsigned DEFAULT NULL,
+  `case_______b` bit(8) DEFAULT NULL,
+  `case_____b_b` bit(8) DEFAULT NULL,
+  `coalesce___b` bit(8) DEFAULT NULL,
+  `coalesce_b_b` bit(8) DEFAULT NULL,
+  `if_______b_b` bit(8) DEFAULT NULL,
   `ifnull___b_b` bit(8) DEFAULT NULL,
-  `least____b_b` int(8) unsigned DEFAULT NULL,
-  `greatest_b_b` int(8) unsigned DEFAULT NULL
+  `least____b_b` bit(8) DEFAULT NULL,
+  `greatest_b_b` bit(8) DEFAULT NULL
 ) ENGINE=MyISAM DEFAULT CHARSET=latin1
 DROP TABLE t2;
 SET STATEMENT sql_mode = 'NO_ENGINE_SUBSTITUTION' FOR
@@ -1383,18 +1383,18 @@ FROM t1;
 SHOW CREATE TABLE t2;
 Table	Create Table
 t2	CREATE TABLE `t2` (
-  `case_____a_b` int(8) unsigned DEFAULT NULL,
-  `case_____b_a` int(8) unsigned DEFAULT NULL,
-  `coalesce_a_b` int(8) unsigned DEFAULT NULL,
-  `coalesce_b_a` int(8) unsigned DEFAULT NULL,
-  `if_______a_b` int(8) unsigned DEFAULT NULL,
-  `if_______b_a` int(8) unsigned DEFAULT NULL,
+  `case_____a_b` bit(8) DEFAULT NULL,
+  `case_____b_a` bit(8) DEFAULT NULL,
+  `coalesce_a_b` bit(8) DEFAULT NULL,
+  `coalesce_b_a` bit(8) DEFAULT NULL,
+  `if_______a_b` bit(8) DEFAULT NULL,
+  `if_______b_a` bit(8) DEFAULT NULL,
   `ifnull___a_b` bit(8) DEFAULT NULL,
   `ifnull___b_a` bit(8) DEFAULT NULL,
-  `least____a_b` int(8) unsigned DEFAULT NULL,
-  `least____b_a` int(8) unsigned DEFAULT NULL,
-  `greatest_a_b` int(8) unsigned DEFAULT NULL,
-  `greatest_b_a` int(8) unsigned DEFAULT NULL
+  `least____a_b` bit(8) DEFAULT NULL,
+  `least____b_a` bit(8) DEFAULT NULL,
+  `greatest_a_b` bit(8) DEFAULT NULL,
+  `greatest_b_a` bit(8) DEFAULT NULL
 ) ENGINE=MyISAM DEFAULT CHARSET=latin1
 DROP TABLE t2;
 DROP TABLE t1;
@@ -1429,8 +1429,8 @@ def					coalesce___a	4	12	1	Y	32896	31	63
 def					coalesce_a_a	4	12	1	Y	32896	31	63
 def					if_______a_a	4	12	1	Y	32896	31	63
 def					ifnull___a_a	4	12	1	Y	32896	31	63
-def					least____a_a	5	23	1	Y	32896	31	63
-def					greatest_a_a	5	23	1	Y	32896	31	63
+def					least____a_a	4	23	1	Y	32896	31	63
+def					greatest_a_a	4	23	1	Y	32896	31	63
 def	test	t1	t1	b	___________b	2	6	6	Y	32768	0	63
 def					case_______b	2	6	6	Y	32896	0	63
 def					case_____b_b	2	6	6	Y	32896	0	63
@@ -1522,23 +1522,23 @@ SHOW CREATE TABLE t2;
 Table	Create Table
 t2	CREATE TABLE `t2` (
   `___________a` float DEFAULT NULL,
-  `case_______a` double DEFAULT NULL,
-  `case_____a_a` double DEFAULT NULL,
-  `coalesce___a` double DEFAULT NULL,
-  `coalesce_a_a` double DEFAULT NULL,
-  `if_______a_a` double DEFAULT NULL,
+  `case_______a` float DEFAULT NULL,
+  `case_____a_a` float DEFAULT NULL,
+  `coalesce___a` float DEFAULT NULL,
+  `coalesce_a_a` float DEFAULT NULL,
+  `if_______a_a` float DEFAULT NULL,
   `ifnull___a_a` float DEFAULT NULL,
-  `least____a_a` double DEFAULT NULL,
-  `greatest_a_a` double DEFAULT NULL,
+  `least____a_a` float DEFAULT NULL,
+  `greatest_a_a` float DEFAULT NULL,
   `___________b` smallint(6) DEFAULT NULL,
-  `case_______b` int(6) DEFAULT NULL,
-  `case_____b_b` int(6) DEFAULT NULL,
-  `coalesce___b` int(6) DEFAULT NULL,
-  `coalesce_b_b` int(6) DEFAULT NULL,
-  `if_______b_b` int(6) DEFAULT NULL,
+  `case_______b` smallint(6) DEFAULT NULL,
+  `case_____b_b` smallint(6) DEFAULT NULL,
+  `coalesce___b` smallint(6) DEFAULT NULL,
+  `coalesce_b_b` smallint(6) DEFAULT NULL,
+  `if_______b_b` smallint(6) DEFAULT NULL,
   `ifnull___b_b` smallint(6) DEFAULT NULL,
-  `least____b_b` int(6) DEFAULT NULL,
-  `greatest_b_b` int(6) DEFAULT NULL
+  `least____b_b` smallint(6) DEFAULT NULL,
+  `greatest_b_b` smallint(6) DEFAULT NULL
 ) ENGINE=MyISAM DEFAULT CHARSET=latin1
 DROP TABLE t2;
 SET STATEMENT sql_mode = 'NO_ENGINE_SUBSTITUTION' FOR
@@ -1560,12 +1560,12 @@ FROM t1;
 SHOW CREATE TABLE t2;
 Table	Create Table
 t2	CREATE TABLE `t2` (
-  `case_____a_b` double DEFAULT NULL,
-  `case_____b_a` double DEFAULT NULL,
-  `coalesce_a_b` double DEFAULT NULL,
-  `coalesce_b_a` double DEFAULT NULL,
-  `if_______a_b` double DEFAULT NULL,
-  `if_______b_a` double DEFAULT NULL,
+  `case_____a_b` float DEFAULT NULL,
+  `case_____b_a` float DEFAULT NULL,
+  `coalesce_a_b` float DEFAULT NULL,
+  `coalesce_b_a` float DEFAULT NULL,
+  `if_______a_b` float DEFAULT NULL,
+  `if_______b_a` float DEFAULT NULL,
   `ifnull___a_b` float DEFAULT NULL,
   `ifnull___b_a` float DEFAULT NULL,
   `least____a_b` double DEFAULT NULL,
@@ -1885,14 +1885,14 @@ t2	CREATE TABLE `t2` (
   `least____a_a` int(11) DEFAULT NULL,
   `greatest_a_a` int(11) DEFAULT NULL,
   `___________b` year(4) DEFAULT NULL,
-  `case_______b` int(4) unsigned DEFAULT NULL,
-  `case_____b_b` int(4) unsigned DEFAULT NULL,
-  `coalesce___b` int(4) unsigned DEFAULT NULL,
-  `coalesce_b_b` int(4) unsigned DEFAULT NULL,
-  `if_______b_b` int(4) unsigned DEFAULT NULL,
+  `case_______b` year(4) DEFAULT NULL,
+  `case_____b_b` year(4) DEFAULT NULL,
+  `coalesce___b` year(4) DEFAULT NULL,
+  `coalesce_b_b` year(4) DEFAULT NULL,
+  `if_______b_b` year(4) DEFAULT NULL,
   `ifnull___b_b` year(4) DEFAULT NULL,
-  `least____b_b` int(4) unsigned DEFAULT NULL,
-  `greatest_b_b` int(4) unsigned DEFAULT NULL
+  `least____b_b` year(4) DEFAULT NULL,
+  `greatest_b_b` year(4) DEFAULT NULL
 ) ENGINE=MyISAM DEFAULT CHARSET=latin1
 DROP TABLE t2;
 SET STATEMENT sql_mode = 'NO_ENGINE_SUBSTITUTION' FOR
@@ -2062,14 +2062,14 @@ t2	CREATE TABLE `t2` (
   `least____a_a` int(10) unsigned DEFAULT NULL,
   `greatest_a_a` int(10) unsigned DEFAULT NULL,
   `___________b` year(4) DEFAULT NULL,
-  `case_______b` int(4) unsigned DEFAULT NULL,
-  `case_____b_b` int(4) unsigned DEFAULT NULL,
-  `coalesce___b` int(4) unsigned DEFAULT NULL,
-  `coalesce_b_b` int(4) unsigned DEFAULT NULL,
-  `if_______b_b` int(4) unsigned DEFAULT NULL,
+  `case_______b` year(4) DEFAULT NULL,
+  `case_____b_b` year(4) DEFAULT NULL,
+  `coalesce___b` year(4) DEFAULT NULL,
+  `coalesce_b_b` year(4) DEFAULT NULL,
+  `if_______b_b` year(4) DEFAULT NULL,
   `ifnull___b_b` year(4) DEFAULT NULL,
-  `least____b_b` int(4) unsigned DEFAULT NULL,
-  `greatest_b_b` int(4) unsigned DEFAULT NULL
+  `least____b_b` year(4) DEFAULT NULL,
+  `greatest_b_b` year(4) DEFAULT NULL
 ) ENGINE=MyISAM DEFAULT CHARSET=latin1
 DROP TABLE t2;
 SET STATEMENT sql_mode = 'NO_ENGINE_SUBSTITUTION' FOR
@@ -3512,5 +3512,40 @@ t2	CREATE TABLE `t2` (
 DROP TABLE t2;
 DROP TABLE t1;
 #
+# MDEV-12617 CASE and CASE-alike hybrid functions do not preserve exact data types
+#
+CREATE TABLE t1 (a FLOAT(10,2));
+CREATE TABLE t2 AS SELECT COALESCE(a) FROM t1;
+SHOW CREATE TABLE t2;
+Table	Create Table
+t2	CREATE TABLE `t2` (
+  `COALESCE(a)` float(10,2) DEFAULT NULL
+) ENGINE=MyISAM DEFAULT CHARSET=latin1
+DROP TABLE t2, t1;
+CREATE TABLE t1 (a FLOAT(10,2));
+CREATE TABLE t2 AS SELECT LEAST(a,a) FROM t1;
+SHOW CREATE TABLE t2;
+Table	Create Table
+t2	CREATE TABLE `t2` (
+  `LEAST(a,a)` float(19,2) DEFAULT NULL
+) ENGINE=MyISAM DEFAULT CHARSET=latin1
+DROP TABLE t2, t1;
+CREATE TABLE t1 (a TINYINT(1));
+CREATE TABLE t2 AS SELECT COALESCE(a) FROM t1;
+SHOW CREATE TABLE t2;
+Table	Create Table
+t2	CREATE TABLE `t2` (
+  `COALESCE(a)` tinyint(4) DEFAULT NULL
+) ENGINE=MyISAM DEFAULT CHARSET=latin1
+DROP TABLE t2, t1;
+CREATE TABLE t1 (a TINYINT(1));
+CREATE TABLE t2 AS SELECT LEAST(a,a) FROM t1;
+SHOW CREATE TABLE t2;
+Table	Create Table
+t2	CREATE TABLE `t2` (
+  `LEAST(a,a)` tinyint(4) DEFAULT NULL
+) ENGINE=MyISAM DEFAULT CHARSET=latin1
+DROP TABLE t2, t1;
+#
 # End of 10.3 tests
 #
diff --git a/mysql-test/r/null.result b/mysql-test/r/null.result
index 8bbdfb2..2fa89a2 100644
--- a/mysql-test/r/null.result
+++ b/mysql-test/r/null.result
@@ -613,22 +613,22 @@ FROM t1;
 SHOW CREATE TABLE t2;
 Table	Create Table
 t2	CREATE TABLE `t2` (
-  `NULLIF(c_tinyint,  1)` int(4) DEFAULT NULL,
-  `NULLIF(c_tinyint,  c_smallint)` int(4) DEFAULT NULL,
-  `NULLIF(c_tinyint,  c_tinyint)` int(4) DEFAULT NULL,
-  `NULLIF(c_tinyint,  c_int)` int(4) DEFAULT NULL,
-  `NULLIF(c_tinyint,  c_bigint)` int(4) DEFAULT NULL,
-  `NULLIF(c_tinyint,  c_float)` int(4) DEFAULT NULL,
-  `NULLIF(c_tinyint,  c_double)` int(4) DEFAULT NULL,
-  `NULLIF(c_tinyint,  c_decimal103)` int(4) DEFAULT NULL,
-  `NULLIF(c_tinyint,  c_varchar10)` int(4) DEFAULT NULL,
-  `NULLIF(c_tinyint,  c_text)` int(4) DEFAULT NULL,
-  `NULLIF(c_tinyint,  c_blob)` int(4) DEFAULT NULL,
-  `NULLIF(c_tinyint,  c_enum)` int(4) DEFAULT NULL,
-  `NULLIF(c_tinyint,  c_datetime3)` int(4) DEFAULT NULL,
-  `NULLIF(c_tinyint,  c_timestamp3)` int(4) DEFAULT NULL,
-  `NULLIF(c_tinyint,  c_date)` int(4) DEFAULT NULL,
-  `NULLIF(c_tinyint,  c_time)` int(4) DEFAULT NULL
+  `NULLIF(c_tinyint,  1)` tinyint(4) DEFAULT NULL,
+  `NULLIF(c_tinyint,  c_smallint)` tinyint(4) DEFAULT NULL,
+  `NULLIF(c_tinyint,  c_tinyint)` tinyint(4) DEFAULT NULL,
+  `NULLIF(c_tinyint,  c_int)` tinyint(4) DEFAULT NULL,
+  `NULLIF(c_tinyint,  c_bigint)` tinyint(4) DEFAULT NULL,
+  `NULLIF(c_tinyint,  c_float)` tinyint(4) DEFAULT NULL,
+  `NULLIF(c_tinyint,  c_double)` tinyint(4) DEFAULT NULL,
+  `NULLIF(c_tinyint,  c_decimal103)` tinyint(4) DEFAULT NULL,
+  `NULLIF(c_tinyint,  c_varchar10)` tinyint(4) DEFAULT NULL,
+  `NULLIF(c_tinyint,  c_text)` tinyint(4) DEFAULT NULL,
+  `NULLIF(c_tinyint,  c_blob)` tinyint(4) DEFAULT NULL,
+  `NULLIF(c_tinyint,  c_enum)` tinyint(4) DEFAULT NULL,
+  `NULLIF(c_tinyint,  c_datetime3)` tinyint(4) DEFAULT NULL,
+  `NULLIF(c_tinyint,  c_timestamp3)` tinyint(4) DEFAULT NULL,
+  `NULLIF(c_tinyint,  c_date)` tinyint(4) DEFAULT NULL,
+  `NULLIF(c_tinyint,  c_time)` tinyint(4) DEFAULT NULL
 ) ENGINE=MyISAM DEFAULT CHARSET=latin1
 DROP TABLE t2;
 CREATE TABLE t2 AS SELECT
@@ -652,22 +652,22 @@ FROM t1;
 SHOW CREATE TABLE t2;
 Table	Create Table
 t2	CREATE TABLE `t2` (
-  `NULLIF(c_smallint,  1)` int(6) DEFAULT NULL,
-  `NULLIF(c_smallint,  c_smallint)` int(6) DEFAULT NULL,
-  `NULLIF(c_smallint,  c_tinyint)` int(6) DEFAULT NULL,
-  `NULLIF(c_smallint,  c_int)` int(6) DEFAULT NULL,
-  `NULLIF(c_smallint,  c_bigint)` int(6) DEFAULT NULL,
-  `NULLIF(c_smallint,  c_float)` int(6) DEFAULT NULL,
-  `NULLIF(c_smallint,  c_double)` int(6) DEFAULT NULL,
-  `NULLIF(c_smallint,  c_decimal103)` int(6) DEFAULT NULL,
-  `NULLIF(c_smallint,  c_varchar10)` int(6) DEFAULT NULL,
-  `NULLIF(c_smallint,  c_text)` int(6) DEFAULT NULL,
-  `NULLIF(c_smallint,  c_blob)` int(6) DEFAULT NULL,
-  `NULLIF(c_smallint,  c_enum)` int(6) DEFAULT NULL,
-  `NULLIF(c_smallint,  c_datetime3)` int(6) DEFAULT NULL,
-  `NULLIF(c_smallint,  c_timestamp3)` int(6) DEFAULT NULL,
-  `NULLIF(c_smallint,  c_date)` int(6) DEFAULT NULL,
-  `NULLIF(c_smallint,  c_time)` int(6) DEFAULT NULL
+  `NULLIF(c_smallint,  1)` smallint(6) DEFAULT NULL,
+  `NULLIF(c_smallint,  c_smallint)` smallint(6) DEFAULT NULL,
+  `NULLIF(c_smallint,  c_tinyint)` smallint(6) DEFAULT NULL,
+  `NULLIF(c_smallint,  c_int)` smallint(6) DEFAULT NULL,
+  `NULLIF(c_smallint,  c_bigint)` smallint(6) DEFAULT NULL,
+  `NULLIF(c_smallint,  c_float)` smallint(6) DEFAULT NULL,
+  `NULLIF(c_smallint,  c_double)` smallint(6) DEFAULT NULL,
+  `NULLIF(c_smallint,  c_decimal103)` smallint(6) DEFAULT NULL,
+  `NULLIF(c_smallint,  c_varchar10)` smallint(6) DEFAULT NULL,
+  `NULLIF(c_smallint,  c_text)` smallint(6) DEFAULT NULL,
+  `NULLIF(c_smallint,  c_blob)` smallint(6) DEFAULT NULL,
+  `NULLIF(c_smallint,  c_enum)` smallint(6) DEFAULT NULL,
+  `NULLIF(c_smallint,  c_datetime3)` smallint(6) DEFAULT NULL,
+  `NULLIF(c_smallint,  c_timestamp3)` smallint(6) DEFAULT NULL,
+  `NULLIF(c_smallint,  c_date)` smallint(6) DEFAULT NULL,
+  `NULLIF(c_smallint,  c_time)` smallint(6) DEFAULT NULL
 ) ENGINE=MyISAM DEFAULT CHARSET=latin1
 DROP TABLE t2;
 CREATE TABLE t2 AS SELECT
@@ -769,22 +769,22 @@ FROM t1;
 SHOW CREATE TABLE t2;
 Table	Create Table
 t2	CREATE TABLE `t2` (
-  `NULLIF(c_float,  1)` double DEFAULT NULL,
-  `NULLIF(c_float,  c_smallint)` double DEFAULT NULL,
-  `NULLIF(c_float,  c_tinyint)` double DEFAULT NULL,
-  `NULLIF(c_float,  c_int)` double DEFAULT NULL,
-  `NULLIF(c_float,  c_bigint)` double DEFAULT NULL,
-  `NULLIF(c_float,  c_float)` double DEFAULT NULL,
-  `NULLIF(c_float,  c_double)` double DEFAULT NULL,
-  `NULLIF(c_float,  c_decimal103)` double DEFAULT NULL,
-  `NULLIF(c_float,  c_varchar10)` double DEFAULT NULL,
-  `NULLIF(c_float,  c_text)` double DEFAULT NULL,
-  `NULLIF(c_float,  c_blob)` double DEFAULT NULL,
-  `NULLIF(c_float,  c_enum)` double DEFAULT NULL,
-  `NULLIF(c_float,  c_datetime3)` double DEFAULT NULL,
-  `NULLIF(c_float,  c_timestamp3)` double DEFAULT NULL,
-  `NULLIF(c_float,  c_date)` double DEFAULT NULL,
-  `NULLIF(c_float,  c_time)` double DEFAULT NULL
+  `NULLIF(c_float,  1)` float DEFAULT NULL,
+  `NULLIF(c_float,  c_smallint)` float DEFAULT NULL,
+  `NULLIF(c_float,  c_tinyint)` float DEFAULT NULL,
+  `NULLIF(c_float,  c_int)` float DEFAULT NULL,
+  `NULLIF(c_float,  c_bigint)` float DEFAULT NULL,
+  `NULLIF(c_float,  c_float)` float DEFAULT NULL,
+  `NULLIF(c_float,  c_double)` float DEFAULT NULL,
+  `NULLIF(c_float,  c_decimal103)` float DEFAULT NULL,
+  `NULLIF(c_float,  c_varchar10)` float DEFAULT NULL,
+  `NULLIF(c_float,  c_text)` float DEFAULT NULL,
+  `NULLIF(c_float,  c_blob)` float DEFAULT NULL,
+  `NULLIF(c_float,  c_enum)` float DEFAULT NULL,
+  `NULLIF(c_float,  c_datetime3)` float DEFAULT NULL,
+  `NULLIF(c_float,  c_timestamp3)` float DEFAULT NULL,
+  `NULLIF(c_float,  c_date)` float DEFAULT NULL,
+  `NULLIF(c_float,  c_time)` float DEFAULT NULL
 ) ENGINE=MyISAM DEFAULT CHARSET=latin1
 DROP TABLE t2;
 CREATE TABLE t2 AS SELECT
diff --git a/mysql-test/t/func_hybrid_type.test b/mysql-test/t/func_hybrid_type.test
index 918320a..0957dfd 100644
--- a/mysql-test/t/func_hybrid_type.test
+++ b/mysql-test/t/func_hybrid_type.test
@@ -510,6 +510,29 @@ SHOW CREATE TABLE t2;
 DROP TABLE t2;
 DROP TABLE t1;
 
+--echo #
+--echo # MDEV-12617 CASE and CASE-alike hybrid functions do not preserve exact data types
+--echo #
+
+CREATE TABLE t1 (a FLOAT(10,2));
+CREATE TABLE t2 AS SELECT COALESCE(a) FROM t1;
+SHOW CREATE TABLE t2;
+DROP TABLE t2, t1;
+
+CREATE TABLE t1 (a FLOAT(10,2));
+CREATE TABLE t2 AS SELECT LEAST(a,a) FROM t1;
+SHOW CREATE TABLE t2;
+DROP TABLE t2, t1;
+
+CREATE TABLE t1 (a TINYINT(1));
+CREATE TABLE t2 AS SELECT COALESCE(a) FROM t1;
+SHOW CREATE TABLE t2;
+DROP TABLE t2, t1;
+
+CREATE TABLE t1 (a TINYINT(1));
+CREATE TABLE t2 AS SELECT LEAST(a,a) FROM t1;
+SHOW CREATE TABLE t2;
+DROP TABLE t2, t1;
 
 --echo #
 --echo # End of 10.3 tests
diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc
index 6712b98..62ebc10 100644
--- a/sql/item_cmpfunc.cc
+++ b/sql/item_cmpfunc.cc
@@ -2786,7 +2786,7 @@ Item_func_nullif::is_null()
 
 Item_func_case::Item_func_case(THD *thd, List<Item> &list,
                                Item *first_expr_arg, Item *else_expr_arg):
-  Item_func_hybrid_field_type(thd),
+  Item_func_case_expression(thd),
   Predicant_to_list_comparator(thd, list.elements/*QQ*/),
   first_expr_num(-1), else_expr_num(-1),
   left_cmp_type(INT_RESULT), m_found_types(0)
diff --git a/sql/item_cmpfunc.h b/sql/item_cmpfunc.h
index 6a6adb2..e9d4426 100644
--- a/sql/item_cmpfunc.h
+++ b/sql/item_cmpfunc.h
@@ -965,13 +965,13 @@ class Item_func_interval :public Item_int_func
 };
 
 
-class Item_func_coalesce :public Item_func_hybrid_field_type
+class Item_func_coalesce :public Item_func_case_expression
 {
 public:
   Item_func_coalesce(THD *thd, Item *a, Item *b):
-    Item_func_hybrid_field_type(thd, a, b) {}
+    Item_func_case_expression(thd, a, b) {}
   Item_func_coalesce(THD *thd, List<Item> &list):
-    Item_func_hybrid_field_type(thd, list) {}
+    Item_func_case_expression(thd, list) {}
   double real_op();
   longlong int_op();
   String *str_op(String *);
@@ -995,7 +995,7 @@ class Item_func_coalesce :public Item_func_hybrid_field_type
     IF(switch, arg1, arg2)
     NVL2(switch, arg1, arg2)
 */
-class Item_func_case_abbreviation2 :public Item_func_hybrid_field_type
+class Item_func_case_abbreviation2 :public Item_func_case_expression
 {
 protected:
   void fix_length_and_dec2(Item **items)
@@ -1034,9 +1034,9 @@ class Item_func_case_abbreviation2 :public Item_func_hybrid_field_type
 
 public:
   Item_func_case_abbreviation2(THD *thd, Item *a, Item *b):
-    Item_func_hybrid_field_type(thd, a, b) { }
+    Item_func_case_expression(thd, a, b) { }
   Item_func_case_abbreviation2(THD *thd, Item *a, Item *b, Item *c):
-    Item_func_hybrid_field_type(thd, a, b, c) { }
+    Item_func_case_expression(thd, a, b, c) { }
 };
 
 
@@ -1056,8 +1056,6 @@ class Item_func_ifnull :public Item_func_case_abbreviation2
     maybe_null= args[1]->maybe_null;
   }
   const char *func_name() const { return "ifnull"; }
-  Field *create_field_for_create_select(TABLE *table)
-  { return tmp_table_field_from_field_type(table); }
 
   table_map not_null_tables() const { return 0; }
   uint decimal_precision() const
@@ -1161,7 +1159,7 @@ class Item_func_nvl2 :public Item_func_case_abbreviation2_switch
 };
 
 
-class Item_func_nullif :public Item_func_hybrid_field_type
+class Item_func_nullif :public Item_func_case_expression
 {
   Arg_comparator cmp;
   /*
@@ -1199,7 +1197,7 @@ class Item_func_nullif :public Item_func_hybrid_field_type
     See also Item_func_nullif::fix_length_and_dec().
   */
   Item_func_nullif(THD *thd, Item *a, Item *b):
-    Item_func_hybrid_field_type(thd, a, b, a),
+    Item_func_case_expression(thd, a, b, a),
     m_cache(NULL),
     m_arg0(NULL)
   { arg_count--; }
@@ -2031,7 +2029,7 @@ class Predicant_to_list_comparator
   function and only comparators for there result types are used.
 */
 
-class Item_func_case :public Item_func_hybrid_field_type,
+class Item_func_case :public Item_func_case_expression,
                       public Predicant_to_list_comparator
 {
   int first_expr_num, else_expr_num;
diff --git a/sql/item_func.h b/sql/item_func.h
index 71fa904..1774e54 100644
--- a/sql/item_func.h
+++ b/sql/item_func.h
@@ -636,6 +636,38 @@ class Item_func_hybrid_field_type: public Item_hybrid_func
 };
 
 
+/*
+  This class resembles SQL standard CASE-alike expressions:
+  CASE and its abbreviations COALESCE, NULLIF, IFNULL, IF.
+ 
+  <case expression> ::=   <case abbreviation>
+                        | <case specification>
+*/
+class Item_func_case_expression: public Item_func_hybrid_field_type
+{
+public:
+  Item_func_case_expression(THD *thd)
+   :Item_func_hybrid_field_type(thd)
+  { }
+  Item_func_case_expression(THD *thd, Item *a)
+   :Item_func_hybrid_field_type(thd, a)
+  { }
+  Item_func_case_expression(THD *thd, Item *a, Item *b)
+   :Item_func_hybrid_field_type(thd, a, b)
+  { }
+  Item_func_case_expression(THD *thd, Item *a, Item *b, Item *c)
+   :Item_func_hybrid_field_type(thd, a, b, c)
+  { }
+  Item_func_case_expression(THD *thd, List<Item> &list):
+    Item_func_hybrid_field_type(thd, list)
+  { }
+  Field *create_tmp_field(bool group, TABLE *table)
+  { return tmp_table_field_from_field_type(table); }
+  Field *create_field_for_create_select(TABLE *table)
+  { return tmp_table_field_from_field_type(table); }
+};
+
+
 class Item_func_numhybrid: public Item_func_hybrid_field_type
 {
 protected:
@@ -1441,6 +1473,10 @@ class Item_func_min_max :public Item_hybrid_func
   Item_func_min_max(THD *thd, List<Item> &list, int cmp_sign_arg):
     Item_hybrid_func(thd, list), cmp_sign(cmp_sign_arg)
   {}
+  Field *create_tmp_field(bool group, TABLE *table)
+  { return tmp_table_field_from_field_type(table); }
+  Field *create_field_for_create_select(TABLE *table)
+  { return tmp_table_field_from_field_type(table); }
   String *val_str_native(String *str);
   double val_real_native();
   longlong val_int_native();
diff --git a/sql/sql_type.cc b/sql/sql_type.cc
index 9b4aa61..60f7c9d 100644
--- a/sql/sql_type.cc
+++ b/sql/sql_type.cc
@@ -381,6 +381,15 @@ Type_handler_hybrid_field_type::aggregate_for_result(const Type_handler *other)
 }
 
 
+const Type_handler *
+Type_handler::bit_and_non_bit_mixture_handler(uint max_char_length)
+{
+  if (max_char_length <= MY_INT32_NUM_DECIMAL_DIGITS)
+    return &type_handler_long;
+  return &type_handler_longlong;
+}
+
+
 /**
   @brief Aggregates field types from the array of items.
 
@@ -420,6 +429,8 @@ Type_handler_hybrid_field_type::aggregate_for_result(const char *funcname,
                                                      Item **items, uint nitems,
                                                      bool treat_bit_as_number)
 {
+  bool bit_and_non_bit_mixture_found= false;
+  uint32 max_char_length;
   if (!nitems || items[0]->result_type() == ROW_RESULT)
   {
     DBUG_ASSERT(0);
@@ -427,12 +438,15 @@ Type_handler_hybrid_field_type::aggregate_for_result(const char *funcname,
     return true;
   }
   set_handler(items[0]->type_handler());
+  max_char_length= items[0]->max_char_length();
   for (uint i= 1 ; i < nitems ; i++)
   {
     const Type_handler *cur= items[i]->type_handler();
+    set_if_bigger(max_char_length, items[i]->max_char_length());
     if (treat_bit_as_number &&
         ((type_handler() == &type_handler_bit) ^ (cur == &type_handler_bit)))
     {
+      bit_and_non_bit_mixture_found= true;
       if (type_handler() == &type_handler_bit)
         set_handler(&type_handler_longlong); // BIT + non-BIT
       else
@@ -445,6 +459,8 @@ Type_handler_hybrid_field_type::aggregate_for_result(const char *funcname,
       return true;
     }
   }
+  if (bit_and_non_bit_mixture_found && type_handler() == &type_handler_longlong)
+    set_handler(Type_handler::bit_and_non_bit_mixture_handler(max_char_length));
   return false;
 }
 
@@ -596,7 +612,9 @@ Type_handler_hybrid_field_type::aggregate_for_min_max(const Type_handler *h)
   }
   else
   {
-    m_type_handler= &type_handler_double;
+    // Preserve FLOAT if two FLOATs, set to DOUBLE otherwise.
+    if (m_type_handler != &type_handler_float || h != &type_handler_float)
+      m_type_handler= &type_handler_double;
   }
   return false;
 }
@@ -606,12 +624,19 @@ bool
 Type_handler_hybrid_field_type::aggregate_for_min_max(const char *funcname,
                                                       Item **items, uint nitems)
 {
+  bool bit_and_non_bit_mixture_found= false;
+  uint32 max_char_length;
   // LEAST/GREATEST require at least two arguments
   DBUG_ASSERT(nitems > 1);
   set_handler(items[0]->type_handler());
+  max_char_length= items[0]->max_char_length();
   for (uint i= 1; i < nitems;  i++)
   {
     const Type_handler *cur= items[i]->type_handler();
+    set_if_bigger(max_char_length, items[i]->max_char_length());
+    // Check if BIT + non-BIT, or non-BIT + BIT
+    bit_and_non_bit_mixture_found|= (m_type_handler == &type_handler_bit) !=
+                                    (cur == &type_handler_bit);
     if (aggregate_for_min_max(cur))
     {
       my_error(ER_ILLEGAL_PARAMETER_DATA_TYPES2_FOR_OPERATION, MYF(0),
@@ -619,6 +644,8 @@ Type_handler_hybrid_field_type::aggregate_for_min_max(const char *funcname,
       return true;
     }
   }
+  if (bit_and_non_bit_mixture_found && type_handler() == &type_handler_longlong)
+    set_handler(Type_handler::bit_and_non_bit_mixture_handler(max_char_length));
   return false;
 }
 
diff --git a/sql/sql_type.h b/sql/sql_type.h
index e3e86ae..478559b 100644
--- a/sql/sql_type.h
+++ b/sql/sql_type.h
@@ -406,6 +406,7 @@ class Type_handler
 public:
   static const Type_handler *blob_type_handler(uint max_octet_length);
   static const Type_handler *string_type_handler(uint max_octet_length);
+  static const Type_handler *bit_and_non_bit_mixture_handler(uint max_char_len);
   /**
     Return a string type handler for Item
     If too_big_for_varchar() returns a BLOB variant, according to length.