#At file:///data0/martin/bzr/bug45261/5.1bt-gca-commit/ based on revid:davi.arnaut@stripped
2936 Martin Hansson 2009-06-29
Bug#45261: Crash, stored procedure + decimal
The truncation procedure for creating field for DECIMAL typed columns
calculated overflow using a function that automatically truncated field
length to avoid overflow, and this was caught much later than the actual
error.
Fixed by creating a new function for field length calculation that does not
truncate, and by adding and assertion in constructor for DECIMAL type
column objects.
@ mysql-test/r/type_newdecimal.result
Bug#45261:
- Wrong test result turned correct.
- Test result.
@ mysql-test/t/type_newdecimal.test
Bug#45261: Test case.
@ sql/field.cc
Bug#45261: Added DBUG_ASSERT to ensure object's invariant is maintained.
@ sql/field.h
Bug#45261: Added comment to explain what member is for.
@ sql/my_decimal.h
Bug#45261: Created non-truncating overload of my_decimal_precision_to_length()
@ sql/sql_select.cc
Bug#45261: Fix: Using new non-truncating function.
modified:
mysql-test/r/type_newdecimal.result
mysql-test/t/type_newdecimal.test
sql/field.cc
sql/field.h
sql/my_decimal.h
sql/sql_select.cc
=== modified file 'mysql-test/r/type_newdecimal.result'
--- a/mysql-test/r/type_newdecimal.result 2008-11-18 09:52:03 +0000
+++ b/mysql-test/r/type_newdecimal.result 2009-06-29 08:20:19 +0000
@@ -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(59,30) NO 0.000000000000000000000000000000
+f1 decimal(65,20) NO 0.00000000000000000000
SELECT f1 FROM t1;
f1
-99999999999999999999999999999.999999999999999999999999999999
+123451234512345123451234512345123451234512345.67890678906789067891
DROP TABLE t1;
select (1.20396873 * 0.89550000 * 0.68000000 * 1.08721696 * 0.99500000 *
1.01500000 * 1.01500000 * 0.99500000);
@@ -1577,3 +1577,47 @@ Error 1264 Out of range value for column
select cast(98.6 as decimal(2,0));
cast(98.6 as decimal(2,0))
99
+CREATE TABLE t1 SELECT
+123456789012345678901234567890123456789012345.123456789012345678901234567890123456789012345
+AS a;
+Warnings:
+Note 1265 Data truncated for column 'a' at row 1
+SELECT a FROM t1;
+a
+123456789012345678901234567890123456789012345.12345678901234567890
+DESC t1;
+Field Type Null Key Default Extra
+a decimal(65,20) NO 0.00000000000000000000
+CREATE TABLE t2 SELECT
+12345678901234567890123456789012345678901234567890123456789012345.1
+AS a;
+Warnings:
+Note 1265 Data truncated for column 'a' at row 1
+SELECT a FROM t2;
+a
+12345678901234567890123456789012345678901234567890123456789012345
+DESC t2;
+Field Type Null Key Default Extra
+a decimal(65,0) NO 0
+CREATE TABLE t3 SELECT
+123456789012345678901234567890123456789012345678901234567890123456.1
+AS a;
+Warnings:
+Warning 1264 Out of range value for column 'a' at row 1
+SELECT a FROM t3;
+a
+99999999999999999999999999999999999999999999999999999999999999999
+DESC t3;
+Field Type Null Key Default Extra
+a decimal(65,0) NO 0
+CREATE TABLE t4
+SELECT .123456789012345678901234567890123456789012345678901234567890123456 AS a;
+Warnings:
+Note 1265 Data truncated for column 'a' at row 1
+SELECT a FROM t4;
+a
+0.123456789012345678901234567890
+DESC t4;
+Field Type Null Key Default Extra
+a decimal(30,30) NO 0.000000000000000000000000000000
+DROP TABLE t1, t2, t3, t4;
=== modified file 'mysql-test/t/type_newdecimal.test'
--- a/mysql-test/t/type_newdecimal.test 2008-11-17 15:43:10 +0000
+++ b/mysql-test/t/type_newdecimal.test 2009-06-29 08:20:19 +0000
@@ -1257,3 +1257,31 @@ select cast(-3.4 as decimal(2,1));
select cast(99.6 as decimal(2,0));
select cast(-13.4 as decimal(2,1));
select cast(98.6 as decimal(2,0));
+#
+# Bug#45261 Crash, stored procedure + decimal
+#
+
+CREATE TABLE t1 SELECT
+123456789012345678901234567890123456789012345.123456789012345678901234567890123456789012345
+AS a;
+SELECT a FROM t1;
+DESC t1;
+
+CREATE TABLE t2 SELECT
+12345678901234567890123456789012345678901234567890123456789012345.1
+AS a;
+SELECT a FROM t2;
+DESC t2;
+
+CREATE TABLE t3 SELECT
+123456789012345678901234567890123456789012345678901234567890123456.1
+AS a;
+SELECT a FROM t3;
+DESC t3;
+
+CREATE TABLE t4
+SELECT .123456789012345678901234567890123456789012345678901234567890123456 AS a;
+SELECT a FROM t4;
+DESC t4;
+
+DROP TABLE t1, t2, t3, t4;
=== modified file 'sql/field.cc'
--- a/sql/field.cc 2009-06-09 16:44:26 +0000
+++ b/sql/field.cc 2009-06-29 08:20:19 +0000
@@ -2473,7 +2473,6 @@ Field_new_decimal::Field_new_decimal(uch
bin_size= my_decimal_get_binary_size(precision, dec);
}
-
Field_new_decimal::Field_new_decimal(uint32 len_arg,
bool maybe_null_arg,
const char *name,
@@ -2485,6 +2484,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-06-29 08:20:19 +0000
@@ -608,6 +608,11 @@ 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/my_decimal.h'
--- a/sql/my_decimal.h 2008-05-20 07:38:17 +0000
+++ b/sql/my_decimal.h 2009-06-29 08:20:19 +0000
@@ -184,18 +184,25 @@ inline uint my_decimal_length_to_precisi
}
inline uint32 my_decimal_precision_to_length(uint precision, uint8 scale,
- bool unsigned_flag)
+ bool unsigned_flag, bool truncate)
{
/*
When precision is 0 it means that original length was also 0. Thus
unsigned_flag is ignored in this case.
*/
DBUG_ASSERT(precision || !scale);
- set_if_smaller(precision, DECIMAL_MAX_PRECISION);
+ if (truncate)
+ set_if_smaller(precision, DECIMAL_MAX_PRECISION);
return (uint32)(precision + (scale>0 ? 1:0) +
(unsigned_flag || !precision ? 0:1));
}
+inline uint32 my_decimal_precision_to_length(uint precision, uint8 scale,
+ bool unsigned_flag)
+{
+ return my_decimal_precision_to_length(precision, scale, unsigned_flag, TRUE);
+}
+
inline
int my_decimal_string_length(const my_decimal *d)
{
=== modified file 'sql/sql_select.cc'
--- a/sql/sql_select.cc 2009-06-07 20:40:53 +0000
+++ b/sql/sql_select.cc 2009-06-29 08:20:19 +0000
@@ -9389,14 +9389,16 @@ static Field *create_tmp_field_from_item
bad and of course throws a truncation warning.
+1: for decimal point
*/
-
- overflow= my_decimal_precision_to_length(intg + dec, dec,
- item->unsigned_flag) - len;
+ const int required_length=
+ my_decimal_precision_to_length(intg + dec, dec,
+ item->unsigned_flag, FALSE);
+ overflow= required_length - len;
if (overflow > 0)
dec= max(0, dec - overflow); // too long, discard fract
else
- len -= item->decimals - dec; // corrected value fits
+ /* Corrected value fits. */
+ len= required_length;
}
new_field= new Field_new_decimal(len, maybe_null, item->name,
Attachment: [text/bzr-bundle] bzr/martin.hansson@sun.com-20090629082019-bldj5u24fwgvu4a2.bundle