[Tarantool-patches] [PATCH v1 4/4] sql: introduce decimal to arithmetic
imeevma at tarantool.org
imeevma at tarantool.org
Mon Aug 16 18:57:05 MSK 2021
This patch introduces arithmetic for DECIMAL in SQL. After this patch,
DECIMAL values can participate in arithmetic along with INTEGER,
UNSIGNED, DOUBLE, and other DECIMAL values.
Part of #4415
---
src/box/sql/mem.c | 124 +++++++++++++++++++++++++++++-
test/sql-tap/decimal.test.lua | 141 +++++++++++++++++++++++++++++++++-
2 files changed, 262 insertions(+), 3 deletions(-)
diff --git a/src/box/sql/mem.c b/src/box/sql/mem.c
index ff8b40d7f..a4ec98f34 100644
--- a/src/box/sql/mem.c
+++ b/src/box/sql/mem.c
@@ -1733,6 +1733,18 @@ mem_get_int(const struct Mem *mem, int64_t *i, bool *is_neg)
}
return -1;
}
+ if (mem->type == MEM_TYPE_DEC) {
+ if (decimal_is_neg(&mem->u.d)) {
+ if (decimal_to_int64(&mem->u.d, i) == NULL)
+ return -1;
+ *is_neg = *i < 0;
+ return 0;
+ }
+ if (decimal_to_uint64(&mem->u.d, (uint64_t *)i) == NULL)
+ return -1;
+ *is_neg = false;
+ return 0;
+ }
return -1;
}
@@ -1760,6 +1772,19 @@ mem_get_uint(const struct Mem *mem, uint64_t *u)
}
return -1;
}
+ if (mem->type == MEM_TYPE_DEC) {
+ if (decimal_is_neg(&mem->u.d)) {
+ int64_t i;
+ if (decimal_to_int64(&mem->u.d, &i) == NULL || i < 0)
+ return -1;
+ assert(i == 0);
+ *u = 0;
+ return 0;
+ }
+ if (decimal_to_uint64(&mem->u.d, u) == NULL)
+ return -1;
+ return 0;
+ }
return -1;
}
@@ -1778,6 +1803,10 @@ mem_get_double(const struct Mem *mem, double *d)
*d = (double)mem->u.u;
return 0;
}
+ if (mem->type == MEM_TYPE_DEC) {
+ *d = atof(decimal_str(&mem->u.d));
+ return 0;
+ }
if (mem->type == MEM_TYPE_STR) {
if (sqlAtoF(mem->z, d, mem->n) == 0)
return -1;
@@ -1786,6 +1815,34 @@ mem_get_double(const struct Mem *mem, double *d)
return -1;
}
+int
+mem_get_dec(const struct Mem *mem, decimal_t *d)
+{
+ if (mem->type == MEM_TYPE_DOUBLE) {
+ if (decimal_from_double(d, mem->u.r) == NULL)
+ return -1;
+ return 0;
+ }
+ if (mem->type == MEM_TYPE_INT) {
+ decimal_from_int64(d, mem->u.r);
+ return 0;
+ }
+ if (mem->type == MEM_TYPE_UINT) {
+ decimal_from_int64(d, mem->u.u);
+ return 0;
+ }
+ if (mem->type == MEM_TYPE_DEC) {
+ *d = mem->u.d;
+ return 0;
+ }
+ if (mem->type == MEM_TYPE_STR) {
+ if (decimal_from_string(d, tt_cstr(mem->z, mem->n)) == NULL)
+ return -1;
+ return 0;
+ }
+ return -1;
+}
+
int
mem_get_bool(const struct Mem *mem, bool *b)
{
@@ -1946,12 +2003,12 @@ mem_concat(struct Mem *a, struct Mem *b, struct Mem *result)
static inline int
check_types_numeric_arithmetic(const struct Mem *a, const struct Mem *b)
{
- if (!mem_is_num(a) || mem_is_metatype(a) || a->type == MEM_TYPE_DEC) {
+ if (!mem_is_num(a) || mem_is_metatype(a)) {
diag_set(ClientError, ER_SQL_TYPE_MISMATCH, mem_str(a),
"integer, unsigned or double");
return -1;
}
- if (!mem_is_num(b) || mem_is_metatype(b) || b->type == MEM_TYPE_DEC) {
+ if (!mem_is_num(b) || mem_is_metatype(b)) {
diag_set(ClientError, ER_SQL_TYPE_MISMATCH, mem_str(b),
"integer, unsigned or double");
return -1;
@@ -1976,6 +2033,20 @@ mem_add(const struct Mem *left, const struct Mem *right, struct Mem *result)
mem_set_double(result, a + b);
return 0;
}
+ if (((left->type | right->type) & MEM_TYPE_DEC) != 0) {
+ decimal_t a;
+ decimal_t b;
+ decimal_t res;
+ mem_get_dec(left, &a);
+ mem_get_dec(right, &b);
+ if (decimal_add(&res, &a, &b) == NULL) {
+ diag_set(ClientError, ER_SQL_EXECUTE,
+ "decimal is overflowed");
+ return -1;
+ }
+ mem_set_dec(result, &res);
+ return 0;
+ }
int64_t res;
bool is_neg;
if (sql_add_int(left->u.i, left->type == MEM_TYPE_INT, right->u.i,
@@ -2004,6 +2075,20 @@ mem_sub(const struct Mem *left, const struct Mem *right, struct Mem *result)
mem_set_double(result, a - b);
return 0;
}
+ if (((left->type | right->type) & MEM_TYPE_DEC) != 0) {
+ decimal_t a;
+ decimal_t b;
+ decimal_t res;
+ mem_get_dec(left, &a);
+ mem_get_dec(right, &b);
+ if (decimal_sub(&res, &a, &b) == NULL) {
+ diag_set(ClientError, ER_SQL_EXECUTE,
+ "decimal is overflowed");
+ return -1;
+ }
+ mem_set_dec(result, &res);
+ return 0;
+ }
int64_t res;
bool is_neg;
if (sql_sub_int(left->u.i, left->type == MEM_TYPE_INT, right->u.i,
@@ -2032,6 +2117,20 @@ mem_mul(const struct Mem *left, const struct Mem *right, struct Mem *result)
mem_set_double(result, a * b);
return 0;
}
+ if (((left->type | right->type) & MEM_TYPE_DEC) != 0) {
+ decimal_t a;
+ decimal_t b;
+ decimal_t res;
+ mem_get_dec(left, &a);
+ mem_get_dec(right, &b);
+ if (decimal_mul(&res, &a, &b) == NULL) {
+ diag_set(ClientError, ER_SQL_EXECUTE,
+ "decimal is overflowed");
+ return -1;
+ }
+ mem_set_dec(result, &res);
+ return 0;
+ }
int64_t res;
bool is_neg;
if (sql_mul_int(left->u.i, left->type == MEM_TYPE_INT, right->u.i,
@@ -2065,6 +2164,27 @@ mem_div(const struct Mem *left, const struct Mem *right, struct Mem *result)
mem_set_double(result, a / b);
return 0;
}
+ if (((left->type | right->type) & MEM_TYPE_DEC) != 0) {
+ decimal_t a;
+ decimal_t b;
+ decimal_t zero;
+ decimal_t res;
+ mem_get_dec(left, &a);
+ mem_get_dec(right, &b);
+ decimal_zero(&zero);
+ if (decimal_compare(&b, &zero) == 0) {
+ diag_set(ClientError, ER_SQL_EXECUTE,
+ "division by zero");
+ return -1;
+ }
+ if (decimal_div(&res, &a, &b) == NULL) {
+ diag_set(ClientError, ER_SQL_EXECUTE,
+ "decimal is overflowed");
+ return -1;
+ }
+ mem_set_dec(result, &res);
+ return 0;
+ }
if (right->u.u == 0) {
diag_set(ClientError, ER_SQL_EXECUTE, "division by zero");
return -1;
diff --git a/test/sql-tap/decimal.test.lua b/test/sql-tap/decimal.test.lua
index 3f1c285cf..3c6215728 100755
--- a/test/sql-tap/decimal.test.lua
+++ b/test/sql-tap/decimal.test.lua
@@ -3,7 +3,7 @@ local build_path = os.getenv("BUILDDIR")
package.cpath = build_path..'/test/sql-tap/?.so;'..build_path..'/test/sql-tap/?.dylib;'..package.cpath
local test = require("sqltester")
-test:plan(84)
+test:plan(101)
local dec = require("decimal")
local dec1 = dec.new("111")
@@ -789,6 +789,145 @@ test:do_execsql_test(
1000
})
+-- Check that arithmetic operations work with UUIDs as intended.
+test:do_execsql_test(
+ "dec-14.1.1",
+ [[
+ SELECT -u FROM t2;
+ ]], {
+ dec.new(-111), dec.new(-3333), dec.new(-55555)
+ })
+
+test:do_execsql_test(
+ "dec-14.1.2",
+ [[
+ SELECT u + 0 FROM t2;
+ ]], {
+ dec1, dec3, dec2
+ })
+
+test:do_execsql_test(
+ "dec-14.1.3",
+ [[
+ SELECT u - 0.5 FROM t2;
+ ]], {
+ 110.5, 3332.5, 55554.5
+ })
+
+test:do_execsql_test(
+ "dec-14.1.4",
+ [[
+ SELECT u * 1 FROM t2;
+ ]], {
+ dec1, dec3, dec2
+ })
+
+test:do_execsql_test(
+ "dec-14.1.5",
+ [[
+ SELECT u / 1.0 FROM t2;
+ ]], {
+ 111, 3333, 55555
+ })
+
+test:do_catchsql_test(
+ "dec-14.1.6",
+ [[
+ SELECT u % 1 FROM t2;
+ ]], {
+ 1, "Type mismatch: can not convert decimal(111) to integer"
+ })
+
+-- Check that bitwise operations work with UUIDs as intended.
+test:do_catchsql_test(
+ "dec-14.2.1",
+ [[
+ SELECT ~u FROM t2;
+ ]], {
+ 1, "Type mismatch: can not convert decimal(111) to unsigned"
+ })
+
+test:do_catchsql_test(
+ "dec-14.2.2",
+ [[
+ SELECT u >> 1 FROM t2;
+ ]], {
+ 1, "Type mismatch: can not convert decimal(111) to unsigned"
+ })
+
+test:do_catchsql_test(
+ "dec-14.2.3",
+ [[
+ SELECT u << 1 FROM t2;
+ ]], {
+ 1, "Type mismatch: can not convert decimal(111) to unsigned"
+ })
+
+test:do_catchsql_test(
+ "dec-14.2.4",
+ [[
+ SELECT u | 1 FROM t2;
+ ]], {
+ 1, "Type mismatch: can not convert decimal(111) to unsigned"
+ })
+
+test:do_catchsql_test(
+ "dec-14.2.5",
+ [[
+ SELECT u & 1 FROM t2;
+ ]], {
+ 1, "Type mismatch: can not convert decimal(111) to unsigned"
+ })
+
+-- Check that logical operations work with UUIDs as intended.
+test:do_catchsql_test(
+ "dec-14.3.1",
+ [[
+ SELECT NOT u FROM t2;
+ ]], {
+ 1, "Type mismatch: can not convert decimal(111) to boolean"
+ })
+
+test:do_catchsql_test(
+ "dec-14.3.2",
+ [[
+ SELECT u AND true FROM t2;
+ ]], {
+ 1, "Type mismatch: can not convert decimal(111) to boolean"
+ })
+
+test:do_catchsql_test(
+ "dec-14.3.3",
+ [[
+ SELECT u OR true FROM t2;
+ ]], {
+ 1, "Type mismatch: can not convert decimal(111) to boolean"
+ })
+
+test:do_catchsql_test(
+ "dec-14.3.4",
+ [[
+ SELECT true AND u FROM t2;
+ ]], {
+ 1, "Type mismatch: can not convert decimal(111) to boolean"
+ })
+
+test:do_catchsql_test(
+ "dec-14.3.5",
+ [[
+ SELECT true OR u FROM t2;
+ ]], {
+ 1, "Type mismatch: can not convert decimal(111) to boolean"
+ })
+
+test:do_catchsql_test(
+ "dec-15",
+ [[
+ SELECT u || u from t2;
+ ]], {
+ 1, "Inconsistent types: expected string or varbinary got decimal(111)"
+ })
+
test:execsql([[
DROP TRIGGER t;
DROP VIEW v;
--
2.25.1
More information about the Tarantool-patches
mailing list