Commit 9840bb21 authored by Alexander Barkov's avatar Alexander Barkov

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.
parent 97f7bfce
...@@ -348,7 +348,6 @@ truncate(4, cast(-2 as unsigned)) truncate(4, 18446744073709551614) truncate(4, ...@@ -348,7 +348,6 @@ truncate(4, cast(-2 as unsigned)) truncate(4, 18446744073709551614) truncate(4,
4 4 0 4 4 0
Warnings: Warnings:
Note 1105 Cast to unsigned converted negative integer to it's positive complement Note 1105 Cast to unsigned converted negative integer to it's positive complement
Note 1105 Cast to unsigned converted negative integer to it's positive complement
select round(10000000000000000000, -19), truncate(10000000000000000000, -19); select round(10000000000000000000, -19), truncate(10000000000000000000, -19);
round(10000000000000000000, -19) truncate(10000000000000000000, -19) round(10000000000000000000, -19) truncate(10000000000000000000, -19)
10000000000000000000 10000000000000000000 10000000000000000000 10000000000000000000
...@@ -1784,7 +1783,7 @@ ROUND(10e0,NULL) AS c3; ...@@ -1784,7 +1783,7 @@ ROUND(10e0,NULL) AS c3;
SHOW CREATE TABLE t1; SHOW CREATE TABLE t1;
Table Create Table Table Create Table
t1 CREATE TABLE `t1` ( t1 CREATE TABLE `t1` (
`c1` double DEFAULT NULL, `c1` int(2) DEFAULT NULL,
`c2` double DEFAULT NULL, `c2` double DEFAULT NULL,
`c3` double DEFAULT NULL `c3` double DEFAULT NULL
) ENGINE=MyISAM DEFAULT CHARSET=latin1 ) ENGINE=MyISAM DEFAULT CHARSET=latin1
......
...@@ -195,5 +195,42 @@ ROUND(0xFFFFFFFFFFFFFFFF,-10) ROUND(0xFFFFFFFFFFFFFFFF,-11) ...@@ -195,5 +195,42 @@ ROUND(0xFFFFFFFFFFFFFFFF,-10) ROUND(0xFFFFFFFFFFFFFFFF,-11)
18446744070000000000 18446744100000000000 18446744070000000000 18446744100000000000
DROP TABLE t1; DROP TABLE t1;
# #
# MDEV-23366 ROUND(18446744073709551615,rand()*0) returns a wrong result
#
SELECT
ROUND(0xFFFFFFFFFFFFFFFF,NULL) AS c1,
ROUND(0xFFFFFFFFFFFFFFFF,rand()*0) AS c2,
ROUND(0xFFFFFFFFFFFFFFFF,-1) AS c3,
ROUND(0xFFFFFFFFFFFFFFFF,-19) AS c4,
ROUND(0xFFFFFFFFFFFFFFFF,rand()*0-19) AS c5;
c1 NULL
c2 18446744073709551615
c3 18446744073709551620
c4 20000000000000000000
c5 20000000000000000000
CREATE OR REPLACE TABLE t1 AS
SELECT
ROUND(0xFFFFFFFFFFFFFFFF,NULL) AS c1,
ROUND(0xFFFFFFFFFFFFFFFF,rand()*0) AS c2,
ROUND(0xFFFFFFFFFFFFFFFF,-1) AS c3,
ROUND(0xFFFFFFFFFFFFFFFF,-19) AS c4,
ROUND(0xFFFFFFFFFFFFFFFF,rand()*0-19) AS c5;
SELECT * FROM t1;
c1 NULL
c2 18446744073709551615
c3 18446744073709551620
c4 20000000000000000000
c5 20000000000000000000
SHOW CREATE TABLE t1;
Table t1
Create Table CREATE TABLE `t1` (
`c1` bigint(20) unsigned DEFAULT NULL,
`c2` decimal(21,0) unsigned NOT NULL,
`c3` decimal(21,0) unsigned NOT NULL,
`c4` decimal(21,0) unsigned NOT NULL,
`c5` decimal(21,0) unsigned NOT NULL
) ENGINE=MyISAM DEFAULT CHARSET=latin1
DROP TABLE t1;
#
# End of 10.4 tests # End of 10.4 tests
# #
...@@ -57,6 +57,31 @@ SELECT * FROM t1; ...@@ -57,6 +57,31 @@ SELECT * FROM t1;
DROP TABLE t1; DROP TABLE t1;
--echo #
--echo # MDEV-23366 ROUND(18446744073709551615,rand()*0) returns a wrong result
--echo #
--vertical_results
SELECT
ROUND(0xFFFFFFFFFFFFFFFF,NULL) AS c1,
ROUND(0xFFFFFFFFFFFFFFFF,rand()*0) AS c2,
ROUND(0xFFFFFFFFFFFFFFFF,-1) AS c3,
ROUND(0xFFFFFFFFFFFFFFFF,-19) AS c4,
ROUND(0xFFFFFFFFFFFFFFFF,rand()*0-19) AS c5;
CREATE OR REPLACE TABLE t1 AS
SELECT
ROUND(0xFFFFFFFFFFFFFFFF,NULL) AS c1,
ROUND(0xFFFFFFFFFFFFFFFF,rand()*0) AS c2,
ROUND(0xFFFFFFFFFFFFFFFF,-1) AS c3,
ROUND(0xFFFFFFFFFFFFFFFF,-19) AS c4,
ROUND(0xFFFFFFFFFFFFFFFF,rand()*0-19) AS c5;
SELECT * FROM t1;
SHOW CREATE TABLE t1;
DROP TABLE t1;
--horizontal_results
--echo # --echo #
--echo # End of 10.4 tests --echo # End of 10.4 tests
--echo # --echo #
...@@ -1163,6 +1163,75 @@ ROUND(a,-2) 9223372036854775800 ...@@ -1163,6 +1163,75 @@ ROUND(a,-2) 9223372036854775800
ROUND(a,-19) 10000000000000000000 ROUND(a,-19) 10000000000000000000
ROUND(a,-20) 0 ROUND(a,-20) 0
ROUND(a,-30) 0 ROUND(a,-30) 0
CALL p1('bigint(22)');
bigint(22)
Table t2
Create Table CREATE TABLE `t2` (
`a` bigint(22) DEFAULT NULL,
`ROUND(a,-1)` decimal(22,0) DEFAULT NULL,
`ROUND(a,-2)` decimal(22,0) DEFAULT NULL,
`ROUND(a,-19)` decimal(22,0) DEFAULT NULL,
`ROUND(a,-20)` decimal(22,0) DEFAULT NULL,
`ROUND(a,-30)` decimal(22,0) DEFAULT NULL
) ENGINE=MyISAM DEFAULT CHARSET=latin1
a -9223372036854775808
ROUND(a,-1) -9223372036854775810
ROUND(a,-2) -9223372036854775800
ROUND(a,-19) -10000000000000000000
ROUND(a,-20) 0
ROUND(a,-30) 0
a 9223372036854775807
ROUND(a,-1) 9223372036854775810
ROUND(a,-2) 9223372036854775800
ROUND(a,-19) 10000000000000000000
ROUND(a,-20) 0
ROUND(a,-30) 0
CALL p1('bigint(23)');
bigint(23)
Table t2
Create Table CREATE TABLE `t2` (
`a` bigint(23) DEFAULT NULL,
`ROUND(a,-1)` decimal(23,0) DEFAULT NULL,
`ROUND(a,-2)` decimal(23,0) DEFAULT NULL,
`ROUND(a,-19)` decimal(23,0) DEFAULT NULL,
`ROUND(a,-20)` decimal(23,0) DEFAULT NULL,
`ROUND(a,-30)` decimal(23,0) DEFAULT NULL
) ENGINE=MyISAM DEFAULT CHARSET=latin1
a -9223372036854775808
ROUND(a,-1) -9223372036854775810
ROUND(a,-2) -9223372036854775800
ROUND(a,-19) -10000000000000000000
ROUND(a,-20) 0
ROUND(a,-30) 0
a 9223372036854775807
ROUND(a,-1) 9223372036854775810
ROUND(a,-2) 9223372036854775800
ROUND(a,-19) 10000000000000000000
ROUND(a,-20) 0
ROUND(a,-30) 0
CALL p1('bigint(30)');
bigint(30)
Table t2
Create Table CREATE TABLE `t2` (
`a` bigint(30) DEFAULT NULL,
`ROUND(a,-1)` decimal(30,0) DEFAULT NULL,
`ROUND(a,-2)` decimal(30,0) DEFAULT NULL,
`ROUND(a,-19)` decimal(30,0) DEFAULT NULL,
`ROUND(a,-20)` decimal(30,0) DEFAULT NULL,
`ROUND(a,-30)` decimal(30,0) DEFAULT NULL
) ENGINE=MyISAM DEFAULT CHARSET=latin1
a -9223372036854775808
ROUND(a,-1) -9223372036854775810
ROUND(a,-2) -9223372036854775800
ROUND(a,-19) -10000000000000000000
ROUND(a,-20) 0
ROUND(a,-30) 0
a 9223372036854775807
ROUND(a,-1) 9223372036854775810
ROUND(a,-2) 9223372036854775800
ROUND(a,-19) 10000000000000000000
ROUND(a,-20) 0
ROUND(a,-30) 0
CALL p1('tinyint unsigned'); CALL p1('tinyint unsigned');
tinyint unsigned tinyint unsigned
Table t2 Table t2
...@@ -1324,7 +1393,103 @@ ROUND(a,-2) 18446744073709551600 ...@@ -1324,7 +1393,103 @@ ROUND(a,-2) 18446744073709551600
ROUND(a,-19) 20000000000000000000 ROUND(a,-19) 20000000000000000000
ROUND(a,-20) 0 ROUND(a,-20) 0
ROUND(a,-30) 0 ROUND(a,-30) 0
CALL p1('bigint(22) unsigned');
bigint(22) unsigned
Table t2
Create Table CREATE TABLE `t2` (
`a` bigint(22) unsigned DEFAULT NULL,
`ROUND(a,-1)` decimal(23,0) unsigned DEFAULT NULL,
`ROUND(a,-2)` decimal(23,0) unsigned DEFAULT NULL,
`ROUND(a,-19)` decimal(23,0) unsigned DEFAULT NULL,
`ROUND(a,-20)` decimal(23,0) unsigned DEFAULT NULL,
`ROUND(a,-30)` decimal(23,0) unsigned DEFAULT NULL
) ENGINE=MyISAM DEFAULT CHARSET=latin1
a 0
ROUND(a,-1) 0
ROUND(a,-2) 0
ROUND(a,-19) 0
ROUND(a,-20) 0
ROUND(a,-30) 0
a 18446744073709551615
ROUND(a,-1) 18446744073709551620
ROUND(a,-2) 18446744073709551600
ROUND(a,-19) 20000000000000000000
ROUND(a,-20) 0
ROUND(a,-30) 0
CALL p1('bigint(23) unsigned');
bigint(23) unsigned
Table t2
Create Table CREATE TABLE `t2` (
`a` bigint(23) unsigned DEFAULT NULL,
`ROUND(a,-1)` decimal(24,0) unsigned DEFAULT NULL,
`ROUND(a,-2)` decimal(24,0) unsigned DEFAULT NULL,
`ROUND(a,-19)` decimal(24,0) unsigned DEFAULT NULL,
`ROUND(a,-20)` decimal(24,0) unsigned DEFAULT NULL,
`ROUND(a,-30)` decimal(24,0) unsigned DEFAULT NULL
) ENGINE=MyISAM DEFAULT CHARSET=latin1
a 0
ROUND(a,-1) 0
ROUND(a,-2) 0
ROUND(a,-19) 0
ROUND(a,-20) 0
ROUND(a,-30) 0
a 18446744073709551615
ROUND(a,-1) 18446744073709551620
ROUND(a,-2) 18446744073709551600
ROUND(a,-19) 20000000000000000000
ROUND(a,-20) 0
ROUND(a,-30) 0
CALL p1('bigint(30) unsigned');
bigint(30) unsigned
Table t2
Create Table CREATE TABLE `t2` (
`a` bigint(30) unsigned DEFAULT NULL,
`ROUND(a,-1)` decimal(31,0) unsigned DEFAULT NULL,
`ROUND(a,-2)` decimal(31,0) unsigned DEFAULT NULL,
`ROUND(a,-19)` decimal(31,0) unsigned DEFAULT NULL,
`ROUND(a,-20)` decimal(31,0) unsigned DEFAULT NULL,
`ROUND(a,-30)` decimal(31,0) unsigned DEFAULT NULL
) ENGINE=MyISAM DEFAULT CHARSET=latin1
a 0
ROUND(a,-1) 0
ROUND(a,-2) 0
ROUND(a,-19) 0
ROUND(a,-20) 0
ROUND(a,-30) 0
a 18446744073709551615
ROUND(a,-1) 18446744073709551620
ROUND(a,-2) 18446744073709551600
ROUND(a,-19) 20000000000000000000
ROUND(a,-20) 0
ROUND(a,-30) 0
DROP PROCEDURE p1; DROP PROCEDURE p1;
# #
# MDEV-23366 ROUND(18446744073709551615,rand()*0) returns a wrong result
#
SELECT
ROUND(18446744073709551615,NULL) AS c1,
ROUND(18446744073709551615,rand()*0) AS c2,
ROUND(18446744073709551615,rand()*0-19) AS c3;
c1 NULL
c2 18446744073709551615
c3 20000000000000000000
CREATE OR REPLACE TABLE t1 AS
SELECT
ROUND(18446744073709551615,NULL) AS c1,
ROUND(18446744073709551615,rand()*0) AS c2,
ROUND(18446744073709551615,rand()*0-19) AS c3;
SELECT * FROM t1;
c1 NULL
c2 18446744073709551615
c3 20000000000000000000
SHOW CREATE TABLE t1;
Table t1
Create Table CREATE TABLE `t1` (
`c1` bigint(20) unsigned DEFAULT NULL,
`c2` decimal(21,0) unsigned NOT NULL,
`c3` decimal(21,0) unsigned NOT NULL
) ENGINE=MyISAM DEFAULT CHARSET=latin1
DROP TABLE t1;
#
# End of 10.4 tests # End of 10.4 tests
# #
...@@ -394,9 +394,9 @@ CALL p1('int'); ...@@ -394,9 +394,9 @@ CALL p1('int');
CALL p1('bigint'); CALL p1('bigint');
CALL p1('bigint(20)'); CALL p1('bigint(20)');
CALL p1('bigint(21)'); CALL p1('bigint(21)');
#CALL p1('bigint(22)'); CALL p1('bigint(22)');
#CALL p1('bigint(23)'); CALL p1('bigint(23)');
#CALL p1('bigint(30)'); CALL p1('bigint(30)');
CALL p1('tinyint unsigned'); CALL p1('tinyint unsigned');
CALL p1('smallint unsigned'); CALL p1('smallint unsigned');
...@@ -405,14 +405,36 @@ CALL p1('int unsigned'); ...@@ -405,14 +405,36 @@ CALL p1('int unsigned');
CALL p1('bigint unsigned'); CALL p1('bigint unsigned');
CALL p1('bigint(20) unsigned'); CALL p1('bigint(20) unsigned');
CALL p1('bigint(21) unsigned'); CALL p1('bigint(21) unsigned');
#CALL p1('bigint(22) unsigned'); CALL p1('bigint(22) unsigned');
#CALL p1('bigint(23) unsigned'); CALL p1('bigint(23) unsigned');
#CALL p1('bigint(30) unsigned'); CALL p1('bigint(30) unsigned');
--horizontal_results --horizontal_results
DROP PROCEDURE p1; DROP PROCEDURE p1;
--echo #
--echo # MDEV-23366 ROUND(18446744073709551615,rand()*0) returns a wrong result
--echo #
--vertical_results
SELECT
ROUND(18446744073709551615,NULL) AS c1,
ROUND(18446744073709551615,rand()*0) AS c2,
ROUND(18446744073709551615,rand()*0-19) AS c3;
CREATE OR REPLACE TABLE t1 AS
SELECT
ROUND(18446744073709551615,NULL) AS c1,
ROUND(18446744073709551615,rand()*0) AS c2,
ROUND(18446744073709551615,rand()*0-19) AS c3;
SELECT * FROM t1;
SHOW CREATE TABLE t1;
DROP TABLE t1;
--horizontal_results
--echo # --echo #
--echo # End of 10.4 tests --echo # End of 10.4 tests
--echo # --echo #
...@@ -2446,6 +2446,20 @@ void Item_func_round::fix_arg_datetime() ...@@ -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. Calculate data type and attributes for INT-alike input.
...@@ -2468,56 +2482,37 @@ void Item_func_round::fix_arg_int(const Type_handler *preferred, ...@@ -2468,56 +2482,37 @@ void Item_func_round::fix_arg_int(const Type_handler *preferred,
bool use_decimal_on_length_increase) bool use_decimal_on_length_increase)
{ {
DBUG_ASSERT(args[0]->decimals == 0); 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(); // Preserve the exact data type and attributes
if (args[1]->null_value) set_handler(preferred);
fix_length_and_dec_double(NOT_FIXED_DEC); }
else if (truncate || else
!val1.neg() /* ROUND(x, n>=0) */ || {
args[0]->decimal_precision() < DECIMAL_LONGLONG_DIGITS) max_length++;
{ if (use_decimal_on_length_increase)
// Here we can keep INT_RESULT set_handler(&type_handler_newdecimal);
// 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());
}
}
else 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 else
fix_length_and_dec_double(args[0]->decimals); set_handler(type_handler_long_or_longlong());
} }
......
...@@ -1754,6 +1754,7 @@ class Item_func_round :public Item_func_hybrid_field_type ...@@ -1754,6 +1754,7 @@ class Item_func_round :public Item_func_hybrid_field_type
bool truncate; bool truncate;
void fix_length_and_dec_decimal(uint decimals_to_set); void fix_length_and_dec_decimal(uint decimals_to_set);
void fix_length_and_dec_double(uint decimals_to_set); void fix_length_and_dec_double(uint decimals_to_set);
bool test_if_length_can_increase();
public: public:
Item_func_round(THD *thd, Item *a, Item *b, bool trunc_arg) Item_func_round(THD *thd, Item *a, Item *b, bool trunc_arg)
:Item_func_hybrid_field_type(thd, a, b), truncate(trunc_arg) {} :Item_func_hybrid_field_type(thd, a, b), truncate(trunc_arg) {}
...@@ -1777,6 +1778,7 @@ class Item_func_round :public Item_func_hybrid_field_type ...@@ -1777,6 +1778,7 @@ class Item_func_round :public Item_func_hybrid_field_type
void fix_arg_int(const Type_handler *preferred, void fix_arg_int(const Type_handler *preferred,
const Type_std_attributes *preferred_attributes, const Type_std_attributes *preferred_attributes,
bool use_decimal_on_length_increase); bool use_decimal_on_length_increase);
void fix_arg_hex_hybrid();
void fix_arg_double(); void fix_arg_double();
void fix_arg_time(); void fix_arg_time();
void fix_arg_datetime(); void fix_arg_datetime();
......
...@@ -5690,7 +5690,7 @@ bool Type_handler_year:: ...@@ -5690,7 +5690,7 @@ bool Type_handler_year::
bool Type_handler_hex_hybrid:: bool Type_handler_hex_hybrid::
Item_func_round_fix_length_and_dec(Item_func_round *item) const 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; return false;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment