From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from [87.239.111.99] (localhost [127.0.0.1]) by dev.tarantool.org (Postfix) with ESMTP id 15F8F6EC40; Mon, 16 Aug 2021 18:59:03 +0300 (MSK) DKIM-Filter: OpenDKIM Filter v2.11.0 dev.tarantool.org 15F8F6EC40 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=tarantool.org; s=dev; t=1629129543; bh=Ffy30fOnhkd2iERtrbJlfc2f4zXe24/3SF2wYSfCYtM=; h=To:Cc:Date:In-Reply-To:References:Subject:List-Id: List-Unsubscribe:List-Archive:List-Post:List-Help:List-Subscribe: From:Reply-To:From; b=Gcdvsfvs2kPr0A1fkcn8ax0Jj10sqS4FSGcl21Vpez9TWytvQUFkVoHMm/JgoOv8V LhReeC8uVTs0VIZJ0tRDL4k2Xp1tO4R//qmXToe5KbhdsaiZ/Dyr84LvPv0HwdBFcu J/t/UlDFB4SrNQ6AUSxpZC7c/C8sj6GVs5Mq5wbs= Received: from smtpng1.i.mail.ru (smtpng1.i.mail.ru [94.100.181.251]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by dev.tarantool.org (Postfix) with ESMTPS id A880A6EC40 for ; Mon, 16 Aug 2021 18:57:06 +0300 (MSK) DKIM-Filter: OpenDKIM Filter v2.11.0 dev.tarantool.org A880A6EC40 Received: by smtpng1.m.smailru.net with esmtpa (envelope-from ) id 1mFez3-0006fa-O4; Mon, 16 Aug 2021 18:57:06 +0300 To: korablev@tarantool.org Cc: tarantool-patches@dev.tarantool.org Date: Mon, 16 Aug 2021 18:57:05 +0300 Message-Id: X-Mailer: git-send-email 2.25.1 In-Reply-To: References: MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-7564579A: 646B95376F6C166E X-77F55803: 4F1203BC0FB41BD92087353F0EC44DD9736CF3E71F18CE0C3E1D5927724F4AAA182A05F538085040FBE4D370A83EB16FE91DBA5BD78253449EB58A249C215F703480CF37668CD0AB X-7FA49CB5: FF5795518A3D127A4AD6D5ED66289B5278DA827A17800CE7A72B1EA4C8D5AD81EA1F7E6F0F101C67BD4B6F7A4D31EC0BCC500DACC3FED6E28638F802B75D45FF8AA50765F7900637CE3A619BB4CB99268638F802B75D45FF36EB9D2243A4F8B5A6FCA7DBDB1FC311F39EFFDF887939037866D6147AF826D8A83AE4B2E444A75723C832DE0878A044117882F4460429724CE54428C33FAD305F5C1EE8F4F765FCAE9A1BBD95851C5BA471835C12D1D9774AD6D5ED66289B52BA9C0B312567BB23117882F44604297287769387670735201E561CDFBCA1751F28451B159A507268D2E47CDBA5A96583BA9C0B312567BB231DD303D21008E29813377AFFFEAFD269A417C69337E82CC2E827F84554CEF50127C277FBC8AE2E8BA83251EDC214901ED5E8D9A59859A8B6300D3B61E77C8D3B089D37D7C0E48F6C5571747095F342E88FB05168BE4CE3AF X-C1DE0DAB: C20DE7B7AB408E4181F030C43753B8186998911F362727C414F749A5E30D975C5B73F950BC6E7FFBD222EB3C7D59507B8787009F3B3D9E789C2B6934AE262D3EE7EAB7254005DCED7532B743992DF240BDC6A1CF3F042BAD6DF99611D93F60EFE37876E7723AB534DC48ACC2A39D04F89CDFB48F4795C241BDAD6C7F3747799A X-C8649E89: 4E36BF7865823D7055A7F0CF078B5EC49A30900B95165D348B532EA2091F4FF6B008DE7D67D04F37EBF02DF73283A1CD72E3611C4710313AC56662C8F3766D2C1D7E09C32AA3244CE1BDCAB2228D4134179A7107AD04C0807101BF96129E4011729B2BEF169E0186 X-D57D3AED: 3ZO7eAau8CL7WIMRKs4sN3D3tLDjz0dLbV79QFUyzQ2Ujvy7cMT6pYYqY16iZVKkSc3dCLJ7zSJH7+u4VD18S7Vl4ZUrpaVfd2+vE6kuoey4m4VkSEu530nj6fImhcD4MUrOEAnl0W826KZ9Q+tr5ycPtXkTV4k65bRjmOUUP8cvGozZ33TWg5HZplvhhXbhDGzqmQDTd6OAevLeAnq3Ra9uf7zvY2zzsIhlcp/Y7m53TZgf2aB4JOg4gkr2biojIrFL/N5KnVEfqBtKR3S33Q== X-Mailru-Sender: 689FA8AB762F7393C37E3C1AEC41BA5DFA341DC582B43C5E8394DE446D30E30483D72C36FC87018B9F80AB2734326CD2FB559BB5D741EB96352A0ABBE4FDA4210A04DAD6CC59E33667EA787935ED9F1B X-Mras: Ok Subject: [Tarantool-patches] [PATCH v1 4/4] sql: introduce decimal to arithmetic X-BeenThere: tarantool-patches@dev.tarantool.org X-Mailman-Version: 2.1.34 Precedence: list List-Id: Tarantool development patches List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , From: Mergen Imeev via Tarantool-patches Reply-To: imeevma@tarantool.org Errors-To: tarantool-patches-bounces@dev.tarantool.org Sender: "Tarantool-patches" 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