diff --git a/mysql-test/r/func_math.result b/mysql-test/r/func_math.result index 565c283ee839bbfa23306de2617f6eeb86429d78..e2e93796afe07863313245dce8863e14d33ba854 100644 --- a/mysql-test/r/func_math.result +++ b/mysql-test/r/func_math.result @@ -155,9 +155,6 @@ select format(col2,6) from t1 where col1=7; format(col2,6) 1,234,567,890,123,456.123450 drop table t1; -select round(150, 2); -round(150, 2) -150.00 select ceil(0.09); ceil(0.09) 1 @@ -168,11 +165,11 @@ create table t1 select round(1, 6); show create table t1; Table Create Table 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 select * from t1; round(1, 6) -1.000000 +1 drop table t1; select abs(-2) * -2; abs(-2) * -2 @@ -285,3 +282,91 @@ format(t2.f2-t2.f1+1,0) 10,000 drop table t1, t2; 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 diff --git a/mysql-test/r/type_newdecimal.result b/mysql-test/r/type_newdecimal.result index f3c7e45de392b20c1dde0648f729c810952e9674..04baa03ce4d263134527f672126c7905bd6c729b 100644 --- a/mysql-test/r/type_newdecimal.result +++ b/mysql-test/r/type_newdecimal.result @@ -763,7 +763,7 @@ truncate(99999999999999999999999999999999999999,31) 99999999999999999999999999999999999999.000000000000000000000000000000 select truncate(99.999999999999999999999999999999999999,31); truncate(99.999999999999999999999999999999999999,31) -100.000000000000000000000000000000 +99.999999999999999999999999999999 select truncate(99999999999999999999999999999999999999,-31); truncate(99999999999999999999999999999999999999,-31) 99999990000000000000000000000000000000 diff --git a/mysql-test/t/func_math.test b/mysql-test/t/func_math.test index 639ced6a1c021dade325975a82a465083c2d4c76..668528b2e9bd42230d5dc4681269ca86abe6a2e4 100644 --- a/mysql-test/t/func_math.test +++ b/mysql-test/t/func_math.test @@ -87,11 +87,6 @@ select format(col2,6) from t1 where col1=7; drop table t1; -# -# Bug #10083 (round doesn't increase decimals) -# -select round(150, 2); - # # Bug @10632 (Ceiling function returns wrong answer) # @@ -196,3 +191,37 @@ select format(t2.f2-t2.f1+1,0) from t1,t2 where t1.f2 = t2.f3 order by t1.f1; drop table t1, t2; 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 diff --git a/sql/item_func.cc b/sql/item_func.cc index c823575b240f2e7d397e285ac963354a866bb576..c823fe12d2504814b429fd2f49fca58885b87171 100644 --- a/sql/item_func.cc +++ b/sql/item_func.cc @@ -619,6 +619,14 @@ Item *Item_func::get_tmp_table_item(THD *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) { DBUG_ASSERT(fixed == 1); @@ -801,7 +809,10 @@ double Item_func_numhybrid::val_real() return result; } case INT_RESULT: - return (double)int_op(); + { + longlong result= int_op(); + return unsigned_flag ? (double) ((ulonglong) result) : (double) result; + } case REAL_RESULT: return real_op(); case STRING_RESULT: @@ -1361,6 +1372,8 @@ longlong Item_func_mod::int_op() DBUG_ASSERT(fixed == 1); longlong value= args[0]->val_int(); longlong val2= args[1]->val_int(); + longlong result; + if ((null_value= args[0]->null_value || args[1]->null_value)) return 0; /* purecov: inspected */ if (val2 == 0) @@ -1370,9 +1383,13 @@ longlong Item_func_mod::int_op() } 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() @@ -1427,6 +1444,7 @@ void Item_func_mod::fix_length_and_dec() { Item_num_op::fix_length_and_dec(); maybe_null= 1; + unsigned_flag= args[0]->unsigned_flag; } @@ -1505,8 +1523,9 @@ double Item_func_abs::real_op() longlong Item_func_abs::int_op() { longlong value= args[0]->val_int(); - null_value= args[0]->null_value; - return value >= 0 ? value : -value; + if ((null_value= args[0]->null_value)) + return 0; + return (value >= 0) || unsigned_flag ? value : -value; } @@ -1527,6 +1546,7 @@ my_decimal *Item_func_abs::decimal_op(my_decimal *decimal_value) void Item_func_abs::fix_length_and_dec() { Item_func_num1::fix_length_and_dec(); + unsigned_flag= args[0]->unsigned_flag; } @@ -1901,6 +1921,10 @@ my_decimal *Item_func_floor::decimal_op(my_decimal *decimal_value) void Item_func_round::fix_length_and_dec() { + int decimals_to_set; + longlong val1; + bool val1_unsigned; + unsigned_flag= args[0]->unsigned_flag; if (!args[1]->const_item()) { @@ -1909,8 +1933,14 @@ void Item_func_round::fix_length_and_dec() hybrid_type= REAL_RESULT; 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) { max_length= args[0]->max_length; @@ -1927,10 +1957,9 @@ void Item_func_round::fix_length_and_dec() max_length= float_length(decimals); break; case INT_RESULT: - if (!decimals_to_set && - (truncate || (args[0]->decimal_precision() < DECIMAL_LONGLONG_DIGITS))) + if ((!decimals_to_set && 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; /* Here we can keep INT_RESULT */ hybrid_type= INT_RESULT; @@ -1956,10 +1985,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; - 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 This will fix that the test round(0.1,1) = round(0.1,1) is true @@ -1969,7 +2000,11 @@ double my_double_round(double value, int dec, bool truncate) tmp=(abs_dec < array_elements(log_10) ? 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) tmp2= dec < 0 ? floor(value/tmp)*tmp : floor(value*tmp)/tmp; @@ -1985,24 +2020,35 @@ double my_double_round(double value, int dec, bool truncate) double Item_func_round::real_op() { double value= args[0]->val_real(); - int dec= (int) args[1]->val_int(); 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; } +/* + 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 value= args[0]->val_int(); - int dec=(int) args[1]->val_int(); + longlong dec= args[1]->val_int(); decimals= 0; - uint abs_dec; + ulonglong abs_dec; if ((null_value= args[0]->null_value || args[1]->null_value)) return 0; - if (dec >= 0) + if ((dec >= 0) || args[1]->unsigned_flag) return value; // integer have not digits after point abs_dec= -dec; @@ -2014,21 +2060,12 @@ longlong Item_func_round::int_op() tmp= log_10_int[abs_dec]; if (truncate) - { - if (unsigned_flag) - value= (ulonglong(value)/tmp)*tmp; - else - value= (value/tmp)*tmp; - } + value= (unsigned_flag) ? + ((ulonglong) value / tmp) * tmp : (value / tmp) * tmp; else - { - 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; - } + value= (unsigned_flag || value >= 0) ? + my_unsigned_round((ulonglong) value, tmp) : + -my_unsigned_round((ulonglong) -value, tmp); return value; } @@ -2036,14 +2073,18 @@ longlong Item_func_round::int_op() my_decimal *Item_func_round::decimal_op(my_decimal *decimal_value) { my_decimal val, *value= args[0]->val_decimal(&val); - int dec=(int) args[1]->val_int(); - if (dec > 0) + longlong dec= args[1]->val_int(); + 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 || - my_decimal_round(E_DEC_FATAL_ERROR, value, dec, truncate, - decimal_value) > 1))) + my_decimal_round(E_DEC_FATAL_ERROR, value, dec, + truncate, decimal_value) > 1))) return decimal_value; return 0; } diff --git a/sql/item_func.h b/sql/item_func.h index 3306b0590971dea5c430e1b85a3d8433423ee23a..f1e3295481c3e39595f97e2da7fdcae860731a0d 100644 --- a/sql/item_func.h +++ b/sql/item_func.h @@ -280,7 +280,7 @@ public: { 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) {} - double val_real() { DBUG_ASSERT(fixed == 1); return (double) val_int(); } + double val_real(); String *val_str(String*str); enum Item_result result_type () const { return INT_RESULT; } void fix_length_and_dec() {} @@ -305,12 +305,6 @@ class Item_func_signed :public Item_int_func public: Item_func_signed(Item *a) :Item_int_func(a) {} 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_from_str(int *error); void fix_length_and_dec() diff --git a/sql/item_strfunc.cc b/sql/item_strfunc.cc index a011a2884c810f4fe947fc72f9e04e5a1b0f0098..572ec9727174a0d34222da8ad6c7b8ddadfdc037 100644 --- a/sql/item_strfunc.cc +++ b/sql/item_strfunc.cc @@ -1951,7 +1951,7 @@ String *Item_func_format::val_str(String *str) double nr= args[0]->val_real(); if ((null_value=args[0]->null_value)) return 0; /* purecov: inspected */ - nr= my_double_round(nr, dec, FALSE); + nr= my_double_round(nr, (longlong) dec, FALSE, FALSE); /* Here default_charset() is right as this is not an automatic conversion */ str->set_real(nr, dec, default_charset()); if (isnan(nr)) diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index 099c9b98c1f92b418dedc15eed8a6f28f3046c43..904d18d690c9713886b1083ff19f2618ab382e51 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -1866,7 +1866,8 @@ ha_rows filesort(THD *thd, TABLE *form,struct st_sort_field *sortorder, ha_rows *examined_rows); void filesort_free_buffers(TABLE *table, bool full); 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 calc_weekday(long daynr,bool sunday_first_day_of_week);