List:Commits« Previous MessageNext Message »
From:Davi Arnaut Date:July 23 2009 10:11pm
Subject:bzr commit into mysql-5.1-bugteam branch (davi:3036) Bug#45261
View as plain text  
# At a local mysql-5.1-bugteam repository of davi

 3036 Davi Arnaut	2009-07-23
      Bug#45261: Crash, stored procedure + decimal
      
      The problem was that creating a DECIMAL column from decimal
      value could lead to a assertion as decimal values can have
      a higher precision than those attached to a table. The assert
      could be triggered by creating a table from a decimal with
      a large (> 30) scale. Also, there was a problem in calculating
      the number of digits in the integral and fractional parts if
      both exceeded the maximum number of digits permitted by the
      new decimal type.
      
      The solution is to ensure that truncation procedure is executed
      when deducing a DECIMAL column from a decimal value of higher
      precision. If the integer part is equal to or bigger than the
      maximum precision for the DECIMAL type (65), the integer part
      is truncated to fit and the fractional becomes zero. Otherwise,
      the fractional part is truncated to fit into the space left
      after the integer part is copied.
      
      This patch borrows code and ideas from Martin Hansson's patch.
     @ mysql-test/r/type_newdecimal.result
        Add test case result for Bug#45261. Also, update test case to
        reflect that a additive operation increases the precision of
        the resulting type by 1.
     @ mysql-test/t/type_newdecimal.test
        Add test case for Bug#45261
     @ sql/field.cc
        Added DBUG_ASSERT to ensure object's invariant is maintained.
     @ sql/field.h
        Explain member variable.
     @ sql/item.cc
        The precision should only be capped when storing the value
        on a table. Also, this makes it impossible to calculate the
        integer part if Item::decimals (the scale) is larger then the
        precision.
        
        Implement method to create a field to hold a decimal value from
        an item.
     @ sql/item.h
        Add method to create a new decimal field.
        Simplify calculation of integer part.
     @ sql/item_cmpfunc.cc
        Do not limit the precision. It will be capped later.
     @ sql/item_func.cc
        Use new method for allocating a new decimal field.
        Add a specialized method for retrieving the precision
        of a user variable item.
     @ sql/item_func.h
        Add method.
     @ sql/item_sum.cc
        Use new method for allocating a new decimal field.
     @ sql/my_decimal.h
        The integer part could be improperly calculated for a decimal
        with 31 digits in the fractional part.
     @ sql/sql_select.cc
        Use new method which truncates the integer or decimal parts
        as needed.

    modified:
      mysql-test/r/type_newdecimal.result
      mysql-test/t/type_newdecimal.test
      sql/field.cc
      sql/field.h
      sql/item.cc
      sql/item.h
      sql/item_cmpfunc.cc
      sql/item_func.cc
      sql/item_func.h
      sql/item_sum.cc
      sql/my_decimal.h
      sql/sql_select.cc
=== modified file 'mysql-test/r/type_newdecimal.result'
--- a/mysql-test/r/type_newdecimal.result	2009-07-03 10:36:04 +0000
+++ b/mysql-test/r/type_newdecimal.result	2009-07-23 22:11:01 +0000
@@ -1495,9 +1495,9 @@ CREATE TABLE t1 (a int DEFAULT NULL, b i
 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;

=== modified file 'mysql-test/t/type_newdecimal.test'
--- a/mysql-test/t/type_newdecimal.test	2009-07-03 10:36:04 +0000
+++ b/mysql-test/t/type_newdecimal.test	2009-07-23 22:11:01 +0000
@@ -1286,3 +1286,137 @@ CREATE TABLE t1 SELECT 1 % .123456789123
 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;

=== modified file 'sql/field.cc'
--- a/sql/field.cc	2009-07-10 23:12:13 +0000
+++ b/sql/field.cc	2009-07-23 22:11:01 +0000
@@ -2485,6 +2485,7 @@ Field_new_decimal::Field_new_decimal(uin
 {
   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);

=== modified file 'sql/field.h'
--- a/sql/field.h	2009-06-09 16:44:26 +0000
+++ b/sql/field.h	2009-07-23 22:11:01 +0000
@@ -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,

=== modified file 'sql/item.cc'
--- a/sql/item.cc	2009-07-19 12:49:40 +0000
+++ b/sql/item.cc	2009-07-23 22:11:01 +0000
@@ -437,15 +437,13 @@ Item::Item(THD *thd, Item *item):
 
 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;
 }
 
 
@@ -4880,6 +4878,88 @@ Field *Item::make_string_field(TABLE *ta
 
 
 /**
+  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 *Item::make_new_decimal_field() const
+{
+  uint32 len;
+  uint intg= decimal_int_part(), scale= decimals;
+
+  DBUG_ASSERT(decimal_precision() >= 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, unsigned_flag);
+
+  return new Field_new_decimal(len, maybe_null, name, scale, unsigned_flag);
+}
+
+
+/**
   Create a field based on field_type of argument.
 
   For now, this is only used to create a field for
@@ -4902,9 +4982,7 @@ Field *Item::tmp_table_field_from_field_
   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= make_new_decimal_field();
     break;
   case MYSQL_TYPE_TINY:
     field= new Field_tiny((uchar*) 0, max_length, null_ptr, 0, Field::NONE,

=== modified file 'sql/item.h'
--- a/sql/item.h	2009-05-25 08:00:40 +0000
+++ b/sql/item.h	2009-07-23 22:11:01 +0000
@@ -539,6 +539,7 @@ public:
   virtual void cleanup();
   virtual void make_field(Send_field *field);
   Field *make_string_field(TABLE *table);
+  Field *make_new_decimal_field() const;
   virtual bool fix_fields(THD *, Item **);
   /*
     should be used in case where we are sure that we do not need
@@ -757,7 +758,7 @@ public:
   { 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); }
+  { 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.

=== modified file 'sql/item_cmpfunc.cc'
--- a/sql/item_cmpfunc.cc	2009-07-03 10:36:04 +0000
+++ b/sql/item_cmpfunc.cc	2009-07-23 22:11:01 +0000
@@ -2183,7 +2183,7 @@ uint Item_func_ifnull::decimal_precision
   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() c
   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()
 
   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;
 }
 
 

=== modified file 'sql/item_func.cc'
--- a/sql/item_func.cc	2009-07-03 10:36:04 +0000
+++ b/sql/item_func.cc	2009-07-23 22:11:01 +0000
@@ -452,45 +452,8 @@ Field *Item_func::tmp_table_field(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= make_new_decimal_field();
     break;
-  }
   case ROW_RESULT:
   default:
     // This case should never be chosen
@@ -4781,6 +4744,19 @@ void Item_func_get_user_var::fix_length_
 }
 
 
+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);

=== modified file 'sql/item_func.h'
--- a/sql/item_func.h	2009-07-15 10:13:45 +0000
+++ b/sql/item_func.h	2009-07-23 22:11:01 +0000
@@ -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);
 

=== modified file 'sql/item_sum.cc'
--- a/sql/item_sum.cc	2009-07-10 14:04:58 +0000
+++ b/sql/item_sum.cc	2009-07-23 22:11:01 +0000
@@ -517,8 +517,7 @@ Field *Item_sum::create_tmp_field(bool g
                                name, table->s, collation.collation);
     break;
   case DECIMAL_RESULT:
-    field= new Field_new_decimal(max_length, maybe_null, name,
-                                 decimals, unsigned_flag);
+    field= make_new_decimal_field();
     break;
   case ROW_RESULT:
   default:

=== modified file 'sql/my_decimal.h'
--- a/sql/my_decimal.h	2009-07-03 10:36:04 +0000
+++ b/sql/my_decimal.h	2009-07-23 22:11:01 +0000
@@ -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 precisi
 }
 
 
-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_precisi
 }
 
 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_le
                   (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)
 {
   /*

=== modified file 'sql/sql_select.cc'
--- a/sql/sql_select.cc	2009-07-16 13:17:47 +0000
+++ b/sql/sql_select.cc	2009-07-23 22:11:01 +0000
@@ -9377,47 +9377,8 @@ static Field *create_tmp_field_from_item
     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= item->make_new_decimal_field();
     break;
-  }
   case ROW_RESULT:
   default:
     // This case should never be choosen


Attachment: [text/bzr-bundle] bzr/davi.arnaut@sun.com-20090723221101-fqd6c9otv1ssfryj.bundle
Thread
bzr commit into mysql-5.1-bugteam branch (davi:3036) Bug#45261Davi Arnaut24 Jul
  • Re: bzr commit into mysql-5.1-bugteam branch (davi:3036) Bug#45261Martin Hansson11 Aug
    • Re: bzr commit into mysql-5.1-bugteam branch (davi:3036) Bug#45261Davi Arnaut11 Aug
      • Re: bzr commit into mysql-5.1-bugteam branch (davi:3036) Bug#45261Martin Hansson12 Aug
        • Re: bzr commit into mysql-5.1-bugteam branch (davi:3036) Bug#45261Davi Arnaut12 Aug
  • Re: bzr commit into mysql-5.1-bugteam branch (davi:3036) Bug#45261Sergei Golubchik24 Aug