summaryrefslogtreecommitdiff
path: root/sql
diff options
context:
space:
mode:
authorunknown <kaa@polly.local>2007-04-28 20:08:58 +0400
committerunknown <kaa@polly.local>2007-04-28 20:08:58 +0400
commit46ca37d27026a28d3238a8f45647ee33153977ca (patch)
treebde7852f6352f14583b5c36df3e0aeaf29e1b85b /sql
parentbb95063a9f66b09028920b1c000a8137385208bd (diff)
parent59a35c9afcdcf9dd9cd9f127dcd417ac7dc0e2c1 (diff)
downloadmariadb-git-46ca37d27026a28d3238a8f45647ee33153977ca.tar.gz
Merge polly.local:/home/kaa/src/maint/bug24912/my51-bug24912
into polly.local:/home/kaa/src/maint/mysql-5.1-maint sql/item_func.cc: Auto merged sql/item_func.h: Auto merged sql/item_strfunc.cc: Auto merged sql/mysql_priv.h: Auto merged
Diffstat (limited to 'sql')
-rw-r--r--sql/item_func.cc115
-rw-r--r--sql/item_func.h8
-rw-r--r--sql/item_strfunc.cc2
-rw-r--r--sql/mysql_priv.h3
4 files changed, 82 insertions, 46 deletions
diff --git a/sql/item_func.cc b/sql/item_func.cc
index 830b033a067..1c8ff8f88d7 100644
--- a/sql/item_func.cc
+++ b/sql/item_func.cc
@@ -620,6 +620,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);
@@ -802,7 +810,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:
@@ -1362,6 +1373,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)
@@ -1371,9 +1384,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()
@@ -1428,6 +1445,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;
}
@@ -1506,8 +1524,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;
}
@@ -1528,6 +1547,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;
}
@@ -1902,6 +1922,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())
{
@@ -1910,8 +1934,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;
@@ -1928,10 +1958,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;
@@ -1957,10 +1986,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
@@ -1970,7 +2001,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;
@@ -1986,24 +2021,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;
@@ -2015,21 +2061,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;
}
@@ -2037,14 +2074,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 28f11e27306..0c989782673 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 4e28967b2b0..a1fd79cbfb0 100644
--- a/sql/item_strfunc.cc
+++ b/sql/item_strfunc.cc
@@ -2028,7 +2028,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 68f5ff022a4..93f290b12e7 100644
--- a/sql/mysql_priv.h
+++ b/sql/mysql_priv.h
@@ -1890,7 +1890,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);