summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTor Didriksen <tor.didriksen@oracle.com>2015-04-21 08:32:31 +0200
committerholyfoot <holyfoot@mariadb.com>2017-03-06 23:36:48 +0400
commite823023e4bb48b0db9ee91477076fc20db704769 (patch)
tree0aa377acc805abb0e117ff7c75d0bba7af465502
parent68d632bc5a1437282dde5460b7a0d3d887fd5483 (diff)
downloadmariadb-git-e823023e4bb48b0db9ee91477076fc20db704769.tar.gz
Bug#18411494 WRONG COMPARSION ON BIG DECIMAL VALUES
Problem: integer literals may be converted to floats for comparison with decimal data. If the integers are large, we may lose precision, and give wrong results. Fix: for <non-const decimal expression> <cmp> <const string expression> or <const string expression> <cmp> <non-const decimal expression> we override the compare_type chosen by item_cmp_type(), and do comparison as decimal rather than float. (cherry picked from commit https://github.com/mysql/mysql-server/commit/1cf3489ba42df1f29c1f3e269443254087505166 and edited by Johannes Weißl <jargon@molb.org>)
-rw-r--r--mysql-test/r/type_newdecimal.result39
-rw-r--r--mysql-test/t/type_newdecimal.test27
-rw-r--r--sql/item_cmpfunc.cc16
3 files changed, 82 insertions, 0 deletions
diff --git a/mysql-test/r/type_newdecimal.result b/mysql-test/r/type_newdecimal.result
index 2f6a3ea2a61..56a04971fa1 100644
--- a/mysql-test/r/type_newdecimal.result
+++ b/mysql-test/r/type_newdecimal.result
@@ -2243,3 +2243,42 @@ DROP TABLE t1;
#
# End of 10.1 tests
#
+#
+# Bug#18408499 UNSIGNED BIGINT HIGH VALUES
+# WRONG NUMERICAL COMPARISON RESULTS
+#
+CREATE TABLE t1(value DECIMAL(24,0) NOT NULL);
+INSERT INTO t1(value)
+VALUES('100000000000000000000001'),
+('100000000000000000000002'),
+('100000000000000000000003');
+SELECT * FROM t1 WHERE value = '100000000000000000000002';
+value
+100000000000000000000002
+SELECT * FROM t1 WHERE '100000000000000000000002' = value;
+value
+100000000000000000000002
+SELECT * FROM t1 WHERE value + 0 = '100000000000000000000002';
+value
+100000000000000000000002
+SELECT * FROM t1 WHERE value = 100000000000000000000002;
+value
+100000000000000000000002
+SELECT * FROM t1 WHERE value + 0 = 100000000000000000000002;
+value
+100000000000000000000002
+PREPARE stmt FROM 'SELECT * FROM t1 WHERE value = ?';
+set @a="100000000000000000000002";
+EXECUTE stmt using @a;
+value
+100000000000000000000002
+set @a=100000000000000000000002;
+EXECUTE stmt using @a;
+value
+100000000000000000000002
+DEALLOCATE PREPARE stmt;
+ALTER TABLE t1 ADD INDEX value (value);
+SELECT * FROM t1 WHERE value = '100000000000000000000002';
+value
+100000000000000000000002
+DROP TABLE t1;
diff --git a/mysql-test/t/type_newdecimal.test b/mysql-test/t/type_newdecimal.test
index b84d01e93be..6fb8fd4f97d 100644
--- a/mysql-test/t/type_newdecimal.test
+++ b/mysql-test/t/type_newdecimal.test
@@ -1738,3 +1738,30 @@ DROP TABLE t1;
--echo #
--echo # End of 10.1 tests
--echo #
+
+--echo #
+--echo # Bug#18408499 UNSIGNED BIGINT HIGH VALUES
+--echo # WRONG NUMERICAL COMPARISON RESULTS
+--echo #
+
+CREATE TABLE t1(value DECIMAL(24,0) NOT NULL);
+INSERT INTO t1(value)
+VALUES('100000000000000000000001'),
+ ('100000000000000000000002'),
+ ('100000000000000000000003');
+SELECT * FROM t1 WHERE value = '100000000000000000000002';
+SELECT * FROM t1 WHERE '100000000000000000000002' = value;
+SELECT * FROM t1 WHERE value + 0 = '100000000000000000000002';
+SELECT * FROM t1 WHERE value = 100000000000000000000002;
+SELECT * FROM t1 WHERE value + 0 = 100000000000000000000002;
+
+PREPARE stmt FROM 'SELECT * FROM t1 WHERE value = ?';
+set @a="100000000000000000000002";
+EXECUTE stmt using @a;
+set @a=100000000000000000000002;
+EXECUTE stmt using @a;
+DEALLOCATE PREPARE stmt;
+
+ALTER TABLE t1 ADD INDEX value (value);
+SELECT * FROM t1 WHERE value = '100000000000000000000002';
+DROP TABLE t1;
diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc
index 7a7b2ca2933..e78f73ee03c 100644
--- a/sql/item_cmpfunc.cc
+++ b/sql/item_cmpfunc.cc
@@ -655,6 +655,22 @@ int Arg_comparator::set_cmp_func(Item_func_or_sum *owner_arg,
return 0;
}
+ if (m_compare_type == REAL_RESULT &&
+ (((*a)->result_type() == DECIMAL_RESULT && !(*a)->const_item() &&
+ (*b)->result_type() == STRING_RESULT && (*b)->const_item()) ||
+ ((*b)->result_type() == DECIMAL_RESULT && !(*b)->const_item() &&
+ (*a)->result_type() == STRING_RESULT && (*a)->const_item())))
+ {
+ /*
+ <non-const decimal expression> <cmp> <const string expression>
+ or
+ <const string expression> <cmp> <non-const decimal expression>
+
+ Do comparison as decimal rather than float, in order not to lose precision.
+ */
+ m_compare_type= DECIMAL_RESULT;
+ }
+
if (m_compare_type == INT_RESULT &&
(*a)->field_type() == MYSQL_TYPE_YEAR &&
(*b)->field_type() == MYSQL_TYPE_YEAR)