From: Mergen Imeev via Tarantool-patches <tarantool-patches@dev.tarantool.org> To: korablev@tarantool.org Cc: tarantool-patches@dev.tarantool.org Subject: [Tarantool-patches] [PATCH v1 4/4] sql: introduce decimal to arithmetic Date: Mon, 16 Aug 2021 18:57:05 +0300 [thread overview] Message-ID: <f77ee52e5fdd6a41f7c36e0b03c86defb0c45536.1629129129.git.imeevma@gmail.com> (raw) In-Reply-To: <cover.1629129129.git.imeevma@gmail.com> 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
next prev parent reply other threads:[~2021-08-16 15:59 UTC|newest] Thread overview: 16+ messages / expand[flat|nested] mbox.gz Atom feed top 2021-08-16 15:56 [Tarantool-patches] [PATCH v1 0/4] Introduce DECIMAL to SQL Mergen Imeev via Tarantool-patches 2021-08-16 15:57 ` [Tarantool-patches] [PATCH v1 1/4] decimal: introduce decimal_is_neg() Mergen Imeev via Tarantool-patches 2021-08-18 16:54 ` Safin Timur via Tarantool-patches 2021-08-16 15:57 ` [Tarantool-patches] [PATCH v1 2/4] sql: introduce field type decimal Mergen Imeev via Tarantool-patches 2021-08-16 19:22 ` Safin Timur via Tarantool-patches 2021-08-18 13:01 ` Mergen Imeev via Tarantool-patches 2021-08-18 16:52 ` Safin Timur via Tarantool-patches 2021-08-16 15:57 ` [Tarantool-patches] [PATCH v1 3/4] sql: introduce cast for decimal Mergen Imeev via Tarantool-patches 2021-08-16 19:34 ` Safin Timur via Tarantool-patches 2021-08-18 13:29 ` Mergen Imeev via Tarantool-patches 2021-08-18 16:53 ` Safin Timur via Tarantool-patches 2021-08-16 15:57 ` Mergen Imeev via Tarantool-patches [this message] 2021-08-16 19:48 ` [Tarantool-patches] [PATCH v1 4/4] sql: introduce decimal to arithmetic Safin Timur via Tarantool-patches 2021-08-17 12:23 ` Serge Petrenko via Tarantool-patches 2021-08-18 13:32 ` Mergen Imeev via Tarantool-patches 2021-08-18 16:53 ` Safin Timur via Tarantool-patches
Reply instructions: You may reply publicly to this message via plain-text email using any one of the following methods: * Save the following mbox file, import it into your mail client, and reply-to-all from there: mbox Avoid top-posting and favor interleaved quoting: https://en.wikipedia.org/wiki/Posting_style#Interleaved_style * Reply using the --to, --cc, and --in-reply-to switches of git-send-email(1): git send-email \ --in-reply-to=f77ee52e5fdd6a41f7c36e0b03c86defb0c45536.1629129129.git.imeevma@gmail.com \ --to=tarantool-patches@dev.tarantool.org \ --cc=imeevma@tarantool.org \ --cc=korablev@tarantool.org \ --subject='Re: [Tarantool-patches] [PATCH v1 4/4] sql: introduce decimal to arithmetic' \ /path/to/YOUR_REPLY https://kernel.org/pub/software/scm/git/docs/git-send-email.html * If your mail client supports setting the In-Reply-To header via mailto: links, try the mailto: link
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox