diff options
Diffstat (limited to 'sql/field.cc')
-rw-r--r-- | sql/field.cc | 104 |
1 files changed, 91 insertions, 13 deletions
diff --git a/sql/field.cc b/sql/field.cc index 5bce6a412fc..d1202657786 100644 --- a/sql/field.cc +++ b/sql/field.cc @@ -1923,16 +1923,16 @@ int Field_decimal::store(const char *from_arg, uint len, CHARSET_INFO *cs) Pointers used when digits move from the left of the '.' to the right of the '.' (explained below) */ - const uchar *int_digits_tail_from; + const uchar *UNINIT_VAR(int_digits_tail_from); /* Number of 0 that need to be added at the left of the '.' (1E3: 3 zeros) */ - uint int_digits_added_zeros; + uint UNINIT_VAR(int_digits_added_zeros); /* Pointer used when digits move from the right of the '.' to the left of the '.' */ - const uchar *frac_digits_head_end; + const uchar *UNINIT_VAR(frac_digits_head_end); /* Number of 0 that need to be added at the right of the '.' (for 1E-3) */ - uint frac_digits_added_zeros; + uint UNINIT_VAR(frac_digits_added_zeros); uchar *pos,*tmp_left_pos,*tmp_right_pos; /* Pointers that are used as limits (begin and end of the field buffer) */ uchar *left_wall,*right_wall; @@ -1943,11 +1943,6 @@ int Field_decimal::store(const char *from_arg, uint len, CHARSET_INFO *cs) */ bool is_cuted_fields_incr=0; - LINT_INIT(int_digits_tail_from); - LINT_INIT(int_digits_added_zeros); - LINT_INIT(frac_digits_head_end); - LINT_INIT(frac_digits_added_zeros); - /* There are three steps in this function : - parse the input string @@ -2486,12 +2481,97 @@ Field_new_decimal::Field_new_decimal(uint32 len_arg, { precision= my_decimal_length_to_precision(len_arg, dec_arg, unsigned_arg); set_if_smaller(precision, DECIMAL_MAX_PRECISION); + DBUG_ASSERT(precision >= dec); DBUG_ASSERT((precision <= DECIMAL_MAX_PRECISION) && (dec <= DECIMAL_MAX_SCALE)); bin_size= my_decimal_get_binary_size(precision, dec); } +/** + Create a field to hold a decimal value from an item. + + @remark The MySQL DECIMAL data type has a characteristic that needs to be + taken into account when deducing the type from a Item_decimal. + + But first, let's briefly recap what is the new MySQL DECIMAL type: + + The declaration syntax for a decimal is DECIMAL(M,D), where: + + * M is the maximum number of digits (the precision). + It has a range of 1 to 65. + * D is the number of digits to the right of the decimal separator (the scale). + It has a range of 0 to 30 and must be no larger than M. + + D and M are used to determine the storage requirements for the integer + and fractional parts of each value. The integer part is to the left of + the decimal separator and to the right is the fractional part. Hence: + + M is the number of digits for the integer and fractional part. + D is the number of digits for the fractional part. + + Consequently, M - D is the number of digits for the integer part. For + example, a DECIMAL(20,10) column has ten digits on either side of + the decimal separator. + + The characteristic that needs to be taken into account is that the + backing type for Item_decimal is a my_decimal that has a higher + precision (DECIMAL_MAX_POSSIBLE_PRECISION, see my_decimal.h) than + DECIMAL. + + Drawing a comparison between my_decimal and DECIMAL: + + * M has a range of 1 to 81. + * D has a range of 0 to 81. + + There can be a difference in range if the decimal contains a integer + part. This is because the fractional part must always be on a group + boundary, leaving at least one group for the integer part. Since each + group is 9 (DIG_PER_DEC1) digits and there are 9 (DECIMAL_BUFF_LENGTH) + groups, the fractional part is limited to 72 digits if there is at + least one digit in the integral part. + + Although the backing type for a DECIMAL is also my_decimal, every + time a my_decimal is stored in a DECIMAL field, the precision and + scale are explicitly capped at 65 (DECIMAL_MAX_PRECISION) and 30 + (DECIMAL_MAX_SCALE) digits, following my_decimal truncation procedure + (FIX_INTG_FRAC_ERROR). +*/ + +Field_new_decimal * +Field_new_decimal::new_decimal_field(const Item *item) +{ + uint32 len; + uint intg= item->decimal_int_part(), scale= item->decimals; + + DBUG_ASSERT(item->decimal_precision() >= item->decimals); + + /* + Employ a procedure along the lines of the my_decimal truncation process: + - If the integer part is equal to or bigger than the maximum precision: + Truncate integer part to fit and the fractional becomes zero. + - Otherwise: + Truncate fractional part to fit. + */ + if (intg >= DECIMAL_MAX_PRECISION) + { + intg= DECIMAL_MAX_PRECISION; + scale= 0; + } + else + { + uint room= min(DECIMAL_MAX_PRECISION - intg, DECIMAL_MAX_SCALE); + if (scale > room) + scale= room; + } + + len= my_decimal_precision_to_length(intg + scale, scale, item->unsigned_flag); + + return new Field_new_decimal(len, item->maybe_null, item->name, scale, + item->unsigned_flag); +} + + int Field_new_decimal::reset(void) { store_value(&decimal_zero); @@ -9923,10 +10003,8 @@ Field *make_field(TABLE_SHARE *share, uchar *ptr, uint32 field_length, TYPELIB *interval, const char *field_name) { - uchar *bit_ptr; - uchar bit_offset; - LINT_INIT(bit_ptr); - LINT_INIT(bit_offset); + uchar *UNINIT_VAR(bit_ptr); + uchar UNINIT_VAR(bit_offset); if (field_type == MYSQL_TYPE_BIT && !f_bit_as_char(pack_flag)) { bit_ptr= null_pos; |