diff options
author | Alexander Barkov <bar@mariadb.com> | 2020-08-03 10:53:06 +0400 |
---|---|---|
committer | Alexander Barkov <bar@mariadb.com> | 2020-08-03 10:53:06 +0400 |
commit | 9840bb21ef683afd59a30a79ebc5bd5ad33a7c1a (patch) | |
tree | d3a79d95550a05c8381855adaa45bc5f2941619d /sql | |
parent | 97f7bfcebcc17df337bd97c0a9535a906547016d (diff) | |
download | mariadb-git-9840bb21ef683afd59a30a79ebc5bd5ad33a7c1a.tar.gz |
MDEV-23366 ROUND(18446744073709551615,rand()*0) returns a wrong result
Changing that in case of *INT and hex hybrid input:
- ROUND(x,NULL) creates a column with the same type as x.
The old code created a DOUBLE column, which was not relevant at all.
This change simplifies the code a lot.
- ROUND(x,non_constant) creates a column of the INT, BIGINT or DECIMAL
data type (depending on the exact type of x).
The old code created a column of the DOUBLE data type,
which lead to precision loss. Hence MDEV-23366.
- ROUND(bigint_30,negative_constant) creates a column of the DECIMAL(30,0)
data type. The old code created DECIMAL(29,0), which looked strange:
the data type promoted to a higher one, but max length reduced.
Now the length attribute is preserved.
Diffstat (limited to 'sql')
-rw-r--r-- | sql/item_func.cc | 87 | ||||
-rw-r--r-- | sql/item_func.h | 2 | ||||
-rw-r--r-- | sql/sql_type.cc | 2 |
3 files changed, 44 insertions, 47 deletions
diff --git a/sql/item_func.cc b/sql/item_func.cc index a98d2db921c..8000d7f6a1d 100644 --- a/sql/item_func.cc +++ b/sql/item_func.cc @@ -2446,6 +2446,20 @@ void Item_func_round::fix_arg_datetime() } +bool Item_func_round::test_if_length_can_increase() +{ + if (truncate) + return false; + if (args[1]->const_item() && !args[1]->is_expensive()) + { + // Length can increase in some cases: e.g. ROUND(9,-1) -> 10. + Longlong_hybrid val1= args[1]->to_longlong_hybrid(); + return !args[1]->null_value && val1.neg(); + } + return true; // ROUND(x,n), where n is not a constant. +} + + /** Calculate data type and attributes for INT-alike input. @@ -2468,56 +2482,37 @@ void Item_func_round::fix_arg_int(const Type_handler *preferred, bool use_decimal_on_length_increase) { DBUG_ASSERT(args[0]->decimals == 0); - if (args[1]->const_item()) + + Type_std_attributes::set(preferred_attrs); + if (!test_if_length_can_increase()) { - Longlong_hybrid val1= args[1]->to_longlong_hybrid(); - if (args[1]->null_value) - fix_length_and_dec_double(NOT_FIXED_DEC); - else if (truncate || - !val1.neg() /* ROUND(x, n>=0) */ || - 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()); - if (preferred) - { - Type_std_attributes::set(preferred_attrs); - if (!length_can_increase) - { - // Preserve the exact data type and attributes - set_handler(preferred); - } - else - { - max_length++; - if (use_decimal_on_length_increase) - set_handler(&type_handler_newdecimal); - else - 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; - if (length_can_increase && use_decimal_on_length_increase) - set_handler(&type_handler_newdecimal); - else - set_handler(type_handler_long_or_longlong()); - } - } + // Preserve the exact data type and attributes + set_handler(preferred); + } + else + { + max_length++; + if (use_decimal_on_length_increase) + set_handler(&type_handler_newdecimal); else - fix_length_and_dec_decimal(val1.to_uint(DECIMAL_MAX_SCALE)); + set_handler(type_handler_long_or_longlong()); } +} + + +void Item_func_round::fix_arg_hex_hybrid() +{ + DBUG_ASSERT(args[0]->decimals == 0); + DBUG_ASSERT(args[0]->decimal_precision() < DECIMAL_LONGLONG_DIGITS); + DBUG_ASSERT(args[0]->unsigned_flag); // no needs to add sign length + bool length_can_increase= test_if_length_can_increase(); + max_length= args[0]->decimal_precision() + MY_TEST(length_can_increase); + unsigned_flag= true; + decimals= 0; + if (length_can_increase && args[0]->max_length >= 8) + set_handler(&type_handler_newdecimal); else - fix_length_and_dec_double(args[0]->decimals); + set_handler(type_handler_long_or_longlong()); } diff --git a/sql/item_func.h b/sql/item_func.h index ea00a0cb033..24601983f86 100644 --- a/sql/item_func.h +++ b/sql/item_func.h @@ -1754,6 +1754,7 @@ class Item_func_round :public Item_func_hybrid_field_type bool truncate; void fix_length_and_dec_decimal(uint decimals_to_set); void fix_length_and_dec_double(uint decimals_to_set); + bool test_if_length_can_increase(); public: Item_func_round(THD *thd, Item *a, Item *b, bool trunc_arg) :Item_func_hybrid_field_type(thd, a, b), truncate(trunc_arg) {} @@ -1777,6 +1778,7 @@ public: void fix_arg_int(const Type_handler *preferred, const Type_std_attributes *preferred_attributes, bool use_decimal_on_length_increase); + void fix_arg_hex_hybrid(); 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 e9d30295406..0b333d44b77 100644 --- a/sql/sql_type.cc +++ b/sql/sql_type.cc @@ -5690,7 +5690,7 @@ bool Type_handler_year:: bool Type_handler_hex_hybrid:: Item_func_round_fix_length_and_dec(Item_func_round *item) const { - item->fix_arg_int(NULL, NULL, item->arguments()[0]->max_length >= 8); + item->fix_arg_hex_hybrid(); return false; } |