[Tarantool-patches] [PATCH v1 1/1] sql: fix cast of small negative DOUBLE to INTEGER

imeevma at tarantool.org imeevma at tarantool.org
Wed Aug 4 11:24:32 MSK 2021


Prior to this patch when DOUBLE value that less than 0.0 and greater
than -1.0 was cast to INTEGER, it was considered to be negative number
though the result was 0. This patch fixes this, so now such DOUBLE
value will be properly cast to INTEGER and UNSIGNED.

Closes #6225
---
https://github.com/tarantool/tarantool/issues/6225
https://github.com/tarantool/tarantool/tree/imeevma/gh-6225-cast-of-small-negative-double-to-int

 ...225-cast-of-small-negative-double-to-int.md |  4 ++++
 src/box/sql/mem.c                              | 18 +++++++++---------
 test/sql-tap/numcast.test.lua                  | 14 +++++++++++++-
 3 files changed, 26 insertions(+), 10 deletions(-)
 create mode 100644 changelogs/unreleased/gh-6225-cast-of-small-negative-double-to-int.md

diff --git a/changelogs/unreleased/gh-6225-cast-of-small-negative-double-to-int.md b/changelogs/unreleased/gh-6225-cast-of-small-negative-double-to-int.md
new file mode 100644
index 000000000..9f5d62231
--- /dev/null
+++ b/changelogs/unreleased/gh-6225-cast-of-small-negative-double-to-int.md
@@ -0,0 +1,4 @@
+## bugfix/sql
+
+* Fixed assert on cast of DOUBLE value that greater than -1.0 and less than 0.0
+  to INTEGER and UNSIGNED (gh-6255).
diff --git a/src/box/sql/mem.c b/src/box/sql/mem.c
index e4ce233e0..6b95e41d3 100644
--- a/src/box/sql/mem.c
+++ b/src/box/sql/mem.c
@@ -787,14 +787,14 @@ double_to_int(struct Mem *mem)
 {
 	assert(mem->type == MEM_TYPE_DOUBLE);
 	double d = mem->u.r;
-	if (d < 0 && d >= (double)INT64_MIN) {
+	if (d <= -1.0 && d >= (double)INT64_MIN) {
 		mem->u.i = (int64_t)d;
 		mem->type = MEM_TYPE_INT;
 		assert(mem->flags == 0);
 		mem->field_type = FIELD_TYPE_INTEGER;
 		return 0;
 	}
-	if (d >= 0 && d < (double)UINT64_MAX) {
+	if (d > -1.0 && d < (double)UINT64_MAX) {
 		mem->u.u = (uint64_t)d;
 		mem->type = MEM_TYPE_UINT;
 		assert(mem->flags == 0);
@@ -809,14 +809,14 @@ double_to_int_precise(struct Mem *mem)
 {
 	assert(mem->type == MEM_TYPE_DOUBLE);
 	double d = mem->u.r;
-	if (d < 0 && d >= (double)INT64_MIN && (double)(int64_t)d == d) {
+	if (d <= -1.0 && d >= (double)INT64_MIN && (double)(int64_t)d == d) {
 		mem->u.i = (int64_t)d;
 		mem->type = MEM_TYPE_INT;
 		assert(mem->flags == 0);
 		mem->field_type = FIELD_TYPE_INTEGER;
 		return 0;
 	}
-	if (d >= 0 && d < (double)UINT64_MAX && (double)(uint64_t)d == d) {
+	if (d > -1.0 && d < (double)UINT64_MAX && (double)(uint64_t)d == d) {
 		mem->u.u = (uint64_t)d;
 		mem->type = MEM_TYPE_UINT;
 		assert(mem->flags == 0);
@@ -831,7 +831,7 @@ double_to_uint(struct Mem *mem)
 {
 	assert(mem->type == MEM_TYPE_DOUBLE);
 	double d = mem->u.r;
-	if (d >= 0 && d < (double)UINT64_MAX) {
+	if (d > -1.0 && d < (double)UINT64_MAX) {
 		mem->u.u = (uint64_t)d;
 		mem->type = MEM_TYPE_UINT;
 		assert(mem->flags == 0);
@@ -846,7 +846,7 @@ double_to_uint_precise(struct Mem *mem)
 {
 	assert(mem->type == MEM_TYPE_DOUBLE);
 	double d = mem->u.r;
-	if (d >= 0 && d < (double)UINT64_MAX && (double)(uint64_t)d == d) {
+	if (d > -1.0 && d < (double)UINT64_MAX && (double)(uint64_t)d == d) {
 		mem->u.u = (uint64_t)d;
 		mem->type = MEM_TYPE_UINT;
 		assert(mem->flags == 0);
@@ -1289,12 +1289,12 @@ mem_get_int(const struct Mem *mem, int64_t *i, bool *is_neg)
 		return sql_atoi64(mem->z, i, is_neg, mem->n);
 	if (mem->type == MEM_TYPE_DOUBLE) {
 		double d = mem->u.r;
-		if (d < 0 && d >= (double)INT64_MIN) {
+		if (d <= -1.0 && d >= (double)INT64_MIN) {
 			*i = (int64_t)d;
 			*is_neg = true;
 			return 0;
 		}
-		if (d >= 0 && d < (double)UINT64_MAX) {
+		if (d > -1.0 && d < (double)UINT64_MAX) {
 			*i = (int64_t)(uint64_t)d;
 			*is_neg = false;
 			return 0;
@@ -1322,7 +1322,7 @@ mem_get_uint(const struct Mem *mem, uint64_t *u)
 	}
 	if (mem->type == MEM_TYPE_DOUBLE) {
 		double d = mem->u.r;
-		if (d >= 0 && d < (double)UINT64_MAX) {
+		if (d > -1.0 && d < (double)UINT64_MAX) {
 			*u = (uint64_t)d;
 			return 0;
 		}
diff --git a/test/sql-tap/numcast.test.lua b/test/sql-tap/numcast.test.lua
index 20aea3c4b..56b11da25 100755
--- a/test/sql-tap/numcast.test.lua
+++ b/test/sql-tap/numcast.test.lua
@@ -1,6 +1,6 @@
 #!/usr/bin/env tarantool
 local test = require("sqltester")
-test:plan(32)
+test:plan(33)
 
 --!./tcltestrunner.lua
 -- 2013 March 20
@@ -261,4 +261,16 @@ test:do_catchsql_test(
         1, "Type mismatch: can not convert double(-2.5) to unsigned"
 })
 
+--
+-- gh-6225: Make sure negative DOUBLE is greater than -1.0 and less than 0.0 can
+-- be converted to INTEGER and UNSIGNED.
+--
+test:do_execsql_test(
+    "numcast-5",
+    [[
+        SELECT CAST(-0.999 AS INTEGER), CAST(-0.111 AS UNSIGNED);
+    ]], {
+        0, 0
+})
+
 test:finish_test()
-- 
2.25.1



More information about the Tarantool-patches mailing list