summaryrefslogtreecommitdiff
path: root/sql/item_func.cc
diff options
context:
space:
mode:
authorunknown <kaa@polly.local>2007-04-28 20:04:03 +0400
committerunknown <kaa@polly.local>2007-04-28 20:04:03 +0400
commit59a35c9afcdcf9dd9cd9f127dcd417ac7dc0e2c1 (patch)
treec87d798baa97ec3d7f94149623cfb252e838756e /sql/item_func.cc
parent272563d81bfd3a736aaf7cabc5bd7e2587adbe47 (diff)
parent38090df9b566da89942f8a90fe71a3fac31efb9d (diff)
downloadmariadb-git-59a35c9afcdcf9dd9cd9f127dcd417ac7dc0e2c1.tar.gz
Merge polly.local:/home/kaa/src/maint/bug24912/my50-bug24912
into polly.local:/home/kaa/src/maint/bug24912/my51-bug24912 mysql-test/r/type_newdecimal.result: Auto merged mysql-test/t/func_math.test: Auto merged sql/item_func.cc: Auto merged sql/item_func.h: Auto merged sql/mysql_priv.h: Auto merged mysql-test/r/func_math.result: Manual merge. sql/item_strfunc.cc: Manual merge.
Diffstat (limited to 'sql/item_func.cc')
-rw-r--r--sql/item_func.cc115
1 files changed, 78 insertions, 37 deletions
diff --git a/sql/item_func.cc b/sql/item_func.cc
index c823575b240..c823fe12d25 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;
}