diff options
author | Monty <monty@mariadb.org> | 2016-06-18 14:28:34 +0300 |
---|---|---|
committer | Monty <monty@mariadb.org> | 2016-06-22 22:04:55 +0300 |
commit | 34eb10e4064a7f38fc7d41016a6bcc5443ebe0c3 (patch) | |
tree | 45f76f02034419697e5bff5f539b20eaac8f8c8d /sql | |
parent | e4062d4d203a6be596d3f61dfe4db1b4f91e24aa (diff) | |
download | mariadb-git-34eb10e4064a7f38fc7d41016a6bcc5443ebe0c3.tar.gz |
MDEV-10138 Support for decimals up to 38 digits
Decimals with float, double and decimal now works the following way:
- DECIMAL_NOT_SPECIFIED is used when declaring DECIMALS without a firm number
of decimals. It's only used in asserts and my_decimal_int_part.
- FLOATING_POINT_DECIMALS (31) is used to mark that a FLOAT or DOUBLE
was defined without decimals. This is regarded as a floating point value.
- Max decimals allowed for FLOAT and DOUBLE is FLOATING_POINT_DECIMALS-1
- Clients assumes that float and double with decimals >= NOT_FIXED_DEC are
floating point values (no decimals)
- In the .frm decimals=FLOATING_POINT_DECIMALS are used to define
floating point for float and double (31, like before)
To ensure compatibility with old clients we do:
- When storing float and double, we change NOT_FIXED_DEC to
FLOATING_POINT_DECIMALS.
- When creating fields from .frm we change for float and double
FLOATING_POINT_DEC to NOT_FIXED_DEC
- When sending definition for a float/decimal field without decimals
to the client as part of a result set we convert NOT_FIXED_DEC to
FLOATING_POINT_DECIMALS.
- variance() and std() has changed to limit the decimals to
FLOATING_POINT_DECIMALS -1 to not get the double converted floating point.
(This was to preserve compatiblity)
- FLOAT and DOUBLE still have 30 as max number of decimals.
Bugs fixed:
variance() printed more decimals than we support for double values.
New behaviour:
- Strings now have 38 decimals instead of 30 when converted to decimal
- CREATE ... SELECT with a decimal with > 30 decimals will create a column
with a smaller range than before as we are trying to preserve the number of
decimals.
Other changes
- We are now using the obsolete bit FIELDFLAG_LEFT_FULLSCREEN to specify
decimals > 31
- NOT_FIXED_DEC is now declared in one place
- For clients, NOT_FIXED_DEC is always 31 (to ensure compatibility).
On the server NOT_FIXED_DEC is DECIMAL_NOT_SPECIFIED (39)
- AUTO_SEC_PART_DIGITS is taken from DECIMAL_NOT_SPECIFIED
- DOUBLE conversion functions are now using DECIMAL_NOT_SPECIFIED instead of
NOT_FIXED_DEC
Diffstat (limited to 'sql')
-rw-r--r-- | sql/field.cc | 66 | ||||
-rw-r--r-- | sql/field.h | 37 | ||||
-rw-r--r-- | sql/item.h | 2 | ||||
-rw-r--r-- | sql/item_func.cc | 5 | ||||
-rw-r--r-- | sql/item_sum.cc | 8 | ||||
-rw-r--r-- | sql/protocol.cc | 4 | ||||
-rw-r--r-- | sql/sql_analyse.cc | 6 | ||||
-rw-r--r-- | sql/sql_string.cc | 2 | ||||
-rw-r--r-- | sql/sql_table.cc | 17 | ||||
-rw-r--r-- | sql/table.cc | 4 |
10 files changed, 109 insertions, 42 deletions
diff --git a/sql/field.cc b/sql/field.cc index a5d2d759edc..0e2bc724db0 100644 --- a/sql/field.cc +++ b/sql/field.cc @@ -4435,13 +4435,13 @@ String *Field_float::val_str(String *val_buffer, char *to=(char*) val_buffer->ptr(); size_t len; - if (dec >= NOT_FIXED_DEC) + if (dec >= FLOATING_POINT_DECIMALS) len= my_gcvt(nr, MY_GCVT_ARG_FLOAT, to_length - 1, to, NULL); else { /* We are safe here because the buffer length is 70, and - fabs(float) < 10^39, dec < NOT_FIXED_DEC. So the resulting string + fabs(float) < 10^39, dec < FLOATING_POINT_DECIMALS. So the resulting string will be not longer than 69 chars + terminating '\0'. */ len= my_fcvt(nr, dec, to, NULL); @@ -4525,7 +4525,7 @@ int Field_float::do_save_field_metadata(uchar *metadata_ptr) void Field_float::sql_type(String &res) const { - if (dec == NOT_FIXED_DEC) + if (dec >= FLOATING_POINT_DECIMALS) { res.set_ascii(STRING_WITH_LEN("float")); } @@ -4606,7 +4606,7 @@ int truncate_double(double *nr, uint field_length, uint dec, return 1; } - if (dec < NOT_FIXED_DEC) + if (dec < FLOATING_POINT_DECIMALS) { uint order= field_length - dec; uint step= array_elements(log_10) - 1; @@ -4788,7 +4788,7 @@ String *Field_double::val_str(String *val_buffer, char *to=(char*) val_buffer->ptr(); size_t len; - if (dec >= NOT_FIXED_DEC) + if (dec >= FLOATING_POINT_DECIMALS) len= my_gcvt(nr, MY_GCVT_ARG_DOUBLE, to_length - 1, to, NULL); else len= my_fcvt(nr, dec, to, NULL); @@ -4847,7 +4847,7 @@ int Field_double::do_save_field_metadata(uchar *metadata_ptr) void Field_double::sql_type(String &res) const { CHARSET_INFO *cs=res.charset(); - if (dec == NOT_FIXED_DEC) + if (dec >= FLOATING_POINT_DECIMALS) { res.set_ascii(STRING_WITH_LEN("double")); } @@ -9772,13 +9772,6 @@ bool Column_definition::check(THD *thd) if (length > MAX_FIELD_BLOBLENGTH) { my_error(ER_TOO_BIG_DISPLAYWIDTH, MYF(0), field_name, MAX_FIELD_BLOBLENGTH); - DBUG_RETURN(1); - } - - if (decimals >= NOT_FIXED_DEC) - { - my_error(ER_TOO_BIG_SCALE, MYF(0), static_cast<ulonglong>(decimals), - field_name, static_cast<ulong>(NOT_FIXED_DEC - 1)); DBUG_RETURN(TRUE); } @@ -9797,7 +9790,7 @@ bool Column_definition::check(THD *thd) def->decimals < length)) { my_error(ER_INVALID_DEFAULT, MYF(0), field_name); - DBUG_RETURN(1); + DBUG_RETURN(TRUE); } else if (def->type() == Item::NULL_ITEM) { @@ -9811,7 +9804,7 @@ bool Column_definition::check(THD *thd) else if (flags & AUTO_INCREMENT_FLAG) { my_error(ER_INVALID_DEFAULT, MYF(0), field_name); - DBUG_RETURN(1); + DBUG_RETURN(TRUE); } } @@ -9839,7 +9832,7 @@ bool Column_definition::check(THD *thd) on_update->decimals < length)) { my_error(ER_INVALID_ON_UPDATE, MYF(0), field_name); - DBUG_RETURN(1); + DBUG_RETURN(TRUE); } sign_len= flags & UNSIGNED_FLAG ? 0 : 1; @@ -9873,6 +9866,12 @@ bool Column_definition::check(THD *thd) case MYSQL_TYPE_NULL: break; case MYSQL_TYPE_NEWDECIMAL: + if (decimals >= NOT_FIXED_DEC) + { + my_error(ER_TOO_BIG_SCALE, MYF(0), static_cast<ulonglong>(decimals), + field_name, static_cast<ulong>(NOT_FIXED_DEC - 1)); + DBUG_RETURN(TRUE); + } my_decimal_trim(&length, &decimals); if (length > DECIMAL_MAX_PRECISION) { @@ -9952,6 +9951,12 @@ bool Column_definition::check(THD *thd) my_error(ER_M_BIGGER_THAN_D, MYF(0), field_name); DBUG_RETURN(TRUE); } + if (decimals != NOT_FIXED_DEC && decimals >= FLOATING_POINT_DECIMALS) + { + my_error(ER_TOO_BIG_SCALE, MYF(0), static_cast<ulonglong>(decimals), + field_name, static_cast<ulong>(FLOATING_POINT_DECIMALS-1)); + DBUG_RETURN(TRUE); + } break; case MYSQL_TYPE_DOUBLE: allowed_type_modifier= AUTO_INCREMENT_FLAG; @@ -9966,6 +9971,12 @@ bool Column_definition::check(THD *thd) my_error(ER_M_BIGGER_THAN_D, MYF(0), field_name); DBUG_RETURN(TRUE); } + if (decimals != NOT_FIXED_DEC && decimals >= FLOATING_POINT_DECIMALS) + { + my_error(ER_TOO_BIG_SCALE, MYF(0), static_cast<ulonglong>(decimals), + field_name, static_cast<ulong>(FLOATING_POINT_DECIMALS-1)); + DBUG_RETURN(TRUE); + } break; case MYSQL_TYPE_TIMESTAMP: case MYSQL_TYPE_TIMESTAMP2: @@ -10280,19 +10291,29 @@ Field *make_field(TABLE_SHARE *share, f_is_zerofill(pack_flag) != 0, f_is_dec(pack_flag) == 0); case MYSQL_TYPE_FLOAT: + { + int decimals= f_decimals(pack_flag); + if (decimals == FLOATING_POINT_DECIMALS) + decimals= NOT_FIXED_DEC; return new (mem_root) Field_float(ptr,field_length,null_pos,null_bit, unireg_check, field_name, - f_decimals(pack_flag), + decimals, f_is_zerofill(pack_flag) != 0, f_is_dec(pack_flag)== 0); + } case MYSQL_TYPE_DOUBLE: + { + int decimals= f_decimals(pack_flag); + if (decimals == FLOATING_POINT_DECIMALS) + decimals= NOT_FIXED_DEC; return new (mem_root) Field_double(ptr,field_length,null_pos,null_bit, unireg_check, field_name, - f_decimals(pack_flag), + decimals, f_is_zerofill(pack_flag) != 0, f_is_dec(pack_flag)== 0); + } case MYSQL_TYPE_TINY: return new (mem_root) Field_tiny(ptr,field_length,null_pos,null_bit, @@ -10459,6 +10480,15 @@ Column_definition::Column_definition(THD *thd, Field *old_field, buff, "YEAR(4)"); } break; + case MYSQL_TYPE_FLOAT: + case MYSQL_TYPE_DOUBLE: + /* + Floating points are stored with FLOATING_POINT_DECIMALS but internally + in MariaDB used with NOT_FIXED_DEC, which is >= FLOATING_POINT_DECIMALS. + */ + if (decimals >= FLOATING_POINT_DECIMALS) + decimals= NOT_FIXED_DEC; + break; default: break; } diff --git a/sql/field.h b/sql/field.h index 736c51c2ac3..27b87dd0472 100644 --- a/sql/field.h +++ b/sql/field.h @@ -51,7 +51,6 @@ enum enum_check_fields CHECK_FIELD_ERROR_FOR_NULL }; - /* Common declarations for Field and Item */ @@ -1699,7 +1698,7 @@ public: uint8 dec_arg, bool zero_arg, bool unsigned_arg) :Field_num(ptr_arg, len_arg, null_ptr_arg, null_bit_arg, unireg_check_arg, field_name_arg, dec_arg, zero_arg, unsigned_arg), - not_fixed(dec_arg >= NOT_FIXED_DEC) + not_fixed(dec_arg >= FLOATING_POINT_DECIMALS) {} Item_result result_type () const { return REAL_RESULT; } Copy_func *get_copy_func(const Field *from) const @@ -2062,12 +2061,18 @@ public: :Field_real(ptr_arg, len_arg, null_ptr_arg, null_bit_arg, unireg_check_arg, field_name_arg, dec_arg, zero_arg, unsigned_arg) - {} + { + if (dec_arg >= FLOATING_POINT_DECIMALS) + dec_arg= NOT_FIXED_DEC; + } Field_float(uint32 len_arg, bool maybe_null_arg, const char *field_name_arg, uint8 dec_arg) :Field_real((uchar*) 0, len_arg, maybe_null_arg ? (uchar*) "": 0, (uint) 0, NONE, field_name_arg, dec_arg, 0, 0) - {} + { + if (dec_arg >= FLOATING_POINT_DECIMALS) + dec_arg= NOT_FIXED_DEC; + } enum_field_types type() const { return MYSQL_TYPE_FLOAT;} enum ha_base_keytype key_type() const { return HA_KEYTYPE_FLOAT; } int store(const char *to,uint length,CHARSET_INFO *charset); @@ -2097,17 +2102,27 @@ public: :Field_real(ptr_arg, len_arg, null_ptr_arg, null_bit_arg, unireg_check_arg, field_name_arg, dec_arg, zero_arg, unsigned_arg) - {} + { + if (dec_arg >= FLOATING_POINT_DECIMALS) + dec_arg= NOT_FIXED_DEC; + } Field_double(uint32 len_arg, bool maybe_null_arg, const char *field_name_arg, uint8 dec_arg) :Field_real((uchar*) 0, len_arg, maybe_null_arg ? (uchar*) "" : 0, (uint) 0, NONE, field_name_arg, dec_arg, 0, 0) - {} + { + if (dec_arg >= FLOATING_POINT_DECIMALS) + dec_arg= NOT_FIXED_DEC; + } Field_double(uint32 len_arg, bool maybe_null_arg, const char *field_name_arg, uint8 dec_arg, bool not_fixed_arg) :Field_real((uchar*) 0, len_arg, maybe_null_arg ? (uchar*) "" : 0, (uint) 0, NONE, field_name_arg, dec_arg, 0, 0) - {not_fixed= not_fixed_arg; } + { + not_fixed= not_fixed_arg; + if (dec_arg >= FLOATING_POINT_DECIMALS) + dec_arg= NOT_FIXED_DEC; + } enum_field_types type() const { return MYSQL_TYPE_DOUBLE;} enum ha_base_keytype key_type() const { return HA_KEYTYPE_DOUBLE; } int store(const char *to,uint length,CHARSET_INFO *charset); @@ -2898,7 +2913,7 @@ new_Field_timestamp(MEM_ROOT *root,uchar *ptr, uchar *null_ptr, uchar null_bit, return new (root) Field_timestamp(ptr, MAX_DATETIME_WIDTH, null_ptr, null_bit, unireg_check, field_name, share); - if (dec == NOT_FIXED_DEC) + if (dec >= FLOATING_POINT_DECIMALS) dec= MAX_DATETIME_PRECISION; return new (root) Field_timestamp_hires(ptr, null_ptr, null_bit, unireg_check, @@ -2914,7 +2929,7 @@ new_Field_time(MEM_ROOT *root, uchar *ptr, uchar *null_ptr, uchar null_bit, return new (root) Field_time(ptr, MIN_TIME_WIDTH, null_ptr, null_bit, unireg_check, field_name); - if (dec == NOT_FIXED_DEC) + if (dec >= FLOATING_POINT_DECIMALS) dec= MAX_DATETIME_PRECISION; return new (root) Field_time_hires(ptr, null_ptr, null_bit, unireg_check, field_name, dec); @@ -2929,7 +2944,7 @@ new_Field_datetime(MEM_ROOT *root, uchar *ptr, uchar *null_ptr, uchar null_bit, return new (root) Field_datetime(ptr, MAX_DATETIME_WIDTH, null_ptr, null_bit, unireg_check, field_name); - if (dec == NOT_FIXED_DEC) + if (dec >= FLOATING_POINT_DECIMALS) dec= MAX_DATETIME_PRECISION; return new (root) Field_datetime_hires(ptr, null_ptr, null_bit, @@ -3886,7 +3901,7 @@ int convert_null_to_field_value_or_error(Field *field); #define FIELDFLAG_HEX_ESCAPE ((uint) 0x10000) #define FIELDFLAG_PACK_SHIFT 3 #define FIELDFLAG_DEC_SHIFT 8 -#define FIELDFLAG_MAX_DEC 31 +#define FIELDFLAG_MAX_DEC 63 #define FIELDFLAG_NUM_SCREEN_TYPE 0x7F01 #define FIELDFLAG_ALFA_SCREEN_TYPE 0x7800 diff --git a/sql/item.h b/sql/item.h index 674ff6e99dc..20e5572e35e 100644 --- a/sql/item.h +++ b/sql/item.h @@ -1107,7 +1107,7 @@ public: virtual Item *clone_item(THD *thd) { return 0; } 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;} + { return decimals < FLOATING_POINT_DECIMALS ? (DBL_DIG+2+decimals_par) : DBL_DIG+8;} /* Returns total number of decimal digits */ virtual uint decimal_precision() const; /* Returns the number of integer part digits only */ diff --git a/sql/item_func.cc b/sql/item_func.cc index c6a3459848e..4f2f400584b 100644 --- a/sql/item_func.cc +++ b/sql/item_func.cc @@ -646,14 +646,15 @@ void Item_func::count_real_length(Item **items, uint nitems) unsigned_flag= false; for (uint i=0 ; i < nitems ; i++) { - if (decimals != NOT_FIXED_DEC) + if (decimals < FLOATING_POINT_DECIMALS) { set_if_bigger(decimals, items[i]->decimals); + /* Will be ignored if items[i]->decimals >= FLOATING_POINT_DECIMALS */ set_if_bigger(length, (items[i]->max_length - items[i]->decimals)); } set_if_bigger(max_length, items[i]->max_length); } - if (decimals != NOT_FIXED_DEC) + if (decimals < FLOATING_POINT_DECIMALS) { max_length= length; length+= decimals; diff --git a/sql/item_sum.cc b/sql/item_sum.cc index f774ee5a561..f7e02bc55f9 100644 --- a/sql/item_sum.cc +++ b/sql/item_sum.cc @@ -1624,7 +1624,8 @@ void Item_sum_avg::fix_length_and_dec() } else { - decimals= MY_MIN(args[0]->decimals + prec_increment, NOT_FIXED_DEC); + decimals= MY_MIN(args[0]->decimals + prec_increment, + FLOATING_POINT_DECIMALS); max_length= MY_MIN(args[0]->max_length + prec_increment, float_length(decimals)); } } @@ -1839,13 +1840,14 @@ void Item_sum_variance::fix_length_and_dec() switch (args[0]->result_type()) { case REAL_RESULT: case STRING_RESULT: - decimals= MY_MIN(args[0]->decimals + 4, NOT_FIXED_DEC); + decimals= MY_MIN(args[0]->decimals + 4, FLOATING_POINT_DECIMALS); break; case INT_RESULT: case DECIMAL_RESULT: { int precision= args[0]->decimal_precision()*2 + prec_increment; - decimals= MY_MIN(args[0]->decimals + prec_increment, DECIMAL_MAX_SCALE); + decimals= MY_MIN(args[0]->decimals + prec_increment, + FLOATING_POINT_DECIMALS-1); max_length= my_decimal_precision_to_length_no_truncation(precision, decimals, unsigned_flag); diff --git a/sql/protocol.cc b/sql/protocol.cc index 6469581b482..608ec553da0 100644 --- a/sql/protocol.cc +++ b/sql/protocol.cc @@ -759,6 +759,10 @@ bool Protocol::send_result_set_metadata(List<Item> *list, uint flags) Send_field field; item->make_field(thd, &field); + /* limit number of decimals for float and double */ + if (field.type == MYSQL_TYPE_FLOAT || field.type == MYSQL_TYPE_DOUBLE) + set_if_smaller(field.decimals, FLOATING_POINT_DECIMALS); + /* Keep things compatible for old clients */ if (field.type == MYSQL_TYPE_VARCHAR) field.type= MYSQL_TYPE_VAR_STRING; diff --git a/sql/sql_analyse.cc b/sql/sql_analyse.cc index b2bc9fc2e87..91a80c552cb 100644 --- a/sql/sql_analyse.cc +++ b/sql/sql_analyse.cc @@ -409,7 +409,7 @@ void field_real::add() if (num == 0.0) empty++; - if ((decs = decimals()) == NOT_FIXED_DEC) + if ((decs = decimals()) >= FLOATING_POINT_DECIMALS) { length= sprintf(buff, "%g", num); if (rint(num) != num) @@ -892,7 +892,7 @@ void field_real::get_opt_type(String *answer, if (!max_notzero_dec_len) { - int len= (int) max_length - ((item->decimals == NOT_FIXED_DEC) ? + int len= (int) max_length - ((item->decimals >= FLOATING_POINT_DECIMALS) ? 0 : (item->decimals + 1)); if (min_arg >= -128 && max_arg <= (min_arg >= 0 ? 255 : 127)) @@ -912,7 +912,7 @@ void field_real::get_opt_type(String *answer, if (min_arg >= 0) answer->append(STRING_WITH_LEN(" UNSIGNED")); } - else if (item->decimals == NOT_FIXED_DEC) + else if (item->decimals >= FLOATING_POINT_DECIMALS) { if (min_arg >= -FLT_MAX && max_arg <= FLT_MAX) answer->append(STRING_WITH_LEN("FLOAT")); diff --git a/sql/sql_string.cc b/sql/sql_string.cc index 40339d599af..767154e019d 100644 --- a/sql/sql_string.cc +++ b/sql/sql_string.cc @@ -136,7 +136,7 @@ bool String::set_real(double num,uint decimals, CHARSET_INFO *cs) size_t len; str_charset=cs; - if (decimals >= NOT_FIXED_DEC) + if (decimals >= FLOATING_POINT_DECIMALS) { len= my_gcvt(num, MY_GCVT_ARG_DOUBLE, sizeof(buff) - 1, buff, NULL); return copy(buff, len, &my_charset_latin1, cs, &dummy_errors); diff --git a/sql/sql_table.cc b/sql/sql_table.cc index 60bfe7cd1aa..6cd73978eed 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -2876,7 +2876,8 @@ int prepare_create_field(Column_definition *sql_field, uint *blob_columns, longlong table_flags) { - unsigned int dup_val_count; + uint dup_val_count; + uint decimals= sql_field->decimals; DBUG_ENTER("prepare_create_field"); /* @@ -2994,8 +2995,18 @@ int prepare_create_field(Column_definition *sql_field, FIELDFLAG_DECIMAL) | (sql_field->flags & ZEROFILL_FLAG ? FIELDFLAG_ZEROFILL : 0) | - (sql_field->decimals << FIELDFLAG_DEC_SHIFT)); + (decimals << FIELDFLAG_DEC_SHIFT)); break; + case MYSQL_TYPE_FLOAT: + case MYSQL_TYPE_DOUBLE: + /* + User specified FLOAT() or DOUBLE() without precision. Change to + FLOATING_POINT_DECIMALS to keep things compatible with earlier MariaDB + versions. + */ + if (decimals >= FLOATING_POINT_DECIMALS) + decimals= FLOATING_POINT_DECIMALS; + /* fall-trough */ case MYSQL_TYPE_TIMESTAMP: case MYSQL_TYPE_TIMESTAMP2: /* fall-through */ @@ -3006,7 +3017,7 @@ int prepare_create_field(Column_definition *sql_field, (sql_field->flags & ZEROFILL_FLAG ? FIELDFLAG_ZEROFILL : 0) | f_settype((uint) sql_field->sql_type) | - (sql_field->decimals << FIELDFLAG_DEC_SHIFT)); + (decimals << FIELDFLAG_DEC_SHIFT)); break; } if (!(sql_field->flags & NOT_NULL_FLAG) || diff --git a/sql/table.cc b/sql/table.cc index 13b6fbf9cce..208d5da37c7 100644 --- a/sql/table.cc +++ b/sql/table.cc @@ -1633,6 +1633,10 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write, bzero((char*) &comment, sizeof(comment)); } + /* Remove >32 decimals from old files */ + if (share->mysql_version < 100200) + pack_flag&= ~(FIELDFLAG_LEFT_FULLSCREEN); + if (interval_nr && charset->mbminlen > 1) { /* Unescape UCS2 intervals from HEX notation */ |