summaryrefslogtreecommitdiff
path: root/sql/item_func.cc
diff options
context:
space:
mode:
authorAlexander Barkov <bar@mariadb.com>2020-07-30 14:30:21 +0400
committerAlexander Barkov <bar@mariadb.com>2020-07-31 07:41:30 +0400
commita874b6c4459a2dd028d6c4a15e43eeb556183de0 (patch)
tree8bdf3954c5be21423cb11bde76823de9b27e9aad /sql/item_func.cc
parentc3958ae407016d7dde8b04ffbacf949c62b2e4eb (diff)
downloadmariadb-git-a874b6c4459a2dd028d6c4a15e43eeb556183de0.tar.gz
MDEV-23337 Rounding functions create a wrong data type for integer input
1. Fixing ROUND(x) and TRUNCATE(x,0) with TINYINT, SMALLINT, MEDIUMINT, BIGINT input to preserve the exact data type of the argument when it's possible. 2. Fixing FLOOR(x) and CEILING(x) with TINYINT, SMALLINT, MEDIUMINT, BIGINT to preserve the exact data type of the argument. 3. Adding dedicated Type_handler_year::Item_func_round_fix_length_and_dec() to easier handle ROUND(x) and TRUNCATE(x,y) for the YEAR(2) and YEAR(4) input. They still return INT(2) UNSIGNED and INT(4) UNSIGNED correspondingly, as before.
Diffstat (limited to 'sql/item_func.cc')
-rw-r--r--sql/item_func.cc50
1 files changed, 44 insertions, 6 deletions
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));