diff options
-rw-r--r-- | mysql-test/r/type_newdecimal.result | 220 | ||||
-rw-r--r-- | mysql-test/t/type_newdecimal.test | 134 | ||||
-rw-r--r-- | sql/field.cc | 85 | ||||
-rw-r--r-- | sql/field.h | 9 | ||||
-rw-r--r-- | sql/item.cc | 25 | ||||
-rw-r--r-- | sql/item.h | 3 | ||||
-rw-r--r-- | sql/item_cmpfunc.cc | 6 | ||||
-rw-r--r-- | sql/item_func.cc | 52 | ||||
-rw-r--r-- | sql/item_func.h | 1 | ||||
-rw-r--r-- | sql/item_sum.cc | 3 | ||||
-rw-r--r-- | sql/my_decimal.h | 14 | ||||
-rw-r--r-- | sql/sql_select.cc | 41 |
12 files changed, 483 insertions, 110 deletions
diff --git a/mysql-test/r/type_newdecimal.result b/mysql-test/r/type_newdecimal.result index 748aadee4fb..c3d1e400b23 100644 --- a/mysql-test/r/type_newdecimal.result +++ b/mysql-test/r/type_newdecimal.result @@ -1495,9 +1495,9 @@ CREATE TABLE t1 (a int DEFAULT NULL, b int DEFAULT NULL); INSERT INTO t1 VALUES (3,30), (1,10), (2,10); SELECT a+CAST(1 AS decimal(65,30)) AS aa, SUM(b) FROM t1 GROUP BY aa; aa SUM(b) -2.000000000000000000000000000000 10 -3.000000000000000000000000000000 10 -4.000000000000000000000000000000 30 +2.00000000000000000000000000000 10 +3.00000000000000000000000000000 10 +4.00000000000000000000000000000 30 SELECT a+CAST(1 AS decimal(65,31)) AS aa, SUM(b) FROM t1 GROUP BY aa; ERROR 42000: Too big scale 31 specified for column '1'. Maximum is 30. DROP TABLE t1; @@ -1521,13 +1521,13 @@ f1 DROP TABLE t1; CREATE TABLE t1 SELECT 123451234512345123451234512345123451234512345.678906789067890678906789067890678906789067890 AS f1; Warnings: -Warning 1264 Out of range value for column 'f1' at row 1 +Note 1265 Data truncated for column 'f1' at row 1 DESC t1; Field Type Null Key Default Extra -f1 decimal(65,30) NO 0.000000000000000000000000000000 +f1 decimal(65,20) NO 0.00000000000000000000 SELECT f1 FROM t1; f1 -99999999999999999999999999999999999.999999999999999999999999999999 +123451234512345123451234512345123451234512345.67890678906789067891 DROP TABLE t1; select (1.20396873 * 0.89550000 * 0.68000000 * 1.08721696 * 0.99500000 * 1.01500000 * 1.01500000 * 0.99500000); @@ -1595,7 +1595,7 @@ Warnings: Note 1265 Data truncated for column 'my_col' at row 1 DESCRIBE t1; Field Type Null Key Default Extra -my_col decimal(65,30) NO 0.000000000000000000000000000000 +my_col decimal(32,30) NO 0.000000000000000000000000000000 SELECT my_col FROM t1; my_col 1.123456789123456789123456789123 @@ -1625,8 +1625,212 @@ Warnings: Note 1265 Data truncated for column 'my_col' at row 1 DESCRIBE t1; Field Type Null Key Default Extra -my_col decimal(65,30) YES NULL +my_col decimal(30,30) YES NULL SELECT my_col FROM t1; my_col 0.012345687012345687012345687012 DROP TABLE t1; +# +# Bug#45261: Crash, stored procedure + decimal +# +DROP TABLE IF EXISTS t1; +CREATE TABLE t1 SELECT +/* 81 */ 100000000000000000000000000000000000000000000000000000000000000000000000000000001 +AS c1; +Warnings: +Warning 1264 Out of range value for column 'c1' at row 1 +DESC t1; +Field Type Null Key Default Extra +c1 decimal(65,0) NO 0 +SELECT * FROM t1; +c1 +99999999999999999999999999999999999999999999999999999999999999999 +DROP TABLE t1; +CREATE TABLE t1 SELECT +/* 81 */ 100000000000000000000000000000000000000000000000000000000000000000000000000000001. +AS c1; +Warnings: +Warning 1264 Out of range value for column 'c1' at row 1 +DESC t1; +Field Type Null Key Default Extra +c1 decimal(65,0) NO 0 +SELECT * FROM t1; +c1 +99999999999999999999999999999999999999999999999999999999999999999 +DROP TABLE t1; +CREATE TABLE t1 SELECT +/* 81 */ 100000000000000000000000000000000000000000000000000000000000000000000000000000001.1 /* 1 */ +AS c1; +Warnings: +Warning 1264 Out of range value for column 'c1' at row 1 +DESC t1; +Field Type Null Key Default Extra +c1 decimal(65,0) NO 0 +SELECT * FROM t1; +c1 +99999999999999999999999999999999999999999999999999999999999999999 +DROP TABLE t1; +CREATE TABLE t1 SELECT +/* 82 */ 1000000000000000000000000000000000000000000000000000000000000000000000000000000001 +AS c1; +Warnings: +Error 1292 Truncated incorrect DECIMAL value: '' +DESC t1; +Field Type Null Key Default Extra +c1 decimal(65,0) NO 0 +SELECT * FROM t1; +c1 +99999999999999999999999999999999999999999999999999999999999999999 +DROP TABLE t1; +CREATE TABLE t1 SELECT +/* 40 */ 1000000000000000000000000000000000000001.1000000000000000000000000000000000000001 /* 40 */ +AS c1; +DESC t1; +Field Type Null Key Default Extra +c1 decimal(65,25) NO 0.0000000000000000000000000 +SELECT * FROM t1; +c1 +1000000000000000000000000000000000000001.1000000000000000000000000 +DROP TABLE t1; +CREATE TABLE t1 SELECT +/* 1 */ 1.10000000000000000000000000000000000000000000000000000000000000000000000000000001 /* 80 */ +AS c1; +DESC t1; +Field Type Null Key Default Extra +c1 decimal(31,30) NO 0.000000000000000000000000000000 +SELECT * FROM t1; +c1 +1.100000000000000000000000000000 +DROP TABLE t1; +CREATE TABLE t1 SELECT +/* 1 */ 1.100000000000000000000000000000000000000000000000000000000000000000000000000000001 /* 81 */ +AS c1; +DESC t1; +Field Type Null Key Default Extra +c1 decimal(31,30) NO 0.000000000000000000000000000000 +SELECT * FROM t1; +c1 +1.100000000000000000000000000000 +DROP TABLE t1; +CREATE TABLE t1 SELECT +.100000000000000000000000000000000000000000000000000000000000000000000000000000001 /* 81 */ +AS c1; +Warnings: +Note 1265 Data truncated for column 'c1' at row 1 +DESC t1; +Field Type Null Key Default Extra +c1 decimal(30,30) NO 0.000000000000000000000000000000 +SELECT * FROM t1; +c1 +0.100000000000000000000000000000 +DROP TABLE t1; +CREATE TABLE t1 SELECT +/* 45 */ 123456789012345678901234567890123456789012345.123456789012345678901234567890123456789012345 /* 45 */ +AS c1; +Warnings: +Note 1265 Data truncated for column 'c1' at row 1 +DESC t1; +Field Type Null Key Default Extra +c1 decimal(65,20) NO 0.00000000000000000000 +SELECT * FROM t1; +c1 +123456789012345678901234567890123456789012345.12345678901234567890 +DROP TABLE t1; +CREATE TABLE t1 SELECT +/* 65 */ 12345678901234567890123456789012345678901234567890123456789012345.1 /* 1 */ +AS c1; +Warnings: +Note 1265 Data truncated for column 'c1' at row 1 +DESC t1; +Field Type Null Key Default Extra +c1 decimal(65,0) NO 0 +SELECT * FROM t1; +c1 +12345678901234567890123456789012345678901234567890123456789012345 +DROP TABLE t1; +CREATE TABLE t1 SELECT +/* 66 */ 123456789012345678901234567890123456789012345678901234567890123456.1 /* 1 */ +AS c1; +Warnings: +Warning 1264 Out of range value for column 'c1' at row 1 +DESC t1; +Field Type Null Key Default Extra +c1 decimal(65,0) NO 0 +SELECT * FROM t1; +c1 +99999999999999999999999999999999999999999999999999999999999999999 +DROP TABLE t1; +CREATE TABLE t1 SELECT +.123456789012345678901234567890123456789012345678901234567890123456 /* 66 */ +AS c1; +Warnings: +Note 1265 Data truncated for column 'c1' at row 1 +DESC t1; +Field Type Null Key Default Extra +c1 decimal(30,30) NO 0.000000000000000000000000000000 +SELECT * FROM t1; +c1 +0.123456789012345678901234567890 +DROP TABLE t1; +CREATE TABLE t1 AS SELECT 123.1234567890123456789012345678901 /* 31 */ AS c1; +Warnings: +Note 1265 Data truncated for column 'c1' at row 1 +DESC t1; +Field Type Null Key Default Extra +c1 decimal(33,30) NO 0.000000000000000000000000000000 +SELECT * FROM t1; +c1 +123.123456789012345678901234567890 +DROP TABLE t1; +CREATE TABLE t1 SELECT 1.1 + CAST(1 AS DECIMAL(65,30)) AS c1; +DESC t1; +Field Type Null Key Default Extra +c1 decimal(65,29) NO 0.00000000000000000000000000000 +SELECT * FROM t1; +c1 +2.10000000000000000000000000000 +DROP TABLE t1; +# +# Test that the integer and decimal parts are properly calculated. +# +CREATE TABLE t1 (a DECIMAL(30,30)); +INSERT INTO t1 VALUES (0.1),(0.2),(0.3); +CREATE TABLE t2 SELECT MIN(a + 0.0000000000000000000000000000001) AS c1 FROM t1; +Warnings: +Note 1265 Data truncated for column 'c1' at row 3 +DESC t2; +Field Type Null Key Default Extra +c1 decimal(32,30) YES NULL +DROP TABLE t1,t2; +CREATE TABLE t1 (a DECIMAL(30,30)); +INSERT INTO t1 VALUES (0.1),(0.2),(0.3); +CREATE TABLE t2 SELECT IFNULL(a + 0.0000000000000000000000000000001, NULL) AS c1 FROM t1; +Warnings: +Note 1265 Data truncated for column 'c1' at row 1 +Note 1265 Data truncated for column 'c1' at row 2 +Note 1265 Data truncated for column 'c1' at row 3 +DESC t2; +Field Type Null Key Default Extra +c1 decimal(32,30) YES NULL +DROP TABLE t1,t2; +CREATE TABLE t1 (a DECIMAL(30,30)); +INSERT INTO t1 VALUES (0.1),(0.2),(0.3); +CREATE TABLE t2 SELECT CASE a WHEN 0.1 THEN 0.0000000000000000000000000000000000000000000000000000000000000000001 END AS c1 FROM t1; +Warnings: +Note 1265 Data truncated for column 'c1' at row 1 +DESC t2; +Field Type Null Key Default Extra +c1 decimal(31,30) YES NULL +DROP TABLE t1,t2; +# +# Test that variables get maximum precision. +# +SET @decimal= 1.1; +CREATE TABLE t1 SELECT @decimal AS c1; +DESC t1; +Field Type Null Key Default Extra +c1 decimal(65,30) YES NULL +SELECT * FROM t1; +c1 +1.100000000000000000000000000000 +DROP TABLE t1; diff --git a/mysql-test/t/type_newdecimal.test b/mysql-test/t/type_newdecimal.test index cd3c3f81510..65bafaae77e 100644 --- a/mysql-test/t/type_newdecimal.test +++ b/mysql-test/t/type_newdecimal.test @@ -1286,3 +1286,137 @@ CREATE TABLE t1 SELECT 1 % .1234567891234567891234567891234567891234567891234567 DESCRIBE t1; SELECT my_col FROM t1; DROP TABLE t1; + +--echo # +--echo # Bug#45261: Crash, stored procedure + decimal +--echo # + +--disable_warnings +DROP TABLE IF EXISTS t1; +--enable_warnings + +CREATE TABLE t1 SELECT + /* 81 */ 100000000000000000000000000000000000000000000000000000000000000000000000000000001 + AS c1; +DESC t1; +SELECT * FROM t1; +DROP TABLE t1; + +CREATE TABLE t1 SELECT + /* 81 */ 100000000000000000000000000000000000000000000000000000000000000000000000000000001. + AS c1; +DESC t1; +SELECT * FROM t1; +DROP TABLE t1; + +CREATE TABLE t1 SELECT + /* 81 */ 100000000000000000000000000000000000000000000000000000000000000000000000000000001.1 /* 1 */ + AS c1; +DESC t1; +SELECT * FROM t1; +DROP TABLE t1; + +CREATE TABLE t1 SELECT + /* 82 */ 1000000000000000000000000000000000000000000000000000000000000000000000000000000001 + AS c1; +DESC t1; +SELECT * FROM t1; +DROP TABLE t1; + +CREATE TABLE t1 SELECT + /* 40 */ 1000000000000000000000000000000000000001.1000000000000000000000000000000000000001 /* 40 */ + AS c1; +DESC t1; +SELECT * FROM t1; +DROP TABLE t1; + +CREATE TABLE t1 SELECT + /* 1 */ 1.10000000000000000000000000000000000000000000000000000000000000000000000000000001 /* 80 */ + AS c1; +DESC t1; +SELECT * FROM t1; +DROP TABLE t1; + +CREATE TABLE t1 SELECT + /* 1 */ 1.100000000000000000000000000000000000000000000000000000000000000000000000000000001 /* 81 */ + AS c1; +DESC t1; +SELECT * FROM t1; +DROP TABLE t1; + +CREATE TABLE t1 SELECT + .100000000000000000000000000000000000000000000000000000000000000000000000000000001 /* 81 */ + AS c1; +DESC t1; +SELECT * FROM t1; +DROP TABLE t1; + +CREATE TABLE t1 SELECT + /* 45 */ 123456789012345678901234567890123456789012345.123456789012345678901234567890123456789012345 /* 45 */ + AS c1; +DESC t1; +SELECT * FROM t1; +DROP TABLE t1; + +CREATE TABLE t1 SELECT + /* 65 */ 12345678901234567890123456789012345678901234567890123456789012345.1 /* 1 */ + AS c1; +DESC t1; +SELECT * FROM t1; +DROP TABLE t1; + +CREATE TABLE t1 SELECT + /* 66 */ 123456789012345678901234567890123456789012345678901234567890123456.1 /* 1 */ + AS c1; +DESC t1; +SELECT * FROM t1; +DROP TABLE t1; + +CREATE TABLE t1 SELECT + .123456789012345678901234567890123456789012345678901234567890123456 /* 66 */ + AS c1; +DESC t1; +SELECT * FROM t1; +DROP TABLE t1; + +CREATE TABLE t1 AS SELECT 123.1234567890123456789012345678901 /* 31 */ AS c1; +DESC t1; +SELECT * FROM t1; +DROP TABLE t1; + +CREATE TABLE t1 SELECT 1.1 + CAST(1 AS DECIMAL(65,30)) AS c1; +DESC t1; +SELECT * FROM t1; +DROP TABLE t1; + +--echo # +--echo # Test that the integer and decimal parts are properly calculated. +--echo # + +CREATE TABLE t1 (a DECIMAL(30,30)); +INSERT INTO t1 VALUES (0.1),(0.2),(0.3); +CREATE TABLE t2 SELECT MIN(a + 0.0000000000000000000000000000001) AS c1 FROM t1; +DESC t2; +DROP TABLE t1,t2; + +CREATE TABLE t1 (a DECIMAL(30,30)); +INSERT INTO t1 VALUES (0.1),(0.2),(0.3); +CREATE TABLE t2 SELECT IFNULL(a + 0.0000000000000000000000000000001, NULL) AS c1 FROM t1; +DESC t2; +DROP TABLE t1,t2; + +CREATE TABLE t1 (a DECIMAL(30,30)); +INSERT INTO t1 VALUES (0.1),(0.2),(0.3); +CREATE TABLE t2 SELECT CASE a WHEN 0.1 THEN 0.0000000000000000000000000000000000000000000000000000000000000000001 END AS c1 FROM t1; +DESC t2; +DROP TABLE t1,t2; + +--echo # +--echo # Test that variables get maximum precision. +--echo # + +SET @decimal= 1.1; +CREATE TABLE t1 SELECT @decimal AS c1; +DESC t1; +SELECT * FROM t1; +DROP TABLE t1; diff --git a/sql/field.cc b/sql/field.cc index 3bfb54fbd15..426effa57cd 100644 --- a/sql/field.cc +++ b/sql/field.cc @@ -2485,12 +2485,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); diff --git a/sql/field.h b/sql/field.h index 5136f760fc1..a9299256f88 100644 --- a/sql/field.h +++ b/sql/field.h @@ -608,6 +608,10 @@ protected: class Field_num :public Field { public: + /** + The scale of the Field's value, i.e. the number of digits to the right + of the decimal point. + */ const uint8 dec; bool zerofill,unsigned_flag; // Purify cannot handle bit fields Field_num(uchar *ptr_arg,uint32 len_arg, uchar *null_ptr_arg, @@ -766,6 +770,11 @@ public: Field_new_decimal(uint32 len_arg, bool maybe_null_arg, const char *field_name_arg, uint8 dec_arg, bool unsigned_arg); + /* + Create a field to hold a decimal value from an item. + Truncates the precision and/or scale if necessary. + */ + static Field_new_decimal *new_decimal_field(const Item *item); enum_field_types type() const { return MYSQL_TYPE_NEWDECIMAL;} enum ha_base_keytype key_type() const { return HA_KEYTYPE_BINARY; } Item_result result_type () const { return DECIMAL_RESULT; } diff --git a/sql/item.cc b/sql/item.cc index 2640b74851b..9551c5feaff 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -435,17 +435,26 @@ Item::Item(THD *thd, Item *item): } +/** + Decimal precision of the item. + + @remark The precision must not be capped as it can be used in conjunction + with Item::decimals to determine the size of the integer part when + constructing a decimal data type. + + @see Item::decimal_int_part() + @see Item::decimals +*/ + uint Item::decimal_precision() const { + uint precision= max_length; Item_result restype= result_type(); if ((restype == DECIMAL_RESULT) || (restype == INT_RESULT)) - { - uint prec= - my_decimal_length_to_precision(max_length, decimals, unsigned_flag); - return min(prec, DECIMAL_MAX_PRECISION); - } - return min(max_length, DECIMAL_MAX_PRECISION); + precision= my_decimal_length_to_precision(max_length, decimals, unsigned_flag); + + return precision; } @@ -4902,9 +4911,7 @@ Field *Item::tmp_table_field_from_field_type(TABLE *table, bool fixed_length) switch (field_type()) { case MYSQL_TYPE_DECIMAL: case MYSQL_TYPE_NEWDECIMAL: - field= new Field_new_decimal((uchar*) 0, max_length, null_ptr, 0, - Field::NONE, name, decimals, 0, - unsigned_flag); + field= Field_new_decimal::new_decimal_field(this); break; case MYSQL_TYPE_TINY: field= new Field_tiny((uchar*) 0, max_length, null_ptr, 0, Field::NONE, diff --git a/sql/item.h b/sql/item.h index 3dfcd7c2612..464085cb101 100644 --- a/sql/item.h +++ b/sql/item.h @@ -755,9 +755,10 @@ 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;} + /** Returns the uncapped decimal precision of this item. */ virtual uint decimal_precision() const; inline int decimal_int_part() const - { return my_decimal_int_part(decimal_precision(), decimals); } + { return 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. diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc index 1ff9ca6a419..53feb753844 100644 --- a/sql/item_cmpfunc.cc +++ b/sql/item_cmpfunc.cc @@ -2183,7 +2183,7 @@ uint Item_func_ifnull::decimal_precision() const int arg1_int_part= args[1]->decimal_int_part(); int max_int_part= max(arg0_int_part, arg1_int_part); int precision= max_int_part + decimals; - return min(precision, DECIMAL_MAX_PRECISION); + return precision; } @@ -2367,7 +2367,7 @@ uint Item_func_if::decimal_precision() const int arg1_prec= args[1]->decimal_int_part(); int arg2_prec= args[2]->decimal_int_part(); int precision=max(arg1_prec,arg2_prec) + decimals; - return min(precision, DECIMAL_MAX_PRECISION); + return precision; } @@ -2775,7 +2775,7 @@ uint Item_func_case::decimal_precision() const 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); + return max_int_part + decimals; } diff --git a/sql/item_func.cc b/sql/item_func.cc index 55d4b37ddb0..6abc78371db 100644 --- a/sql/item_func.cc +++ b/sql/item_func.cc @@ -452,45 +452,8 @@ Field *Item_func::tmp_table_field(TABLE *table) return make_string_field(table); break; case DECIMAL_RESULT: - { - uint8 dec= decimals; - uint8 intg= decimal_precision() - dec; - uint32 len= max_length; - - /* - Trying to put too many digits overall in a DECIMAL(prec,dec) - will always throw a warning. We must limit dec to - DECIMAL_MAX_SCALE however to prevent an assert() later. - */ - - if (dec > 0) - { - int overflow; - - dec= min(dec, DECIMAL_MAX_SCALE); - - /* - If the value still overflows the field with the corrected dec, - we'll throw out decimals rather than integers. This is still - bad and of course throws a truncation warning. - */ - - const int required_length= - my_decimal_precision_to_length(intg + dec, dec, - unsigned_flag); - - overflow= required_length - len; - - if (overflow > 0) - dec= max(0, dec - overflow); // too long, discard fract - else - /* Corrected value fits. */ - len= required_length; - } - - field= new Field_new_decimal(len, maybe_null, name, dec, unsigned_flag); + field= Field_new_decimal::new_decimal_field(this); break; - } case ROW_RESULT: default: // This case should never be chosen @@ -4781,6 +4744,19 @@ void Item_func_get_user_var::fix_length_and_dec() } +uint Item_func_get_user_var::decimal_precision() const +{ + uint precision= max_length; + Item_result restype= result_type(); + + /* Default to maximum as the precision is unknown a priori. */ + if ((restype == DECIMAL_RESULT) || (restype == INT_RESULT)) + precision= DECIMAL_MAX_PRECISION; + + return precision; +} + + bool Item_func_get_user_var::const_item() const { return (!var_entry || current_thd->query_id != var_entry->update_query_id); diff --git a/sql/item_func.h b/sql/item_func.h index 025ac12fe07..fdbbff89e60 100644 --- a/sql/item_func.h +++ b/sql/item_func.h @@ -1393,6 +1393,7 @@ public: table_map used_tables() const { return const_item() ? 0 : RAND_TABLE_BIT; } bool eq(const Item *item, bool binary_cmp) const; + uint decimal_precision() const; private: bool set_value(THD *thd, sp_rcontext *ctx, Item **it); diff --git a/sql/item_sum.cc b/sql/item_sum.cc index 38251294053..08a48c6ce2f 100644 --- a/sql/item_sum.cc +++ b/sql/item_sum.cc @@ -517,8 +517,7 @@ Field *Item_sum::create_tmp_field(bool group, TABLE *table, name, table->s, collation.collation); break; case DECIMAL_RESULT: - field= new Field_new_decimal(max_length, maybe_null, name, - decimals, unsigned_flag); + field= Field_new_decimal::new_decimal_field(this); break; case ROW_RESULT: default: diff --git a/sql/my_decimal.h b/sql/my_decimal.h index 21669e82c44..b1df1395dcd 100644 --- a/sql/my_decimal.h +++ b/sql/my_decimal.h @@ -48,10 +48,12 @@ C_MODE_END digits * number of decimal digits in one our big digit - number of decimal digits in one our big digit decreased by 1 (because we always put decimal point on the border of our big digits)) + + This value is 65 due to historical reasons partly due to it being used + as the maximum allowed precision and not the actual maximum precision. */ #define DECIMAL_MAX_PRECISION (DECIMAL_MAX_POSSIBLE_PRECISION - 8*2) #define DECIMAL_MAX_SCALE 30 -#define DECIMAL_NOT_SPECIFIED 31 /** maximum length of string representation (number of maximum decimal @@ -75,12 +77,6 @@ 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. @@ -184,7 +180,7 @@ inline uint my_decimal_length_to_precision(uint length, uint scale, } inline uint32 my_decimal_precision_to_length_no_truncation(uint precision, - uint8 scale, + uint scale, bool unsigned_flag) { /* @@ -196,7 +192,7 @@ inline uint32 my_decimal_precision_to_length_no_truncation(uint precision, (unsigned_flag || !precision ? 0 : 1)); } -inline uint32 my_decimal_precision_to_length(uint precision, uint8 scale, +inline uint32 my_decimal_precision_to_length(uint precision, uint scale, bool unsigned_flag) { /* diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 7f6c5e834a3..5cb0de1ba5c 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -9377,47 +9377,8 @@ static Field *create_tmp_field_from_item(THD *thd, Item *item, TABLE *table, new_field->set_derivation(item->collation.derivation); break; case DECIMAL_RESULT: - { - uint8 dec= item->decimals; - uint8 intg= ((Item_decimal *) item)->decimal_precision() - dec; - uint32 len= item->max_length; - - /* - Trying to put too many digits overall in a DECIMAL(prec,dec) - will always throw a warning. We must limit dec to - DECIMAL_MAX_SCALE however to prevent an assert() later. - */ - - if (dec > 0) - { - signed int overflow; - - dec= min(dec, DECIMAL_MAX_SCALE); - - /* - If the value still overflows the field with the corrected dec, - we'll throw out decimals rather than integers. This is still - bad and of course throws a truncation warning. - +1: for decimal point - */ - - const int required_length= - my_decimal_precision_to_length(intg + dec, dec, - item->unsigned_flag); - - overflow= required_length - len; - - if (overflow > 0) - dec= max(0, dec - overflow); // too long, discard fract - else - /* Corrected value fits. */ - len= required_length; - } - - new_field= new Field_new_decimal(len, maybe_null, item->name, - dec, item->unsigned_flag); + new_field= Field_new_decimal::new_decimal_field(item); break; - } case ROW_RESULT: default: // This case should never be choosen |