diff options
author | Alexander Barkov <bar@mariadb.com> | 2020-08-05 08:14:49 +0400 |
---|---|---|
committer | Alexander Barkov <bar@mariadb.com> | 2020-08-05 08:14:49 +0400 |
commit | 0e80f5a6934692dd7a47b6d31104fa194bbf18ec (patch) | |
tree | b65752de8101426747f75b570a81c365417c9a0a | |
parent | a09a06d597319c23eeeb9372c93ffc17eafefb07 (diff) | |
download | mariadb-git-0e80f5a6934692dd7a47b6d31104fa194bbf18ec.tar.gz |
MDEV-23105 Cast number string with many leading zeros to decimal gives unexpected result
Skip leading zeros when converting a string to decimal_t.
-rw-r--r-- | mysql-test/r/type_newdecimal.result | 44 | ||||
-rw-r--r-- | mysql-test/t/type_newdecimal.test | 22 | ||||
-rw-r--r-- | strings/decimal.c | 18 |
3 files changed, 84 insertions, 0 deletions
diff --git a/mysql-test/r/type_newdecimal.result b/mysql-test/r/type_newdecimal.result index 7b6629bc7b2..0c8f4f55442 100644 --- a/mysql-test/r/type_newdecimal.result +++ b/mysql-test/r/type_newdecimal.result @@ -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 # # diff --git a/mysql-test/t/type_newdecimal.test b/mysql-test/t/type_newdecimal.test index 9b1beeb3f51..7338780c616 100644 --- a/mysql-test/t/type_newdecimal.test +++ b/mysql-test/t/type_newdecimal.test @@ -1822,6 +1822,28 @@ 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 # diff --git a/strings/decimal.c b/strings/decimal.c index 56d09e8f303..dd2e1236489 100644 --- a/strings/decimal.c +++ b/strings/decimal.c @@ -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; |