1
0
mirror of https://github.com/MariaDB/server.git synced 2025-07-30 16:24:05 +03:00

Bug#45261: Crash, stored procedure + decimal

The problem was that creating a DECIMAL column from a decimal
value could lead to a failed 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 an 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.
  Implement method to create a field to hold a decimal value
  from an item.
sql/field.h:
  Explain member variable. Add method to create a new decimal field.
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 than the
  precision.
sql/item.h:
  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 to return the precision of a user variable.
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.
This commit is contained in:
Davi Arnaut
2009-08-24 16:47:08 -03:00
parent e31a41d10b
commit fc39459504
12 changed files with 483 additions and 110 deletions

View File

@ -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;

View File

@ -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;