Commit 38090df9 authored by unknown's avatar unknown

Fix for bug #24912 "problems with bigint in abs() ceiling() round() truncate()...

Fix for bug #24912 "problems with bigint in abs() ceiling() round() truncate() mod()" and a number of related problems:

- unsigned flag was not handled correctly for a number of mathematical funcions, which led to incorrect results
- passing large values as the number of decimals to ROUND() resulted in incorrect results and even server crashes in some cases
- reverted the fix and the testcase for bug #10083 as it violates the manual
- fixed some testcases which relied on broken ROUND() behavior


mysql-test/r/func_math.result:
  - Removed the testcase for bug #10083 (not a bug according to the manual)
  - Changed the testcase for bug #9837 to expect a correct ROUND() behavior
  - Added testcases for bug #24912 and all related bugs found
mysql-test/r/type_newdecimal.result:
  Fixed a truncate() testcase which relied on broken behavior
mysql-test/t/func_math.test:
  - Removed the testcase for bug #10083 (not a bug according to the manual)
  - Changed the testcase for bug #9837 to expect a correct ROUND() behavior
  - Added testcases for bug #24912 and all related bugs found
sql/item_func.cc:
  Various changes to fix bug #24912 and all related bugs found:
  - honor unsigned_flag in various Item_* functions
  - correctly handle out-of-range numbers of decimals in Item_func_round::fix_length_and_dec()
  - changed the argument specifying the number of decimals in my_double_round() from int to longlong, added a new argument to pass the 'unsigned flag'
  - changed my_double_round() to correctly handle large values passed as the 'number of decimals' argument
  - added a my_double_round() analog for BIGINT UNSIGNED arguments (my_unsigned_round())
  - fixed Item_func_round()::int_op() to not overflow even when the result is within integer range
  - fixed a bug Item_founc_round()::decimal_op() which resulted in crash when a large number of decimals was passed to my_decimal_round()
sql/item_func.h:
  Various fixed to correctly handle unsigned values.
sql/item_strfunc.cc:
  Changed the call to my_double_round() to match the new declaration.
sql/mysql_priv.h:
  Changed the declaration for my_double_round() to be able pass arbitrary integers as number of decimals (both signed and unsigned)
parent a491b2c1
set autocommit=1;
reset master;
create table bug16206 (a int);
insert into bug16206 values(1);
start transaction;
insert into bug16206 values(2);
commit;
show binlog events;
Log_name Pos Event_type Server_id End_log_pos Info
f n Format_desc 1 n Server ver: VERSION, Binlog ver: 4
f n Query 1 n use `test`; create table bug16206 (a int)
f n Query 1 n use `test`; insert into bug16206 values(1)
f n Query 1 n use `test`; insert into bug16206 values(2)
drop table bug16206;
reset master;
create table bug16206 (a int) engine= bdb;
insert into bug16206 values(0);
insert into bug16206 values(1);
start transaction;
insert into bug16206 values(2);
commit;
insert into bug16206 values(3);
show binlog events;
Log_name Pos Event_type Server_id End_log_pos Info
f n Format_desc 1 n Server ver: VERSION, Binlog ver: 4
f n Query 1 n use `test`; create table bug16206 (a int) engine= bdb
f n Query 1 n use `test`; insert into bug16206 values(0)
f n Query 1 n use `test`; insert into bug16206 values(1)
f n Query 1 n use `test`; BEGIN
f n Query 1 n use `test`; insert into bug16206 values(2)
f n Query 1 n use `test`; COMMIT
f n Query 1 n use `test`; insert into bug16206 values(3)
drop table bug16206;
set autocommit=0;
End of 5.0 tests
...@@ -143,9 +143,6 @@ select format(col2,6) from t1 where col1=7; ...@@ -143,9 +143,6 @@ select format(col2,6) from t1 where col1=7;
format(col2,6) format(col2,6)
1,234,567,890,123,456.123450 1,234,567,890,123,456.123450
drop table t1; drop table t1;
select round(150, 2);
round(150, 2)
150.00
select ceil(0.09); select ceil(0.09);
ceil(0.09) ceil(0.09)
1 1
...@@ -156,11 +153,11 @@ create table t1 select round(1, 6); ...@@ -156,11 +153,11 @@ create table t1 select round(1, 6);
show create table t1; show create table t1;
Table Create Table Table Create Table
t1 CREATE TABLE `t1` ( t1 CREATE TABLE `t1` (
`round(1, 6)` decimal(7,6) NOT NULL default '0.000000' `round(1, 6)` int(1) NOT NULL default '0'
) ENGINE=MyISAM DEFAULT CHARSET=latin1 ) ENGINE=MyISAM DEFAULT CHARSET=latin1
select * from t1; select * from t1;
round(1, 6) round(1, 6)
1.000000 1
drop table t1; drop table t1;
select abs(-2) * -2; select abs(-2) * -2;
abs(-2) * -2 abs(-2) * -2
...@@ -238,3 +235,91 @@ format(t2.f2-t2.f1+1,0) ...@@ -238,3 +235,91 @@ format(t2.f2-t2.f1+1,0)
10,000 10,000
drop table t1, t2; drop table t1, t2;
set names default; set names default;
select cast(-2 as unsigned), 18446744073709551614, -2;
cast(-2 as unsigned) 18446744073709551614 -2
18446744073709551614 18446744073709551614 -2
select abs(cast(-2 as unsigned)), abs(18446744073709551614), abs(-2);
abs(cast(-2 as unsigned)) abs(18446744073709551614) abs(-2)
18446744073709551614 18446744073709551614 2
select ceiling(cast(-2 as unsigned)), ceiling(18446744073709551614), ceiling(-2);
ceiling(cast(-2 as unsigned)) ceiling(18446744073709551614) ceiling(-2)
18446744073709551614 18446744073709551614 -2
select floor(cast(-2 as unsigned)), floor(18446744073709551614), floor(-2);
floor(cast(-2 as unsigned)) floor(18446744073709551614) floor(-2)
18446744073709551614 18446744073709551614 -2
select format(cast(-2 as unsigned), 2), format(18446744073709551614, 2), format(-2, 2);
format(cast(-2 as unsigned), 2) format(18446744073709551614, 2) format(-2, 2)
18,446,744,073,709,551,614.00 18,446,744,073,709,551,614.00 -2.00
select sqrt(cast(-2 as unsigned)), sqrt(18446744073709551614), sqrt(-2);
sqrt(cast(-2 as unsigned)) sqrt(18446744073709551614) sqrt(-2)
4294967296 4294967296 NULL
select round(cast(-2 as unsigned), 1), round(18446744073709551614, 1), round(-2, 1);
round(cast(-2 as unsigned), 1) round(18446744073709551614, 1) round(-2, 1)
18446744073709551614 18446744073709551614 -2
select round(4, cast(-2 as unsigned)), round(4, 18446744073709551614), round(4, -2);
round(4, cast(-2 as unsigned)) round(4, 18446744073709551614) round(4, -2)
4 4 0
select truncate(cast(-2 as unsigned), 1), truncate(18446744073709551614, 1), truncate(-2, 1);
truncate(cast(-2 as unsigned), 1) truncate(18446744073709551614, 1) truncate(-2, 1)
18446744073709551614 18446744073709551614 -2
select truncate(4, cast(-2 as unsigned)), truncate(4, 18446744073709551614), truncate(4, -2);
truncate(4, cast(-2 as unsigned)) truncate(4, 18446744073709551614) truncate(4, -2)
4 4 0
select round(10000000000000000000, -19), truncate(10000000000000000000, -19);
round(10000000000000000000, -19) truncate(10000000000000000000, -19)
10000000000000000000 10000000000000000000
select round(1e0, -309), truncate(1e0, -309);
round(1e0, -309) truncate(1e0, -309)
0 0
select round(1e1,308), truncate(1e1, 308);
round(1e1,308) truncate(1e1, 308)
10 10
select round(1e1, 2147483648), truncate(1e1, 2147483648);
round(1e1, 2147483648) truncate(1e1, 2147483648)
10 10
select round(1.1e1, 4294967295), truncate(1.1e1, 4294967295);
round(1.1e1, 4294967295) truncate(1.1e1, 4294967295)
11 11
select round(1.12e1, 4294967296), truncate(1.12e1, 4294967296);
round(1.12e1, 4294967296) truncate(1.12e1, 4294967296)
11.2 11.2
select round(1.5, 2147483640), truncate(1.5, 2147483640);
round(1.5, 2147483640) truncate(1.5, 2147483640)
1.500000000000000000000000000000 1.500000000000000000000000000000
select round(1.5, -2147483649), round(1.5, 2147483648);
round(1.5, -2147483649) round(1.5, 2147483648)
0 1.500000000000000000000000000000
select truncate(1.5, -2147483649), truncate(1.5, 2147483648);
truncate(1.5, -2147483649) truncate(1.5, 2147483648)
0 1.500000000000000000000000000000
select round(1.5, -4294967296), round(1.5, 4294967296);
round(1.5, -4294967296) round(1.5, 4294967296)
0 1.500000000000000000000000000000
select truncate(1.5, -4294967296), truncate(1.5, 4294967296);
truncate(1.5, -4294967296) truncate(1.5, 4294967296)
0 1.500000000000000000000000000000
select round(1.5, -9223372036854775808), round(1.5, 9223372036854775808);
round(1.5, -9223372036854775808) round(1.5, 9223372036854775808)
0 1.500000000000000000000000000000
select truncate(1.5, -9223372036854775808), truncate(1.5, 9223372036854775808);
truncate(1.5, -9223372036854775808) truncate(1.5, 9223372036854775808)
0 1.500000000000000000000000000000
select round(1.5, 18446744073709551615), truncate(1.5, 18446744073709551615);
round(1.5, 18446744073709551615) truncate(1.5, 18446744073709551615)
1.500000000000000000000000000000 1.500000000000000000000000000000
select round(18446744073709551614, -1), truncate(18446744073709551614, -1);
round(18446744073709551614, -1) truncate(18446744073709551614, -1)
18446744073709551610 18446744073709551610
select round(4, -4294967200), truncate(4, -4294967200);
round(4, -4294967200) truncate(4, -4294967200)
0 0
select mod(cast(-2 as unsigned), 3), mod(18446744073709551614, 3), mod(-2, 3);
mod(cast(-2 as unsigned), 3) mod(18446744073709551614, 3) mod(-2, 3)
2 2 -2
select mod(5, cast(-2 as unsigned)), mod(5, 18446744073709551614), mod(5, -2);
mod(5, cast(-2 as unsigned)) mod(5, 18446744073709551614) mod(5, -2)
5 5 1
select pow(cast(-2 as unsigned), 5), pow(18446744073709551614, 5), pow(-2, 5);
pow(cast(-2 as unsigned), 5) pow(18446744073709551614, 5) pow(-2, 5)
2.1359870359209e+96 2.1359870359209e+96 -32
End of 5.0 tests
...@@ -763,7 +763,7 @@ truncate(99999999999999999999999999999999999999,31) ...@@ -763,7 +763,7 @@ truncate(99999999999999999999999999999999999999,31)
99999999999999999999999999999999999999.000000000000000000000000000000 99999999999999999999999999999999999999.000000000000000000000000000000
select truncate(99.999999999999999999999999999999999999,31); select truncate(99.999999999999999999999999999999999999,31);
truncate(99.999999999999999999999999999999999999,31) truncate(99.999999999999999999999999999999999999,31)
100.000000000000000000000000000000 99.999999999999999999999999999999
select truncate(99999999999999999999999999999999999999,-31); select truncate(99999999999999999999999999999999999999,-31);
truncate(99999999999999999999999999999999999999,-31) truncate(99999999999999999999999999999999999999,-31)
99999990000000000000000000000000000000 99999990000000000000000000000000000000
......
-- source include/not_embedded.inc
-- source include/have_bdb.inc
#
# Bug #16206: Superfluous COMMIT event in binlog when updating BDB in autocommit mode
#
set autocommit=1;
let $VERSION=`select version()`;
reset master;
create table bug16206 (a int);
insert into bug16206 values(1);
start transaction;
insert into bug16206 values(2);
commit;
--replace_result $VERSION VERSION
--replace_column 1 f 2 n 5 n
show binlog events;
drop table bug16206;
reset master;
create table bug16206 (a int) engine= bdb;
insert into bug16206 values(0);
insert into bug16206 values(1);
start transaction;
insert into bug16206 values(2);
commit;
insert into bug16206 values(3);
--replace_result $VERSION VERSION
--replace_column 1 f 2 n 5 n
show binlog events;
drop table bug16206;
set autocommit=0;
--echo End of 5.0 tests
...@@ -82,11 +82,6 @@ select format(col2,6) from t1 where col1=7; ...@@ -82,11 +82,6 @@ select format(col2,6) from t1 where col1=7;
drop table t1; drop table t1;
#
# Bug #10083 (round doesn't increase decimals)
#
select round(150, 2);
# #
# Bug @10632 (Ceiling function returns wrong answer) # Bug @10632 (Ceiling function returns wrong answer)
# #
...@@ -177,3 +172,37 @@ select format(t2.f2-t2.f1+1,0) from t1,t2 ...@@ -177,3 +172,37 @@ select format(t2.f2-t2.f1+1,0) from t1,t2
where t1.f2 = t2.f3 order by t1.f1; where t1.f2 = t2.f3 order by t1.f1;
drop table t1, t2; drop table t1, t2;
set names default; set names default;
# Bug 24912 -- misc functions have trouble with unsigned
select cast(-2 as unsigned), 18446744073709551614, -2;
select abs(cast(-2 as unsigned)), abs(18446744073709551614), abs(-2);
select ceiling(cast(-2 as unsigned)), ceiling(18446744073709551614), ceiling(-2);
select floor(cast(-2 as unsigned)), floor(18446744073709551614), floor(-2);
select format(cast(-2 as unsigned), 2), format(18446744073709551614, 2), format(-2, 2);
select sqrt(cast(-2 as unsigned)), sqrt(18446744073709551614), sqrt(-2);
select round(cast(-2 as unsigned), 1), round(18446744073709551614, 1), round(-2, 1);
select round(4, cast(-2 as unsigned)), round(4, 18446744073709551614), round(4, -2);
select truncate(cast(-2 as unsigned), 1), truncate(18446744073709551614, 1), truncate(-2, 1);
select truncate(4, cast(-2 as unsigned)), truncate(4, 18446744073709551614), truncate(4, -2);
select round(10000000000000000000, -19), truncate(10000000000000000000, -19);
select round(1e0, -309), truncate(1e0, -309);
select round(1e1,308), truncate(1e1, 308);
select round(1e1, 2147483648), truncate(1e1, 2147483648);
select round(1.1e1, 4294967295), truncate(1.1e1, 4294967295);
select round(1.12e1, 4294967296), truncate(1.12e1, 4294967296);
select round(1.5, 2147483640), truncate(1.5, 2147483640);
select round(1.5, -2147483649), round(1.5, 2147483648);
select truncate(1.5, -2147483649), truncate(1.5, 2147483648);
select round(1.5, -4294967296), round(1.5, 4294967296);
select truncate(1.5, -4294967296), truncate(1.5, 4294967296);
select round(1.5, -9223372036854775808), round(1.5, 9223372036854775808);
select truncate(1.5, -9223372036854775808), truncate(1.5, 9223372036854775808);
select round(1.5, 18446744073709551615), truncate(1.5, 18446744073709551615);
select round(18446744073709551614, -1), truncate(18446744073709551614, -1);
select round(4, -4294967200), truncate(4, -4294967200);
select mod(cast(-2 as unsigned), 3), mod(18446744073709551614, 3), mod(-2, 3);
select mod(5, cast(-2 as unsigned)), mod(5, 18446744073709551614), mod(5, -2);
select pow(cast(-2 as unsigned), 5), pow(18446744073709551614, 5), pow(-2, 5);
--echo End of 5.0 tests
...@@ -614,6 +614,14 @@ Item *Item_func::get_tmp_table_item(THD *thd) ...@@ -614,6 +614,14 @@ Item *Item_func::get_tmp_table_item(THD *thd)
return copy_or_same(thd); return copy_or_same(thd);
} }
double Item_int_func::val_real()
{
DBUG_ASSERT(fixed == 1);
return unsigned_flag ? (double) ((ulonglong) val_int()) : (double) val_int();
}
String *Item_int_func::val_str(String *str) String *Item_int_func::val_str(String *str)
{ {
DBUG_ASSERT(fixed == 1); DBUG_ASSERT(fixed == 1);
...@@ -802,7 +810,10 @@ double Item_func_numhybrid::val_real() ...@@ -802,7 +810,10 @@ double Item_func_numhybrid::val_real()
return result; return result;
} }
case INT_RESULT: case INT_RESULT:
return (double)int_op(); {
longlong result= int_op();
return unsigned_flag ? (double) ((ulonglong) result) : (double) result;
}
case REAL_RESULT: case REAL_RESULT:
return real_op(); return real_op();
case STRING_RESULT: case STRING_RESULT:
...@@ -1339,6 +1350,8 @@ longlong Item_func_mod::int_op() ...@@ -1339,6 +1350,8 @@ longlong Item_func_mod::int_op()
DBUG_ASSERT(fixed == 1); DBUG_ASSERT(fixed == 1);
longlong value= args[0]->val_int(); longlong value= args[0]->val_int();
longlong val2= args[1]->val_int(); longlong val2= args[1]->val_int();
longlong result;
if ((null_value= args[0]->null_value || args[1]->null_value)) if ((null_value= args[0]->null_value || args[1]->null_value))
return 0; /* purecov: inspected */ return 0; /* purecov: inspected */
if (val2 == 0) if (val2 == 0)
...@@ -1348,9 +1361,13 @@ longlong Item_func_mod::int_op() ...@@ -1348,9 +1361,13 @@ longlong Item_func_mod::int_op()
} }
if (args[0]->unsigned_flag) if (args[0]->unsigned_flag)
return ((ulonglong) value) % val2; result= args[1]->unsigned_flag ?
((ulonglong) value) % ((ulonglong) val2) : ((ulonglong) value) % val2;
else
result= args[1]->unsigned_flag ?
value % ((ulonglong) val2) : value % val2;
return value % val2; return result;
} }
double Item_func_mod::real_op() double Item_func_mod::real_op()
...@@ -1405,6 +1422,7 @@ void Item_func_mod::fix_length_and_dec() ...@@ -1405,6 +1422,7 @@ void Item_func_mod::fix_length_and_dec()
{ {
Item_num_op::fix_length_and_dec(); Item_num_op::fix_length_and_dec();
maybe_null= 1; maybe_null= 1;
unsigned_flag= args[0]->unsigned_flag;
} }
...@@ -1483,8 +1501,9 @@ double Item_func_abs::real_op() ...@@ -1483,8 +1501,9 @@ double Item_func_abs::real_op()
longlong Item_func_abs::int_op() longlong Item_func_abs::int_op()
{ {
longlong value= args[0]->val_int(); longlong value= args[0]->val_int();
null_value= args[0]->null_value; if ((null_value= args[0]->null_value))
return value >= 0 ? value : -value; return 0;
return (value >= 0) || unsigned_flag ? value : -value;
} }
...@@ -1505,6 +1524,7 @@ my_decimal *Item_func_abs::decimal_op(my_decimal *decimal_value) ...@@ -1505,6 +1524,7 @@ my_decimal *Item_func_abs::decimal_op(my_decimal *decimal_value)
void Item_func_abs::fix_length_and_dec() void Item_func_abs::fix_length_and_dec()
{ {
Item_func_num1::fix_length_and_dec(); Item_func_num1::fix_length_and_dec();
unsigned_flag= args[0]->unsigned_flag;
} }
...@@ -1879,6 +1899,10 @@ my_decimal *Item_func_floor::decimal_op(my_decimal *decimal_value) ...@@ -1879,6 +1899,10 @@ my_decimal *Item_func_floor::decimal_op(my_decimal *decimal_value)
void Item_func_round::fix_length_and_dec() void Item_func_round::fix_length_and_dec()
{ {
int decimals_to_set;
longlong val1;
bool val1_unsigned;
unsigned_flag= args[0]->unsigned_flag; unsigned_flag= args[0]->unsigned_flag;
if (!args[1]->const_item()) if (!args[1]->const_item())
{ {
...@@ -1888,7 +1912,13 @@ void Item_func_round::fix_length_and_dec() ...@@ -1888,7 +1912,13 @@ void Item_func_round::fix_length_and_dec()
return; return;
} }
int decimals_to_set= max((int)args[1]->val_int(), 0); val1= args[1]->val_int();
val1_unsigned= args[1]->unsigned_flag;
if (val1 < 0)
decimals_to_set= val1_unsigned ? INT_MAX : 0;
else
decimals_to_set= (val1 > INT_MAX) ? INT_MAX : (int) val1;
if (args[0]->decimals == NOT_FIXED_DEC) if (args[0]->decimals == NOT_FIXED_DEC)
{ {
max_length= args[0]->max_length; max_length= args[0]->max_length;
...@@ -1905,10 +1935,9 @@ void Item_func_round::fix_length_and_dec() ...@@ -1905,10 +1935,9 @@ void Item_func_round::fix_length_and_dec()
max_length= float_length(decimals); max_length= float_length(decimals);
break; break;
case INT_RESULT: case INT_RESULT:
if (!decimals_to_set && if ((!decimals_to_set && truncate) || (args[0]->decimal_precision() < DECIMAL_LONGLONG_DIGITS))
(truncate || (args[0]->decimal_precision() < DECIMAL_LONGLONG_DIGITS)))
{ {
int length_can_increase= test(!truncate && (args[1]->val_int() < 0)); int length_can_increase= test(!truncate && (val1 < 0) && !val1_unsigned);
max_length= args[0]->max_length + length_can_increase; max_length= args[0]->max_length + length_can_increase;
/* Here we can keep INT_RESULT */ /* Here we can keep INT_RESULT */
hybrid_type= INT_RESULT; hybrid_type= INT_RESULT;
...@@ -1934,10 +1963,12 @@ void Item_func_round::fix_length_and_dec() ...@@ -1934,10 +1963,12 @@ void Item_func_round::fix_length_and_dec()
} }
} }
double my_double_round(double value, int dec, bool truncate) double my_double_round(double value, longlong dec, bool dec_unsigned,
bool truncate)
{ {
double tmp; double tmp;
uint abs_dec= abs(dec); bool dec_negative= (dec < 0) && !dec_unsigned;
ulonglong abs_dec= dec_negative ? -dec : dec;
/* /*
tmp2 is here to avoid return the value with 80 bit precision tmp2 is here to avoid return the value with 80 bit precision
This will fix that the test round(0.1,1) = round(0.1,1) is true This will fix that the test round(0.1,1) = round(0.1,1) is true
...@@ -1947,7 +1978,11 @@ double my_double_round(double value, int dec, bool truncate) ...@@ -1947,7 +1978,11 @@ double my_double_round(double value, int dec, bool truncate)
tmp=(abs_dec < array_elements(log_10) ? tmp=(abs_dec < array_elements(log_10) ?
log_10[abs_dec] : pow(10.0,(double) abs_dec)); log_10[abs_dec] : pow(10.0,(double) abs_dec));
if (truncate) if (dec_negative && isinf(tmp))
tmp2= 0;
else if (!dec_negative && isinf(value * tmp))
tmp2= value;
else if (truncate)
{ {
if (value >= 0) if (value >= 0)
tmp2= dec < 0 ? floor(value/tmp)*tmp : floor(value*tmp)/tmp; tmp2= dec < 0 ? floor(value/tmp)*tmp : floor(value*tmp)/tmp;
...@@ -1963,24 +1998,35 @@ double my_double_round(double value, int dec, bool truncate) ...@@ -1963,24 +1998,35 @@ double my_double_round(double value, int dec, bool truncate)
double Item_func_round::real_op() double Item_func_round::real_op()
{ {
double value= args[0]->val_real(); double value= args[0]->val_real();
int dec= (int) args[1]->val_int();
if (!(null_value= args[0]->null_value || args[1]->null_value)) if (!(null_value= args[0]->null_value || args[1]->null_value))
return my_double_round(value, dec, truncate); return my_double_round(value, args[1]->val_int(), args[1]->unsigned_flag,
truncate);
return 0.0; return 0.0;
} }
/*
Rounds a given value to a power of 10 specified as the 'to' argument,
avoiding overflows when the value is close to the ulonglong range boundary.
*/
static inline ulonglong my_unsigned_round(ulonglong value, ulonglong to)
{
ulonglong tmp= value / to * to;
return (value - tmp < (to >> 1)) ? tmp : tmp + to;
}
longlong Item_func_round::int_op() longlong Item_func_round::int_op()
{ {
longlong value= args[0]->val_int(); longlong value= args[0]->val_int();
int dec=(int) args[1]->val_int(); longlong dec= args[1]->val_int();
decimals= 0; decimals= 0;
uint abs_dec; ulonglong abs_dec;
if ((null_value= args[0]->null_value || args[1]->null_value)) if ((null_value= args[0]->null_value || args[1]->null_value))
return 0; return 0;
if (dec >= 0) if ((dec >= 0) || args[1]->unsigned_flag)
return value; // integer have not digits after point return value; // integer have not digits after point
abs_dec= -dec; abs_dec= -dec;
...@@ -1992,21 +2038,12 @@ longlong Item_func_round::int_op() ...@@ -1992,21 +2038,12 @@ longlong Item_func_round::int_op()
tmp= log_10_int[abs_dec]; tmp= log_10_int[abs_dec];
if (truncate) if (truncate)
{ value= (unsigned_flag) ?
if (unsigned_flag) ((ulonglong) value / tmp) * tmp : (value / tmp) * tmp;
value= (ulonglong(value)/tmp)*tmp;
else else
value= (value/tmp)*tmp; value= (unsigned_flag || value >= 0) ?
} my_unsigned_round((ulonglong) value, tmp) :
else -my_unsigned_round((ulonglong) -value, tmp);
{
if (unsigned_flag)
value= ((ulonglong(value)+(tmp>>1))/tmp)*tmp;
else if ( value >= 0)
value= ((value+(tmp>>1))/tmp)*tmp;
else
value= ((value-(tmp>>1))/tmp)*tmp;
}
return value; return value;
} }
...@@ -2014,14 +2051,18 @@ longlong Item_func_round::int_op() ...@@ -2014,14 +2051,18 @@ longlong Item_func_round::int_op()
my_decimal *Item_func_round::decimal_op(my_decimal *decimal_value) my_decimal *Item_func_round::decimal_op(my_decimal *decimal_value)
{ {
my_decimal val, *value= args[0]->val_decimal(&val); my_decimal val, *value= args[0]->val_decimal(&val);
int dec=(int) args[1]->val_int(); longlong dec= args[1]->val_int();
if (dec > 0) if (dec > 0 || (dec < 0 && args[1]->unsigned_flag))
{ {
decimals= min(dec, DECIMAL_MAX_SCALE); // to get correct output dec= min((ulonglong) dec, DECIMAL_MAX_SCALE);
decimals= dec; // to get correct output
} }
else if (dec < INT_MIN)
dec= INT_MIN;
if (!(null_value= (args[0]->null_value || args[1]->null_value || if (!(null_value= (args[0]->null_value || args[1]->null_value ||
my_decimal_round(E_DEC_FATAL_ERROR, value, dec, truncate, my_decimal_round(E_DEC_FATAL_ERROR, value, dec,
decimal_value) > 1))) truncate, decimal_value) > 1)))
return decimal_value; return decimal_value;
return 0; return 0;
} }
......
...@@ -278,7 +278,7 @@ public: ...@@ -278,7 +278,7 @@ public:
{ max_length= 21; } { max_length= 21; }
Item_int_func(List<Item> &list) :Item_func(list) { max_length= 21; } Item_int_func(List<Item> &list) :Item_func(list) { max_length= 21; }
Item_int_func(THD *thd, Item_int_func *item) :Item_func(thd, item) {} Item_int_func(THD *thd, Item_int_func *item) :Item_func(thd, item) {}
double val_real() { DBUG_ASSERT(fixed == 1); return (double) val_int(); } double val_real();
String *val_str(String*str); String *val_str(String*str);
enum Item_result result_type () const { return INT_RESULT; } enum Item_result result_type () const { return INT_RESULT; }
void fix_length_and_dec() {} void fix_length_and_dec() {}
...@@ -303,12 +303,6 @@ class Item_func_signed :public Item_int_func ...@@ -303,12 +303,6 @@ class Item_func_signed :public Item_int_func
public: public:
Item_func_signed(Item *a) :Item_int_func(a) {} Item_func_signed(Item *a) :Item_int_func(a) {}
const char *func_name() const { return "cast_as_signed"; } const char *func_name() const { return "cast_as_signed"; }
double val_real()
{
double tmp= args[0]->val_real();
null_value= args[0]->null_value;
return tmp;
}
longlong val_int(); longlong val_int();
longlong val_int_from_str(int *error); longlong val_int_from_str(int *error);
void fix_length_and_dec() void fix_length_and_dec()
......
...@@ -1918,7 +1918,7 @@ String *Item_func_format::val_str(String *str) ...@@ -1918,7 +1918,7 @@ String *Item_func_format::val_str(String *str)
double nr= args[0]->val_real(); double nr= args[0]->val_real();
if ((null_value=args[0]->null_value)) if ((null_value=args[0]->null_value))
return 0; /* purecov: inspected */ return 0; /* purecov: inspected */
nr= my_double_round(nr, decimals, FALSE); nr= my_double_round(nr, (longlong) decimals, FALSE, FALSE);
/* Here default_charset() is right as this is not an automatic conversion */ /* Here default_charset() is right as this is not an automatic conversion */
str->set(nr,decimals, default_charset()); str->set(nr,decimals, default_charset());
if (isnan(nr)) if (isnan(nr))
......
...@@ -1537,7 +1537,8 @@ ha_rows filesort(THD *thd, TABLE *form,struct st_sort_field *sortorder, ...@@ -1537,7 +1537,8 @@ ha_rows filesort(THD *thd, TABLE *form,struct st_sort_field *sortorder,
ha_rows max_rows, ha_rows *examined_rows); ha_rows max_rows, ha_rows *examined_rows);
void filesort_free_buffers(TABLE *table, bool full); void filesort_free_buffers(TABLE *table, bool full);
void change_double_for_sort(double nr,byte *to); void change_double_for_sort(double nr,byte *to);
double my_double_round(double value, int dec, bool truncate); double my_double_round(double value, longlong dec, bool dec_unsigned,
bool truncate);
int get_quick_record(SQL_SELECT *select); int get_quick_record(SQL_SELECT *select);
int calc_weekday(long daynr,bool sunday_first_day_of_week); int calc_weekday(long daynr,bool sunday_first_day_of_week);
uint calc_week(TIME *l_time, uint week_behaviour, uint *year); uint calc_week(TIME *l_time, uint week_behaviour, uint *year);
......
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