Skip to content

Commit

Permalink
MDEV-23105 Cast number string with many leading zeros to decimal give…
Browse files Browse the repository at this point in the history
…s unexpected result

Skip leading zeros when converting a string to decimal_t.
  • Loading branch information
abarkov committed Aug 5, 2020
1 parent a09a06d commit 0e80f5a
Show file tree
Hide file tree
Showing 3 changed files with 84 additions and 0 deletions.
44 changes: 44 additions & 0 deletions mysql-test/r/type_newdecimal.result
Original file line number Diff line number Diff line change
Expand Up @@ -2342,6 +2342,50 @@ t1 CREATE TABLE `t1` (
) ENGINE=MyISAM DEFAULT CHARSET=latin1
DROP TABLE t1;
#
# MDEV-23105 Cast number string with many leading zeros to decimal gives unexpected result
#
SELECT CAST(0000000000000000000000000000000000000000000000000000000000000000000000000000000020.01 AS DECIMAL(15,2)) as val;
val
20.01
SET sql_mode='';
CREATE TABLE t1 (a TEXT);
INSERT INTO t1 VALUES (CONCAT(REPEAT('0',100),1));
INSERT INTO t1 VALUES (CONCAT(REPEAT('0',100),1,'.0'));
INSERT INTO t1 VALUES (CONCAT(REPEAT('0',100),1,'.9'));
INSERT INTO t1 VALUES (CONCAT(REPEAT('0',100),1,'.99'));
INSERT INTO t1 VALUES (CONCAT(REPEAT('0',100),1,'.994'));
INSERT INTO t1 VALUES (CONCAT(REPEAT('0',100),1,'.995'));
INSERT INTO t1 VALUES (CONCAT(REPEAT('0',100),1,'.999'));
CREATE TABLE t2 (a TEXT, d DECIMAL(15,2));
INSERT IGNORE INTO t2 (a,d) SELECT a, a FROM t1;
Warnings:
Note 1265 Data truncated for column 'd' at row 5
Note 1265 Data truncated for column 'd' at row 6
Note 1265 Data truncated for column 'd' at row 7
INSERT IGNORE INTO t2 (a,d) SELECT CONCAT('-',a), CONCAT('-',a) FROM t1;
Warnings:
Note 1265 Data truncated for column 'd' at row 5
Note 1265 Data truncated for column 'd' at row 6
Note 1265 Data truncated for column 'd' at row 7
SELECT d, a FROM t2 ORDER BY d,a;
d a
-2.00 -00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001.995
-2.00 -00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001.999
-1.99 -00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001.99
-1.99 -00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001.994
-1.90 -00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001.9
-1.00 -00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001
-1.00 -00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001.0
1.00 00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001
1.00 00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001.0
1.90 00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001.9
1.99 00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001.99
1.99 00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001.994
2.00 00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001.995
2.00 00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001.999
DROP TABLE t1, t2;
SET sql_mode=DEFAULT;
#
# End of 10.1 tests
#
#
Expand Down
22 changes: 22 additions & 0 deletions mysql-test/t/type_newdecimal.test
Original file line number Diff line number Diff line change
Expand Up @@ -1821,6 +1821,28 @@ CREATE TABLE t1 (a DECIMAL(2,1) DEFAULT 0.10001e0);
SHOW CREATE TABLE t1;
DROP TABLE t1;

--echo #
--echo # MDEV-23105 Cast number string with many leading zeros to decimal gives unexpected result
--echo #

SELECT CAST(0000000000000000000000000000000000000000000000000000000000000000000000000000000020.01 AS DECIMAL(15,2)) as val;

SET sql_mode='';
CREATE TABLE t1 (a TEXT);
INSERT INTO t1 VALUES (CONCAT(REPEAT('0',100),1));
INSERT INTO t1 VALUES (CONCAT(REPEAT('0',100),1,'.0'));
INSERT INTO t1 VALUES (CONCAT(REPEAT('0',100),1,'.9'));
INSERT INTO t1 VALUES (CONCAT(REPEAT('0',100),1,'.99'));
INSERT INTO t1 VALUES (CONCAT(REPEAT('0',100),1,'.994'));
INSERT INTO t1 VALUES (CONCAT(REPEAT('0',100),1,'.995'));
INSERT INTO t1 VALUES (CONCAT(REPEAT('0',100),1,'.999'));
CREATE TABLE t2 (a TEXT, d DECIMAL(15,2));
INSERT IGNORE INTO t2 (a,d) SELECT a, a FROM t1;
INSERT IGNORE INTO t2 (a,d) SELECT CONCAT('-',a), CONCAT('-',a) FROM t1;
SELECT d, a FROM t2 ORDER BY d,a;
DROP TABLE t1, t2;
SET sql_mode=DEFAULT;

--echo #
--echo # End of 10.1 tests
--echo #
Expand Down
18 changes: 18 additions & 0 deletions strings/decimal.c
Original file line number Diff line number Diff line change
Expand Up @@ -812,6 +812,24 @@ internal_str2dec(const char *from, decimal_t *to, char **end, my_bool fixed)
while (s < end_of_string && my_isdigit(&my_charset_latin1, *s))
s++;
intg= (int) (s-s1);
/*
If the integer part is long enough and it has multiple leading zeros,
let's trim them, so this expression can return 1 without overflowing:
CAST(CONCAT(REPEAT('0',90),'1') AS DECIMAL(10))
*/
if (intg > DIG_PER_DEC1 && s1[0] == '0' && s1[1] == '0')
{
/*
Keep at least one digit, to avoid an empty string.
So we trim '0000' to '0' rather than to ''.
Otherwise the below code (converting digits to to->buf)
would fail on a fatal error.
*/
const char *iend= s - 1;
for ( ; s1 < iend && *s1 == '0'; s1++)
{ }
intg= (int) (s-s1);
}
if (s < end_of_string && *s=='.')
{
endp= s+1;
Expand Down

0 comments on commit 0e80f5a

Please sign in to comment.