diff options
Diffstat (limited to 'sql/field.cc')
-rw-r--r-- | sql/field.cc | 144 |
1 files changed, 94 insertions, 50 deletions
diff --git a/sql/field.cc b/sql/field.cc index ed085de1db3..9f46552d5bd 100644 --- a/sql/field.cc +++ b/sql/field.cc @@ -1922,16 +1922,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; @@ -1942,11 +1942,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 @@ -2485,12 +2480,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); @@ -4598,7 +4678,6 @@ bool Field_double::send_binary(Protocol *protocol) int Field_double::cmp(const uchar *a_ptr, const uchar *b_ptr) { - ASSERT_COLUMN_MARKED_FOR_READ; double a,b; #ifdef WORDS_BIGENDIAN if (table->s->db_low_byte_first) @@ -6271,48 +6350,15 @@ check_string_copy_error(Field_str *field, const char *end, CHARSET_INFO *cs) { - const char *pos, *end_orig; - char tmp[64], *t; + const char *pos; + char tmp[32]; if (!(pos= well_formed_error_pos) && !(pos= cannot_convert_error_pos)) return FALSE; - end_orig= end; - set_if_smaller(end, pos + 6); + convert_to_printable(tmp, sizeof(tmp), pos, (end - pos), cs, 6); - for (t= tmp; pos < end; pos++) - { - /* - If the source string is ASCII compatible (mbminlen==1) - and the source character is in ASCII printable range (0x20..0x7F), - then display the character as is. - - Otherwise, if the source string is not ASCII compatible (e.g. UCS2), - or the source character is not in the printable range, - then print the character using HEX notation. - */ - if (((unsigned char) *pos) >= 0x20 && - ((unsigned char) *pos) <= 0x7F && - cs->mbminlen == 1) - { - *t++= *pos; - } - else - { - *t++= '\\'; - *t++= 'x'; - *t++= _dig_vec_upper[((unsigned char) *pos) >> 4]; - *t++= _dig_vec_upper[((unsigned char) *pos) & 15]; - } - } - if (end_orig > end) - { - *t++= '.'; - *t++= '.'; - *t++= '.'; - } - *t= '\0'; push_warning_printf(field->table->in_use, field->table->in_use->abort_on_warning ? MYSQL_ERROR::WARN_LEVEL_ERROR : @@ -9951,10 +9997,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; |