List:Commits« Previous MessageNext Message »
From:Davi Arnaut Date:November 2 2009 11:21am
Subject:bzr commit into mysql-5.1-bugteam branch (davi:3172) Bug#45261
Bug#48370
View as plain text  
# At a local mysql-5.1-bugteam repository of davi

 3172 Davi Arnaut	2009-11-02
      Bug#48370: Absolutely wrong calculations with GROUP BY and decimal fields when using IF
      Bug#45261: Crash, stored procedure + decimal
      
      Revert fix for Bug#45261 due to unforeseen bugs.

    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-08-24 19:47:08 +0000
+++ b/mysql-test/r/type_newdecimal.result	2009-11-02 11:21:39 +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.00000000000000000000000000000	10
-3.00000000000000000000000000000	10
-4.00000000000000000000000000000	30
+2.000000000000000000000000000000	10
+3.000000000000000000000000000000	10
+4.000000000000000000000000000000	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:
-Note	1265	Data truncated for column 'f1' at row 1
+Warning	1264	Out of range value for column 'f1' at row 1
 DESC t1;
 Field	Type	Null	Key	Default	Extra
-f1	decimal(65,20)	NO		0.00000000000000000000	
+f1	decimal(65,30)	NO		0.000000000000000000000000000000	
 SELECT f1 FROM t1;
 f1
-123451234512345123451234512345123451234512345.67890678906789067891
+99999999999999999999999999999999999.999999999999999999999999999999
 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(32,30)	NO		0.000000000000000000000000000000	
+my_col	decimal(65,30)	NO		0.000000000000000000000000000000	
 SELECT my_col FROM t1;
 my_col
 1.123456789123456789123456789123
@@ -1625,212 +1625,8 @@ Warnings:
 Note	1265	Data truncated for column 'my_col' at row 1
 DESCRIBE t1;
 Field	Type	Null	Key	Default	Extra
-my_col	decimal(30,30)	YES		NULL	
+my_col	decimal(65,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-08-24 19:47:08 +0000
+++ b/mysql-test/t/type_newdecimal.test	2009-11-02 11:21:39 +0000
@@ -1286,137 +1286,3 @@ 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-10-09 21:57:43 +0000
+++ b/sql/field.cc	2009-11-02 11:21:39 +0000
@@ -2480,97 +2480,12 @@ 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);
 }
 
 
-/**
-  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);

=== modified file 'sql/field.h'
--- a/sql/field.h	2009-10-09 21:57:43 +0000
+++ b/sql/field.h	2009-11-02 11:21:39 +0000
@@ -621,10 +621,6 @@ 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,
@@ -782,11 +778,6 @@ 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; }

=== modified file 'sql/item.cc'
--- a/sql/item.cc	2009-10-13 04:43:27 +0000
+++ b/sql/item.cc	2009-11-02 11:21:39 +0000
@@ -435,26 +435,17 @@ 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))
-    precision= my_decimal_length_to_precision(max_length, decimals, unsigned_flag);
-
-  return precision;
+  {
+    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);
 }
 
 
@@ -4908,7 +4899,9 @@ Field *Item::tmp_table_field_from_field_
   switch (field_type()) {
   case MYSQL_TYPE_DECIMAL:
   case MYSQL_TYPE_NEWDECIMAL:
-    field= Field_new_decimal::new_decimal_field(this);
+    field= new Field_new_decimal((uchar*) 0, max_length, null_ptr, 0,
+                                 Field::NONE, name, decimals, 0,
+                                 unsigned_flag);
     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-08-28 10:55:59 +0000
+++ b/sql/item.h	2009-11-02 11:21:39 +0000
@@ -762,10 +762,9 @@ 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 decimal_precision() - decimals; }
+  { return my_decimal_int_part(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-10-05 05:27:36 +0000
+++ b/sql/item_cmpfunc.cc	2009-11-02 11:21:39 +0000
@@ -2191,7 +2191,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 precision;
+  return min(precision, DECIMAL_MAX_PRECISION);
 }
 
 
@@ -2375,7 +2375,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 precision;
+  return min(precision, DECIMAL_MAX_PRECISION);
 }
 
 
@@ -2783,7 +2783,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 max_int_part + decimals;
+  return min(max_int_part + decimals, DECIMAL_MAX_PRECISION);
 }
 
 

=== modified file 'sql/item_func.cc'
--- a/sql/item_func.cc	2009-09-24 13:28:13 +0000
+++ b/sql/item_func.cc	2009-11-02 11:21:39 +0000
@@ -451,8 +451,45 @@ Field *Item_func::tmp_table_field(TABLE 
     return make_string_field(table);
     break;
   case DECIMAL_RESULT:
-    field= Field_new_decimal::new_decimal_field(this);
+  {
+    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);
     break;
+  }
   case ROW_RESULT:
   default:
     // This case should never be chosen
@@ -4739,19 +4776,6 @@ 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-08-24 19:47:08 +0000
+++ b/sql/item_func.h	2009-11-02 11:21:39 +0000
@@ -1393,7 +1393,6 @@ 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-08-24 19:47:08 +0000
+++ b/sql/item_sum.cc	2009-11-02 11:21:39 +0000
@@ -517,7 +517,8 @@ Field *Item_sum::create_tmp_field(bool g
                                name, table->s, collation.collation);
     break;
   case DECIMAL_RESULT:
-    field= Field_new_decimal::new_decimal_field(this);
+    field= new Field_new_decimal(max_length, maybe_null, name,
+                                 decimals, unsigned_flag);
     break;
   case ROW_RESULT:
   default:

=== modified file 'sql/my_decimal.h'
--- a/sql/my_decimal.h	2009-08-24 19:47:08 +0000
+++ b/sql/my_decimal.h	2009-11-02 11:21:39 +0000
@@ -48,12 +48,10 @@ 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
@@ -77,6 +75,12 @@ 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.
 
@@ -180,7 +184,7 @@ inline uint my_decimal_length_to_precisi
 }
 
 inline uint32 my_decimal_precision_to_length_no_truncation(uint precision,
-                                                           uint scale,
+                                                           uint8 scale,
                                                            bool unsigned_flag)
 {
   /*
@@ -192,7 +196,7 @@ inline uint32 my_decimal_precision_to_le
                   (unsigned_flag || !precision ? 0 : 1));
 }
 
-inline uint32 my_decimal_precision_to_length(uint precision, uint scale,
+inline uint32 my_decimal_precision_to_length(uint precision, uint8 scale,
                                              bool unsigned_flag)
 {
   /*

=== modified file 'sql/sql_select.cc'
--- a/sql/sql_select.cc	2009-10-30 16:17:44 +0000
+++ b/sql/sql_select.cc	2009-11-02 11:21:39 +0000
@@ -9433,8 +9433,47 @@ static Field *create_tmp_field_from_item
     new_field->set_derivation(item->collation.derivation);
     break;
   case DECIMAL_RESULT:
-    new_field= Field_new_decimal::new_decimal_field(item);
+  {
+    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);
     break;
+  }
   case ROW_RESULT:
   default:
     // This case should never be choosen


Attachment: [text/bzr-bundle] bzr/davi.arnaut@sun.com-20091102112139-1h6obsalgvprn51a.bundle
Thread
bzr commit into mysql-5.1-bugteam branch (davi:3172) Bug#45261Bug#48370Davi Arnaut2 Nov