diff --git a/mysql-test/main/metadata.result b/mysql-test/main/metadata.result index 1f917bfe5ab..0704f5b8ae8 100644 --- a/mysql-test/main/metadata.result +++ b/mysql-test/main/metadata.result @@ -715,16 +715,16 @@ CEILING(11111111), CEILING(111111111), CEILING(1111111111) LIMIT 0; Catalog Database Table Table_alias Column Column_alias Type Length Max length Is_null Flags Decimals Charsetnr -def CEILING(1) 3 3 0 N 32897 0 63 -def CEILING(11) 3 4 0 N 32897 0 63 -def CEILING(111) 3 5 0 N 32897 0 63 -def CEILING(1111) 3 6 0 N 32897 0 63 -def CEILING(11111) 3 7 0 N 32897 0 63 -def CEILING(111111) 3 8 0 N 32897 0 63 -def CEILING(1111111) 3 9 0 N 32897 0 63 -def CEILING(11111111) 8 10 0 N 32897 0 63 -def CEILING(111111111) 8 11 0 N 32897 0 63 -def CEILING(1111111111) 8 12 0 N 32897 0 63 +def CEILING(1) 3 1 0 N 32897 0 63 +def CEILING(11) 3 2 0 N 32897 0 63 +def CEILING(111) 3 3 0 N 32897 0 63 +def CEILING(1111) 3 4 0 N 32897 0 63 +def CEILING(11111) 3 5 0 N 32897 0 63 +def CEILING(111111) 3 6 0 N 32897 0 63 +def CEILING(1111111) 3 7 0 N 32897 0 63 +def CEILING(11111111) 3 8 0 N 32897 0 63 +def CEILING(111111111) 3 9 0 N 32897 0 63 +def CEILING(1111111111) 8 10 0 N 32897 0 63 CEILING(1) CEILING(11) CEILING(111) CEILING(1111) CEILING(11111) CEILING(111111) CEILING(1111111) CEILING(11111111) CEILING(111111111) CEILING(1111111111) SELECT FLOOR(1), @@ -738,16 +738,16 @@ FLOOR(11111111), FLOOR(111111111), FLOOR(1111111111) LIMIT 0; Catalog Database Table Table_alias Column Column_alias Type Length Max length Is_null Flags Decimals Charsetnr -def FLOOR(1) 3 3 0 N 32897 0 63 -def FLOOR(11) 3 4 0 N 32897 0 63 -def FLOOR(111) 3 5 0 N 32897 0 63 -def FLOOR(1111) 3 6 0 N 32897 0 63 -def FLOOR(11111) 3 7 0 N 32897 0 63 -def FLOOR(111111) 3 8 0 N 32897 0 63 -def FLOOR(1111111) 3 9 0 N 32897 0 63 -def FLOOR(11111111) 8 10 0 N 32897 0 63 -def FLOOR(111111111) 8 11 0 N 32897 0 63 -def FLOOR(1111111111) 8 12 0 N 32897 0 63 +def FLOOR(1) 3 1 0 N 32897 0 63 +def FLOOR(11) 3 2 0 N 32897 0 63 +def FLOOR(111) 3 3 0 N 32897 0 63 +def FLOOR(1111) 3 4 0 N 32897 0 63 +def FLOOR(11111) 3 5 0 N 32897 0 63 +def FLOOR(111111) 3 6 0 N 32897 0 63 +def FLOOR(1111111) 3 7 0 N 32897 0 63 +def FLOOR(11111111) 3 8 0 N 32897 0 63 +def FLOOR(111111111) 3 9 0 N 32897 0 63 +def FLOOR(1111111111) 8 10 0 N 32897 0 63 FLOOR(1) FLOOR(11) FLOOR(111) FLOOR(1111) FLOOR(11111) FLOOR(111111) FLOOR(1111111) FLOOR(11111111) FLOOR(111111111) FLOOR(1111111111) SELECT ROUND(1), diff --git a/mysql-test/main/type_int.result b/mysql-test/main/type_int.result index 0d744f6a97b..609889cd5e8 100644 --- a/mysql-test/main/type_int.result +++ b/mysql-test/main/type_int.result @@ -412,5 +412,221 @@ COUNT(*) 0 DROP TABLE t1; # +# MDEV-23337 Rounding functions create a wrong data type for numeric input +# +CREATE PROCEDURE p1(t VARCHAR(64)) +BEGIN +SELECT t AS ``; +EXECUTE IMMEDIATE REPLACE('CREATE TABLE t1 (a TYPE)', 'TYPE', t); +INSERT IGNORE INTO t1 VALUES (-100000000000000000000000000000); +INSERT IGNORE INTO t1 VALUES (100000000000000000000000000000); +CREATE TABLE t2 AS SELECT a, ROUND(a), TRUNCATE(a,0), FLOOR(a), CEILING(a) FROM t1; +SHOW CREATE TABLE t2; +SELECT * FROM t2; +DROP TABLE t1, t2; +END; +$$ +CALL p1('tinyint'); + tinyint +Table t2 +Create Table CREATE TABLE `t2` ( + `a` tinyint(4) DEFAULT NULL, + `ROUND(a)` tinyint(4) DEFAULT NULL, + `TRUNCATE(a,0)` tinyint(4) DEFAULT NULL, + `FLOOR(a)` tinyint(4) DEFAULT NULL, + `CEILING(a)` tinyint(4) DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +a -128 +ROUND(a) -128 +TRUNCATE(a,0) -128 +FLOOR(a) -128 +CEILING(a) -128 +a 127 +ROUND(a) 127 +TRUNCATE(a,0) 127 +FLOOR(a) 127 +CEILING(a) 127 +CALL p1('smallint'); + smallint +Table t2 +Create Table CREATE TABLE `t2` ( + `a` smallint(6) DEFAULT NULL, + `ROUND(a)` smallint(6) DEFAULT NULL, + `TRUNCATE(a,0)` smallint(6) DEFAULT NULL, + `FLOOR(a)` smallint(6) DEFAULT NULL, + `CEILING(a)` smallint(6) DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +a -32768 +ROUND(a) -32768 +TRUNCATE(a,0) -32768 +FLOOR(a) -32768 +CEILING(a) -32768 +a 32767 +ROUND(a) 32767 +TRUNCATE(a,0) 32767 +FLOOR(a) 32767 +CEILING(a) 32767 +CALL p1('mediumint'); + mediumint +Table t2 +Create Table CREATE TABLE `t2` ( + `a` mediumint(9) DEFAULT NULL, + `ROUND(a)` mediumint(9) DEFAULT NULL, + `TRUNCATE(a,0)` mediumint(9) DEFAULT NULL, + `FLOOR(a)` mediumint(9) DEFAULT NULL, + `CEILING(a)` mediumint(9) DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +a -8388608 +ROUND(a) -8388608 +TRUNCATE(a,0) -8388608 +FLOOR(a) -8388608 +CEILING(a) -8388608 +a 8388607 +ROUND(a) 8388607 +TRUNCATE(a,0) 8388607 +FLOOR(a) 8388607 +CEILING(a) 8388607 +CALL p1('int'); + int +Table t2 +Create Table CREATE TABLE `t2` ( + `a` int(11) DEFAULT NULL, + `ROUND(a)` int(11) DEFAULT NULL, + `TRUNCATE(a,0)` int(11) DEFAULT NULL, + `FLOOR(a)` int(11) DEFAULT NULL, + `CEILING(a)` int(11) DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +a -2147483648 +ROUND(a) -2147483648 +TRUNCATE(a,0) -2147483648 +FLOOR(a) -2147483648 +CEILING(a) -2147483648 +a 2147483647 +ROUND(a) 2147483647 +TRUNCATE(a,0) 2147483647 +FLOOR(a) 2147483647 +CEILING(a) 2147483647 +CALL p1('bigint'); + bigint +Table t2 +Create Table CREATE TABLE `t2` ( + `a` bigint(20) DEFAULT NULL, + `ROUND(a)` bigint(20) DEFAULT NULL, + `TRUNCATE(a,0)` bigint(20) DEFAULT NULL, + `FLOOR(a)` bigint(20) DEFAULT NULL, + `CEILING(a)` bigint(20) DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +a -9223372036854775808 +ROUND(a) -9223372036854775808 +TRUNCATE(a,0) -9223372036854775808 +FLOOR(a) -9223372036854775808 +CEILING(a) -9223372036854775808 +a 9223372036854775807 +ROUND(a) 9223372036854775807 +TRUNCATE(a,0) 9223372036854775807 +FLOOR(a) 9223372036854775807 +CEILING(a) 9223372036854775807 +CALL p1('tinyint unsigned'); + tinyint unsigned +Table t2 +Create Table CREATE TABLE `t2` ( + `a` tinyint(3) unsigned DEFAULT NULL, + `ROUND(a)` tinyint(3) unsigned DEFAULT NULL, + `TRUNCATE(a,0)` tinyint(3) unsigned DEFAULT NULL, + `FLOOR(a)` tinyint(3) unsigned DEFAULT NULL, + `CEILING(a)` tinyint(3) unsigned DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +a 0 +ROUND(a) 0 +TRUNCATE(a,0) 0 +FLOOR(a) 0 +CEILING(a) 0 +a 255 +ROUND(a) 255 +TRUNCATE(a,0) 255 +FLOOR(a) 255 +CEILING(a) 255 +CALL p1('smallint unsigned'); + smallint unsigned +Table t2 +Create Table CREATE TABLE `t2` ( + `a` smallint(5) unsigned DEFAULT NULL, + `ROUND(a)` smallint(5) unsigned DEFAULT NULL, + `TRUNCATE(a,0)` smallint(5) unsigned DEFAULT NULL, + `FLOOR(a)` smallint(5) unsigned DEFAULT NULL, + `CEILING(a)` smallint(5) unsigned DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +a 0 +ROUND(a) 0 +TRUNCATE(a,0) 0 +FLOOR(a) 0 +CEILING(a) 0 +a 65535 +ROUND(a) 65535 +TRUNCATE(a,0) 65535 +FLOOR(a) 65535 +CEILING(a) 65535 +CALL p1('mediumint unsigned'); + mediumint unsigned +Table t2 +Create Table CREATE TABLE `t2` ( + `a` mediumint(8) unsigned DEFAULT NULL, + `ROUND(a)` mediumint(8) unsigned DEFAULT NULL, + `TRUNCATE(a,0)` mediumint(8) unsigned DEFAULT NULL, + `FLOOR(a)` mediumint(8) unsigned DEFAULT NULL, + `CEILING(a)` mediumint(8) unsigned DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +a 0 +ROUND(a) 0 +TRUNCATE(a,0) 0 +FLOOR(a) 0 +CEILING(a) 0 +a 16777215 +ROUND(a) 16777215 +TRUNCATE(a,0) 16777215 +FLOOR(a) 16777215 +CEILING(a) 16777215 +CALL p1('int unsigned'); + int unsigned +Table t2 +Create Table CREATE TABLE `t2` ( + `a` int(10) unsigned DEFAULT NULL, + `ROUND(a)` int(10) unsigned DEFAULT NULL, + `TRUNCATE(a,0)` int(10) unsigned DEFAULT NULL, + `FLOOR(a)` int(10) unsigned DEFAULT NULL, + `CEILING(a)` int(10) unsigned DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +a 0 +ROUND(a) 0 +TRUNCATE(a,0) 0 +FLOOR(a) 0 +CEILING(a) 0 +a 4294967295 +ROUND(a) 4294967295 +TRUNCATE(a,0) 4294967295 +FLOOR(a) 4294967295 +CEILING(a) 4294967295 +CALL p1('bigint unsigned'); + bigint unsigned +Table t2 +Create Table CREATE TABLE `t2` ( + `a` bigint(20) unsigned DEFAULT NULL, + `ROUND(a)` bigint(20) unsigned DEFAULT NULL, + `TRUNCATE(a,0)` bigint(20) unsigned DEFAULT NULL, + `FLOOR(a)` bigint(20) unsigned DEFAULT NULL, + `CEILING(a)` bigint(20) unsigned DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +a 0 +ROUND(a) 0 +TRUNCATE(a,0) 0 +FLOOR(a) 0 +CEILING(a) 0 +a 18446744073709551615 +ROUND(a) 18446744073709551615 +TRUNCATE(a,0) 18446744073709551615 +FLOOR(a) 18446744073709551615 +CEILING(a) 18446744073709551615 +DROP PROCEDURE p1; +# # End of 10.4 tests # diff --git a/mysql-test/main/type_int.test b/mysql-test/main/type_int.test index 662be57abd3..4822f22521f 100644 --- a/mysql-test/main/type_int.test +++ b/mysql-test/main/type_int.test @@ -293,6 +293,41 @@ SELECT COUNT(*) FROM t1 WHERE a XOR a; DROP TABLE t1; +--echo # +--echo # MDEV-23337 Rounding functions create a wrong data type for numeric input +--echo # + +DELIMITER $$; +CREATE PROCEDURE p1(t VARCHAR(64)) +BEGIN + SELECT t AS ``; + EXECUTE IMMEDIATE REPLACE('CREATE TABLE t1 (a TYPE)', 'TYPE', t); + INSERT IGNORE INTO t1 VALUES (-100000000000000000000000000000); + INSERT IGNORE INTO t1 VALUES (100000000000000000000000000000); + CREATE TABLE t2 AS SELECT a, ROUND(a), TRUNCATE(a,0), FLOOR(a), CEILING(a) FROM t1; + SHOW CREATE TABLE t2; + SELECT * FROM t2; + DROP TABLE t1, t2; +END; +$$ +DELIMITER ;$$ + +--vertical_results +CALL p1('tinyint'); +CALL p1('smallint'); +CALL p1('mediumint'); +CALL p1('int'); +CALL p1('bigint'); + +CALL p1('tinyint unsigned'); +CALL p1('smallint unsigned'); +CALL p1('mediumint unsigned'); +CALL p1('int unsigned'); +CALL p1('bigint unsigned'); +--horizontal_results + +DROP PROCEDURE p1; + --echo # --echo # End of 10.4 tests --echo # diff --git a/mysql-test/main/type_year.result b/mysql-test/main/type_year.result index 71285418588..583a2818e02 100644 --- a/mysql-test/main/type_year.result +++ b/mysql-test/main/type_year.result @@ -585,5 +585,32 @@ COALESCE(a) DATE(COALESCE(a)) NULL NULL DROP TABLE t1; # +# MDEV-23337 Rounding functions create a wrong data type for numeric input +# +CREATE TABLE t1 (a YEAR(2)); +Warnings: +Note 1287 'YEAR(2)' is deprecated and will be removed in a future release. Please use YEAR(4) instead +CREATE TABLE t2 AS SELECT a, ROUND(a), TRUNCATE(a,0), FLOOR(a), CEILING(a) FROM t1; +Warnings: +Note 1287 'YEAR(2)' is deprecated and will be removed in a future release. Please use YEAR(4) instead +DESC t2; +Field Type Null Key Default Extra +a year(2) YES NULL +ROUND(a) int(2) unsigned YES NULL +TRUNCATE(a,0) int(2) unsigned YES NULL +FLOOR(a) int(2) unsigned YES NULL +CEILING(a) int(2) unsigned YES NULL +DROP TABLE t2,t1; +CREATE TABLE t1 (a YEAR(4)); +CREATE TABLE t2 AS SELECT a, ROUND(a), TRUNCATE(a,0), FLOOR(a), CEILING(a) FROM t1; +DESC t2; +Field Type Null Key Default Extra +a year(4) YES NULL +ROUND(a) int(4) unsigned YES NULL +TRUNCATE(a,0) int(4) unsigned YES NULL +FLOOR(a) int(4) unsigned YES NULL +CEILING(a) int(4) unsigned YES NULL +DROP TABLE t2,t1; +# # End of 10.4 tests # diff --git a/mysql-test/main/type_year.test b/mysql-test/main/type_year.test index 3c578b3ab59..19692148146 100644 --- a/mysql-test/main/type_year.test +++ b/mysql-test/main/type_year.test @@ -326,6 +326,21 @@ INSERT INTO t1 VALUES (NULL); SELECT COALESCE(a), DATE(COALESCE(a)) FROM t1; DROP TABLE t1; +--echo # +--echo # MDEV-23337 Rounding functions create a wrong data type for numeric input +--echo # + +CREATE TABLE t1 (a YEAR(2)); +CREATE TABLE t2 AS SELECT a, ROUND(a), TRUNCATE(a,0), FLOOR(a), CEILING(a) FROM t1; +DESC t2; +DROP TABLE t2,t1; + +CREATE TABLE t1 (a YEAR(4)); +CREATE TABLE t2 AS SELECT a, ROUND(a), TRUNCATE(a,0), FLOOR(a), CEILING(a) FROM t1; +DESC t2; +DROP TABLE t2,t1; + + --echo # --echo # End of 10.4 tests --echo # diff --git a/sql/item_func.cc b/sql/item_func.cc index 9f934a95f4d..8e36804888b 100644 --- a/sql/item_func.cc +++ b/sql/item_func.cc @@ -2446,8 +2446,24 @@ void Item_func_round::fix_arg_datetime() } -void Item_func_round::fix_arg_int() +/** + Calculate data type and attributes for INT-alike input. + + @param [IN] preferred - The preferred data type handler for simple cases + such as ROUND(x) and TRUNCATE(x,0), when the input + is short enough to fit into an integer type + (without extending to DECIMAL). + - If `preferred` is not NULL, then the code tries + to preserve the given data type handler and + data type attributes of the argument. + - If `preferred` is NULL, then the code fully + calculates attributes using + args[0]->decimal_precision() and chooses between + INT and BIGINT, depending on attributes. +*/ +void Item_func_round::fix_arg_int(const Type_handler *preferred) { + DBUG_ASSERT(args[0]->decimals == 0); if (args[1]->const_item()) { Longlong_hybrid val1= args[1]->to_longlong_hybrid(); @@ -2456,13 +2472,35 @@ void Item_func_round::fix_arg_int() else if ((!val1.to_uint(DECIMAL_MAX_SCALE) && truncate) || args[0]->decimal_precision() < DECIMAL_LONGLONG_DIGITS) { + // Here we can keep INT_RESULT // Length can increase in some cases: ROUND(9,-1) -> 10 int length_can_increase= MY_TEST(!truncate && val1.neg()); - max_length= args[0]->decimal_precision() + length_can_increase; - // Here we can keep INT_RESULT - unsigned_flag= args[0]->unsigned_flag; - decimals= 0; - set_handler(type_handler_long_or_longlong()); + if (preferred) + { + Type_std_attributes::set(args[0]); + if (!length_can_increase) + { + // Preserve the exact data type and attributes + set_handler(preferred); + } + else + { + max_length++; + set_handler(type_handler_long_or_longlong()); + } + } + else + { + /* + This branch is currently used for hex hybrid only. + It's known to be unsigned. So sign length is 0. + */ + DBUG_ASSERT(args[0]->unsigned_flag); // no needs to add sign length + max_length= args[0]->decimal_precision() + length_can_increase; + unsigned_flag= true; + decimals= 0; + set_handler(type_handler_long_or_longlong()); + } } else fix_length_and_dec_decimal(val1.to_uint(DECIMAL_MAX_SCALE)); diff --git a/sql/item_func.h b/sql/item_func.h index d1ebf98ccda..549e576dbd3 100644 --- a/sql/item_func.h +++ b/sql/item_func.h @@ -1774,7 +1774,7 @@ public: return NULL; } void fix_arg_decimal(); - void fix_arg_int(); + void fix_arg_int(const Type_handler *preferred); void fix_arg_double(); void fix_arg_time(); void fix_arg_datetime(); diff --git a/sql/sql_type.cc b/sql/sql_type.cc index 88afc51564d..ee25a6c4fc5 100644 --- a/sql/sql_type.cc +++ b/sql/sql_type.cc @@ -5659,7 +5659,15 @@ bool Type_handler_row:: bool Type_handler_int_result:: Item_func_round_fix_length_and_dec(Item_func_round *item) const { - item->fix_arg_int(); + item->fix_arg_int(this); + return false; +} + + +bool Type_handler_year:: + Item_func_round_fix_length_and_dec(Item_func_round *item) const +{ + item->fix_arg_int(&type_handler_long); // 10.5 merge: fix to type_handler_ulong return false; } @@ -5667,7 +5675,7 @@ bool Type_handler_int_result:: bool Type_handler_hex_hybrid:: Item_func_round_fix_length_and_dec(Item_func_round *item) const { - item->fix_arg_int(); + item->fix_arg_int(NULL); return false; } @@ -5766,7 +5774,17 @@ bool Type_handler_row:: bool Type_handler_int_result:: Item_func_int_val_fix_length_and_dec(Item_func_int_val *item) const { - item->fix_length_and_dec_int_or_decimal(); + item->Type_std_attributes::set(item->arguments()[0]); + item->set_handler(this); + return false; +} + + +bool Type_handler_year:: + Item_func_int_val_fix_length_and_dec(Item_func_int_val *item) const +{ + item->Type_std_attributes::set(item->arguments()[0]); + item->set_handler(&type_handler_long); return false; } diff --git a/sql/sql_type.h b/sql/sql_type.h index d6828832bf1..dd3319dce5c 100644 --- a/sql/sql_type.h +++ b/sql/sql_type.h @@ -5124,6 +5124,8 @@ public: const Column_definition_attributes *attr, uint32 flags) const; Item_cache *Item_get_cache(THD *thd, const Item *item) const; + bool Item_func_round_fix_length_and_dec(Item_func_round *) const; + bool Item_func_int_val_fix_length_and_dec(Item_func_int_val *) const; void Item_get_date(THD *thd, Item *item, Temporal::Warn *warn, MYSQL_TIME *ltime, date_mode_t fuzzydate) const; void Item_func_hybrid_field_type_get_date(THD *,