diff options
-rw-r--r-- | BitKeeper/etc/logging_ok | 1 | ||||
-rw-r--r-- | include/decimal.h | 2 | ||||
-rw-r--r-- | sql/field.cc | 92 | ||||
-rw-r--r-- | sql/field.h | 16 | ||||
-rw-r--r-- | sql/ha_ndbcluster.cc | 2 | ||||
-rw-r--r-- | sql/item.cc | 76 | ||||
-rw-r--r-- | sql/item.h | 13 | ||||
-rw-r--r-- | sql/item_cmpfunc.cc | 31 | ||||
-rw-r--r-- | sql/item_cmpfunc.h | 9 | ||||
-rw-r--r-- | sql/item_func.cc | 181 | ||||
-rw-r--r-- | sql/item_func.h | 8 | ||||
-rw-r--r-- | sql/item_sum.cc | 83 | ||||
-rw-r--r-- | sql/item_sum.h | 15 | ||||
-rw-r--r-- | sql/my_decimal.cc | 20 | ||||
-rw-r--r-- | sql/my_decimal.h | 46 | ||||
-rw-r--r-- | sql/mysqld.cc | 8 | ||||
-rw-r--r-- | sql/set_var.cc | 4 | ||||
-rw-r--r-- | sql/sp_head.cc | 5 | ||||
-rw-r--r-- | sql/sql_class.h | 1 | ||||
-rw-r--r-- | sql/sql_parse.cc | 7 | ||||
-rw-r--r-- | sql/sql_select.cc | 5 | ||||
-rw-r--r-- | sql/sql_show.cc | 7 | ||||
-rw-r--r-- | strings/decimal.c | 26 |
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"); |