summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--BitKeeper/etc/logging_ok1
-rw-r--r--include/decimal.h2
-rw-r--r--sql/field.cc92
-rw-r--r--sql/field.h16
-rw-r--r--sql/ha_ndbcluster.cc2
-rw-r--r--sql/item.cc76
-rw-r--r--sql/item.h13
-rw-r--r--sql/item_cmpfunc.cc31
-rw-r--r--sql/item_cmpfunc.h9
-rw-r--r--sql/item_func.cc181
-rw-r--r--sql/item_func.h8
-rw-r--r--sql/item_sum.cc83
-rw-r--r--sql/item_sum.h15
-rw-r--r--sql/my_decimal.cc20
-rw-r--r--sql/my_decimal.h46
-rw-r--r--sql/mysqld.cc8
-rw-r--r--sql/set_var.cc4
-rw-r--r--sql/sp_head.cc5
-rw-r--r--sql/sql_class.h1
-rw-r--r--sql/sql_parse.cc7
-rw-r--r--sql/sql_select.cc5
-rw-r--r--sql/sql_show.cc7
-rw-r--r--strings/decimal.c26
23 files changed, 417 insertions, 241 deletions
diff --git a/BitKeeper/etc/logging_ok b/BitKeeper/etc/logging_ok
index 101e845ccf1..01d80957543 100644
--- a/BitKeeper/etc/logging_ok
+++ b/BitKeeper/etc/logging_ok
@@ -82,6 +82,7 @@ hf@bisonxp.(none)
hf@deer.(none)
hf@deer.mysql.r18.ru
hf@genie.(none)
+holyfoot@mysql.com
igor@hundin.mysql.fi
igor@linux.local
igor@rurik.mysql.com
diff --git a/include/decimal.h b/include/decimal.h
index 7b49841ca88..2648e04c1cf 100644
--- a/include/decimal.h
+++ b/include/decimal.h
@@ -39,7 +39,7 @@ int decimal2longlong(decimal_t *from, longlong *to);
int longlong2decimal(longlong from, decimal_t *to);
int decimal2double(decimal_t *from, double *to);
int double2decimal(double from, decimal_t *to);
-void decimal_optimize_fraction(decimal_t *from);
+int decimal_actual_fraction(decimal_t *from);
int decimal2bin(decimal_t *from, char *to, int precision, int scale);
int bin2decimal(char *from, decimal_t *to, int precision, int scale);
diff --git a/sql/field.cc b/sql/field.cc
index c59d9b63fca..78266441bda 100644
--- a/sql/field.cc
+++ b/sql/field.cc
@@ -2229,12 +2229,6 @@ void Field_decimal::sql_type(String &res) const
** Field_new_decimal
****************************************************************************/
-/*
- Constructors of new decimal field. In case of using NOT_FIXED_DEC it try
- to use maximally allowed length (DECIMAL_MAX_LENGTH) and number of digits
- after decimal point maximally close to half of this range
- (min(DECIMAL_MAX_LENGTH/2, NOT_FIXED_DEC-1))
-*/
Field_new_decimal::Field_new_decimal(char *ptr_arg,
uint32 len_arg, uchar *null_ptr_arg,
uchar null_bit_arg,
@@ -2243,17 +2237,15 @@ Field_new_decimal::Field_new_decimal(char *ptr_arg,
struct st_table *table_arg,
uint8 dec_arg,bool zero_arg,
bool unsigned_arg)
- :Field_num(ptr_arg,
- (dec_arg == NOT_FIXED_DEC || len_arg > DECIMAL_MAX_LENGTH ?
- DECIMAL_MAX_LENGTH : len_arg),
+ :Field_num(ptr_arg, len_arg,
null_ptr_arg, null_bit_arg,
unireg_check_arg, field_name_arg, table_arg,
- (dec_arg == NOT_FIXED_DEC ?
- min(DECIMAL_MAX_LENGTH / 2, NOT_FIXED_DEC - 1) :
- dec_arg),
- zero_arg, unsigned_arg)
+ dec_arg, zero_arg, unsigned_arg)
{
- bin_size= my_decimal_get_binary_size(field_length, dec);
+ precision= my_decimal_length_to_precision(len_arg, dec_arg, unsigned_arg);
+ DBUG_ASSERT((precision <= DECIMAL_MAX_PRECISION) &&
+ (dec <= DECIMAL_MAX_SCALE));
+ bin_size= my_decimal_get_binary_size(precision, dec);
}
@@ -2261,18 +2253,18 @@ Field_new_decimal::Field_new_decimal(uint32 len_arg,
bool maybe_null,
const char *name,
struct st_table *t_arg,
- uint8 dec_arg)
- :Field_num((char*) 0,
- (dec_arg == NOT_FIXED_DEC|| len_arg > DECIMAL_MAX_LENGTH ?
- DECIMAL_MAX_LENGTH : len_arg),
+ uint8 dec_arg,
+ bool unsigned_arg)
+ :Field_num((char*) 0, len_arg,
maybe_null ? (uchar*) "": 0, 0,
NONE, name, t_arg,
- (dec_arg == NOT_FIXED_DEC ?
- min(DECIMAL_MAX_LENGTH / 2, NOT_FIXED_DEC - 1) :
- dec_arg),
- 0, 0)
+ dec_arg,
+ 0, unsigned_arg)
{
- bin_size= my_decimal_get_binary_size(field_length, dec);
+ precision= my_decimal_length_to_precision(len_arg, dec_arg, unsigned_arg);
+ DBUG_ASSERT((precision <= DECIMAL_MAX_PRECISION) &&
+ (dec <= DECIMAL_MAX_SCALE));
+ bin_size= my_decimal_get_binary_size(precision, dec);
}
@@ -2295,7 +2287,7 @@ void Field_new_decimal::set_value_on_overflow(my_decimal *decimal_value,
bool sign)
{
DBUG_ENTER("Field_new_decimal::set_value_on_overflow");
- max_my_decimal(decimal_value, field_length, decimals());
+ max_my_decimal(decimal_value, precision, decimals());
if (sign)
{
if (unsigned_flag)
@@ -2326,10 +2318,14 @@ void Field_new_decimal::set_value_on_overflow(my_decimal *decimal_value,
bool Field_new_decimal::store_value(const my_decimal *decimal_value)
{
- my_decimal *dec= (my_decimal*)decimal_value;
int error= 0;
DBUG_ENTER("Field_new_decimal::store_value");
- dbug_print_decimal("enter", "value: %s", dec);
+#ifndef DBUG_OFF
+ {
+ char dbug_buff[DECIMAL_MAX_STR_LENGTH+1];
+ DBUG_PRINT("enter", ("value: %s", dbug_decimal_as_string(dbug_buff, decimal_value)));
+ }
+#endif
/* check that we do not try to write negative value in unsigned field */
if (unsigned_flag && decimal_value->sign())
@@ -2337,25 +2333,27 @@ bool Field_new_decimal::store_value(const my_decimal *decimal_value)
DBUG_PRINT("info", ("unsigned overflow"));
set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
error= 1;
- dec= &decimal_zero;
+ decimal_value= &decimal_zero;
+ }
+#ifndef DBUG_OFF
+ {
+ char dbug_buff[DECIMAL_MAX_STR_LENGTH+1];
+ DBUG_PRINT("info", ("saving with precision %d, scale: %d, value %s",
+ (int)precision, (int)dec,
+ dbug_decimal_as_string(dbug_buff, decimal_value)));
}
- DBUG_PRINT("info", ("saving with precision %d, scale: %d",
- (int)field_length, (int)decimals()));
- dbug_print_decimal("info", "value: %s", dec);
+#endif
- if (warn_if_overflow(my_decimal2binary(E_DEC_FATAL_ERROR &
- ~E_DEC_OVERFLOW,
- dec, ptr,
- field_length,
- decimals())))
+ if (warn_if_overflow(my_decimal2binary(E_DEC_FATAL_ERROR & ~E_DEC_OVERFLOW,
+ decimal_value, ptr, precision, dec)))
{
my_decimal buff;
DBUG_PRINT("info", ("overflow"));
- set_value_on_overflow(&buff, dec->sign());
- my_decimal2binary(E_DEC_FATAL_ERROR, &buff, ptr, field_length, decimals());
+ set_value_on_overflow(&buff, decimal_value->sign());
+ my_decimal2binary(E_DEC_FATAL_ERROR, &buff, ptr, precision, dec);
error= 1;
}
- DBUG_EXECUTE("info", print_decimal_buff(dec, (byte *) ptr, bin_size););
+ DBUG_EXECUTE("info", print_decimal_buff(decimal_value, (byte *) ptr, bin_size););
DBUG_RETURN(error);
}
@@ -2387,7 +2385,11 @@ int Field_new_decimal::store(const char *from, uint length,
break;
}
- dbug_print_decimal("enter", "value: %s", &decimal_value);
+#ifndef DBUG_OFF
+ char dbug_buff[DECIMAL_MAX_STR_LENGTH+1];
+ DBUG_PRINT("enter", ("value: %s",
+ dbug_decimal_as_string(dbug_buff, &decimal_value)));
+#endif
store_value(&decimal_value);
DBUG_RETURN(err);
}
@@ -2477,8 +2479,7 @@ my_decimal* Field_new_decimal::val_decimal(my_decimal *decimal_value)
{
DBUG_ENTER("Field_new_decimal::val_decimal");
binary2my_decimal(E_DEC_FATAL_ERROR, ptr, decimal_value,
- field_length,
- decimals());
+ precision, dec);
DBUG_EXECUTE("info", print_decimal_buff(decimal_value, (byte *) ptr,
bin_size););
DBUG_RETURN(decimal_value);
@@ -2489,12 +2490,9 @@ String *Field_new_decimal::val_str(String *val_buffer,
String *val_ptr __attribute__((unused)))
{
my_decimal decimal_value;
- int fixed_precision= (zerofill ?
- (field_length + (decimals() ? 1 : 0)) :
- 0);
+ uint fixed_precision= zerofill ? precision : 0;
my_decimal2string(E_DEC_FATAL_ERROR, val_decimal(&decimal_value),
- fixed_precision, decimals(), '0',
- val_buffer);
+ fixed_precision, dec, '0', val_buffer);
return val_buffer;
}
@@ -2516,7 +2514,7 @@ void Field_new_decimal::sql_type(String &str) const
{
CHARSET_INFO *cs= str.charset();
str.length(cs->cset->snprintf(cs, (char*) str.ptr(), str.alloced_length(),
- "decimal(%d,%d)", field_length, (int)dec));
+ "decimal(%d,%d)", precision, (int)dec));
add_zerofill_and_unsigned(str);
}
diff --git a/sql/field.h b/sql/field.h
index ac9c2f351b3..853b5dd13ff 100644
--- a/sql/field.h
+++ b/sql/field.h
@@ -300,8 +300,6 @@ public:
int warn_if_overflow(int op_result);
/* maximum possible display length */
virtual uint32 max_length()= 0;
- /* length of field value symbolic representation (in bytes) */
- virtual uint32 representation_length() { return field_length; }
/* convert decimal to longlong with overflow check */
longlong convert_decimal2longlong(const my_decimal *val, bool unsigned_flag,
int *err);
@@ -438,7 +436,13 @@ public:
/* New decimal/numeric field which use fixed point arithmetic */
class Field_new_decimal :public Field_num {
public:
+ /* The maximum number of decimal digits can be stored */
+ uint precision;
uint bin_size;
+ /* Constructors take max_length of the field as a parameter - not the */
+ /* precision as the number of decimal digits allowed */
+ /* So for example we need to count length from precision handling */
+ /* CREATE TABLE ( DECIMAL(x,y)) */
Field_new_decimal(char *ptr_arg, uint32 len_arg, uchar *null_ptr_arg,
uchar null_bit_arg,
enum utype unireg_check_arg, const char *field_name_arg,
@@ -446,7 +450,8 @@ public:
uint8 dec_arg, bool zero_arg, bool unsigned_arg);
Field_new_decimal(uint32 len_arg, bool maybe_null_arg,
const char *field_name_arg,
- struct st_table *table_arg, uint8 dec_arg);
+ struct st_table *table_arg, uint8 dec_arg,
+ bool unsigned_arg);
enum_field_types type() const { return FIELD_TYPE_NEWDECIMAL;}
enum ha_base_keytype key_type() const { return HA_KEYTYPE_BINARY; }
Item_result result_type () const { return DECIMAL_RESULT; }
@@ -465,10 +470,7 @@ public:
void sort_string(char *buff, uint length);
bool zero_pack() const { return 0; }
void sql_type(String &str) const;
- uint32 max_length()
- { return field_length + 1 + (dec ? 1 : 0) + (field_length == dec ? 1 : 0); }
- uint32 representation_length()
- { return field_length + 1 + (dec ? 1 : 0) + (field_length == dec ? 1 : 0); };
+ uint32 max_length() { return field_length; }
uint size_of() const { return sizeof(*this); }
uint32 pack_length() const { return (uint32) bin_size; }
};
diff --git a/sql/ha_ndbcluster.cc b/sql/ha_ndbcluster.cc
index b61dbd1792c..99fb05b24d3 100644
--- a/sql/ha_ndbcluster.cc
+++ b/sql/ha_ndbcluster.cc
@@ -3621,7 +3621,7 @@ static int create_ndb_column(NDBCOL &col,
case MYSQL_TYPE_NEWDECIMAL:
{
Field_new_decimal *f= (Field_new_decimal*)field;
- uint precision= f->field_length;
+ uint precision= f->precision;
uint scale= f->decimals();
if (field->flags & UNSIGNED_FLAG)
{
diff --git a/sql/item.cc b/sql/item.cc
index a2649d7506f..f10b24e48e6 100644
--- a/sql/item.cc
+++ b/sql/item.cc
@@ -80,7 +80,7 @@ Hybrid_type_traits_decimal::fix_length_and_dec(Item *item, Item *arg) const
{
item->decimals= arg->decimals;
item->max_length= min(arg->max_length + DECIMAL_LONGLONG_DIGITS,
- DECIMAL_MAX_LENGTH);
+ DECIMAL_MAX_STR_LENGTH);
}
@@ -348,6 +348,17 @@ Item::Item(THD *thd, Item *item):
}
+uint Item::decimal_precision() const
+{
+ Item_result restype= result_type();
+
+ if ((restype == DECIMAL_RESULT) || (restype == INT_RESULT))
+ return min(my_decimal_length_to_precision(max_length, decimals, unsigned_flag),
+ DECIMAL_MAX_PRECISION);
+ return min(max_length, DECIMAL_MAX_PRECISION);
+}
+
+
void Item::print_item_w_name(String *str)
{
print(str);
@@ -938,10 +949,8 @@ bool DTCollation::aggregate(DTCollation &dt, uint flags)
return 1;
}
if (collation->state & MY_CS_BINSORT)
- {
return 0;
- }
- else if (dt.collation->state & MY_CS_BINSORT)
+ if (dt.collation->state & MY_CS_BINSORT)
{
set(dt);
return 0;
@@ -1021,7 +1030,7 @@ void Item_field::set_field(Field *field_par)
field=result_field=field_par; // for easy coding with fields
maybe_null=field->maybe_null();
decimals= field->decimals();
- max_length= field_par->representation_length();
+ max_length= field_par->field_length;
table_name= *field_par->table_name;
field_name= field_par->field_name;
db_name= field_par->table->s->db;
@@ -1359,18 +1368,18 @@ Item_decimal::Item_decimal(const char *str_arg, uint length,
str2my_decimal(E_DEC_FATAL_ERROR, str_arg, length, charset, &decimal_value);
name= (char*) str_arg;
decimals= (uint8) decimal_value.frac;
- max_length= my_decimal_max_length(&decimal_value);
fixed= 1;
- unsigned_flag= !decimal_value.sign();
+ max_length= my_decimal_precision_to_length(decimal_value.intg + decimals,
+ decimals, unsigned_flag);
}
Item_decimal::Item_decimal(longlong val, bool unsig)
{
int2my_decimal(E_DEC_FATAL_ERROR, val, unsig, &decimal_value);
decimals= (uint8) decimal_value.frac;
- max_length= my_decimal_max_length(&decimal_value);
fixed= 1;
- unsigned_flag= !decimal_value.sign();
+ max_length= my_decimal_precision_to_length(decimal_value.intg + decimals,
+ decimals, unsigned_flag);
}
@@ -1378,9 +1387,9 @@ Item_decimal::Item_decimal(double val, int precision, int scale)
{
double2my_decimal(E_DEC_FATAL_ERROR, val, &decimal_value);
decimals= (uint8) decimal_value.frac;
- max_length= my_decimal_max_length(&decimal_value);
fixed= 1;
- unsigned_flag= !decimal_value.sign();
+ max_length= my_decimal_precision_to_length(decimal_value.intg + decimals,
+ decimals, unsigned_flag);
}
@@ -1392,7 +1401,6 @@ Item_decimal::Item_decimal(const char *str, const my_decimal *val_arg,
decimals= (uint8) decimal_par;
max_length= length;
fixed= 1;
- unsigned_flag= !decimal_value.sign();
}
@@ -1400,19 +1408,20 @@ Item_decimal::Item_decimal(my_decimal *value_par)
{
my_decimal2decimal(value_par, &decimal_value);
decimals= (uint8) decimal_value.frac;
- max_length= my_decimal_max_length(value_par);
fixed= 1;
- unsigned_flag= !decimal_value.sign();
+ max_length= my_decimal_precision_to_length(decimal_value.intg + decimals,
+ decimals, !decimal_value.sign());
}
Item_decimal::Item_decimal(const char *bin, int precision, int scale)
{
- binary2my_decimal(E_DEC_FATAL_ERROR, bin, &decimal_value, precision, scale);
+ binary2my_decimal(E_DEC_FATAL_ERROR, bin,
+ &decimal_value, precision, scale);
decimals= (uint8) decimal_value.frac;
- max_length= my_decimal_max_length(&decimal_value);
fixed= 1;
- unsigned_flag= !decimal_value.sign();
+ max_length= my_decimal_precision_to_length(precision, decimals,
+ !decimal_value.sign());
}
@@ -1672,7 +1681,8 @@ void Item_param::set_decimal(const char *str, ulong length)
str2my_decimal(E_DEC_FATAL_ERROR, str, &decimal_value, &end);
state= DECIMAL_VALUE;
decimals= decimal_value.frac;
- max_length= decimal_value.intg + decimals + 2;
+ max_length= my_decimal_precision_to_length(decimal_value.precision(),
+ decimals, unsigned_flag);
maybe_null= 0;
DBUG_VOID_RETURN;
}
@@ -1823,7 +1833,8 @@ bool Item_param::set_from_user_var(THD *thd, const user_var_entry *entry)
my_decimal2decimal(ent_value, &decimal_value);
state= DECIMAL_VALUE;
decimals= ent_value->frac;
- max_length= ent_value->intg + decimals + 2;
+ max_length= my_decimal_precision_to_length(ent_value->precision(),
+ decimals, unsigned_flag);
break;
}
default:
@@ -3173,11 +3184,8 @@ Field *Item::tmp_table_field_from_field_type(TABLE *table)
switch (field_type()) {
case MYSQL_TYPE_DECIMAL:
- return new Field_decimal((char*) 0, max_length, null_ptr, 0, Field::NONE,
- name, table, decimals, 0, unsigned_flag);
case MYSQL_TYPE_NEWDECIMAL:
- return new Field_new_decimal((char*) 0, max_length - (decimals?1:0),
- null_ptr, 0,
+ return new Field_new_decimal((char*) 0, max_length, null_ptr, 0,
Field::NONE, name, table, decimals, 0,
unsigned_flag);
case MYSQL_TYPE_TINY:
@@ -4878,6 +4886,7 @@ Item_type_holder::Item_type_holder(THD *thd, Item *item)
/* fix variable decimals which always is NOT_FIXED_DEC */
if (Field::result_merge_type(fld_type) == INT_RESULT)
decimals= 0;
+ prev_decimal_int_part= item->decimal_int_part();
}
@@ -5000,18 +5009,12 @@ bool Item_type_holder::join_types(THD *thd, Item *item)
}
if (Field::result_merge_type(fld_type) == DECIMAL_RESULT)
{
- int item_length= display_length(item);
- int intp1= item_length - min(item->decimals, NOT_FIXED_DEC - 1);
- int intp2= max_length - min(decimals, NOT_FIXED_DEC - 1);
- /* can't be overflow because it work only for decimals (no strings) */
- int dec_length= max(intp1, intp2) + decimals;
- max_length= max(max_length, (uint) max(item_length, dec_length));
- /*
- we can't allow decimals to be NOT_FIXED_DEC, to prevent creation
- decimal with max precision (see Field_new_decimal constcuctor)
- */
- if (decimals >= NOT_FIXED_DEC)
- decimals= NOT_FIXED_DEC - 1;
+ decimals= min(max(decimals, item->decimals), DECIMAL_MAX_SCALE);
+ int precision= min(max(prev_decimal_int_part, item->decimal_int_part())
+ + decimals, DECIMAL_MAX_PRECISION);
+ unsigned_flag&= item->unsigned_flag;
+ max_length= my_decimal_precision_to_length(precision, decimals,
+ unsigned_flag);
}
else
max_length= max(max_length, display_length(item));
@@ -5032,6 +5035,9 @@ bool Item_type_holder::join_types(THD *thd, Item *item)
}
maybe_null|= item->maybe_null;
get_full_info(item);
+
+ /* Remember decimal integer part to be used in DECIMAL_RESULT handleng */
+ prev_decimal_int_part= decimal_int_part();
DBUG_PRINT("info", ("become type: %d len: %u dec: %u",
(int) fld_type, max_length, (uint) decimals));
DBUG_RETURN(FALSE);
diff --git a/sql/item.h b/sql/item.h
index f762896ba34..e5ec68d606e 100644
--- a/sql/item.h
+++ b/sql/item.h
@@ -258,7 +258,7 @@ public:
Item *next;
uint32 max_length;
uint name_length; /* Length of name */
- uint8 marker,decimals;
+ uint8 marker, decimals;
my_bool maybe_null; /* If item may be null */
my_bool null_value; /* if item is null */
my_bool unsigned_flag;
@@ -442,6 +442,9 @@ public:
virtual cond_result eq_cmp_result() const { return COND_OK; }
inline uint float_length(uint decimals_par) const
{ return decimals != NOT_FIXED_DEC ? (DBL_DIG+2+decimals_par) : DBL_DIG+8;}
+ virtual uint decimal_precision() const;
+ inline int decimal_int_part() const
+ { return my_decimal_int_part(decimal_precision(), decimals); }
/*
Returns true if this is constant (during query execution, i.e. its value
will not change until next fix_fields) and its value is known.
@@ -937,7 +940,7 @@ public:
{ max_length=length; fixed= 1; }
#ifdef HAVE_LONG_LONG
Item_int(longlong i,uint length=21) :value(i)
- { max_length=length; fixed= 1;}
+ { max_length=length; fixed= 1; }
#endif
Item_int(const char *str_arg,longlong i,uint length) :value(i)
{ max_length=length; name=(char*) str_arg; fixed= 1; }
@@ -956,6 +959,7 @@ public:
void cleanup() {}
void print(String *str);
Item_num *neg() { value= -value; return this; }
+ uint decimal_precision() const { return (uint)(max_length - test(value < 0)); }
};
@@ -983,6 +987,7 @@ public:
int save_in_field(Field *field, bool no_conversions);
void print(String *str);
Item_num *neg ();
+ uint decimal_precision() const { return max_length; }
};
@@ -1022,6 +1027,7 @@ public:
unsigned_flag= !decimal_value.sign();
return this;
}
+ uint decimal_precision() const { return decimal_value.precision(); }
};
class Item_float :public Item_num
@@ -1784,6 +1790,9 @@ protected:
enum_field_types fld_type;
void get_full_info(Item *item);
+
+ /* It is used to count decimal precision in join_types */
+ int prev_decimal_int_part;
public:
Item_type_holder(THD*, Item*);
diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc
index 66354560756..8c44972e469 100644
--- a/sql/item_cmpfunc.cc
+++ b/sql/item_cmpfunc.cc
@@ -1134,6 +1134,14 @@ Item_func_ifnull::fix_length_and_dec()
cached_field_type= Item_func::field_type();
}
+
+uint Item_func_ifnull::decimal_precision() const
+{
+ int max_int_part=max(args[0]->decimal_int_part(),args[1]->decimal_int_part());
+ return min(max_int_part + decimals, DECIMAL_MAX_PRECISION);
+}
+
+
enum_field_types Item_func_ifnull::field_type() const
{
return cached_field_type;
@@ -1251,6 +1259,14 @@ Item_func_if::fix_length_and_dec()
}
+uint Item_func_if::decimal_precision() const
+{
+ int precision=(max(args[1]->decimal_int_part(),args[2]->decimal_int_part())+
+ decimals);
+ return min(precision, DECIMAL_MAX_PRECISION);
+}
+
+
double
Item_func_if::val_real()
{
@@ -1304,7 +1320,8 @@ Item_func_nullif::fix_length_and_dec()
{
max_length=args[0]->max_length;
decimals=args[0]->decimals;
- agg_result_type(&cached_result_type, args, 2);
+ unsigned_flag= args[0]->unsigned_flag;
+ cached_result_type= args[0]->result_type();
if (cached_result_type == STRING_RESULT &&
agg_arg_charsets(collation, args, arg_count, MY_COLL_CMP_CONV))
return;
@@ -1616,6 +1633,18 @@ void Item_func_case::fix_length_and_dec()
}
+uint Item_func_case::decimal_precision() const
+{
+ int max_int_part=0;
+ for (uint i=0 ; i < ncases ; i+=2)
+ set_if_bigger(max_int_part, args[i+1]->decimal_int_part());
+
+ if (else_expr_num != -1)
+ set_if_bigger(max_int_part, args[else_expr_num]->decimal_int_part());
+ return min(max_int_part + decimals, DECIMAL_MAX_PRECISION);
+}
+
+
/* TODO: Fix this so that it prints the whole CASE expression */
void Item_func_case::print(String *str)
diff --git a/sql/item_cmpfunc.h b/sql/item_cmpfunc.h
index e917e13c5aa..78357c457cb 100644
--- a/sql/item_cmpfunc.h
+++ b/sql/item_cmpfunc.h
@@ -95,6 +95,7 @@ public:
Item_bool_func(THD *thd, Item_bool_func *item) :Item_int_func(thd, item) {}
bool is_bool_func() { return 1; }
void fix_length_and_dec() { decimals=0; max_length=1; }
+ uint decimal_precision() const { return 1; }
};
class Item_cache;
@@ -208,6 +209,7 @@ public:
bool is_null() { return test(args[0]->is_null() || args[1]->is_null()); }
bool is_bool_func() { return 1; }
CHARSET_INFO *compare_collation() { return cmp.cmp_collation.collation; }
+ uint decimal_precision() const { return 1; }
friend class Arg_comparator;
};
@@ -411,6 +413,7 @@ public:
void fix_length_and_dec();
void print(String *str);
CHARSET_INFO *compare_collation() { return cmp_collation.collation; }
+ uint decimal_precision() const { return 1; }
};
@@ -445,6 +448,7 @@ public:
longlong val_int();
void fix_length_and_dec();
const char *func_name() const { return "interval"; }
+ uint decimal_precision() const { return 2; }
};
@@ -485,6 +489,7 @@ public:
void fix_length_and_dec();
const char *func_name() const { return "ifnull"; }
Field *tmp_table_field(TABLE *table);
+ uint decimal_precision() const;
};
@@ -507,6 +512,7 @@ public:
return Item_func::fix_fields(thd, tlist, ref);
}
void fix_length_and_dec();
+ uint decimal_precision() const;
const char *func_name() const { return "if"; }
table_map not_null_tables() const { return 0; }
};
@@ -525,6 +531,7 @@ public:
my_decimal *val_decimal(my_decimal *);
enum Item_result result_type () const { return cached_result_type; }
void fix_length_and_dec();
+ uint decimal_precision() const { return args[0]->decimal_precision(); }
const char *func_name() const { return "nullif"; }
void print(String *str) { Item_func::print(str); }
table_map not_null_tables() const { return 0; }
@@ -563,6 +570,7 @@ public:
String *val_str(String *);
my_decimal *val_decimal(my_decimal *);
void fix_length_and_dec();
+ uint decimal_precision() const;
table_map not_null_tables() const { return 0; }
enum Item_result result_type () const { return cached_result_type; }
const char *func_name() const { return "case"; }
@@ -825,6 +833,7 @@ class Item_func_in :public Item_int_func
}
longlong val_int();
void fix_length_and_dec();
+ uint decimal_precision() const { return 1; }
void cleanup()
{
DBUG_ENTER("Item_func_in::cleanup");
diff --git a/sql/item_func.cc b/sql/item_func.cc
index 4e3011ac4bb..546120ba5cd 100644
--- a/sql/item_func.cc
+++ b/sql/item_func.cc
@@ -534,8 +534,10 @@ Field *Item_func::tmp_table_field(TABLE *t_arg)
res= make_string_field(t_arg);
break;
case DECIMAL_RESULT:
- res= new Field_new_decimal(max_length + (decimals?1:0), maybe_null,
- name, t_arg, decimals);
+ res= new Field_new_decimal(my_decimal_precision_to_length(decimal_precision(),
+ decimals,
+ unsigned_flag),
+ maybe_null, name, t_arg, decimals, unsigned_flag);
break;
case ROW_RESULT:
default:
@@ -590,19 +592,18 @@ void Item_func_numhybrid::fix_num_length_and_dec()
void Item_func::count_decimal_length()
{
- uint32 length= 0;
+ int max_int_part= 0;
decimals= 0;
+ unsigned_flag= 1;
for (uint i=0 ; i < arg_count ; i++)
{
set_if_bigger(decimals, args[i]->decimals);
- set_if_bigger(length, (args[i]->max_length - args[i]->decimals));
+ set_if_bigger(max_int_part, args[i]->decimal_int_part());
+ set_if_smaller(unsigned_flag, args[i]->unsigned_flag);
}
- max_length= length;
- length+= decimals;
- if (length < max_length) // If previous operation gave overflow
- max_length= UINT_MAX32;
- else
- max_length= length;
+ int precision= min(max_int_part + decimals, DECIMAL_MAX_PRECISION);
+ max_length= my_decimal_precision_to_length(precision, decimals,
+ unsigned_flag);
}
@@ -616,8 +617,12 @@ void Item_func::count_decimal_length()
void Item_func::count_only_length()
{
max_length= 0;
+ unsigned_flag= 0;
for (uint i=0 ; i < arg_count ; i++)
+ {
set_if_bigger(max_length, args[i]->max_length);
+ set_if_bigger(unsigned_flag, args[i]->unsigned_flag);
+ }
}
@@ -719,7 +724,6 @@ void Item_num_op::find_num_type(void)
{
decimals= 0;
hybrid_type=INT_RESULT;
- unsigned_flag=args[0]->unsigned_flag | args[1]->unsigned_flag;
result_precision();
}
DBUG_PRINT("info", ("Type: %s",
@@ -1075,9 +1079,17 @@ my_decimal *Item_func_plus::decimal_op(my_decimal *decimal_value)
void Item_func_additive_op::result_precision()
{
decimals= max(args[0]->decimals, args[1]->decimals);
- max_length= (max(args[0]->max_length - args[0]->decimals,
- args[1]->max_length - args[1]->decimals) +
- decimals + 1);
+ int max_int_part= max(args[0]->decimal_precision() - args[0]->decimals,
+ args[1]->decimal_precision() - args[1]->decimals);
+ int precision= min(max_int_part + 1 + decimals, DECIMAL_MAX_PRECISION);
+
+ /* Integer operations keep unsigned_flag if one of arguments is unsigned */
+ if (result_type() == INT_RESULT)
+ unsigned_flag= args[0]->unsigned_flag | args[1]->unsigned_flag;
+ else
+ unsigned_flag= args[0]->unsigned_flag & args[1]->unsigned_flag;
+ max_length= my_decimal_precision_to_length(precision, decimals,
+ unsigned_flag);
}
@@ -1172,10 +1184,15 @@ my_decimal *Item_func_mul::decimal_op(my_decimal *decimal_value)
void Item_func_mul::result_precision()
{
- decimals= args[0]->decimals + args[1]->decimals;
- max_length= ((args[0]->max_length - args[0]->decimals) +
- (args[1]->max_length - args[1]->decimals) +
- decimals);
+ /* Integer operations keep unsigned_flag if one of arguments is unsigned */
+ if (result_type() == INT_RESULT)
+ unsigned_flag= args[0]->unsigned_flag | args[1]->unsigned_flag;
+ else
+ unsigned_flag= args[0]->unsigned_flag & args[1]->unsigned_flag;
+ decimals= min(args[0]->decimals + args[1]->decimals, DECIMAL_MAX_SCALE);
+ int precision= min(args[0]->decimal_precision() + args[1]->decimal_precision(),
+ DECIMAL_MAX_PRECISION);
+ max_length= my_decimal_precision_to_length(precision, decimals,unsigned_flag);
}
@@ -1207,7 +1224,7 @@ my_decimal *Item_func_div::decimal_op(my_decimal *decimal_value)
if ((null_value= args[1]->null_value))
return 0;
switch (my_decimal_div(E_DEC_FATAL_ERROR & ~E_DEC_DIV_ZERO, decimal_value,
- val1, val2, DECIMAL_DIV_SCALE_INCREASE)) {
+ val1, val2, prec_increment)) {
case E_DEC_TRUNCATED:
case E_DEC_OK:
return decimal_value;
@@ -1222,11 +1239,16 @@ my_decimal *Item_func_div::decimal_op(my_decimal *decimal_value)
void Item_func_div::result_precision()
{
- decimals= (args[0]->decimals + args[0]->decimals +
- DECIMAL_DIV_SCALE_INCREASE);
- max_length= ((args[0]->max_length - args[0]->decimals) +
- (args[1]->max_length - args[1]->decimals) +
- decimals);
+ uint precision=min(args[0]->decimal_precision() + prec_increment,
+ DECIMAL_MAX_PRECISION);
+ /* Integer operations keep unsigned_flag if one of arguments is unsigned */
+ if (result_type() == INT_RESULT)
+ unsigned_flag= args[0]->unsigned_flag | args[1]->unsigned_flag;
+ else
+ unsigned_flag= args[0]->unsigned_flag & args[1]->unsigned_flag;
+ decimals= min(args[0]->decimals + prec_increment, DECIMAL_MAX_SCALE);
+ max_length= my_decimal_precision_to_length(precision, decimals,
+ unsigned_flag);
}
@@ -1234,10 +1256,11 @@ void Item_func_div::fix_length_and_dec()
{
DBUG_ENTER("Item_func_div::fix_length_and_dec");
Item_num_op::fix_length_and_dec();
+ prec_increment= current_thd->variables.div_precincrement;
switch(hybrid_type) {
case REAL_RESULT:
{
- decimals=max(args[0]->decimals,args[1]->decimals)+2;
+ decimals=max(args[0]->decimals,args[1]->decimals)+prec_increment;
set_if_smaller(decimals, NOT_FIXED_DEC);
max_length=args[0]->max_length - args[0]->decimals + decimals;
uint tmp=float_length(decimals);
@@ -1383,7 +1406,6 @@ void Item_func_neg::fix_num_length_and_dec()
decimals= args[0]->decimals;
/* 1 add because sign can appear */
max_length= args[0]->max_length + 1;
- unsigned_flag= 0;
}
@@ -1408,6 +1430,7 @@ void Item_func_neg::fix_length_and_dec()
hybrid_type= DECIMAL_RESULT;
DBUG_PRINT("info", ("Type changed: DECIMAL_RESULT"));
}
+ unsigned_flag= 0;
DBUG_VOID_RETURN;
}
@@ -1792,17 +1815,65 @@ my_decimal *Item_func_floor::decimal_op(my_decimal *decimal_value)
}
-void Item_func_round::fix_num_length_and_dec()
+void Item_func_round::fix_length_and_dec()
{
- max_length= args[0]->max_length;
- decimals= NOT_FIXED_DEC;
- if (args[1]->const_item())
+ unsigned_flag= args[0]->unsigned_flag;
+ if (!args[1]->const_item())
{
- int tmp=(int) args[1]->val_int();
- if (tmp < 0)
- decimals=0;
+ max_length= args[0]->max_length;
+ decimals= args[0]->decimals;
+ hybrid_type= REAL_RESULT;
+ return;
+ }
+
+ int decimals_to_set= max(args[1]->val_int(), 0);
+ if (args[0]->decimals == NOT_FIXED_DEC)
+ {
+ max_length= args[0]->max_length;
+ decimals= min(decimals_to_set, NOT_FIXED_DEC);
+ hybrid_type= REAL_RESULT;
+ return;
+ }
+
+ switch (args[0]->result_type())
+ {
+ case REAL_RESULT:
+ case STRING_RESULT:
+ hybrid_type= REAL_RESULT;
+ decimals= min(decimals_to_set, NOT_FIXED_DEC);
+ max_length= float_length(decimals);
+ break;
+ case INT_RESULT:
+ if (truncate || (args[0]->decimal_precision() < DECIMAL_LONGLONG_DIGITS))
+ {
+ /* Here we can keep INT_RESULT */
+ hybrid_type= INT_RESULT;
+ int length_can_increase= !truncate && (args[1]->val_int() < 0);
+ max_length= args[0]->max_length + length_can_increase;
+ decimals= 0;
+ break;
+ }
+ case DECIMAL_RESULT:
+ {
+ hybrid_type= DECIMAL_RESULT;
+ int decimals_delta= args[0]->decimals - decimals_to_set;
+ int precision= args[0]->decimal_precision();
+ if (decimals_delta > 0)
+ {
+ int length_increase= truncate ? 0:1;
+ precision-= decimals_delta - length_increase;
+ decimals= decimals_to_set;
+ }
else
- decimals=min(tmp, NOT_FIXED_DEC);
+ /* Decimals to set is bigger that the original scale */
+ /* we keep original decimals value */
+ decimals= args[0]->decimals;
+ max_length= my_decimal_precision_to_length(precision, decimals,
+ unsigned_flag);
+ break;
+ }
+ default:
+ DBUG_ASSERT(0); /* This result type isn't handled */
}
}
@@ -1880,7 +1951,9 @@ 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)
- decimals= dec; // to get correct output
+ {
+ decimals= min(dec, DECIMAL_MAX_SCALE); // to get correct output
+ }
if ((null_value= (args[0]->null_value || args[1]->null_value ||
my_decimal_round(E_DEC_FATAL_ERROR, value, dec, truncate,
decimal_value) > 1)))
@@ -1972,6 +2045,7 @@ double Item_func_units::val_real()
void Item_func_min_max::fix_length_and_dec()
{
+ int max_int_part=0;
decimals=0;
max_length=0;
maybe_null=1;
@@ -1981,12 +2055,16 @@ void Item_func_min_max::fix_length_and_dec()
{
set_if_bigger(max_length, args[i]->max_length);
set_if_bigger(decimals, args[i]->decimals);
+ set_if_bigger(max_int_part, args[i]->decimal_int_part());
if (!args[i]->maybe_null)
maybe_null=0;
cmp_type=item_cmp_type(cmp_type,args[i]->result_type());
}
if (cmp_type == STRING_RESULT)
agg_arg_charsets(collation, args, arg_count, MY_COLL_CMP_CONV);
+ else if ((cmp_type == DECIMAL_RESULT) || (cmp_type == INT_RESULT))
+ max_length= my_decimal_precision_to_length(max_int_part+decimals, decimals,
+ unsigned_flag);
}
@@ -3913,15 +3991,17 @@ void Item_func_get_user_var::fix_length_and_dec()
switch (var_entry->type) {
case REAL_RESULT:
max_length= DBL_DIG + 8;
+ break;
case INT_RESULT:
max_length= MAX_BIGINT_WIDTH;
+ decimals=0;
break;
case STRING_RESULT:
max_length= MAX_BLOB_WIDTH;
break;
case DECIMAL_RESULT:
- max_length= DECIMAL_MAX_LENGTH;
- decimals= min(DECIMAL_MAX_LENGTH / 2, NOT_FIXED_DEC - 1);
+ max_length= DECIMAL_MAX_STR_LENGTH;
+ decimals= DECIMAL_MAX_SCALE;
break;
case ROW_RESULT: // Keep compiler happy
default:
@@ -4757,7 +4837,7 @@ Item_func_sp::fix_length_and_dec()
if (result_field)
{
decimals= result_field->decimals();
- max_length= result_field->representation_length();
+ max_length= result_field->field_length;
DBUG_VOID_RETURN;
}
@@ -4769,29 +4849,12 @@ Item_func_sp::fix_length_and_dec()
}
else
{
- if (!field)
- field= sp_result_field();
-
+ field= sp_result_field();
decimals= field->decimals();
- max_length= field->representation_length();
-
- switch (field->result_type()) {
- case STRING_RESULT:
- maybe_null= 1;
- case REAL_RESULT:
- case INT_RESULT:
- case DECIMAL_RESULT:
- break;
- case ROW_RESULT:
- default:
- // This case should never be chosen
- DBUG_ASSERT(0);
- break;
- }
-
- if (field != result_field)
- delete field;
+ max_length= field->field_length;
+ maybe_null= 1;
}
+ delete field;
DBUG_VOID_RETURN;
}
diff --git a/sql/item_func.h b/sql/item_func.h
index 76d1151f3bf..57faa05ce23 100644
--- a/sql/item_func.h
+++ b/sql/item_func.h
@@ -267,6 +267,8 @@ public:
void fix_length_and_dec()
{ max_length=args[0]->max_length; unsigned_flag=0; }
void print(String *str);
+ uint decimal_precision() const { return args[0]->decimal_precision(); }
+
};
@@ -296,7 +298,7 @@ public:
longlong val_int();
my_decimal *val_decimal(my_decimal*);
enum Item_result result_type () const { return DECIMAL_RESULT; }
- enum_field_types field_type() const { return MYSQL_TYPE_DECIMAL; }
+ enum_field_types field_type() const { return MYSQL_TYPE_NEWDECIMAL; }
void fix_length_and_dec() {};
};
@@ -346,6 +348,7 @@ public:
class Item_func_div :public Item_num_op
{
public:
+ uint prec_increment;
Item_func_div(Item *a,Item *b) :Item_num_op(a,b) {}
longlong int_op() { DBUG_ASSERT(0); return 0; }
double real_op();
@@ -390,6 +393,7 @@ public:
const char *func_name() const { return "-"; }
void fix_length_and_dec();
void fix_num_length_and_dec();
+ uint decimal_precision() const { return args[0]->decimal_precision(); }
};
@@ -593,7 +597,7 @@ public:
double real_op();
longlong int_op();
my_decimal *decimal_op(my_decimal *);
- void fix_num_length_and_dec();
+ void fix_length_and_dec();
};
diff --git a/sql/item_sum.cc b/sql/item_sum.cc
index 3dd4b6618a2..a7bc08ea170 100644
--- a/sql/item_sum.cc
+++ b/sql/item_sum.cc
@@ -156,8 +156,8 @@ Field *Item_sum::create_tmp_field(bool group, TABLE *table,
collation.collation);
return make_string_field(table);
case DECIMAL_RESULT:
- return new Field_new_decimal(max_length - (decimals?1:0),
- maybe_null, name, table, decimals);
+ return new Field_new_decimal(max_length, maybe_null, name, table,
+ decimals, unsigned_flag);
case ROW_RESULT:
default:
// This case should never be choosen
@@ -372,13 +372,16 @@ void Item_sum_sum::fix_length_and_dec()
break;
case INT_RESULT:
case DECIMAL_RESULT:
+ {
/* SUM result can't be longer than length(arg) + length(MAX_ROWS) */
- max_length= min(args[0]->max_length + DECIMAL_LONGLONG_DIGITS,
- DECIMAL_MAX_LENGTH);
+ int precision= args[0]->decimal_precision() + DECIMAL_LONGLONG_DIGITS;
+ max_length= my_decimal_precision_to_length(precision, decimals,
+ unsigned_flag);
curr_dec_buff= 0;
hybrid_type= DECIMAL_RESULT;
my_decimal_set_zero(dec_buffs);
break;
+ }
case ROW_RESULT:
default:
DBUG_ASSERT(0);
@@ -725,11 +728,12 @@ void
Item_sum_avg_distinct::fix_length_and_dec()
{
Item_sum_distinct::fix_length_and_dec();
+ prec_increment= current_thd->variables.div_precincrement;
/*
AVG() will divide val by count. We need to reserve digits
after decimal point as the result can be fractional.
*/
- decimals= min(decimals + 4, NOT_FIXED_DEC);
+ decimals= min(decimals + prec_increment, NOT_FIXED_DEC);
}
@@ -790,14 +794,19 @@ void Item_sum_avg::fix_length_and_dec()
{
Item_sum_sum::fix_length_and_dec();
maybe_null=null_value=1;
- decimals= min(args[0]->decimals + 4, NOT_FIXED_DEC);
+ prec_increment= current_thd->variables.div_precincrement;
if (hybrid_type == DECIMAL_RESULT)
{
- f_scale= args[0]->decimals;
- max_length= DECIMAL_MAX_LENGTH + (f_scale ? 1 : 0);
- f_precision= DECIMAL_MAX_LENGTH;
+ int precision= args[0]->decimal_precision() + prec_increment;
+ decimals= min(args[0]->decimals + prec_increment, DECIMAL_MAX_SCALE);
+ max_length= my_decimal_precision_to_length(precision, decimals,
+ unsigned_flag);
+ f_precision= min(precision+DECIMAL_LONGLONG_DIGITS, DECIMAL_MAX_PRECISION);
+ f_scale= args[0]->decimals;
dec_bin_size= my_decimal_get_binary_size(f_precision, f_scale);
}
+ else
+ decimals= min(args[0]->decimals + prec_increment, NOT_FIXED_DEC);
}
@@ -822,8 +831,8 @@ Field *Item_sum_avg::create_tmp_field(bool group, TABLE *table,
0, name, table, &my_charset_bin);
}
if (hybrid_type == DECIMAL_RESULT)
- return new Field_new_decimal(f_precision,
- maybe_null, name, table, f_scale);
+ return new Field_new_decimal(max_length, maybe_null, name, table,
+ decimals, unsigned_flag);
return new Field_double(max_length, maybe_null, name, table, decimals);
}
@@ -868,7 +877,7 @@ my_decimal *Item_sum_avg::val_decimal(my_decimal *val)
}
sum_dec= Item_sum_sum::val_decimal(&sum);
int2my_decimal(E_DEC_FATAL_ERROR, count, 0, &cnt);
- my_decimal_div(E_DEC_FATAL_ERROR, val, sum_dec, &cnt, 4);
+ my_decimal_div(E_DEC_FATAL_ERROR, val, sum_dec, &cnt, prec_increment);
return val;
}
@@ -905,7 +914,8 @@ Item *Item_sum_std::copy_or_same(THD* thd)
Item_sum_variance::Item_sum_variance(THD *thd, Item_sum_variance *item):
Item_sum_num(thd, item), hybrid_type(item->hybrid_type),
- cur_dec(item->cur_dec), count(item->count), sample(item->sample)
+ cur_dec(item->cur_dec), count(item->count), sample(item->sample),
+ prec_increment(item->prec_increment)
{
if (hybrid_type == DECIMAL_RESULT)
{
@@ -929,20 +939,21 @@ void Item_sum_variance::fix_length_and_dec()
{
DBUG_ENTER("Item_sum_variance::fix_length_and_dec");
maybe_null= null_value= 1;
- decimals= min(args[0]->decimals + 4, NOT_FIXED_DEC);
+ prec_increment= current_thd->variables.div_precincrement;
switch (args[0]->result_type()) {
case REAL_RESULT:
case STRING_RESULT:
+ decimals= min(args[0]->decimals + 4, NOT_FIXED_DEC);
hybrid_type= REAL_RESULT;
sum= 0.0;
break;
case INT_RESULT:
case DECIMAL_RESULT:
- /*
- SUM result can't be longer than length(arg)*2 +
- digits_after_the_point_to_add
- */
- max_length= args[0]->max_length*2 + 4;
+ {
+ int precision= args[0]->decimal_precision()*2 + prec_increment;
+ decimals= min(args[0]->decimals + prec_increment, DECIMAL_MAX_SCALE);
+ max_length= my_decimal_precision_to_length(precision, decimals,
+ unsigned_flag);
cur_dec= 0;
hybrid_type= DECIMAL_RESULT;
my_decimal_set_zero(dec_sum);
@@ -954,12 +965,15 @@ void Item_sum_variance::fix_length_and_dec()
column_value * column_value
*/
f_scale0= args[0]->decimals;
- f_precision0= DECIMAL_MAX_LENGTH / 2;
- f_scale1= min(f_scale0 * 2, NOT_FIXED_DEC - 1);
- f_precision1= DECIMAL_MAX_LENGTH;
+ f_precision0= min(args[0]->decimal_precision() + DECIMAL_LONGLONG_DIGITS,
+ DECIMAL_MAX_PRECISION);
+ f_scale1= min(args[0]->decimals * 2, DECIMAL_MAX_SCALE);
+ f_precision1= min(args[0]->decimal_precision()*2 + DECIMAL_LONGLONG_DIGITS,
+ DECIMAL_MAX_PRECISION);
dec_bin_size0= my_decimal_get_binary_size(f_precision0, f_scale0);
dec_bin_size1= my_decimal_get_binary_size(f_precision1, f_scale1);
break;
+ }
case ROW_RESULT:
default:
DBUG_ASSERT(0);
@@ -997,8 +1011,8 @@ Field *Item_sum_variance::create_tmp_field(bool group, TABLE *table,
0, name, table, &my_charset_bin);
}
if (hybrid_type == DECIMAL_RESULT)
- return new Field_new_decimal(DECIMAL_MAX_LENGTH,
- maybe_null, name, table, f_scale1 + 4);
+ return new Field_new_decimal(max_length, maybe_null, name, table,
+ decimals, unsigned_flag);
return new Field_double(max_length, maybe_null,name,table,decimals);
}
@@ -1083,9 +1097,11 @@ my_decimal *Item_sum_variance::val_decimal(my_decimal *dec_buf)
int2my_decimal(E_DEC_FATAL_ERROR, count-sample, 0, &count1_buf);
my_decimal_mul(E_DEC_FATAL_ERROR, &sum_sqr_buf,
dec_sum+cur_dec, dec_sum+cur_dec);
- my_decimal_div(E_DEC_FATAL_ERROR, dec_buf, &sum_sqr_buf, &count_buf, 2);
+ my_decimal_div(E_DEC_FATAL_ERROR, dec_buf,
+ &sum_sqr_buf, &count_buf, prec_increment);
my_decimal_sub(E_DEC_FATAL_ERROR, &sum_sqr_buf, dec_sqr+cur_dec, dec_buf);
- my_decimal_div(E_DEC_FATAL_ERROR, dec_buf, &sum_sqr_buf, &count1_buf, 2);
+ my_decimal_div(E_DEC_FATAL_ERROR, dec_buf,
+ &sum_sqr_buf, &count1_buf, prec_increment);
return dec_buf;
}
@@ -1929,10 +1945,12 @@ Item_avg_field::Item_avg_field(Item_result res_type, Item_sum_avg *item)
{
name=item->name;
decimals=item->decimals;
- max_length=item->max_length;
+ max_length= item->max_length;
+ unsigned_flag= item->unsigned_flag;
field=item->result_field;
maybe_null=1;
hybrid_type= res_type;
+ prec_increment= item->prec_increment;
if (hybrid_type == DECIMAL_RESULT)
{
f_scale= item->f_scale;
@@ -1941,7 +1959,6 @@ Item_avg_field::Item_avg_field(Item_result res_type, Item_sum_avg *item)
}
}
-
double Item_avg_field::val_real()
{
// fix_fields() never calls for this Item
@@ -1982,7 +1999,8 @@ my_decimal *Item_avg_field::val_decimal(my_decimal *dec_buf)
binary2my_decimal(E_DEC_FATAL_ERROR,
field->ptr, &dec_field, f_precision, f_scale);
int2my_decimal(E_DEC_FATAL_ERROR, count, 0, &dec_count);
- my_decimal_div(E_DEC_FATAL_ERROR, dec_buf, &dec_field, &dec_count, 4);
+ my_decimal_div(E_DEC_FATAL_ERROR, dec_buf,
+ &dec_field, &dec_count, prec_increment);
return dec_buf;
}
@@ -2054,9 +2072,11 @@ Item_variance_field::Item_variance_field(Item_sum_variance *item)
name=item->name;
decimals=item->decimals;
max_length=item->max_length;
+ unsigned_flag= item->unsigned_flag;
field=item->result_field;
maybe_null=1;
sample= item->sample;
+ prec_increment= item->prec_increment;
if ((hybrid_type= item->hybrid_type) == DECIMAL_RESULT)
{
f_scale0= item->f_scale0;
@@ -2116,9 +2136,10 @@ my_decimal *Item_variance_field::val_decimal(my_decimal *dec_buf)
binary2my_decimal(E_DEC_FATAL_ERROR, field->ptr+dec_bin_size0,
&dec_sqr, f_precision1, f_scale1);
my_decimal_mul(E_DEC_FATAL_ERROR, &tmp, &dec_sum, &dec_sum);
- my_decimal_div(E_DEC_FATAL_ERROR, dec_buf, &tmp, &dec_count, 2);
+ my_decimal_div(E_DEC_FATAL_ERROR, dec_buf, &tmp, &dec_count, prec_increment);
my_decimal_sub(E_DEC_FATAL_ERROR, &dec_sum, &dec_sqr, dec_buf);
- my_decimal_div(E_DEC_FATAL_ERROR, dec_buf, &dec_sum, &dec1_count, 2);
+ my_decimal_div(E_DEC_FATAL_ERROR, dec_buf,
+ &dec_sum, &dec1_count, prec_increment);
return dec_buf;
}
diff --git a/sql/item_sum.h b/sql/item_sum.h
index 8c8360b0726..fb72fed1c5e 100644
--- a/sql/item_sum.h
+++ b/sql/item_sum.h
@@ -237,6 +237,7 @@ private:
Item_sum_avg_distinct(THD *thd, Item_sum_avg_distinct *original)
:Item_sum_distinct(thd, original) {}
public:
+ uint prec_increment;
Item_sum_avg_distinct(Item *item_arg) : Item_sum_distinct(item_arg) {}
void fix_length_and_dec();
@@ -343,8 +344,8 @@ class Item_avg_field :public Item_result_field
public:
Field *field;
Item_result hybrid_type;
- uint f_precision, f_scale;
- uint dec_bin_size;
+ uint f_precision, f_scale, dec_bin_size;
+ uint prec_increment;
Item_avg_field(Item_result res_type, Item_sum_avg *item);
enum Type type() const { return FIELD_AVG_ITEM; }
double val_real();
@@ -366,12 +367,14 @@ class Item_sum_avg :public Item_sum_sum
{
public:
ulonglong count;
- uint f_precision, f_scale;
- uint dec_bin_size;
+ uint prec_increment;
+ uint f_precision, f_scale, dec_bin_size;
Item_sum_avg(Item *item_par) :Item_sum_sum(item_par), count(0) {}
Item_sum_avg(THD *thd, Item_sum_avg *item)
- :Item_sum_sum(thd, item), count(item->count) {}
+ :Item_sum_sum(thd, item), count(item->count),
+ prec_increment(item->prec_increment) {}
+
void fix_length_and_dec();
enum Sumfunctype sum_func () const {return AVG_FUNC;}
void clear();
@@ -402,6 +405,7 @@ public:
uint f_precision1, f_scale1;
uint dec_bin_size0, dec_bin_size1;
uint sample;
+ uint prec_increment;
Item_variance_field(Item_sum_variance *item);
enum Type type() const {return FIELD_VARIANCE_ITEM; }
double val_real();
@@ -446,6 +450,7 @@ public:
uint f_precision1, f_scale1;
uint dec_bin_size0, dec_bin_size1;
uint sample;
+ uint prec_increment;
Item_sum_variance(Item *item_par, uint sample_arg) :Item_sum_num(item_par),
hybrid_type(REAL_RESULT), cur_dec(0), count(0), sample(sample_arg)
diff --git a/sql/my_decimal.cc b/sql/my_decimal.cc
index b4bbef4a637..14c15cdc4ef 100644
--- a/sql/my_decimal.cc
+++ b/sql/my_decimal.cc
@@ -81,7 +81,7 @@ int decimal_operation_results(int result)
*/
int my_decimal2string(uint mask, const my_decimal *d,
- int fixed_prec, int fixed_dec,
+ uint fixed_prec, uint fixed_dec,
char filler, String *str)
{
int length= (fixed_prec ? (fixed_prec + 1) : my_decimal_string_length(d));
@@ -89,7 +89,7 @@ int my_decimal2string(uint mask, const my_decimal *d,
if (str->alloc(length))
return check_result(mask, E_DEC_OOM);
result= decimal2string((decimal_t*) d, (char*) str->ptr(),
- &length, fixed_prec, fixed_dec,
+ &length, (int)fixed_prec, fixed_dec,
filler);
str->length(length);
return check_result(mask, result);
@@ -123,7 +123,7 @@ int my_decimal2binary(uint mask, const my_decimal *d, char *bin, int prec,
int err1= E_DEC_OK, err2;
my_decimal rounded;
my_decimal2decimal(d, &rounded);
- decimal_optimize_fraction(&rounded);
+ rounded.frac= decimal_actual_fraction(&rounded);
if (scale < rounded.frac)
{
err1= E_DEC_TRUNCATED;
@@ -220,18 +220,16 @@ print_decimal_buff(const my_decimal *dec, const byte* ptr, int length)
}
-void dbug_print_decimal(const char *tag, const char *format, my_decimal *val)
+const char *dbug_decimal_as_string(char *buff, const my_decimal *val)
{
- char buff[DECIMAL_MAX_STR_LENGTH];
- String str(buff, sizeof(buff), &my_charset_bin);
+ int length= DECIMAL_MAX_STR_LENGTH;
if (!val)
- str.set("NULL", 4, &my_charset_bin);
- else
- my_decimal2string(0, val, 0, 0, 0, &str);
- DBUG_PRINT(tag, (format, (char*) str.ptr()));
+ return "NULL";
+ (void)decimal2string((decimal_t*) val, buff, &length, 0,0,0);
+ return buff;
}
-#endif
+#endif /*DBUG_OFF*/
#endif /*MYSQL_CLIENT*/
diff --git a/sql/my_decimal.h b/sql/my_decimal.h
index 03801390d82..27fd33cffbe 100644
--- a/sql/my_decimal.h
+++ b/sql/my_decimal.h
@@ -35,27 +35,27 @@ C_MODE_END
#define DECIMAL_LONG_DIGITS 10
#define DECIMAL_LONG3_DIGITS 8
-/* number of digits on which we increase scale of devision result */
-#define DECIMAL_DIV_SCALE_INCREASE 5
-
/* maximum length of buffer in our big digits (uint32) */
-#define DECIMAL_BUFF_LENGTH 8
+#define DECIMAL_BUFF_LENGTH 9
/*
- maximum guaranteed length of number in decimal digits (number of our
+ maximum guaranteed precision of number in decimal digits (number of our
digits * number of decimal digits in one our big digit - number of decimal
digits in one our big digit decreased on 1 (because we always put decimal
point on the border of our big digits))
*/
-#define DECIMAL_MAX_LENGTH ((8 * 9) - 8)
+#define DECIMAL_MAX_PRECISION ((DECIMAL_BUFF_LENGTH * 9) - 8*2)
+#define DECIMAL_MAX_SCALE 30
+#define DECIMAL_NOT_SPECIFIED 31
+
/*
maximum length of string representation (number of maximum decimal
digits + 1 position for sign + 1 position for decimal point)
*/
-#define DECIMAL_MAX_STR_LENGTH (DECIMAL_MAX_LENGTH + 2)
+#define DECIMAL_MAX_STR_LENGTH (DECIMAL_MAX_PRECISION + 2)
/*
maximum size of packet length
*/
-#define DECIMAL_MAX_FIELD_SIZE DECIMAL_MAX_LENGTH
+#define DECIMAL_MAX_FIELD_SIZE DECIMAL_MAX_PRECISION
inline uint my_decimal_size(uint precision, uint scale)
@@ -68,6 +68,12 @@ inline uint my_decimal_size(uint precision, uint scale)
}
+inline int my_decimal_int_part(uint precision, uint decimals)
+{
+ return precision - ((decimals == DECIMAL_NOT_SPECIFIED) ? 0 : decimals);
+}
+
+
/*
my_decimal class limits 'decimal_t' type to what we need in MySQL
It contains internally all necessary space needed by the instance so
@@ -99,15 +105,16 @@ public:
bool sign() const { return decimal_t::sign; }
void sign(bool s) { decimal_t::sign= s; }
+ uint precision() const { return intg + frac; }
};
#ifndef DBUG_OFF
void print_decimal(const my_decimal *dec);
void print_decimal_buff(const my_decimal *dec, const byte* ptr, int length);
-void dbug_print_decimal(const char *tag, const char *format, my_decimal *val);
+const char *dbug_decimal_as_string(char *buff, const my_decimal *val);
#else
-#define dbug_print_decimal(A,B,C)
+#define dbug_decimal_as_string(A) NULL
#endif
#ifndef MYSQL_CLIENT
@@ -126,6 +133,18 @@ inline int check_result(uint mask, int result)
return result;
}
+inline uint my_decimal_length_to_precision(uint length, uint scale,
+ bool unsigned_flag)
+{
+ return (uint) (length - (scale>0 ? 1:0) - (unsigned_flag ? 0:1));
+}
+
+inline uint32 my_decimal_precision_to_length(uint precision, uint8 scale,
+ bool unsigned_flag)
+{
+ set_if_smaller(precision, DECIMAL_MAX_PRECISION);
+ return (uint32)(precision + (scale>0 ? 1:0) + (unsigned_flag ? 0:1));
+}
inline
int my_decimal_string_length(const my_decimal *d)
@@ -209,8 +228,8 @@ int my_decimal_ceiling(uint mask, const my_decimal *from, my_decimal *to)
#ifndef MYSQL_CLIENT
-int my_decimal2string(uint mask, const my_decimal *d, int fixed_prec,
- int fixed_dec, char filler, String *str);
+int my_decimal2string(uint mask, const my_decimal *d, uint fixed_prec,
+ uint fixed_dec, char filler, String *str);
#endif
inline
@@ -326,7 +345,8 @@ int my_decimal_cmp(const my_decimal *a, const my_decimal *b)
inline
void max_my_decimal(my_decimal *to, int precision, int frac)
{
- DBUG_ASSERT(precision <= DECIMAL_MAX_LENGTH);
+ DBUG_ASSERT((precision <= DECIMAL_MAX_PRECISION)&&
+ (frac <= DECIMAL_MAX_SCALE));
max_decimal(precision, frac, (decimal_t*) to);
}
diff --git a/sql/mysqld.cc b/sql/mysqld.cc
index 3bfa498e521..408821e58bb 100644
--- a/sql/mysqld.cc
+++ b/sql/mysqld.cc
@@ -4218,7 +4218,8 @@ enum options_mysqld
OPT_PRELOAD_BUFFER_SIZE,
OPT_QUERY_CACHE_LIMIT, OPT_QUERY_CACHE_MIN_RES_UNIT, OPT_QUERY_CACHE_SIZE,
OPT_QUERY_CACHE_TYPE, OPT_QUERY_CACHE_WLOCK_INVALIDATE, OPT_RECORD_BUFFER,
- OPT_RECORD_RND_BUFFER, OPT_RELAY_LOG_SPACE_LIMIT, OPT_RELAY_LOG_PURGE,
+ OPT_RECORD_RND_BUFFER, OPT_DIV_PRECINCREMENT, OPT_RELAY_LOG_SPACE_LIMIT,
+ OPT_RELAY_LOG_PURGE,
OPT_SLAVE_NET_TIMEOUT, OPT_SLAVE_COMPRESSED_PROTOCOL, OPT_SLOW_LAUNCH_TIME,
OPT_SLAVE_TRANS_RETRIES, OPT_READONLY, OPT_DEBUGGING,
OPT_SORT_BUFFER, OPT_TABLE_CACHE,
@@ -5439,6 +5440,11 @@ The minimum value for this variable is 4096.",
(gptr*) &max_system_variables.read_rnd_buff_size, 0,
GET_ULONG, REQUIRED_ARG, 256*1024L, IO_SIZE*2+MALLOC_OVERHEAD,
~0L, MALLOC_OVERHEAD, IO_SIZE, 0},
+ {"div_precision_increment", OPT_DIV_PRECINCREMENT,
+ "Precision of the result of '/' operator will be increased on that value.",
+ (gptr*) &global_system_variables.div_precincrement,
+ (gptr*) &max_system_variables.div_precincrement, 0, GET_ULONG,
+ REQUIRED_ARG, 4, 0, DECIMAL_MAX_SCALE, 0, 0, 0},
{"record_buffer", OPT_RECORD_BUFFER,
"Alias for read_buffer_size",
(gptr*) &global_system_variables.read_buff_size,
diff --git a/sql/set_var.cc b/sql/set_var.cc
index 0e6e36f63a2..b7760cd038b 100644
--- a/sql/set_var.cc
+++ b/sql/set_var.cc
@@ -295,6 +295,8 @@ sys_var_thd_ulong sys_read_buff_size("read_buffer_size",
sys_var_bool_ptr sys_readonly("read_only", &opt_readonly);
sys_var_thd_ulong sys_read_rnd_buff_size("read_rnd_buffer_size",
&SV::read_rnd_buff_size);
+sys_var_thd_ulong sys_div_precincrement("div_precision_increment",
+ &SV::div_precincrement);
#ifdef HAVE_REPLICATION
sys_var_bool_ptr sys_relay_log_purge("relay_log_purge",
&relay_log_purge);
@@ -567,6 +569,7 @@ sys_var *sys_variables[]=
&sys_connect_timeout,
&sys_date_format,
&sys_datetime_format,
+ &sys_div_precincrement,
&sys_default_week_format,
&sys_delay_key_write,
&sys_delayed_insert_limit,
@@ -754,6 +757,7 @@ struct show_var_st init_vars[]= {
{"datadir", mysql_real_data_home, SHOW_CHAR},
{sys_date_format.name, (char*) &sys_date_format, SHOW_SYS},
{sys_datetime_format.name, (char*) &sys_datetime_format, SHOW_SYS},
+ {sys_div_precincrement.name,(char*) &sys_div_precincrement,SHOW_SYS},
{sys_default_week_format.name, (char*) &sys_default_week_format, SHOW_SYS},
{sys_delay_key_write.name, (char*) &sys_delay_key_write, SHOW_SYS},
{sys_delayed_insert_limit.name, (char*) &sys_delayed_insert_limit,SHOW_SYS},
diff --git a/sql/sp_head.cc b/sql/sp_head.cc
index ad14116a4c7..9fb4b1c7fac 100644
--- a/sql/sp_head.cc
+++ b/sql/sp_head.cc
@@ -164,7 +164,10 @@ sp_eval_func_item(THD *thd, Item *it, enum enum_field_types type)
it= new Item_null();
else
it= new Item_decimal(val);
- dbug_print_decimal("info", "DECIMAL_RESULT: %s", val);
+#ifndef DBUG_OFF
+ char dbug_buff[DECIMAL_MAX_STR_LENGTH+1];
+ DBUG_PRINT("info", ("DECIMAL_RESULT: %s", dbug_decimal_as_string(dbug_buff, val)));
+#endif
break;
}
case STRING_RESULT:
diff --git a/sql/sql_class.h b/sql/sql_class.h
index 62cee00043b..4393da6df2a 100644
--- a/sql/sql_class.h
+++ b/sql/sql_class.h
@@ -523,6 +523,7 @@ struct system_variables
ulong query_cache_type;
ulong read_buff_size;
ulong read_rnd_buff_size;
+ ulong div_precincrement;
ulong sortbuff_size;
ulong table_type;
ulong tmp_table_size;
diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc
index 22febd50035..07e075a6171 100644
--- a/sql/sql_parse.cc
+++ b/sql/sql_parse.cc
@@ -5410,9 +5410,14 @@ new_create_field(THD *thd, char *field_name, enum_field_types type,
}
new_field->pack_length=
my_decimal_get_binary_size(new_field->length, new_field->decimals);
- if (new_field->length <= DECIMAL_MAX_LENGTH &&
+ if (new_field->length <= DECIMAL_MAX_PRECISION &&
new_field->length >= new_field->decimals)
+ {
+ new_field->length=
+ my_decimal_precision_to_length(new_field->length, new_field->decimals,
+ type_modifier & UNSIGNED_FLAG);
break;
+ }
my_error(ER_WRONG_FIELD_SPEC, MYF(0), field_name);
DBUG_RETURN(NULL);
case MYSQL_TYPE_VARCHAR:
diff --git a/sql/sql_select.cc b/sql/sql_select.cc
index 32624bb3305..7fe966c7542 100644
--- a/sql/sql_select.cc
+++ b/sql/sql_select.cc
@@ -7781,9 +7781,8 @@ static Field *create_tmp_field_from_item(THD *thd, Item *item, TABLE *table,
new_field= item->make_string_field(table);
break;
case DECIMAL_RESULT:
- new_field= new Field_new_decimal(item->max_length - (item->decimals?1:0),
- maybe_null,
- item->name, table, item->decimals);
+ new_field= new Field_new_decimal(item->max_length, maybe_null, item->name,
+ table, item->decimals, item->unsigned_flag);
break;
case ROW_RESULT:
default:
diff --git a/sql/sql_show.cc b/sql/sql_show.cc
index d6027699257..cf0050a774b 100644
--- a/sql/sql_show.cc
+++ b/sql/sql_show.cc
@@ -2362,10 +2362,10 @@ static int get_schema_column_record(THD *thd, struct st_table_list *tables,
strlen((const char*) pos), cs);
if (field->has_charset())
{
- table->field[8]->store((longlong) field->representation_length()/
+ table->field[8]->store((longlong) field->field_length/
field->charset()->mbmaxlen);
table->field[8]->set_notnull();
- table->field[9]->store((longlong) field->representation_length());
+ table->field[9]->store((longlong) field->field_length);
table->field[9]->set_notnull();
}
@@ -2373,7 +2373,8 @@ static int get_schema_column_record(THD *thd, struct st_table_list *tables,
uint dec =field->decimals();
switch (field->type()) {
case FIELD_TYPE_NEWDECIMAL:
- table->field[10]->store((longlong) field->field_length);
+ table->field[10]->store((longlong)
+ ((Field_new_decimal*)field)->precision);
table->field[10]->set_notnull();
table->field[11]->store((longlong) field->decimals());
table->field[11]->set_notnull();
diff --git a/strings/decimal.c b/strings/decimal.c
index 9af95511f6d..52003a930c0 100644
--- a/strings/decimal.c
+++ b/strings/decimal.c
@@ -274,20 +274,20 @@ static dec1 *remove_leading_zeroes(decimal_t *from, int *intg_result)
/*
- Remove ending 0 digits from fraction part
+ Count actual length of fraction part (without ending zeroes)
SYNOPSIS
- decimal_optimize_fraction()
+ decimal_actual_fraction()
from number for processing
*/
-void decimal_optimize_fraction(decimal_t *from)
+int decimal_actual_fraction(decimal_t *from)
{
int frac= from->frac, i;
dec1 *buf0= from->buf + ROUND_UP(from->intg) + ROUND_UP(frac) - 1;
if (frac == 0)
- return;
+ return 0;
i= ((frac - 1) % DIG_PER_DEC1 + 1);
while (frac > 0 && *buf0 == 0)
@@ -302,7 +302,7 @@ void decimal_optimize_fraction(decimal_t *from)
*buf0 % powers10[i++] == 0;
frac--);
}
- from->frac= frac;
+ return frac;
}
@@ -332,23 +332,15 @@ int decimal2string(decimal_t *from, char *to, int *to_len,
int fixed_precision, int fixed_decimals,
char filler)
{
- int len, intg, frac=from->frac, i, intg_len, frac_len, fill;
+ int len, intg, frac= from->frac, i, intg_len, frac_len, fill;
/* number digits before decimal point */
int fixed_intg= (fixed_precision ?
- (fixed_precision -
- (from->sign ? 1 : 0) -
- (fixed_decimals ? 1 : 0) -
- fixed_decimals) :
- 0);
+ (fixed_precision - fixed_decimals) : 0);
int error=E_DEC_OK;
char *s=to;
dec1 *buf, *buf0=from->buf, tmp;
DBUG_ASSERT(*to_len >= 2+from->sign);
- DBUG_ASSERT(fixed_precision == 0 ||
- (fixed_precision < *to_len &&
- fixed_precision > ((from->sign ? 1 : 0) +
- (fixed_decimals ? 1 : 0))));
/* removing leading zeroes */
buf0= remove_leading_zeroes(from, &intg);
@@ -2609,7 +2601,7 @@ void test_fr(const char *s1, const char *orig)
printf("%-40s => ", s);
end= strend(s1);
string2decimal(s1, &a, &end);
- decimal_optimize_fraction(&a);
+ a.frac= decimal_actual_fraction(&a);
print_decimal(&a, orig, 0, 0);
printf("\n");
}
@@ -2947,7 +2939,7 @@ int main()
test_sh("123456789.987654321", 0, "123456789.987654321", 0);
a.len= sizeof(buf1)/sizeof(dec1);
- printf("==== decimal_optimize_fraction ====\n");
+ printf("==== decimal_actual_fraction ====\n");
test_fr("1.123456789000000000", "1.123456789");
test_fr("1.12345678000000000", "1.12345678");
test_fr("1.1234567000000000", "1.1234567");