Tarantool development patches archive
 help / color / mirror / Atom feed
* [Tarantool-patches] [PATCH v1 0/4] Introduce DECIMAL to SQL
@ 2021-08-16 15:56 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
                   ` (3 more replies)
  0 siblings, 4 replies; 16+ messages in thread
From: Mergen Imeev via Tarantool-patches @ 2021-08-16 15:56 UTC (permalink / raw)
  To: korablev; +Cc: tarantool-patches

After this patch-set decimal will be available in SQL. It will have defined
comparison operations, arithmetic operations, implicit and explicit casts.
However, some functions cannot work with DECIMAL for now, and it will be fixed a
bit later in patch-set that fixes SQL built-in functions. Also, for now DECIMAL
will not have literals in SQL, since it still was not decided how to implement
them.

https://github.com/tarantool/tarantool/issues/4415
https://github.com/tarantool/tarantool/tree/imeevma/gh-4415-introduce-decimal-type-in-sql

Mergen Imeev (4):
  decimal: introduce decimal_is_neg()
  sql: introduce field type decimal
  sql: introduce cast for decimal
  sql: introduce decimal to arithmetic

 extra/mkkeywordhash.c                         |   2 +-
 src/box/sql/expr.c                            |   3 +
 src/box/sql/func.c                            |   4 +
 src/box/sql/mem.c                             | 615 +++++++++++-
 src/box/sql/mem.h                             |  18 +-
 src/box/sql/parse.y                           |   1 +
 src/box/sql/sqlInt.h                          |   1 +
 src/lib/core/decimal.c                        |   6 +
 src/lib/core/decimal.h                        |   4 +
 test/sql-tap/CMakeLists.txt                   |   1 +
 test/sql-tap/decimal.c                        |  48 +
 test/sql-tap/decimal.test.lua                 | 944 ++++++++++++++++++
 test/sql-tap/engine.cfg                       |   3 +
 .../gh-5913-segfault-on-select-uuid.test.lua  |  83 --
 .../sql-tap/gh-6024-funcs-return-bin.test.lua |   8 +-
 test/unit/decimal.c                           |   7 +-
 test/unit/decimal.result                      |  10 +-
 17 files changed, 1633 insertions(+), 125 deletions(-)
 create mode 100644 test/sql-tap/decimal.c
 create mode 100755 test/sql-tap/decimal.test.lua
 delete mode 100755 test/sql-tap/gh-5913-segfault-on-select-uuid.test.lua

-- 
2.25.1


^ permalink raw reply	[flat|nested] 16+ messages in thread

* [Tarantool-patches] [PATCH v1 1/4] decimal: introduce decimal_is_neg()
  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 ` 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
                   ` (2 subsequent siblings)
  3 siblings, 1 reply; 16+ messages in thread
From: Mergen Imeev via Tarantool-patches @ 2021-08-16 15:57 UTC (permalink / raw)
  To: korablev; +Cc: tarantool-patches

This patch introduces function decimal_is_neg() which checks that
decimal is less than zero.

Needed for #4415
---
 src/lib/core/decimal.c   |  6 ++++++
 src/lib/core/decimal.h   |  4 ++++
 test/unit/decimal.c      |  7 ++++++-
 test/unit/decimal.result | 10 +++++++++-
 4 files changed, 25 insertions(+), 2 deletions(-)

diff --git a/src/lib/core/decimal.c b/src/lib/core/decimal.c
index 6d2ccb96f..7543ef44d 100644
--- a/src/lib/core/decimal.c
+++ b/src/lib/core/decimal.c
@@ -115,6 +115,12 @@ decimal_is_int(decimal_t *dec)
 	return decNumberIsInt(dec);
 }
 
+bool
+decimal_is_neg(const decimal_t *dec)
+{
+	return decNumberIsNegative(dec) && !decNumberIsZero(dec);
+}
+
 decimal_t *
 decimal_from_string(decimal_t *dec, const char *str)
 {
diff --git a/src/lib/core/decimal.h b/src/lib/core/decimal.h
index aeafd2c68..15a576648 100644
--- a/src/lib/core/decimal.h
+++ b/src/lib/core/decimal.h
@@ -81,6 +81,10 @@ decimal_zero(decimal_t *dec);
 bool
 decimal_is_int(decimal_t *dec);
 
+/** @return true if the decimal is negative, false otherwise. */
+bool
+decimal_is_neg(const decimal_t *dec);
+
 /**
  * Initialize a decimal with a value from the string.
  *
diff --git a/test/unit/decimal.c b/test/unit/decimal.c
index aea646e15..328b1b668 100644
--- a/test/unit/decimal.c
+++ b/test/unit/decimal.c
@@ -336,7 +336,7 @@ test_mp_print(void)
 int
 main(void)
 {
-	plan(304);
+	plan(312);
 
 	dectest(314, 271, uint64, uint64_t);
 	dectest(65535, 23456, uint64, uint64_t);
@@ -416,5 +416,10 @@ main(void)
 	dectest_is(is_int, 1.0000, true);
 	dectest_is(is_int, 1.0000001, false);
 
+	dectest_is(is_neg, 1, false);
+	dectest_is(is_neg, -1, true);
+	dectest_is(is_neg, 0, false);
+	dectest_is(is_neg, -0, false);
+
 	return check_plan();
 }
diff --git a/test/unit/decimal.result b/test/unit/decimal.result
index b7da2d2ce..ceaaf718a 100644
--- a/test/unit/decimal.result
+++ b/test/unit/decimal.result
@@ -1,4 +1,4 @@
-1..304
+1..312
 ok 1 - decimal(314)
 ok 2 - decimal(271)
 ok 3 - decimal(314) + decimal(271)
@@ -747,3 +747,11 @@ ok 301 - decimal_from_string(1.0000)
 ok 302 - decimal_is_int(1.0000) - expected true
 ok 303 - decimal_from_string(1.0000001)
 ok 304 - decimal_is_int(1.0000001) - expected false
+ok 305 - decimal_from_string(1)
+ok 306 - decimal_is_neg(1) - expected false
+ok 307 - decimal_from_string(-1)
+ok 308 - decimal_is_neg(-1) - expected true
+ok 309 - decimal_from_string(0)
+ok 310 - decimal_is_neg(0) - expected false
+ok 311 - decimal_from_string(-0)
+ok 312 - decimal_is_neg(-0) - expected false
-- 
2.25.1


^ permalink raw reply	[flat|nested] 16+ messages in thread

* [Tarantool-patches] [PATCH v1 2/4] sql: introduce field type decimal
  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-16 15:57 ` Mergen Imeev via Tarantool-patches
  2021-08-16 19:22   ` 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 15:57 ` [Tarantool-patches] [PATCH v1 4/4] sql: introduce decimal to arithmetic Mergen Imeev via Tarantool-patches
  3 siblings, 1 reply; 16+ messages in thread
From: Mergen Imeev via Tarantool-patches @ 2021-08-16 15:57 UTC (permalink / raw)
  To: korablev; +Cc: tarantool-patches

This patch introduces a decimal field type. However, implicit and
explicit casts and arithmetic operations for this type will be presented
in next few patches. Literals also will be introduced later.

Part of #4415
---
 extra/mkkeywordhash.c                         |   2 +-
 src/box/sql/expr.c                            |   3 +
 src/box/sql/func.c                            |   4 +
 src/box/sql/mem.c                             | 173 +++++--
 src/box/sql/mem.h                             |  18 +-
 src/box/sql/parse.y                           |   1 +
 src/box/sql/sqlInt.h                          |   1 +
 test/sql-tap/CMakeLists.txt                   |   1 +
 test/sql-tap/decimal.c                        |  48 ++
 test/sql-tap/decimal.test.lua                 | 441 ++++++++++++++++++
 test/sql-tap/engine.cfg                       |   3 +
 .../gh-5913-segfault-on-select-uuid.test.lua  |  83 ----
 .../sql-tap/gh-6024-funcs-return-bin.test.lua |   8 +-
 13 files changed, 661 insertions(+), 125 deletions(-)
 create mode 100644 test/sql-tap/decimal.c
 create mode 100755 test/sql-tap/decimal.test.lua
 delete mode 100755 test/sql-tap/gh-5913-segfault-on-select-uuid.test.lua

diff --git a/extra/mkkeywordhash.c b/extra/mkkeywordhash.c
index 0d998506c..1c9d12295 100644
--- a/extra/mkkeywordhash.c
+++ b/extra/mkkeywordhash.c
@@ -196,7 +196,7 @@ static Keyword aKeywordTable[] = {
   { "CURRENT_TIMESTAMP",      "TK_STANDARD",    true  },
   { "DATE",                   "TK_STANDARD",    true  },
   { "DATETIME",               "TK_STANDARD",    true  },
-  { "DECIMAL",                "TK_STANDARD",    true  },
+  { "DECIMAL",                "TK_DECIMAL",     true  },
   { "DECLARE",                "TK_STANDARD",    true  },
   { "DENSE_RANK",             "TK_STANDARD",    true  },
   { "DESCRIBE",               "TK_STANDARD",    true  },
diff --git a/src/box/sql/expr.c b/src/box/sql/expr.c
index c67a7091c..275dbc5ba 100644
--- a/src/box/sql/expr.c
+++ b/src/box/sql/expr.c
@@ -387,6 +387,8 @@ sql_type_result(enum field_type lhs, enum field_type rhs)
 			return FIELD_TYPE_NUMBER;
 		if (lhs == FIELD_TYPE_DOUBLE || rhs == FIELD_TYPE_DOUBLE)
 			return FIELD_TYPE_DOUBLE;
+		if (lhs == FIELD_TYPE_DECIMAL || rhs == FIELD_TYPE_DECIMAL)
+			return FIELD_TYPE_DECIMAL;
 		if (lhs == FIELD_TYPE_INTEGER || rhs == FIELD_TYPE_INTEGER)
 			return FIELD_TYPE_INTEGER;
 		assert(lhs == FIELD_TYPE_UNSIGNED ||
@@ -2229,6 +2231,7 @@ sqlExprCanBeNull(const Expr * p)
 		op = p->op2;
 	switch (op) {
 	case TK_INTEGER:
+	case TK_DECIMAL:
 	case TK_STRING:
 	case TK_FLOAT:
 	case TK_BLOB:
diff --git a/src/box/sql/func.c b/src/box/sql/func.c
index 1551d3ef2..e9572c56c 100644
--- a/src/box/sql/func.c
+++ b/src/box/sql/func.c
@@ -171,6 +171,9 @@ typeofFunc(sql_context * context, int NotUsed, sql_value ** argv)
 	case MEM_TYPE_UINT:
 		z = "integer";
 		break;
+	case MEM_TYPE_DEC:
+		z = "decimal";
+		break;
 	case MEM_TYPE_STR:
 		z = "string";
 		break;
@@ -1111,6 +1114,7 @@ quoteFunc(sql_context * context, int argc, sql_value ** argv)
 		sql_result_text(context, buf, UUID_STR_LEN, SQL_TRANSIENT);
 		break;
 	}
+	case MEM_TYPE_DEC:
 	case MEM_TYPE_UINT:
 	case MEM_TYPE_INT: {
 			sql_result_value(context, argv[0]);
diff --git a/src/box/sql/mem.c b/src/box/sql/mem.c
index 066940fac..016f0e80b 100644
--- a/src/box/sql/mem.c
+++ b/src/box/sql/mem.c
@@ -42,6 +42,7 @@
 #include "lua/utils.h"
 #include "lua/serializer.h"
 #include "lua/msgpack.h"
+#include "lua/decimal.h"
 #include "uuid/mp_uuid.h"
 #include "mp_decimal.h"
 
@@ -86,6 +87,7 @@ mem_type_class(enum mem_type type)
 		return MEM_CLASS_NULL;
 	case MEM_TYPE_UINT:
 	case MEM_TYPE_INT:
+	case MEM_TYPE_DEC:
 	case MEM_TYPE_DOUBLE:
 		return MEM_CLASS_NUMBER;
 	case MEM_TYPE_STR:
@@ -107,6 +109,8 @@ mem_is_field_compatible(const struct Mem *mem, enum field_type type)
 {
 	if (mem->type == MEM_TYPE_UUID)
 		return (field_ext_type[type] & (1U << MP_UUID)) != 0;
+	if (mem->type == MEM_TYPE_DEC)
+		return (field_ext_type[type] & (1U << MP_DECIMAL)) != 0;
 	enum mp_type mp_type = mem_mp_type(mem);
 	assert(mp_type != MP_EXT);
 	return field_mp_plain_type_is_compatible(type, mp_type, true);
@@ -132,6 +136,9 @@ mem_str(const struct Mem *mem)
 	case MEM_TYPE_DOUBLE:
 		sql_snprintf(STR_VALUE_MAX_LEN, buf, "%!.15g", mem->u.r);
 		return tt_sprintf("%s(%s)", type, buf);
+	case MEM_TYPE_DEC:
+		decimal_to_string(&mem->u.d, buf);
+		return tt_sprintf("%s(%s)", type, buf);
 	case MEM_TYPE_BIN: {
 		int len = MIN(mem->n, STR_VALUE_MAX_LEN / 2);
 		for (int i = 0; i < len; ++i) {
@@ -172,6 +179,7 @@ mem_type_class_to_str(const struct Mem *mem)
 		return "NULL";
 	case MEM_TYPE_UINT:
 	case MEM_TYPE_INT:
+	case MEM_TYPE_DEC:
 	case MEM_TYPE_DOUBLE:
 		return "number";
 	case MEM_TYPE_STR:
@@ -284,6 +292,15 @@ mem_set_double(struct Mem *mem, double value)
 	mem->type = MEM_TYPE_DOUBLE;
 }
 
+void
+mem_set_dec(struct Mem *mem, decimal_t *d)
+{
+	mem_clear(mem);
+	mem->u.d = *d;
+	mem->type = MEM_TYPE_DEC;
+	assert(mem->flags == 0);
+}
+
 void
 mem_set_uuid(struct Mem *mem, const struct tt_uuid *uuid)
 {
@@ -1191,6 +1208,10 @@ mem_cast_explicit(struct Mem *mem, enum field_type type)
 		return -1;
 	case FIELD_TYPE_NUMBER:
 		return mem_to_number(mem);
+	case FIELD_TYPE_DECIMAL:
+		if (mem->type == MEM_TYPE_DEC)
+			return 0;
+		return -1;
 	case FIELD_TYPE_UUID:
 		if (mem->type == MEM_TYPE_UUID) {
 			mem->flags = 0;
@@ -1274,6 +1295,10 @@ mem_cast_implicit(struct Mem *mem, enum field_type type)
 			return -1;
 		mem->flags = MEM_Number;
 		return 0;
+	case FIELD_TYPE_DECIMAL:
+		if (mem->type == MEM_TYPE_DEC)
+			return 0;
+		return -1;
 	case FIELD_TYPE_MAP:
 		if (mem->type == MEM_TYPE_MAP)
 			return 0;
@@ -1595,12 +1620,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)) {
+	if (!mem_is_num(a) || mem_is_metatype(a) || a->type == MEM_TYPE_DEC) {
 		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)) {
+	if (!mem_is_num(b) || mem_is_metatype(b) || b->type == MEM_TYPE_DEC) {
 		diag_set(ClientError, ER_SQL_TYPE_MISMATCH, mem_str(b),
 			 "integer, unsigned or double");
 		return -1;
@@ -1916,19 +1941,84 @@ mem_cmp_num(const struct Mem *a, const struct Mem *b)
 			return -1;
 		return 0;
 	}
-	if (a->type == MEM_TYPE_DOUBLE) {
-		if (b->type == MEM_TYPE_INT)
-			return double_compare_nint64(a->u.r, b->u.i, 1);
-		return double_compare_uint64(a->u.r, b->u.u, 1);
-	}
-	if (b->type == MEM_TYPE_DOUBLE) {
-		if (a->type == MEM_TYPE_INT)
+	if ((a->type & b->type & MEM_TYPE_DEC) != 0)
+		return decimal_compare(&a->u.d, &b->u.d);
+	switch (a->type) {
+	case MEM_TYPE_INT:
+		switch (b->type) {
+		case MEM_TYPE_UINT:
+			return -1;
+		case MEM_TYPE_DOUBLE:
 			return double_compare_nint64(b->u.r, a->u.i, -1);
-		return double_compare_uint64(b->u.r, a->u.u, -1);
+		case MEM_TYPE_DEC: {
+			decimal_t dec;
+			decimal_from_int64(&dec, a->u.i);
+			return decimal_compare(&dec, &b->u.d);
+		}
+		default:
+			unreachable();
+		}
+	case MEM_TYPE_UINT:
+		switch (b->type) {
+		case MEM_TYPE_INT:
+			return 1;
+		case MEM_TYPE_DOUBLE:
+			return double_compare_uint64(b->u.r, a->u.u, -1);
+		case MEM_TYPE_DEC: {
+			decimal_t dec;
+			decimal_from_uint64(&dec, a->u.u);
+			return decimal_compare(&dec, &b->u.d);
+		}
+		default:
+			unreachable();
+		}
+	case MEM_TYPE_DOUBLE:
+		switch (b->type) {
+		case MEM_TYPE_INT:
+			return double_compare_nint64(a->u.r, b->u.i, 1);
+		case MEM_TYPE_UINT:
+			return double_compare_uint64(a->u.r, b->u.u, 1);
+		case MEM_TYPE_DEC: {
+			if (a->u.r >= 1e38)
+				return 1;
+			if (a->u.r <= -1e38)
+				return -1;
+			decimal_t dec;
+			decimal_t *d = decimal_from_double(&dec, a->u.r);
+			assert(d != NULL && d == &dec);
+			return decimal_compare(d, &b->u.d);
+		}
+		default:
+			unreachable();
+		}
+	case MEM_TYPE_DEC:
+		switch (b->type) {
+		case MEM_TYPE_INT: {
+			decimal_t dec;
+			decimal_from_int64(&dec, b->u.i);
+			return decimal_compare(&a->u.d, &dec);
+		}
+		case MEM_TYPE_UINT: {
+			decimal_t dec;
+			decimal_from_uint64(&dec, b->u.u);
+			return decimal_compare(&a->u.d, &dec);
+		}
+		case MEM_TYPE_DOUBLE: {
+			if (b->u.r >= 1e38)
+				return 1;
+			if (b->u.r <= -1e38)
+				return -1;
+			decimal_t dec;
+			decimal_t *d = decimal_from_double(&dec, b->u.r);
+			assert(d != NULL && d == &dec);
+			return decimal_compare(&a->u.d, d);
+		}
+		default:
+			unreachable();
+		}
+	default:
+		unreachable();
 	}
-	if (a->type == MEM_TYPE_INT)
-		return -1;
-	assert(a->type == MEM_TYPE_UINT && b->type == MEM_TYPE_INT);
 	return 1;
 }
 
@@ -2035,6 +2125,11 @@ mem_cmp_msgpack(const struct Mem *a, const char **b, int *result,
 			if (uuid_unpack(b, len, &mem.u.uuid) == NULL)
 				return -1;
 			break;
+		} else if (type == MP_DECIMAL) {
+			mem.type = MEM_TYPE_DEC;
+			if (decimal_unpack(b, len, &mem.u.d) == 0)
+				return -1;
+			break;
 		}
 		*b += len;
 		mem.type = MEM_TYPE_BIN;
@@ -2121,6 +2216,8 @@ mem_type_to_str(const struct Mem *p)
 		return "boolean";
 	case MEM_TYPE_UUID:
 		return "uuid";
+	case MEM_TYPE_DEC:
+		return "decimal";
 	default:
 		unreachable();
 	}
@@ -2149,6 +2246,7 @@ mem_mp_type(const struct Mem *mem)
 		return MP_BOOL;
 	case MEM_TYPE_DOUBLE:
 		return MP_DOUBLE;
+	case MEM_TYPE_DEC:
 	case MEM_TYPE_UUID:
 		return MP_EXT;
 	default:
@@ -2319,6 +2417,9 @@ memTracePrint(Mem *p)
 	case MEM_TYPE_UUID:
 		printf(" uuid:%s", tt_uuid_str(&p->u.uuid));
 		return;
+	case MEM_TYPE_DEC:
+		printf(" decimal:%s", decimal_str(&p->u.d));
+		return;
 	default: {
 		char zBuf[200];
 		sqlVdbeMemPrettyPrint(p, zBuf);
@@ -2583,6 +2684,13 @@ mem_from_mp_ephemeral(struct Mem *mem, const char *buf, uint32_t *len)
 			mem->type = MEM_TYPE_UUID;
 			mem->flags = 0;
 			break;
+		} else if (type == MP_DECIMAL) {
+			buf = svp;
+			if (mp_decode_decimal(&buf, &mem->u.d) == NULL)
+				return -1;
+			mem->type = MEM_TYPE_DEC;
+			mem->flags = 0;
+			break;
 		}
 		buf += size;
 		mem->z = (char *)svp;
@@ -2715,6 +2823,9 @@ mpstream_encode_vdbe_mem(struct mpstream *stream, struct Mem *var)
 	case MEM_TYPE_UUID:
 		mpstream_encode_uuid(stream, &var->u.uuid);
 		return;
+	case MEM_TYPE_DEC:
+		mpstream_encode_decimal(stream, &var->u.d);
+		return;
 	default:
 		unreachable();
 	}
@@ -2804,6 +2915,9 @@ port_vdbemem_dump_lua(struct port *base, struct lua_State *L, bool is_flat)
 		case MEM_TYPE_UUID:
 			*luaL_pushuuid(L) = mem->u.uuid;
 			break;
+		case MEM_TYPE_DEC:
+			*lua_pushdecimal(L) = mem->u.d;
+			break;
 		default:
 			unreachable();
 		}
@@ -2926,26 +3040,10 @@ port_lua_get_vdbemem(struct port *base, uint32_t *size)
 		case MP_EXT: {
 			assert(field.ext_type == MP_UUID ||
 			       field.ext_type == MP_DECIMAL);
-			char *buf;
-			uint32_t size;
-			uint32_t svp = region_used(&fiber()->gc);
-			if (field.ext_type == MP_UUID) {
+			if (field.ext_type == MP_UUID)
 				mem_set_uuid(&val[i], field.uuidval);
-				break;
-			} else {
-				size = mp_sizeof_decimal(field.decval);
-				buf = region_alloc(&fiber()->gc, size);
-				if (buf == NULL) {
-					diag_set(OutOfMemory, size,
-						 "region_alloc", "buf");
-					goto error;
-				}
-				mp_encode_decimal(buf, field.decval);
-			}
-			int rc = mem_copy_bin(&val[i], buf, size);
-			region_truncate(&fiber()->gc, svp);
-			if (rc != 0)
-				goto error;
+			else
+				mem_set_dec(&val[i], field.decval);
 			break;
 		}
 		case MP_NIL:
@@ -3049,6 +3147,17 @@ port_c_get_vdbemem(struct port *base, uint32_t *size)
 				}
 				val[i].type = MEM_TYPE_UUID;
 				break;
+			} else if (type == MP_DECIMAL) {
+				decimal_t *d = &val[i].u.d;
+				data = str;
+				if (mp_decode_decimal(&data, d) == NULL) {
+					diag_set(ClientError,
+						 ER_INVALID_MSGPACK, "Invalid "
+						 "MP_DECIMAL MsgPack format");
+					goto error;
+				}
+				val[i].type = MEM_TYPE_DEC;
+				break;
 			}
 			data += len;
 			if (mem_copy_bin(&val[i], str, data - str) != 0)
diff --git a/src/box/sql/mem.h b/src/box/sql/mem.h
index 5f004646e..543944b80 100644
--- a/src/box/sql/mem.h
+++ b/src/box/sql/mem.h
@@ -31,6 +31,7 @@
  */
 #include "box/field_def.h"
 #include "uuid/tt_uuid.h"
+#include "decimal.h"
 
 struct sql;
 struct Vdbe;
@@ -49,10 +50,11 @@ enum mem_type {
 	MEM_TYPE_BOOL		= 1 << 7,
 	MEM_TYPE_DOUBLE		= 1 << 8,
 	MEM_TYPE_UUID		= 1 << 9,
-	MEM_TYPE_INVALID	= 1 << 10,
-	MEM_TYPE_FRAME		= 1 << 11,
-	MEM_TYPE_PTR		= 1 << 12,
-	MEM_TYPE_AGG		= 1 << 13,
+	MEM_TYPE_DEC		= 1 << 10,
+	MEM_TYPE_INVALID	= 1 << 11,
+	MEM_TYPE_FRAME		= 1 << 12,
+	MEM_TYPE_PTR		= 1 << 13,
+	MEM_TYPE_AGG		= 1 << 14,
 };
 
 /*
@@ -75,6 +77,7 @@ struct Mem {
 		struct func *func;
 		struct VdbeFrame *pFrame;	/* Used when flags==MEM_Frame */
 		struct tt_uuid uuid;
+		decimal_t d;
 	} u;
 	/** Type of the value this MEM contains. */
 	enum mem_type type;
@@ -138,7 +141,8 @@ static inline bool
 mem_is_num(const struct Mem *mem)
 {
 	enum mem_type type = mem->type;
-	return (type & (MEM_TYPE_UINT | MEM_TYPE_INT | MEM_TYPE_DOUBLE)) != 0;
+	return (type & (MEM_TYPE_UINT | MEM_TYPE_INT | MEM_TYPE_DOUBLE |
+			MEM_TYPE_DEC)) != 0;
 }
 
 static inline bool
@@ -306,6 +310,10 @@ mem_set_double(struct Mem *mem, double value);
 void
 mem_set_uuid(struct Mem *mem, const struct tt_uuid *uuid);
 
+/** Clear MEM and set it to DECIMAL. */
+void
+mem_set_dec(struct Mem *mem, decimal_t *dec);
+
 /** Clear MEM and set it to STRING. The string belongs to another object. */
 void
 mem_set_str_ephemeral(struct Mem *mem, char *value, uint32_t len);
diff --git a/src/box/sql/parse.y b/src/box/sql/parse.y
index bd041e862..436c98cd9 100644
--- a/src/box/sql/parse.y
+++ b/src/box/sql/parse.y
@@ -1863,6 +1863,7 @@ number_typedef(A) ::= NUMBER . { A.type = FIELD_TYPE_NUMBER; }
 number_typedef(A) ::= DOUBLE . { A.type = FIELD_TYPE_DOUBLE; }
 number_typedef(A) ::= INT|INTEGER_KW . { A.type = FIELD_TYPE_INTEGER; }
 number_typedef(A) ::= UNSIGNED . { A.type = FIELD_TYPE_UNSIGNED; }
+number_typedef(A) ::= DECIMAL . { A.type = FIELD_TYPE_DECIMAL; }
 
 /**
  * NUMERIC type is temporary disabled. To be enabled when
diff --git a/src/box/sql/sqlInt.h b/src/box/sql/sqlInt.h
index 60fa1678d..e893cccc0 100644
--- a/src/box/sql/sqlInt.h
+++ b/src/box/sql/sqlInt.h
@@ -1236,6 +1236,7 @@ enum trim_side_mask {
 #define sql_type_is_numeric(X)  ((X) == FIELD_TYPE_INTEGER || \
 				 (X) == FIELD_TYPE_NUMBER || \
 				 (X) == FIELD_TYPE_UNSIGNED || \
+				 (X) == FIELD_TYPE_DECIMAL || \
 				 (X) == FIELD_TYPE_DOUBLE)
 
 /*
diff --git a/test/sql-tap/CMakeLists.txt b/test/sql-tap/CMakeLists.txt
index bd2b9f33f..87f23b2f7 100644
--- a/test/sql-tap/CMakeLists.txt
+++ b/test/sql-tap/CMakeLists.txt
@@ -2,3 +2,4 @@ include_directories(${MSGPUCK_INCLUDE_DIRS})
 build_module(gh-5938-wrong-string-length gh-5938-wrong-string-length.c)
 build_module(gh-6024-funcs-return-bin gh-6024-funcs-return-bin.c)
 build_module(sql_uuid sql_uuid.c)
+build_module(decimal decimal.c)
diff --git a/test/sql-tap/decimal.c b/test/sql-tap/decimal.c
new file mode 100644
index 000000000..4d9d1ce19
--- /dev/null
+++ b/test/sql-tap/decimal.c
@@ -0,0 +1,48 @@
+#include "msgpuck.h"
+#include "module.h"
+#include "mp_decimal.h"
+#include "mp_extension_types.h"
+
+enum {
+	BUF_SIZE = 512,
+};
+
+int
+is_dec(box_function_ctx_t *ctx, const char *args, const char *args_end)
+{
+	(void)args_end;
+	uint32_t arg_count = mp_decode_array(&args);
+	if (arg_count != 1) {
+		return box_error_set(__FILE__, __LINE__, ER_PROC_C,
+				     "invalid argument count");
+	}
+	bool is_uuid;
+	if (mp_typeof(*args) == MP_EXT) {
+		const char *str = args;
+		int8_t type;
+		mp_decode_extl(&str, &type);
+		is_uuid = type == MP_DECIMAL;
+	} else {
+		is_uuid = false;
+	}
+
+	char res[BUF_SIZE];
+	memset(res, '\0', BUF_SIZE);
+	char *end = mp_encode_bool(res, is_uuid);
+	box_return_mp(ctx, res, end);
+	return 0;
+}
+
+int
+ret_dec(box_function_ctx_t *ctx, const char *args, const char *args_end)
+{
+	(void)args;
+	(void)args_end;
+	decimal_t dec;
+	decimal_from_string(&dec, "111");
+	char res[BUF_SIZE];
+	memset(res, '\0', BUF_SIZE);
+	char *end = mp_encode_decimal(res, &dec);
+	box_return_mp(ctx, res, end);
+	return 0;
+}
diff --git a/test/sql-tap/decimal.test.lua b/test/sql-tap/decimal.test.lua
new file mode 100755
index 000000000..dd69ca370
--- /dev/null
+++ b/test/sql-tap/decimal.test.lua
@@ -0,0 +1,441 @@
+#!/usr/bin/env tarantool
+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(43)
+
+local dec = require("decimal")
+local dec1 = dec.new("111")
+local dec2 = dec.new("55555")
+local dec3 = dec.new("3333")
+
+-- Check that it is possible to create spaces with DECIMAL field.
+test:do_execsql_test(
+    "dec-1",
+    [[
+        CREATE TABLE t1 (i INT PRIMARY KEY, u DECIMAL);
+        CREATE TABLE t2 (u DECIMAL PRIMARY KEY);
+    ]], {
+    })
+
+box.space.T1:insert({1, dec1})
+box.space.T1:insert({2, dec2})
+box.space.T1:insert({3, dec3})
+box.space.T1:insert({4, dec1})
+box.space.T1:insert({5, dec1})
+box.space.T1:insert({6, dec2})
+box.space.T2:insert({dec1})
+box.space.T2:insert({dec2})
+box.space.T2:insert({dec3})
+
+-- Check that SELECT can work with DECIMAL.
+test:do_execsql_test(
+    "dec-2.1.1",
+    [[
+        SELECT * FROM t1;
+    ]], {
+        1, dec1, 2, dec2, 3, dec3, 4, dec1, 5, dec1, 6, dec2
+    })
+
+test:do_execsql_test(
+    "dec-2.1.2",
+    [[
+        SELECT * FROM t2;
+    ]], {
+        dec1, dec3, dec2
+    })
+
+-- Check that ORDER BY can work with DECIMAL.
+test:do_execsql_test(
+    "dec-2.2.1",
+    [[
+        SELECT * FROM t1 ORDER BY u;
+    ]], {
+        1, dec1, 4, dec1, 5, dec1, 3, dec3, 2, dec2, 6, dec2
+    })
+
+test:do_execsql_test(
+    "dec-2.2.2",
+    [[
+        SELECT * FROM t1 ORDER BY u DESC;
+    ]], {
+        2, dec2, 6, dec2, 3, dec3, 1, dec1, 4, dec1, 5, dec1
+    })
+
+test:do_execsql_test(
+    "dec-2.2.3",
+    [[
+        SELECT * FROM t2 ORDER BY u;
+    ]], {
+        dec1, dec3, dec2
+    })
+
+test:do_execsql_test(
+    "dec-2.2.4",
+    [[
+        SELECT * FROM t2 ORDER BY u DESC;
+    ]], {
+        dec2, dec3, dec1
+    })
+
+-- Check that GROUP BY can work with DECIMAL.
+test:do_execsql_test(
+    "dec-2.3.1",
+    [[
+        SELECT count(*), u FROM t1 GROUP BY u;
+    ]], {
+        3, dec1, 1, dec3, 2, dec2
+    })
+
+test:do_execsql_test(
+    "dec-2.3.2",
+    [[
+        SELECT count(*), u FROM t2 GROUP BY u;
+    ]], {
+        1, dec1, 1, dec3, 1, dec2
+    })
+
+-- Check that subselects can work with DECIMAL.
+test:do_execsql_test(
+    "dec-2.4",
+    [[
+        SELECT * FROM (SELECT * FROM (SELECT * FROM t2 LIMIT 2) LIMIT 2 OFFSET 1);
+    ]], {
+        dec3
+    })
+
+-- Check that DISTINCT can work with DECIMAL.
+test:do_execsql_test(
+    "dec-2.5",
+    [[
+        SELECT DISTINCT u FROM t1;
+    ]], {
+        dec1, dec2, dec3
+    })
+
+-- Check that VIEW can work with DECIMAL.
+test:do_execsql_test(
+    "dec-2.6",
+    [[
+        CREATE VIEW v AS SELECT u FROM t1;
+        SELECT * FROM v;
+    ]], {
+        dec1, dec2, dec3, dec1, dec1, dec2
+    })
+
+test:execsql([[
+    CREATE TABLE t3 (s SCALAR PRIMARY KEY);
+]])
+
+-- Check that SCALAR field can contain DECIMAL and use it in index.
+test:do_execsql_test(
+    "dec-3",
+    [[
+        INSERT INTO t3 SELECT u FROM t2;
+        SELECT s, typeof(s) FROM t3;
+    ]], {
+        dec1, "scalar", dec3, "scalar", dec2, "scalar"
+    })
+
+-- Check that ephemeral space can work with DECIMAL.
+test:do_execsql_test(
+    "dec-4",
+    [[
+        EXPLAIN SELECT * from (VALUES(1)), t2;
+    ]], {
+        "/OpenTEphemeral/"
+    })
+
+test:execsql([[
+    CREATE TABLE t5f (u DECIMAL PRIMARY KEY, f DECIMAL REFERENCES t5f(u));
+    CREATE TABLE t5c (i INT PRIMARY KEY, f DECIMAL,
+                      CONSTRAINT ck CHECK(f != 111));
+    CREATE TABLE t5u (i INT PRIMARY KEY, f DECIMAL UNIQUE);
+]])
+
+-- Check that FOREIGN KEY constraint can work with DECIMAL.
+test:do_catchsql_test(
+    "dec-5.1.1",
+    [[
+        INSERT INTO t5f SELECT (SELECT u from t2 LIMIT 1 OFFSET 1), (SELECT u from t2 LIMIT 1);
+    ]], {
+        1, "Failed to execute SQL statement: FOREIGN KEY constraint failed"
+    })
+
+test:do_execsql_test(
+    "dec-5.1.2",
+    [[
+        INSERT INTO t5f SELECT u, u from t2 LIMIT 1;
+        SELECT * from t5f;
+    ]], {
+        dec1, dec1
+    })
+
+test:do_execsql_test(
+    "dec-5.1.3",
+    [[
+        INSERT INTO t5f SELECT (SELECT u from t2 LIMIT 1 OFFSET 1), (SELECT u from t2 LIMIT 1);
+        SELECT * from t5f;
+    ]], {
+        dec1, dec1, dec3, dec1
+    })
+
+-- Check that CHECK constraint can work with DECIMAL.
+test:do_catchsql_test(
+    "dec-5.2.1",
+    [[
+        INSERT INTO t5c SELECT 1, u FROM t2 LIMIT 1;
+    ]], {
+        1, "Check constraint failed 'CK': f != 111"
+    })
+
+test:do_execsql_test(
+    "dec-5.2.2",
+    [[
+        INSERT INTO t5c SELECT 2, u FROM t2 LIMIT 1 OFFSET 1;
+        SELECT * from t5c;
+    ]], {
+        2, dec3
+    })
+
+-- Check that UNIQUE constraint can work with DECIMAL.
+test:do_execsql_test(
+    "dec-5.3.1",
+    [[
+        INSERT INTO t5u SELECT 1, u FROM t2 LIMIT 1;
+        SELECT * from t5u;
+    ]], {
+        1, dec1
+    })
+
+test:do_catchsql_test(
+    "dec-5.3.2",
+    [[
+        INSERT INTO t5u SELECT 2, u FROM t2 LIMIT 1;
+    ]], {
+        1, 'Duplicate key exists in unique index "unique_unnamed_T5U_2" in '..
+           'space "T5U" with old tuple - [1, 111] and new tuple - [2, 111]'
+    })
+
+local func = {language = 'Lua', body = 'function(x) return type(x) end',
+              returns = 'string', param_list = {'any'}, exports = {'SQL'}}
+box.schema.func.create('RETURN_TYPE', func);
+
+-- Check that Lua user-defined functions can accept DECIMAL.
+test:do_execsql_test(
+    "dec-6.1",
+    [[
+        SELECT RETURN_TYPE(u) FROM t2;
+    ]], {
+        "cdata", "cdata", "cdata"
+    })
+
+func = {language = 'Lua', returns = 'decimal', param_list = {}, exports = {'SQL'},
+        body = 'function(x) return require("decimal").new("111") end'}
+box.schema.func.create('GET_DEC', func);
+
+-- Check that Lua user-defined functions can return DECIMAL.
+test:do_execsql_test(
+    "dec-6.2",
+    [[
+        SELECT GET_DEC();
+    ]], {
+        dec1
+    })
+
+func = {language = 'C', returns = 'boolean', param_list = {'any'}, exports = {'SQL'}}
+box.schema.func.create("decimal.is_dec", func)
+
+-- Check that C user-defined functions can accept DECIMAL.
+test:do_execsql_test(
+    "dec-6.3",
+    [[
+        SELECT "decimal.is_dec"(i), "decimal.is_dec"(u) FROM t1 LIMIT 1;
+    ]], {
+        false, true
+    })
+
+func = {language = 'C', returns = 'decimal', param_list = {}, exports = {'SQL'}}
+box.schema.func.create("decimal.ret_dec", func)
+
+-- Check that C user-defined functions can return DECIMAL.
+test:do_execsql_test(
+    "dec-6.4",
+    [[
+        SELECT "decimal.ret_dec"();
+    ]], {
+        dec1
+    })
+
+test:execsql([[
+    CREATE TABLE t7 (i INT PRIMARY KEY AUTOINCREMENT, u DECIMAL);
+    CREATE TABLE t7t (u DECIMAL PRIMARY KEY);
+    CREATE TRIGGER t AFTER INSERT ON t7 FOR EACH ROW BEGIN INSERT INTO t7t SELECT new.u; END;
+]])
+
+-- Check that trigger can work with DECIMAL.
+test:do_execsql_test(
+    "dec-7",
+    [[
+        INSERT INTO t7(u) SELECT * FROM t2;
+        SELECT * FROM t7t;
+    ]], {
+        dec1, dec3, dec2
+    })
+
+-- Check that JOIN by DECIMAL field works.
+test:do_execsql_test(
+    "dec-8.1",
+    [[
+        SELECT * FROM t1 JOIN t2 on t1.u = t2.u;
+    ]], {
+        1, dec1, dec1, 2, dec2, dec2, 3, dec3, dec3,
+        4, dec1, dec1, 5, dec1, dec1, 6, dec2, dec2
+    })
+
+test:do_execsql_test(
+    "dec-8.2",
+    [[
+        SELECT * FROM t1 LEFT JOIN t2 on t1.u = t2.u;
+    ]], {
+        1, dec1, dec1, 2, dec2, dec2, 3, dec3, dec3,
+        4, dec1, dec1, 5, dec1, dec1, 6, dec2, dec2
+    })
+
+test:do_execsql_test(
+    "dec-8.3",
+    [[
+        SELECT * FROM t1 INNER JOIN t2 on t1.u = t2.u;
+    ]], {
+        1, dec1, dec1, 2, dec2, dec2, 3, dec3, dec3,
+        4, dec1, dec1, 5, dec1, dec1, 6, dec2, dec2
+    })
+
+-- Check that comparison with DECIMAL works as intended.
+test:do_execsql_test(
+    "dec-9.1.1",
+    [[
+        SELECT u > 1 FROM t2;
+    ]], {
+        true, true, true
+    })
+
+test:do_catchsql_test(
+    "dec-9.1.2",
+    [[
+        SELECT u > CAST('11111111-1111-1111-1111-111111111111' AS UUID) FROM t2;
+    ]], {
+        1, "Type mismatch: can not convert uuid('11111111-1111-1111-1111-111111111111') to number"
+    })
+
+test:do_catchsql_test(
+    "dec-9.1.3",
+    [[
+        SELECT u > '1' FROM t2;
+    ]], {
+        1, "Type mismatch: can not convert string('1') to number"
+    })
+
+test:do_execsql_test(
+    "dec-9.1.4",
+    [[
+        SELECT u > 1.5 FROM t2;
+    ]], {
+        true, true, true
+    })
+
+test:do_execsql_test(
+    "dec-9.1.5",
+    [[
+        SELECT u > -1 FROM t2;
+    ]], {
+        true, true, true
+    })
+
+test:do_catchsql_test(
+    "dec-9.1.6",
+    [[
+        SELECT u > true FROM t2;
+    ]], {
+        1, "Type mismatch: can not convert boolean(TRUE) to number"
+    })
+
+test:do_catchsql_test(
+    "dec-9.1.7",
+    [[
+        SELECT u > x'31' FROM t2;
+    ]], {
+        1, "Type mismatch: can not convert varbinary(x'31') to number"
+    })
+
+test:do_execsql_test(
+    "dec-9.2.1",
+    [[
+        SELECT u = 1 FROM t2;
+    ]], {
+        false, false, false
+    })
+
+test:do_catchsql_test(
+    "dec-9.2.2",
+    [[
+        SELECT u = CAST('11111111-1111-1111-1111-111111111111' AS UUID) FROM t2;
+    ]], {
+        1, "Type mismatch: can not convert uuid('11111111-1111-1111-1111-111111111111') to number"
+    })
+
+test:do_catchsql_test(
+    "dec-9.2.3",
+    [[
+        SELECT u = '1' FROM t2;
+    ]], {
+        1, "Type mismatch: can not convert string('1') to number"
+    })
+
+test:do_execsql_test(
+    "dec-9.2.4",
+    [[
+        SELECT u = 1.5 FROM t2;
+    ]], {
+        false, false, false
+    })
+
+test:do_execsql_test(
+    "dec-9.2.5",
+    [[
+        SELECT u = -1 FROM t2;
+    ]], {
+        false, false, false
+    })
+
+test:do_catchsql_test(
+    "dec-9.2.6",
+    [[
+        SELECT u = true FROM t2;
+    ]], {
+        1, "Type mismatch: can not convert boolean(TRUE) to number"
+    })
+
+test:do_catchsql_test(
+    "dec-9.2.7",
+    [[
+        SELECT u = x'31' FROM t2;
+    ]], {
+        1, "Type mismatch: can not convert varbinary(x'31') to number"
+    })
+
+test:execsql([[
+    DROP TRIGGER t;
+    DROP VIEW v;
+    DROP TABLE t7t;
+    DROP TABLE t7;
+    DROP TABLE t5u;
+    DROP TABLE t5c;
+    DROP TABLE t5f;
+    DROP TABLE t3;
+    DROP TABLE t2;
+    DROP TABLE t1;
+]])
+
+test:finish_test()
diff --git a/test/sql-tap/engine.cfg b/test/sql-tap/engine.cfg
index 820c72b00..511d0a716 100644
--- a/test/sql-tap/engine.cfg
+++ b/test/sql-tap/engine.cfg
@@ -26,6 +26,9 @@
     "metatypes.test.lua": {
         "memtx": {"engine": "memtx"}
     },
+    "decimal.test.lua": {
+        "memtx": {"engine": "memtx"}
+    },
     "gh-4077-iproto-execute-no-bind.test.lua": {},
     "*": {
         "memtx": {"engine": "memtx"},
diff --git a/test/sql-tap/gh-5913-segfault-on-select-uuid.test.lua b/test/sql-tap/gh-5913-segfault-on-select-uuid.test.lua
deleted file mode 100755
index 8847fede4..000000000
--- a/test/sql-tap/gh-5913-segfault-on-select-uuid.test.lua
+++ /dev/null
@@ -1,83 +0,0 @@
-#!/usr/bin/env tarantool
-local test = require("sqltester")
-test:plan(4)
-
-local uuid = require("uuid").fromstr("11111111-1111-1111-1111-111111111111")
-local decimal = require("decimal").new(111.111)
-
-box.schema.create_space('T')
-box.space.T:format({{name = "I", type = "integer"}, {name = "U", type = "uuid"},
-                    {name = "D", type = "decimal"}})
-box.space.T:create_index("primary")
-box.space.T:insert({1, uuid, decimal})
-
---
--- Make sure that there is no segmentation fault on select from field that
--- contains UUID or DECIMAL. Currently SQL does not support DECIMAL, so it is
--- treated as VARBINARY.
---
-test:do_execsql_test(
-    "gh-5913-1",
-    [[
-        SELECT i, u, d FROM t;
-        SELECT i, u from t;
-    ]], {
-        1, uuid
-    })
-
-box.schema.create_space('T1')
-box.space.T1:format({{name = "I", type = "integer"},
-                     {name = "U", type = "uuid", is_nullable = true},
-                     {name = "D", type = "decimal", is_nullable = true}})
-box.space.T1:create_index("primary")
-
---
--- Since SQL does not support DECIMAL and it is treated as VARBINARY, it cannot
--- be inserted from SQL.
---
-test:do_catchsql_test(
-    "gh-5913-2",
-    [[
-        INSERT INTO t1 SELECT i, NULL, d FROM t;
-    ]], {
-        1, "Type mismatch: can not convert varbinary(x'C70501030111111C') to decimal"
-    })
-
---
--- Still, if DECIMAL fields does not selected directly, insert is working
--- properly in case the space which receives these values is empty.
---
-test:do_execsql_test(
-    "gh-5913-3",
-    [[
-        INSERT INTO t1 SELECT * FROM t;
-        SELECT count() FROM t1;
-    ]], {
-        1
-    })
-
-box.schema.create_space('TU')
-box.space.TU:format({{name = "I", type = "integer"},
-                     {name = "U", type = "uuid"}})
-box.space.TU:create_index("primary")
-box.space.TU:insert({1, uuid})
-
-box.schema.create_space('TD')
-box.space.TD:format({{name = "I", type = "integer"},
-                     {name = "D", type = "decimal"}})
-box.space.TD:create_index("primary")
-box.space.TD:insert({1, decimal})
-
---
--- Update of DECIMAL also does not lead to segfault, however throws an error
--- since after changing value cannot be inserted into the field from SQL.
---
-test:do_catchsql_test(
-    "gh-5913-4",
-    [[
-        UPDATE td SET d = d;
-    ]], {
-        1, "Type mismatch: can not convert varbinary(x'C70501030111111C') to decimal"
-    })
-
-test:finish_test()
diff --git a/test/sql-tap/gh-6024-funcs-return-bin.test.lua b/test/sql-tap/gh-6024-funcs-return-bin.test.lua
index 79464afd1..6fd009261 100755
--- a/test/sql-tap/gh-6024-funcs-return-bin.test.lua
+++ b/test/sql-tap/gh-6024-funcs-return-bin.test.lua
@@ -38,7 +38,7 @@ test:do_execsql_test(
 box.schema.func.create("gh-6024-funcs-return-bin.ret_decimal", {
     language = "C",
     param_list = {},
-    returns = "varbinary",
+    returns = "decimal",
     exports = {"SQL"},
 })
 
@@ -47,7 +47,7 @@ test:do_execsql_test(
     [[
         SELECT typeof("gh-6024-funcs-return-bin.ret_decimal"());
     ]], {
-        "varbinary"
+        "decimal"
     })
 
 box.schema.func.create("get_uuid", {
@@ -69,7 +69,7 @@ test:do_execsql_test(
 box.schema.func.create("get_decimal", {
     language = "LUA",
     param_list = {},
-    returns = "varbinary",
+    returns = "decimal",
     body = "function(x) return require('decimal').new('9999999999999999999.9999999999999999999') end",
     exports = {"SQL"},
 })
@@ -79,7 +79,7 @@ test:do_execsql_test(
     [[
         SELECT typeof("get_decimal"()), "get_decimal"() == "gh-6024-funcs-return-bin.ret_decimal"();
     ]], {
-        "varbinary", true
+        "decimal", true
     })
 
 box.schema.func.drop("gh-6024-funcs-return-bin.ret_bin")
-- 
2.25.1


^ permalink raw reply	[flat|nested] 16+ messages in thread

* [Tarantool-patches] [PATCH v1 3/4] sql: introduce cast for decimal
  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-16 15:57 ` [Tarantool-patches] [PATCH v1 2/4] sql: introduce field type decimal Mergen Imeev via Tarantool-patches
@ 2021-08-16 15:57 ` Mergen Imeev via Tarantool-patches
  2021-08-16 19:34   ` Safin Timur via Tarantool-patches
  2021-08-16 15:57 ` [Tarantool-patches] [PATCH v1 4/4] sql: introduce decimal to arithmetic Mergen Imeev via Tarantool-patches
  3 siblings, 1 reply; 16+ messages in thread
From: Mergen Imeev via Tarantool-patches @ 2021-08-16 15:57 UTC (permalink / raw)
  To: korablev; +Cc: tarantool-patches

This patch introduces implicit and explicit cast of the DECIMAL field in
SQL. Implicit cast to and from DECIMAL is precise. Explicit conversion
to decimal and back could lead to loss of precision.

Part of #4415
---
 src/box/sql/mem.c             | 334 ++++++++++++++++++++++++++++++-
 test/sql-tap/decimal.test.lua | 366 +++++++++++++++++++++++++++++++++-
 2 files changed, 695 insertions(+), 5 deletions(-)

diff --git a/src/box/sql/mem.c b/src/box/sql/mem.c
index 016f0e80b..ff8b40d7f 100644
--- a/src/box/sql/mem.c
+++ b/src/box/sql/mem.c
@@ -678,6 +678,17 @@ int_to_double_forced(struct Mem *mem)
 	return CMP_OLD_NEW(i, d, int64_t);
 }
 
+static inline int
+int_to_dec(struct Mem *mem)
+{
+	assert(mem->type == MEM_TYPE_INT);
+	int64_t i = mem->u.i;
+	decimal_from_int64(&mem->u.d, i);
+	mem->type = MEM_TYPE_DEC;
+	mem->flags = 0;
+	return 0;
+}
+
 static inline int
 uint_to_double_precise(struct Mem *mem)
 {
@@ -709,6 +720,17 @@ uint_to_double_forced(struct Mem *mem)
 	return CMP_OLD_NEW(u, d, uint64_t);
 }
 
+static inline int
+uint_to_dec(struct Mem *mem)
+{
+	assert(mem->type == MEM_TYPE_UINT);
+	int64_t u = mem->u.u;
+	decimal_from_uint64(&mem->u.d, u);
+	mem->type = MEM_TYPE_DEC;
+	mem->flags = 0;
+	return 0;
+}
+
 static inline int
 int_to_str0(struct Mem *mem)
 {
@@ -855,6 +877,19 @@ str_to_double(struct Mem *mem)
 	return 0;
 }
 
+static inline int
+str_to_dec(struct Mem *mem)
+{
+	assert(mem->type == MEM_TYPE_STR);
+	decimal_t dec;
+	decimal_t *d;
+	d = decimal_from_string(&dec, mem->z);
+	if (d == NULL)
+		return -1;
+	mem_set_dec(mem, &dec);
+	return 0;
+}
+
 static inline int
 double_to_int(struct Mem *mem)
 {
@@ -988,6 +1023,69 @@ double_to_uint_forced(struct Mem *mem)
 	return res;
 }
 
+static inline int
+double_to_dec(struct Mem *mem)
+{
+	assert(mem->type == MEM_TYPE_DOUBLE);
+	double d = mem->u.r;
+	if (d >= 1e38 || d <= -1e38)
+		return -1;
+	decimal_from_double(&mem->u.d, d);
+	mem->type = MEM_TYPE_DEC;
+	mem->flags = 0;
+	return 0;
+}
+
+static inline int
+double_to_dec_precise(struct Mem *mem)
+{
+	assert(mem->type == MEM_TYPE_DOUBLE);
+	double d = mem->u.r;
+	if (d >= 1e38 || d <= -1e38)
+		return -1;
+	decimal_t dec;
+	decimal_from_double(&dec, d);
+	if (atof(decimal_str(&mem->u.d)) != d)
+		return -1;
+	mem->u.d = dec;
+	mem->type = MEM_TYPE_DEC;
+	mem->flags = 0;
+	return 0;
+}
+
+/**
+ * Cast MEM with DOUBLE to DECIMAL. Doesn't fail. The return value is < 0 if
+ * the original value is less than the result, > 0 if the original value is
+ * greater than the result, and 0 if the cast is precise.
+ */
+static inline int
+double_to_dec_forced(struct Mem *mem)
+{
+	assert(mem->type == MEM_TYPE_DOUBLE);
+	double d = mem->u.r;
+	mem->type = MEM_TYPE_DEC;
+	mem->flags = 0;
+	if (d >= 1e38) {
+		const char *val = "99999999999999999999999999999999999999";
+		assert(strlen(val) == 38);
+		decimal_from_string(&mem->u.d, val);
+		return 1;
+	}
+	if (d <= -1e38) {
+		const char *val = "-99999999999999999999999999999999999999";
+		assert(strlen(val) == 39);
+		decimal_from_string(&mem->u.d, val);
+		return -1;
+	}
+	decimal_from_double(&mem->u.d, d);
+	double tmp = atof(decimal_str(&mem->u.d));
+	if (d > tmp)
+		return 1;
+	if (d < tmp)
+		return -1;
+	return 0;
+}
+
 static inline int
 double_to_str0(struct Mem *mem)
 {
@@ -1001,6 +1099,173 @@ double_to_str0(struct Mem *mem)
 	return 0;
 }
 
+static inline int
+dec_to_int(struct Mem *mem)
+{
+	assert(mem->type == MEM_TYPE_DEC);
+	if (decimal_is_neg(&mem->u.d)) {
+		int64_t i;
+		if (decimal_to_int64(&mem->u.d, &i) == NULL)
+			return -1;
+		assert(i < 0);
+		mem->u.i = i;
+		mem->type = MEM_TYPE_INT;
+		mem->flags = 0;
+		return 0;
+	}
+	uint64_t u;
+	if (decimal_to_uint64(&mem->u.d, &u) == NULL)
+		return -1;
+	mem->u.u = u;
+	mem->type = MEM_TYPE_UINT;
+	mem->flags = 0;
+	return 0;
+}
+
+static inline int
+dec_to_int_precise(struct Mem *mem)
+{
+	assert(mem->type == MEM_TYPE_DEC);
+	if (!decimal_is_int(&mem->u.d))
+		return -1;
+	return dec_to_int(mem);
+}
+
+static inline int
+dec_to_int_forced(struct Mem *mem)
+{
+	assert(mem->type == MEM_TYPE_DEC);
+	if (decimal_is_neg(&mem->u.d)) {
+		int64_t i;
+		mem->type = MEM_TYPE_INT;
+		mem->flags = 0;
+		if (decimal_to_int64(&mem->u.d, &i) == NULL) {
+			mem->u.i = INT64_MIN;
+			return -1;
+		}
+		assert(i < 0);
+		mem->u.i = i;
+		/*
+		 * Decimal is floored when cast to int, which means that after
+		 * cast it becomes bigger if it was not integer.
+		 */
+		return decimal_is_int(&mem->u.d) ? 0 : -1;
+	}
+	uint64_t u;
+	mem->type = MEM_TYPE_UINT;
+	mem->flags = 0;
+	if (decimal_to_uint64(&mem->u.d, &u) == NULL) {
+		mem->u.u = UINT64_MAX;
+		return 1;
+	}
+	mem->u.u = u;
+	/*
+	 * Decimal is floored when cast to uint, which means that after cast it
+	 * becomes less if it was not integer.
+	 */
+	return decimal_is_int(&mem->u.d) ? 0 : 1;
+}
+
+static inline int
+dec_to_uint(struct Mem *mem)
+{
+	assert(mem->type == MEM_TYPE_DEC);
+	uint64_t u;
+	if (decimal_to_uint64(&mem->u.d, &u) == NULL)
+		return -1;
+	mem->u.u = u;
+	mem->type = MEM_TYPE_UINT;
+	mem->flags = 0;
+	return 0;
+}
+
+static inline int
+dec_to_uint_precise(struct Mem *mem)
+{
+	assert(mem->type == MEM_TYPE_DEC);
+	if (!decimal_is_int(&mem->u.d))
+		return -1;
+	return dec_to_uint(mem);
+}
+
+static inline int
+dec_to_uint_forced(struct Mem *mem)
+{
+	assert(mem->type == MEM_TYPE_DEC);
+	uint64_t u;
+	mem->type = MEM_TYPE_UINT;
+	mem->flags = 0;
+	if (decimal_to_uint64(&mem->u.d, &u) == NULL) {
+		if (decimal_is_neg(&mem->u.d)) {
+			mem->u.u = 0;
+			return -1;
+		}
+		mem->u.u = UINT64_MAX;
+		return 1;
+	}
+	mem->u.u = u;
+	/*
+	 * Decimal is floored when cast to uint, which means that after cast if
+	 * if was not integer it becomes less if it was positive, and move if it
+	 * was negative.
+	 */
+	if (decimal_is_int(&mem->u.d))
+		return 0;
+	return decimal_is_neg(&mem->u.d) ? -1 : 1;
+}
+
+static inline int
+dec_to_double(struct Mem *mem)
+{
+	assert(mem->type == MEM_TYPE_DEC);
+	double r = atof(decimal_str(&mem->u.d));
+	mem->u.r = r;
+	mem->type = MEM_TYPE_DOUBLE;
+	mem->flags = 0;
+	return 0;
+}
+
+static inline int
+dec_to_double_precise(struct Mem *mem)
+{
+	assert(mem->type == MEM_TYPE_DEC);
+	double r = atof(decimal_str(&mem->u.d));
+	decimal_t d;
+	decimal_t *dec = decimal_from_double(&d, r);
+	if (dec == NULL || decimal_compare(dec, &mem->u.d) != 0)
+		return -1;
+	mem->u.r = r;
+	mem->type = MEM_TYPE_DOUBLE;
+	mem->flags = 0;
+	return 0;
+}
+
+static inline int
+dec_to_double_forced(struct Mem *mem)
+{
+	assert(mem->type == MEM_TYPE_DEC);
+	mem->type = MEM_TYPE_DOUBLE;
+	mem->flags = 0;
+	double r = atof(decimal_str(&mem->u.d));
+	int res;
+	decimal_t d;
+	if (r <= -1e38)
+		res = 1;
+	else if (r >= 1e38)
+		res = -1;
+	else
+		res = decimal_compare(&mem->u.d, decimal_from_double(&d, r));
+	mem->u.r = r;
+	return res;
+}
+
+static inline int
+dec_to_str0(struct Mem *mem)
+{
+	assert(mem->type == MEM_TYPE_DEC);
+	return mem_copy_str0(mem, decimal_str(&mem->u.d));
+}
+
 static inline int
 bool_to_str0(struct Mem *mem)
 {
@@ -1053,6 +1318,8 @@ mem_to_int(struct Mem *mem)
 		return str_to_int(mem);
 	if (mem->type == MEM_TYPE_DOUBLE)
 		return double_to_int(mem);
+	if (mem->type == MEM_TYPE_DEC)
+		return dec_to_int(mem);
 	return -1;
 }
 
@@ -1068,6 +1335,8 @@ mem_to_int_precise(struct Mem *mem)
 		return str_to_int(mem);
 	if (mem->type == MEM_TYPE_DOUBLE)
 		return double_to_int_precise(mem);
+	if (mem->type == MEM_TYPE_DEC)
+		return dec_to_int(mem);
 	return -1;
 }
 
@@ -1083,6 +1352,8 @@ mem_to_double(struct Mem *mem)
 		return int_to_double(mem);
 	if (mem->type == MEM_TYPE_STR)
 		return str_to_double(mem);
+	if (mem->type == MEM_TYPE_DEC)
+		return dec_to_double(mem);
 	return -1;
 }
 
@@ -1129,6 +1400,8 @@ mem_to_str0(struct Mem *mem)
 		return array_to_str0(mem);
 	case MEM_TYPE_UUID:
 		return uuid_to_str0(mem);
+	case MEM_TYPE_DEC:
+		return dec_to_str0(mem);
 	default:
 		return -1;
 	}
@@ -1157,6 +1430,8 @@ mem_to_str(struct Mem *mem)
 		return array_to_str0(mem);
 	case MEM_TYPE_UUID:
 		return uuid_to_str0(mem);
+	case MEM_TYPE_DEC:
+		return dec_to_str0(mem);
 	default:
 		return -1;
 	}
@@ -1177,6 +1452,8 @@ mem_cast_explicit(struct Mem *mem, enum field_type type)
 			return str_to_uint(mem);
 		case MEM_TYPE_DOUBLE:
 			return double_to_uint(mem);
+		case MEM_TYPE_DEC:
+			return dec_to_uint(mem);
 		default:
 			return -1;
 		}
@@ -1209,9 +1486,21 @@ mem_cast_explicit(struct Mem *mem, enum field_type type)
 	case FIELD_TYPE_NUMBER:
 		return mem_to_number(mem);
 	case FIELD_TYPE_DECIMAL:
-		if (mem->type == MEM_TYPE_DEC)
+		switch (mem->type) {
+		case MEM_TYPE_INT:
+			return int_to_dec(mem);
+		case MEM_TYPE_UINT:
+			return uint_to_dec(mem);
+		case MEM_TYPE_STR:
+			return str_to_dec(mem);
+		case MEM_TYPE_DOUBLE:
+			return double_to_dec(mem);
+		case MEM_TYPE_DEC:
+			mem->flags = 0;
 			return 0;
-		return -1;
+		default:
+			return -1;
+		}
 	case FIELD_TYPE_UUID:
 		if (mem->type == MEM_TYPE_UUID) {
 			mem->flags = 0;
@@ -1252,6 +1541,8 @@ mem_cast_implicit(struct Mem *mem, enum field_type type)
 		}
 		if (mem->type == MEM_TYPE_DOUBLE)
 			return double_to_uint_precise(mem);
+		if (mem->type == MEM_TYPE_DEC)
+			return dec_to_uint_precise(mem);
 		return -1;
 	case FIELD_TYPE_STRING:
 		if (mem->type == MEM_TYPE_STR) {
@@ -1268,6 +1559,8 @@ mem_cast_implicit(struct Mem *mem, enum field_type type)
 			return int_to_double_precise(mem);
 		if (mem->type == MEM_TYPE_UINT)
 			return uint_to_double_precise(mem);
+		if (mem->type == MEM_TYPE_DEC)
+			return dec_to_double_precise(mem);
 		return -1;
 	case FIELD_TYPE_INTEGER:
 		if ((mem->type & (MEM_TYPE_INT | MEM_TYPE_UINT)) != 0) {
@@ -1276,6 +1569,8 @@ mem_cast_implicit(struct Mem *mem, enum field_type type)
 		}
 		if (mem->type == MEM_TYPE_DOUBLE)
 			return double_to_int_precise(mem);
+		if (mem->type == MEM_TYPE_DEC)
+			return dec_to_int_precise(mem);
 		return -1;
 	case FIELD_TYPE_BOOLEAN:
 		if (mem->type == MEM_TYPE_BOOL) {
@@ -1296,9 +1591,19 @@ mem_cast_implicit(struct Mem *mem, enum field_type type)
 		mem->flags = MEM_Number;
 		return 0;
 	case FIELD_TYPE_DECIMAL:
-		if (mem->type == MEM_TYPE_DEC)
+		switch (mem->type) {
+		case MEM_TYPE_INT:
+			return int_to_dec(mem);
+		case MEM_TYPE_UINT:
+			return uint_to_dec(mem);
+		case MEM_TYPE_DOUBLE:
+			return double_to_dec_precise(mem);
+		case MEM_TYPE_DEC:
+			mem->flags = 0;
 			return 0;
-		return -1;
+		default:
+			return -1;
+		}
 	case FIELD_TYPE_MAP:
 		if (mem->type == MEM_TYPE_MAP)
 			return 0;
@@ -1343,6 +1648,8 @@ mem_cast_implicit_number(struct Mem *mem, enum field_type type)
 			return -1;
 		case MEM_TYPE_DOUBLE:
 			return double_to_uint_forced(mem);
+		case MEM_TYPE_DEC:
+			return dec_to_uint_forced(mem);
 		default:
 			unreachable();
 		}
@@ -1353,6 +1660,8 @@ mem_cast_implicit_number(struct Mem *mem, enum field_type type)
 			return int_to_double_forced(mem);
 		case MEM_TYPE_UINT:
 			return uint_to_double_forced(mem);
+		case MEM_TYPE_DEC:
+			return dec_to_double_forced(mem);
 		case MEM_TYPE_DOUBLE:
 			mem->flags = 0;
 			return 0;
@@ -1368,6 +1677,23 @@ mem_cast_implicit_number(struct Mem *mem, enum field_type type)
 			return 0;
 		case MEM_TYPE_DOUBLE:
 			return double_to_int_forced(mem);
+		case MEM_TYPE_DEC:
+			return dec_to_int_forced(mem);
+		default:
+			unreachable();
+		}
+		break;
+	case FIELD_TYPE_DECIMAL:
+		switch (mem->type) {
+		case MEM_TYPE_INT:
+			return int_to_dec(mem);
+		case MEM_TYPE_UINT:
+			return uint_to_dec(mem);
+		case MEM_TYPE_DEC:
+			mem->flags = 0;
+			return 0;
+		case MEM_TYPE_DOUBLE:
+			return double_to_dec_forced(mem);
 		default:
 			unreachable();
 		}
diff --git a/test/sql-tap/decimal.test.lua b/test/sql-tap/decimal.test.lua
index dd69ca370..3f1c285cf 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(43)
+test:plan(84)
 
 local dec = require("decimal")
 local dec1 = dec.new("111")
@@ -425,6 +425,370 @@ test:do_catchsql_test(
         1, "Type mismatch: can not convert varbinary(x'31') to number"
     })
 
+-- Check that explicit cast from DECIMAL to another types works as intended.
+test:do_execsql_test(
+    "dec-10.1.1",
+    [[
+        SELECT cast(u AS UNSIGNED) FROM t2;
+    ]], {
+        111, 3333, 55555
+    })
+
+test:do_execsql_test(
+    "dec-10.1.2",
+    [[
+        SELECT cast(u AS STRING) FROM t2;
+    ]], {
+        "111", "3333", "55555"
+    })
+
+test:do_execsql_test(
+    "dec-10.1.3",
+    [[
+        SELECT cast(u AS NUMBER) FROM t2;
+    ]], {
+        dec1, dec3, dec2
+    })
+
+test:do_execsql_test(
+    "dec-10.1.4",
+    [[
+        SELECT cast(u AS DOUBLE) FROM t2;
+    ]], {
+        111, 3333, 55555
+    })
+
+test:do_execsql_test(
+    "dec-10.1.5",
+    [[
+        SELECT cast(u AS INTEGER) FROM t2;
+    ]], {
+        111, 3333, 55555
+    })
+
+test:do_catchsql_test(
+    "dec-10.1.6",
+    [[
+        SELECT cast(u AS BOOLEAN) FROM t2;
+    ]], {
+        1, "Type mismatch: can not convert decimal(111) to boolean"
+    })
+
+test:do_catchsql_test(
+    "dec-10.1.7",
+    [[
+        SELECT hex(cast(u AS VARBINARY)) FROM t2;
+    ]], {
+        1, "Type mismatch: can not convert decimal(111) to varbinary"
+    })
+
+test:do_execsql_test(
+    "dec-10.1.8",
+    [[
+        SELECT cast(u AS SCALAR) FROM t2;
+    ]], {
+        dec1, dec3, dec2
+    })
+
+test:do_catchsql_test(
+    "dec-10.1.9",
+    [[
+        SELECT cast(u AS UUID) FROM t2;
+    ]], {
+        1, "Type mismatch: can not convert decimal(111) to uuid"
+    })
+
+-- Check that explicit cast from another types to DECIMAL works as intended.
+test:do_execsql_test(
+    "dec-10.2.1",
+    [[
+        SELECT cast(111 AS DECIMAL);
+    ]], {
+        dec1
+    })
+
+test:do_catchsql_test(
+    "dec-10.2.2",
+    [[
+        SELECT cast(x'1234567890abcdef' AS DECIMAL) FROM t2 LIMIT 1;
+    ]], {
+        1, "Type mismatch: can not convert varbinary(x'1234567890ABCDEF') to decimal"
+    })
+
+test:do_execsql_test(
+    "dec-10.2.3",
+    [[
+        SELECT cast('111' AS DECIMAL);
+    ]], {
+        dec1
+    })
+
+test:do_execsql_test(
+    "dec-10.2.4",
+    [[
+        SELECT cast(111.0 AS DECIMAL);
+    ]], {
+        dec1
+    })
+
+test:do_execsql_test(
+    "dec-10.2.5",
+    [[
+        SELECT cast(-1 AS DECIMAL);
+    ]], {
+        dec.new(-1)
+    })
+
+test:do_catchsql_test(
+    "dec-10.2.6",
+    [[
+        SELECT cast(true AS DECIMAL);
+    ]], {
+        1, "Type mismatch: can not convert boolean(TRUE) to decimal"
+    })
+
+test:do_catchsql_test(
+    "dec-10.2.7",
+    [[
+        SELECT cast(cast(x'11111111111111111111111111111111' AS UUID) AS DECIMAL);
+    ]], {
+        1, "Type mismatch: can not convert uuid('11111111-1111-1111-1111-111111111111') to decimal"
+    })
+
+test:execsql([[
+    CREATE TABLE tu (id INT PRIMARY KEY AUTOINCREMENT, u UNSIGNED);
+    CREATE TABLE ts (id INT PRIMARY KEY AUTOINCREMENT, s STRING);
+    CREATE TABLE tn (id INT PRIMARY KEY AUTOINCREMENT, n NUMBER);
+    CREATE TABLE td (id INT PRIMARY KEY AUTOINCREMENT, d DOUBLE);
+    CREATE TABLE ti (id INT PRIMARY KEY AUTOINCREMENT, i INTEGER);
+    CREATE TABLE tb (id INT PRIMARY KEY AUTOINCREMENT, b BOOLEAN);
+    CREATE TABLE tv (id INT PRIMARY KEY AUTOINCREMENT, v VARBINARY);
+    CREATE TABLE tsc (id INT PRIMARY KEY AUTOINCREMENT, sc SCALAR);
+    CREATE TABLE tuu (id INT PRIMARY KEY AUTOINCREMENT, uu UUID);
+    CREATE TABLE tsu (s STRING PRIMARY KEY, u UUID);
+]])
+
+-- Check that implcit cast from DECIMAL to another types works as intended.
+test:do_execsql_test(
+    "dec-11.1.1",
+    [[
+        INSERT INTO tu(u) SELECT u FROM t2;
+        SELECT * FROM tu;
+    ]], {
+        1, 111, 2, 3333, 3, 55555
+    })
+
+test:do_catchsql_test(
+    "dec-11.1.2",
+    [[
+        INSERT INTO ts(s) SELECT u FROM t2;
+    ]], {
+        1, "Type mismatch: can not convert decimal(111) to string"
+    })
+
+test:do_execsql_test(
+    "dec-11.1.3",
+    [[
+        INSERT INTO tn(n) SELECT u FROM t2;
+        SELECT * FROM tn;
+    ]], {
+        1, dec1, 2, dec3, 3, dec2
+    })
+
+test:do_execsql_test(
+    "dec-11.1.4",
+    [[
+        INSERT INTO td(d) SELECT u FROM t2;
+        SELECT * FROM td;
+    ]], {
+        1, 111, 2, 3333, 3, 55555
+    })
+
+test:do_execsql_test(
+    "dec-11.1.5",
+    [[
+        INSERT INTO ti(i) SELECT u FROM t2;
+        SELECT * FROM ti;
+    ]], {
+        1, 111, 2, 3333, 3, 55555
+    })
+
+test:do_catchsql_test(
+    "dec-11.1.6",
+    [[
+        INSERT INTO tb(b) SELECT u FROM t2;
+    ]], {
+        1, "Type mismatch: can not convert decimal(111) to boolean"
+    })
+
+test:do_catchsql_test(
+    "dec-11.1.7",
+    [[
+        INSERT INTO tv(v) SELECT u FROM t2;
+    ]], {
+        1, "Type mismatch: can not convert decimal(111) to varbinary"
+    })
+
+test:do_execsql_test(
+    "dec-11.1.8",
+    [[
+        INSERT INTO tsc(sc) SELECT u FROM t2;
+        SELECT * FROM tsc;
+    ]], {
+        1, dec1, 2, dec3, 3, dec2
+    })
+
+test:do_catchsql_test(
+    "dec-11.1.9",
+    [[
+        INSERT INTO tuu(uu) SELECT u FROM t2;
+    ]], {
+        1, "Type mismatch: can not convert decimal(111) to uuid"
+    })
+
+-- Check that implicit cast from another types to DECIMAL works as intended.
+test:do_catchsql_test(
+    "dec-11.2.1",
+    [[
+        INSERT INTO tsu VALUES ('1_unsigned', 1);
+    ]], {
+        1, "Type mismatch: can not convert integer(1) to uuid"
+    })
+
+test:do_catchsql_test(
+    "dec-11.2.2",
+    [[
+        INSERT INTO tsu VALUES ('2_string_right', '11111111-1111-1111-1111-111111111111');
+    ]], {
+        1, "Type mismatch: can not convert string('11111111-1111-1111-1111-111111111111') to uuid"
+    })
+
+test:do_catchsql_test(
+    "dec-11.2.3",
+    [[
+        INSERT INTO tsu VALUES ('3_string_wrong', '1');
+    ]], {
+        1, "Type mismatch: can not convert string('1') to uuid"
+    })
+
+test:do_catchsql_test(
+    "dec-11.2.4",
+    [[
+        INSERT INTO tsu VALUES ('4_double', 1.5);
+    ]], {
+        1, "Type mismatch: can not convert double(1.5) to uuid"
+    })
+
+test:do_catchsql_test(
+    "dec-11.2.5",
+    [[
+        INSERT INTO tsu VALUES ('5_integer', -1);
+    ]], {
+        1, "Type mismatch: can not convert integer(-1) to uuid"
+    })
+
+test:do_catchsql_test(
+    "dec-11.2.6",
+    [[
+        INSERT INTO tsu VALUES ('6_boolean', true);
+    ]], {
+        1, "Type mismatch: can not convert boolean(TRUE) to uuid"
+    })
+
+test:do_catchsql_test(
+    "dec-11.2.7",
+    [[
+        INSERT INTO tsu SELECT '7_varbinary', x'11111111111111111111111111111111' FROM t2 LIMIT 1;
+    ]], {
+        1, "Type mismatch: can not convert varbinary(x'11111111111111111111111111111111') to uuid"
+    })
+
+test:do_catchsql_test(
+    "dec-11.2.8",
+    [[
+        INSERT INTO tsu VALUES ('8_varbinary', x'1234567890abcdef');
+    ]], {
+        1, "Type mismatch: can not convert varbinary(x'1234567890ABCDEF') to uuid"
+    })
+
+-- Check that LIMIT accepts DECIMAL as argument.
+test:do_execsql_test(
+    "dec-12.1",
+    [[
+        SELECT 1 LIMIT (SELECT u FROM t1 LIMIT 1);
+    ]], {
+        1
+    })
+
+-- Check that OFFSET accepts DECIMAL as argument.
+test:do_execsql_test(
+    "dec-12.2",
+    [[
+        SELECT 1 LIMIT 1 OFFSET (SELECT u FROM t1 LIMIT 1);
+    ]], {
+    })
+
+-- Check that other numeric values could be used to search in DECIMAL index.
+test:do_execsql_test(
+    "dec-13.1.1",
+    [[
+        SELECT * FROM t2 WHERE u > 123;
+    ]], {
+        dec3, dec2
+    })
+
+test:do_execsql_test(
+    "dec-13.1.2",
+    [[
+        SELECT * FROM t2 WHERE u < 123.5;
+    ]], {
+        dec1
+    })
+
+test:execsql([[
+    CREATE TABLE t13i (i INTEGER PRIMARY KEY);
+    CREATE TABLE t13u (u UNSIGNED PRIMARY KEY);
+    CREATE TABLE t13d (d DOUBLE PRIMARY KEY);
+    CREATE TABLE t13n (n NUMBER PRIMARY KEY);
+    INSERT INTO t13i VALUES (1), (1000);
+    INSERT INTO t13u VALUES (1), (1000);
+    INSERT INTO t13d VALUES (1), (1000);
+    INSERT INTO t13n VALUES (1), (1000);
+]])
+
+-- Check that DECIMAL values could be used to search in other numeric indexes.
+test:do_execsql_test(
+    "dec-13.2.1",
+    [[
+        SELECT * FROM t13i WHERE CAST(111 AS DECIMAL) > i;
+    ]], {
+        1
+    })
+
+test:do_execsql_test(
+    "dec-13.2.2",
+    [[
+        SELECT * FROM t13u WHERE CAST(111 AS DECIMAL) < u;
+    ]], {
+        1000
+    })
+
+test:do_execsql_test(
+    "dec-13.2.3",
+    [[
+        SELECT * FROM t13d WHERE CAST(111 AS DECIMAL) > d;
+    ]], {
+        1
+    })
+
+test:do_execsql_test(
+    "dec-13.2.4",
+    [[
+        SELECT * FROM t13n WHERE CAST(111 AS DECIMAL) < n;
+    ]], {
+        1000
+    })
+
 test:execsql([[
     DROP TRIGGER t;
     DROP VIEW v;
-- 
2.25.1


^ permalink raw reply	[flat|nested] 16+ messages in thread

* [Tarantool-patches] [PATCH v1 4/4] sql: introduce decimal to arithmetic
  2021-08-16 15:56 [Tarantool-patches] [PATCH v1 0/4] Introduce DECIMAL to SQL Mergen Imeev via Tarantool-patches
                   ` (2 preceding siblings ...)
  2021-08-16 15:57 ` [Tarantool-patches] [PATCH v1 3/4] sql: introduce cast for decimal Mergen Imeev via Tarantool-patches
@ 2021-08-16 15:57 ` Mergen Imeev via Tarantool-patches
  2021-08-16 19:48   ` Safin Timur via Tarantool-patches
  3 siblings, 1 reply; 16+ messages in thread
From: Mergen Imeev via Tarantool-patches @ 2021-08-16 15:57 UTC (permalink / raw)
  To: korablev; +Cc: 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


^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [Tarantool-patches] [PATCH v1 2/4] sql: introduce field type decimal
  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
  0 siblings, 1 reply; 16+ messages in thread
From: Safin Timur via Tarantool-patches @ 2021-08-16 19:22 UTC (permalink / raw)
  To: imeevma, korablev; +Cc: tarantool-patches

Please see below several notes...

On 16.08.2021 18:57, Mergen Imeev via Tarantool-patches wrote:
> This patch introduces a decimal field type. However, implicit and
> explicit casts and arithmetic operations for this type will be presented
> in next few patches. Literals also will be introduced later.
> 
> Part of #4415
> ---
>   extra/mkkeywordhash.c                         |   2 +-
>   src/box/sql/expr.c                            |   3 +
>   src/box/sql/func.c                            |   4 +
>   src/box/sql/mem.c                             | 173 +++++--
>   src/box/sql/mem.h                             |  18 +-
>   src/box/sql/parse.y                           |   1 +
>   src/box/sql/sqlInt.h                          |   1 +
>   test/sql-tap/CMakeLists.txt                   |   1 +
>   test/sql-tap/decimal.c                        |  48 ++
>   test/sql-tap/decimal.test.lua                 | 441 ++++++++++++++++++
>   test/sql-tap/engine.cfg                       |   3 +
>   .../gh-5913-segfault-on-select-uuid.test.lua  |  83 ----
>   .../sql-tap/gh-6024-funcs-return-bin.test.lua |   8 +-
>   13 files changed, 661 insertions(+), 125 deletions(-)
>   create mode 100644 test/sql-tap/decimal.c
>   create mode 100755 test/sql-tap/decimal.test.lua
>   delete mode 100755 test/sql-tap/gh-5913-segfault-on-select-uuid.test.lua
> 
> diff --git a/extra/mkkeywordhash.c b/extra/mkkeywordhash.c
> index 0d998506c..1c9d12295 100644
> --- a/extra/mkkeywordhash.c
> +++ b/extra/mkkeywordhash.c
> @@ -196,7 +196,7 @@ static Keyword aKeywordTable[] = {
>     { "CURRENT_TIMESTAMP",      "TK_STANDARD",    true  },
>     { "DATE",                   "TK_STANDARD",    true  },
>     { "DATETIME",               "TK_STANDARD",    true  },
> -  { "DECIMAL",                "TK_STANDARD",    true  },
> +  { "DECIMAL",                "TK_DECIMAL",     true  },

DEC is standard alias to DECIMAL. We should support that (similarly to 
INTEGER vs INT).

>     { "DECLARE",                "TK_STANDARD",    true  },
>     { "DENSE_RANK",             "TK_STANDARD",    true  },
>     { "DESCRIBE",               "TK_STANDARD",    true  },
> diff --git a/src/box/sql/expr.c b/src/box/sql/expr.c
> index c67a7091c..275dbc5ba 100644
> --- a/src/box/sql/expr.c
> +++ b/src/box/sql/expr.c

...

> diff --git a/src/box/sql/mem.c b/src/box/sql/mem.c
> index 066940fac..016f0e80b 100644
> --- a/src/box/sql/mem.c
> +++ b/src/box/sql/mem.c
> @@ -1191,6 +1208,10 @@ mem_cast_explicit(struct Mem *mem, enum field_type
> type)
>   		return -1;
>   	case FIELD_TYPE_NUMBER:
>   		return mem_to_number(mem);
> +	case FIELD_TYPE_DECIMAL:
> +		if (mem->type == MEM_TYPE_DEC)
> +			return 0;
> +		return -1;

So CAST(expr as DECIMAL) will only work for DECIMALs? It should be 
rather behaving similar to other numeric types.

>   	case FIELD_TYPE_UUID:
>   		if (mem->type == MEM_TYPE_UUID) {
>   			mem->flags = 0;
> @@ -1274,6 +1295,10 @@ mem_cast_implicit(struct Mem *mem, enum field_type
> type)
>   			return -1;
>   		mem->flags = MEM_Number;
>   		return 0;
> +	case FIELD_TYPE_DECIMAL:
> +		if (mem->type == MEM_TYPE_DEC)
> +			return 0;
> +		return -1;

Same question as above - implicit conversions to decimal should be 
numeric-like.


>   	case FIELD_TYPE_MAP:
>   		if (mem->type == MEM_TYPE_MAP)
>   			return 0;
> @@ -1595,12 +1620,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)) {
> +	if (!mem_is_num(a) || mem_is_metatype(a) || a->type ==
> MEM_TYPE_DEC) {
>   		diag_set(ClientError, ER_SQL_TYPE_MISMATCH, mem_str(a),
>   			 "integer, unsigned or double");
>   		return -1;

I don't understant - we would raise if not a numeric (and decimal is 
part of numeric) or decimal specifically? So you do not want arithmetic 
types with decimals?


>   	}
> -	if (!mem_is_num(b) || mem_is_metatype(b)) {
> +	if (!mem_is_num(b) || mem_is_metatype(b) || b->type ==
> MEM_TYPE_DEC) {

The same confusion as above..

>   		diag_set(ClientError, ER_SQL_TYPE_MISMATCH, mem_str(b),
>   			 "integer, unsigned or double");
>   		return -1;
> @@ -2926,26 +3040,10 @@ port_lua_get_vdbemem(struct port *base, uint32_t
> *size)
>   		case MP_EXT: {
>   			assert(field.ext_type == MP_UUID ||
>   			       field.ext_type == MP_DECIMAL);
> -			char *buf;
> -			uint32_t size;
> -			uint32_t svp = region_used(&fiber()->gc);
> -			if (field.ext_type == MP_UUID) {
> +			if (field.ext_type == MP_UUID)
>   				mem_set_uuid(&val[i], field.uuidval);
> -				break;
> -			} else {
> -				size = mp_sizeof_decimal(field.decval);
> -				buf = region_alloc(&fiber()->gc, size);
> -				if (buf == NULL) {
> -					diag_set(OutOfMemory, size,
> -						 "region_alloc", "buf");
> -					goto error;
> -				}
> -				mp_encode_decimal(buf, field.decval);
> -			}
> -			int rc = mem_copy_bin(&val[i], buf, size);
> -			region_truncate(&fiber()->gc, svp);
> -			if (rc != 0)
> -				goto error;
> +			else
> +				mem_set_dec(&val[i], field.decval);

Nice! Now it's much compacter and is more readable than before!

> diff --git a/src/box/sql/parse.y b/src/box/sql/parse.y
> index bd041e862..436c98cd9 100644
> --- a/src/box/sql/parse.y
> +++ b/src/box/sql/parse.y
> @@ -1863,6 +1863,7 @@ number_typedef(A) ::= NUMBER . { A.type =
> FIELD_TYPE_NUMBER; }
>   number_typedef(A) ::= DOUBLE . { A.type = FIELD_TYPE_DOUBLE; }
>   number_typedef(A) ::= INT|INTEGER_KW . { A.type = FIELD_TYPE_INTEGER; }
>   number_typedef(A) ::= UNSIGNED . { A.type = FIELD_TYPE_UNSIGNED; }
> +number_typedef(A) ::= DECIMAL . { A.type = FIELD_TYPE_DECIMAL; }

Here please add alias to DEC, as it was done with INT.

> diff --git a/test/sql-tap/CMakeLists.txt b/test/sql-tap/CMakeLists.txt
> index bd2b9f33f..87f23b2f7 100644
> --- a/test/sql-tap/CMakeLists.txt
> +++ b/test/sql-tap/CMakeLists.txt
> @@ -2,3 +2,4 @@ include_directories(${MSGPUCK_INCLUDE_DIRS})
>   build_module(gh-5938-wrong-string-length gh-5938-wrong-string-length.c)
>   build_module(gh-6024-funcs-return-bin gh-6024-funcs-return-bin.c)
>   build_module(sql_uuid sql_uuid.c)
> +build_module(decimal decimal.c)
> diff --git a/test/sql-tap/decimal.c b/test/sql-tap/decimal.c
> new file mode 100644
> index 000000000..4d9d1ce19
> --- /dev/null
> +++ b/test/sql-tap/decimal.c
> @@ -0,0 +1,48 @@
> +#include "msgpuck.h"
> +#include "module.h"
> +#include "mp_decimal.h"
> +#include "mp_extension_types.h"
> +
> +enum {
> +	BUF_SIZE = 512,
> +};
> +
> +int
> +is_dec(box_function_ctx_t *ctx, const char *args, const char *args_end)
> +{
> +	(void)args_end;
> +	uint32_t arg_count = mp_decode_array(&args);
> +	if (arg_count != 1) {
> +		return box_error_set(__FILE__, __LINE__, ER_PROC_C,
> +				     "invalid argument count");
> +	}
> +	bool is_uuid;
> +	if (mp_typeof(*args) == MP_EXT) {
> +		const char *str = args;
> +		int8_t type;
> +		mp_decode_extl(&str, &type);
> +		is_uuid = type == MP_DECIMAL;
> +	} else {
> +		is_uuid = false;

Here we see remnants from copy-paste from uuid related code, I assume 
you meant that variable should be named `is_decimal`.

> +	}
> +
> +	char res[BUF_SIZE];
> +	memset(res, '\0', BUF_SIZE);
> +	char *end = mp_encode_bool(res, is_uuid);
> +	box_return_mp(ctx, res, end);
> +	return 0;
> +}
> +
> +int
> diff --git a/test/sql-tap/engine.cfg b/test/sql-tap/engine.cfg
> index 820c72b00..511d0a716 100644
> --- a/test/sql-tap/engine.cfg
> +++ b/test/sql-tap/engine.cfg
> @@ -26,6 +26,9 @@
>       "metatypes.test.lua": {
>           "memtx": {"engine": "memtx"}
>       },
> +    "decimal.test.lua": {
> +        "memtx": {"engine": "memtx"}
> +    },

BTW, why this exception for the test?

>       "gh-4077-iproto-execute-no-bind.test.lua": {},
>       "*": {
>           "memtx": {"engine": "memtx"},

Thanks,
Timur

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [Tarantool-patches] [PATCH v1 3/4] sql: introduce cast for decimal
  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
  0 siblings, 1 reply; 16+ messages in thread
From: Safin Timur via Tarantool-patches @ 2021-08-16 19:34 UTC (permalink / raw)
  To: imeevma, korablev; +Cc: tarantool-patches

Oh, now I see the reasonable implicit and explicit casts table for 
decimals. I disavow my prior complain about missing directions for 
CAST(exprt AS DECIMAL).

Below there is minor complain though...

On 16.08.2021 18:57, Mergen Imeev via Tarantool-patches wrote:
> This patch introduces implicit and explicit cast of the DECIMAL field in
> SQL. Implicit cast to and from DECIMAL is precise. Explicit conversion
> to decimal and back could lead to loss of precision.
> 
> Part of #4415
> ---
>   src/box/sql/mem.c             | 334 ++++++++++++++++++++++++++++++-
>   test/sql-tap/decimal.test.lua | 366 +++++++++++++++++++++++++++++++++-
>   2 files changed, 695 insertions(+), 5 deletions(-)
> 
> diff --git a/src/box/sql/mem.c b/src/box/sql/mem.c
> index 016f0e80b..ff8b40d7f 100644
> --- a/src/box/sql/mem.c
> +++ b/src/box/sql/mem.c
> @@ -678,6 +678,17 @@ int_to_double_forced(struct Mem *mem)
>   	return CMP_OLD_NEW(i, d, int64_t);
>   }
>   
> +static inline int
> +int_to_dec(struct Mem *mem)
> +{
> +	assert(mem->type == MEM_TYPE_INT);
> +	int64_t i = mem->u.i;
> +	decimal_from_int64(&mem->u.d, i);
> +	mem->type = MEM_TYPE_DEC;
> +	mem->flags = 0;
> +	return 0;
> +}
> +
>   static inline int
>   uint_to_double_precise(struct Mem *mem)
>   {
> @@ -709,6 +720,17 @@ uint_to_double_forced(struct Mem *mem)
>   	return CMP_OLD_NEW(u, d, uint64_t);
>   }
>   
> +static inline int
> +uint_to_dec(struct Mem *mem)
> +{
> +	assert(mem->type == MEM_TYPE_UINT);
> +	int64_t u = mem->u.u;
> +	decimal_from_uint64(&mem->u.d, u);
> +	mem->type = MEM_TYPE_DEC;
> +	mem->flags = 0;
> +	return 0;
> +}
> +
>   static inline int
>   int_to_str0(struct Mem *mem)
>   {
> @@ -855,6 +877,19 @@ str_to_double(struct Mem *mem)
>   	return 0;
>   }
>   
> +static inline int
> +str_to_dec(struct Mem *mem)
> +{
> +	assert(mem->type == MEM_TYPE_STR);
> +	decimal_t dec;
> +	decimal_t *d;
> +	d = decimal_from_string(&dec, mem->z);
> +	if (d == NULL)
> +		return -1;
> +	mem_set_dec(mem, &dec);
> +	return 0;
> +}
> +
>   static inline int
>   double_to_int(struct Mem *mem)
>   {
> @@ -988,6 +1023,69 @@ double_to_uint_forced(struct Mem *mem)
>   	return res;
>   }
>   
> +static inline int
> +double_to_dec(struct Mem *mem)
> +{
> +	assert(mem->type == MEM_TYPE_DOUBLE);
> +	double d = mem->u.r;
> +	if (d >= 1e38 || d <= -1e38)
> +		return -1;
> +	decimal_from_double(&mem->u.d, d);
> +	mem->type = MEM_TYPE_DEC;
> +	mem->flags = 0;
> +	return 0;
> +}
> +
> +static inline int
> +double_to_dec_precise(struct Mem *mem)
> +{
> +	assert(mem->type == MEM_TYPE_DOUBLE);
> +	double d = mem->u.r;
> +	if (d >= 1e38 || d <= -1e38)
> +		return -1;
> +	decimal_t dec;
> +	decimal_from_double(&dec, d);
> +	if (atof(decimal_str(&mem->u.d)) != d)
> +		return -1;
> +	mem->u.d = dec;
> +	mem->type = MEM_TYPE_DEC;
> +	mem->flags = 0;
> +	return 0;
> +}
> +
> +/**
> + * Cast MEM with DOUBLE to DECIMAL. Doesn't fail. The return value is < 0
> if
> + * the original value is less than the result, > 0 if the original value
> is
> + * greater than the result, and 0 if the cast is precise.
> + */
> +static inline int
> +double_to_dec_forced(struct Mem *mem)
> +{
> +	assert(mem->type == MEM_TYPE_DOUBLE);
> +	double d = mem->u.r;
> +	mem->type = MEM_TYPE_DEC;
> +	mem->flags = 0;
> +	if (d >= 1e38) {
> +		const char *val =
> "99999999999999999999999999999999999999";
> +		assert(strlen(val) == 38);
> +		decimal_from_string(&mem->u.d, val);
> +		return 1;
> +	}
> +	if (d <= -1e38) {
> +		const char *val =
> "-99999999999999999999999999999999999999";
> +		assert(strlen(val) == 39);
> +		decimal_from_string(&mem->u.d, val);
> +		return -1;
> +	}
> +	decimal_from_double(&mem->u.d, d);
> +	double tmp = atof(decimal_str(&mem->u.d));
> +	if (d > tmp)
> +		return 1;
> +	if (d < tmp)
> +		return -1;
> +	return 0;
> +}
> +
>   static inline int
>   double_to_str0(struct Mem *mem)
>   {
> @@ -1001,6 +1099,173 @@ double_to_str0(struct Mem *mem)
>   	return 0;
>   }
>   
> +static inline int
> +dec_to_int(struct Mem *mem)
> +{
> +	assert(mem->type == MEM_TYPE_DEC);
> +	if (decimal_is_neg(&mem->u.d)) {
> +		int64_t i;
> +		if (decimal_to_int64(&mem->u.d, &i) == NULL)
> +			return -1;
> +		assert(i < 0);
> +		mem->u.i = i;
> +		mem->type = MEM_TYPE_INT;
> +		mem->flags = 0;
> +		return 0;
> +	}
> +	uint64_t u;
> +	if (decimal_to_uint64(&mem->u.d, &u) == NULL)
> +		return -1;
> +	mem->u.u = u;
> +	mem->type = MEM_TYPE_UINT;
> +	mem->flags = 0;
> +	return 0;
> +}
> +
> +static inline int
> +dec_to_int_precise(struct Mem *mem)
> +{
> +	assert(mem->type == MEM_TYPE_DEC);
> +	if (!decimal_is_int(&mem->u.d))
> +		return -1;
> +	return dec_to_int(mem);
> +}
> +
> +static inline int
> +dec_to_int_forced(struct Mem *mem)
> +{
> +	assert(mem->type == MEM_TYPE_DEC);
> +	if (decimal_is_neg(&mem->u.d)) {
> +		int64_t i;
> +		mem->type = MEM_TYPE_INT;
> +		mem->flags = 0;
> +		if (decimal_to_int64(&mem->u.d, &i) == NULL) {
> +			mem->u.i = INT64_MIN;
> +			return -1;
> +		}
> +		assert(i < 0);
> +		mem->u.i = i;
> +		/*
> +		 * Decimal is floored when cast to int, which means that
> after
> +		 * cast it becomes bigger if it was not integer.
> +		 */
> +		return decimal_is_int(&mem->u.d) ? 0 : -1;
> +	}
> +	uint64_t u;
> +	mem->type = MEM_TYPE_UINT;
> +	mem->flags = 0;
> +	if (decimal_to_uint64(&mem->u.d, &u) == NULL) {
> +		mem->u.u = UINT64_MAX;
> +		return 1;
> +	}
> +	mem->u.u = u;
> +	/*
> +	 * Decimal is floored when cast to uint, which means that after
> cast it
> +	 * becomes less if it was not integer.
> +	 */
> +	return decimal_is_int(&mem->u.d) ? 0 : 1;
> +}
> +
> +static inline int
> +dec_to_uint(struct Mem *mem)
> +{
> +	assert(mem->type == MEM_TYPE_DEC);
> +	uint64_t u;
> +	if (decimal_to_uint64(&mem->u.d, &u) == NULL)
> +		return -1;
> +	mem->u.u = u;
> +	mem->type = MEM_TYPE_UINT;
> +	mem->flags = 0;
> +	return 0;
> +}
> +
> +static inline int
> +dec_to_uint_precise(struct Mem *mem)
> +{
> +	assert(mem->type == MEM_TYPE_DEC);
> +	if (!decimal_is_int(&mem->u.d))
> +		return -1;
> +	return dec_to_uint(mem);
> +}
> +
> +static inline int
> +dec_to_uint_forced(struct Mem *mem)
> +{
> +	assert(mem->type == MEM_TYPE_DEC);
> +	uint64_t u;
> +	mem->type = MEM_TYPE_UINT;
> +	mem->flags = 0;
> +	if (decimal_to_uint64(&mem->u.d, &u) == NULL) {
> +		if (decimal_is_neg(&mem->u.d)) {
> +			mem->u.u = 0;
> +			return -1;
> +		}
> +		mem->u.u = UINT64_MAX;
> +		return 1;
> +	}
> +	mem->u.u = u;
> +	/*
> +	 * Decimal is floored when cast to uint, which means that after
> cast if
> +	 * if was not integer it becomes less if it was positive, and move
> if it
> +	 * was negative.
> +	 */
> +	if (decimal_is_int(&mem->u.d))
> +		return 0;
> +	return decimal_is_neg(&mem->u.d) ? -1 : 1;
> +}
> +
> +static inline int
> +dec_to_double(struct Mem *mem)
> +{
> +	assert(mem->type == MEM_TYPE_DEC);
> +	double r = atof(decimal_str(&mem->u.d));
> +	mem->u.r = r;
> +	mem->type = MEM_TYPE_DOUBLE;
> +	mem->flags = 0;
> +	return 0;
> +}
> +
> +static inline int
> +dec_to_double_precise(struct Mem *mem)
> +{
> +	assert(mem->type == MEM_TYPE_DEC);
> +	double r = atof(decimal_str(&mem->u.d));
> +	decimal_t d;
> +	decimal_t *dec = decimal_from_double(&d, r);
> +	if (dec == NULL || decimal_compare(dec, &mem->u.d) != 0)
> +		return -1;
> +	mem->u.r = r;
> +	mem->type = MEM_TYPE_DOUBLE;
> +	mem->flags = 0;
> +	return 0;
> +}
> +
> +static inline int
> +dec_to_double_forced(struct Mem *mem)
> +{
> +	assert(mem->type == MEM_TYPE_DEC);
> +	mem->type = MEM_TYPE_DOUBLE;
> +	mem->flags = 0;
> +	double r = atof(decimal_str(&mem->u.d));
> +	int res;
> +	decimal_t d;
> +	if (r <= -1e38)
> +		res = 1;
> +	else if (r >= 1e38)
> +		res = -1;
> +	else
> +		res = decimal_compare(&mem->u.d, decimal_from_double(&d,
> r));
> +	mem->u.r = r;
> +	return res;
> +}
> +
> +static inline int
> +dec_to_str0(struct Mem *mem)
> +{
> +	assert(mem->type == MEM_TYPE_DEC);
> +	return mem_copy_str0(mem, decimal_str(&mem->u.d));
> +}
> +
>   static inline int
>   bool_to_str0(struct Mem *mem)
>   {
> @@ -1053,6 +1318,8 @@ mem_to_int(struct Mem *mem)
>   		return str_to_int(mem);
>   	if (mem->type == MEM_TYPE_DOUBLE)
>   		return double_to_int(mem);
> +	if (mem->type == MEM_TYPE_DEC)
> +		return dec_to_int(mem);
>   	return -1;
>   }
>   
> @@ -1068,6 +1335,8 @@ mem_to_int_precise(struct Mem *mem)
>   		return str_to_int(mem);
>   	if (mem->type == MEM_TYPE_DOUBLE)
>   		return double_to_int_precise(mem);
> +	if (mem->type == MEM_TYPE_DEC)
> +		return dec_to_int(mem);
>   	return -1;
>   }
>   
> @@ -1083,6 +1352,8 @@ mem_to_double(struct Mem *mem)
>   		return int_to_double(mem);
>   	if (mem->type == MEM_TYPE_STR)
>   		return str_to_double(mem);
> +	if (mem->type == MEM_TYPE_DEC)
> +		return dec_to_double(mem);
>   	return -1;
>   }
>   
> @@ -1129,6 +1400,8 @@ mem_to_str0(struct Mem *mem)
>   		return array_to_str0(mem);
>   	case MEM_TYPE_UUID:
>   		return uuid_to_str0(mem);
> +	case MEM_TYPE_DEC:
> +		return dec_to_str0(mem);
>   	default:
>   		return -1;
>   	}
> @@ -1157,6 +1430,8 @@ mem_to_str(struct Mem *mem)
>   		return array_to_str0(mem);
>   	case MEM_TYPE_UUID:
>   		return uuid_to_str0(mem);
> +	case MEM_TYPE_DEC:
> +		return dec_to_str0(mem);
>   	default:
>   		return -1;
>   	}
> @@ -1177,6 +1452,8 @@ mem_cast_explicit(struct Mem *mem, enum field_type
> type)
>   			return str_to_uint(mem);
>   		case MEM_TYPE_DOUBLE:
>   			return double_to_uint(mem);
> +		case MEM_TYPE_DEC:
> +			return dec_to_uint(mem);
>   		default:
>   			return -1;
>   		}
> @@ -1209,9 +1486,21 @@ mem_cast_explicit(struct Mem *mem, enum field_type
> type)
>   	case FIELD_TYPE_NUMBER:
>   		return mem_to_number(mem);
>   	case FIELD_TYPE_DECIMAL:
> -		if (mem->type == MEM_TYPE_DEC)
> +		switch (mem->type) {
> +		case MEM_TYPE_INT:
> +			return int_to_dec(mem);
> +		case MEM_TYPE_UINT:
> +			return uint_to_dec(mem);
> +		case MEM_TYPE_STR:
> +			return str_to_dec(mem);
> +		case MEM_TYPE_DOUBLE:
> +			return double_to_dec(mem);
> +		case MEM_TYPE_DEC:
> +			mem->flags = 0;
>   			return 0;
> -		return -1;
> +		default:
> +			return -1;
> +		}

For consistency with majority of other numeric directions (mem_to_int(), 
mem_to_number(), mem_to_double()) I'd extract this code similarly to 
mem_to_decimal(). But this is up to you...


>   	case FIELD_TYPE_UUID:
>   		if (mem->type == MEM_TYPE_UUID) {
>   			mem->flags = 0;
...

> @@ -1296,9 +1591,19 @@ mem_cast_implicit(struct Mem *mem, enum field_type
> type)
>   		mem->flags = MEM_Number;
>   		return 0;
>   	case FIELD_TYPE_DECIMAL:
> -		if (mem->type == MEM_TYPE_DEC)
> +		switch (mem->type) {
> +		case MEM_TYPE_INT:
> +			return int_to_dec(mem);
> +		case MEM_TYPE_UINT:
> +			return uint_to_dec(mem);
> +		case MEM_TYPE_DOUBLE:
> +			return double_to_dec_precise(mem);
> +		case MEM_TYPE_DEC:
> +			mem->flags = 0;
>   			return 0;
> -		return -1;
> +		default:
> +			return -1;
> +		}
>   	case FIELD_TYPE_MAP:
>   		if (mem->type == MEM_TYPE_MAP)
>   			return 0;
> @@ -1343,6 +1648,8 @@ mem_cast_implicit_number(struct Mem *mem, enum
> field_type type)
>   			return -1;
>   		case MEM_TYPE_DOUBLE:
>   			return double_to_uint_forced(mem);
> +		case MEM_TYPE_DEC:
> +			return dec_to_uint_forced(mem);
>   		default:
>   			unreachable();
>   		}
> @@ -1353,6 +1660,8 @@ mem_cast_implicit_number(struct Mem *mem, enum
> field_type type)
>   			return int_to_double_forced(mem);
>   		case MEM_TYPE_UINT:
>   			return uint_to_double_forced(mem);
> +		case MEM_TYPE_DEC:
> +			return dec_to_double_forced(mem);
>   		case MEM_TYPE_DOUBLE:
>   			mem->flags = 0;
>   			return 0;
> @@ -1368,6 +1677,23 @@ mem_cast_implicit_number(struct Mem *mem, enum
> field_type type)
>   			return 0;
>   		case MEM_TYPE_DOUBLE:
>   			return double_to_int_forced(mem);
> +		case MEM_TYPE_DEC:
> +			return dec_to_int_forced(mem);
> +		default:
> +			unreachable();
> +		}
> +		break;
> +	case FIELD_TYPE_DECIMAL:
> +		switch (mem->type) {
> +		case MEM_TYPE_INT:
> +			return int_to_dec(mem);
> +		case MEM_TYPE_UINT:
> +			return uint_to_dec(mem);
> +		case MEM_TYPE_DEC:
> +			mem->flags = 0;
> +			return 0;
> +		case MEM_TYPE_DOUBLE:
> +			return double_to_dec_forced(mem);
>   		default:
>   			unreachable();
>   		}

Otherwise great patch, with the expected extension to explicit/implicit 
casts tables.

Thanks,
Timur

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [Tarantool-patches] [PATCH v1 4/4] sql: introduce decimal to arithmetic
  2021-08-16 15:57 ` [Tarantool-patches] [PATCH v1 4/4] sql: introduce decimal to arithmetic Mergen Imeev via Tarantool-patches
@ 2021-08-16 19:48   ` 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
  0 siblings, 2 replies; 16+ messages in thread
From: Safin Timur via Tarantool-patches @ 2021-08-16 19:48 UTC (permalink / raw)
  To: imeevma, korablev, Serge Petrenko; +Cc: tarantool-patches



On 16.08.2021 18:57, Mergen Imeev via Tarantool-patches wrote:
> 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;
> +	}

1st question is - was it intentionally that you call here atof, while 
few lines belowe we use sqlAtoF?

2nd complain is - it all looks that we miss decimal_to_double() function 
in src/core/decimal.c (where we do have decimal_from_double() but not 
the reverse direction). It will look more consistent if this 
implementation would be there. And, I stll believe, there is better way 
for converting decimal to double, than converting it to string, and then 
to double.

[Though quick navigation over decNumber API didn't reveal the direct 
approach. Serge, could you please recommend the easiest way here?]


> @@ -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)) {

And now it looks better than before, and less confusing, I do not have 
complains here anymore...


>   		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)) {

And here.

>   		diag_set(ClientError, ER_SQL_TYPE_MISMATCH, mem_str(b),
>   			 "integer, unsigned or double");
>   		return -1;

Thanks,
Timur

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [Tarantool-patches] [PATCH v1 4/4] sql: introduce decimal to arithmetic
  2021-08-16 19:48   ` 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
  1 sibling, 0 replies; 16+ messages in thread
From: Serge Petrenko via Tarantool-patches @ 2021-08-17 12:23 UTC (permalink / raw)
  To: Safin Timur, imeevma, korablev; +Cc: tarantool-patches



16.08.2021 22:48, Safin Timur пишет:
>
>
> On 16.08.2021 18:57, Mergen Imeev via Tarantool-patches wrote:
>> 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;
>> +    }
>
> 1st question is - was it intentionally that you call here atof, while 
> few lines belowe we use sqlAtoF?
>
> 2nd complain is - it all looks that we miss decimal_to_double() 
> function in src/core/decimal.c (where we do have decimal_from_double() 
> but not the reverse direction). It will look more consistent if this 
> implementation would be there. And, I stll believe, there is better 
> way for converting decimal to double, than converting it to string, 
> and then to double.
>
> [Though quick navigation over decNumber API didn't reveal the direct 
> approach. Serge, could you please recommend the easiest way here?]

I agree, it would be best to introduce decimal_to_double() to 
src/lib/core/decimal

I can't think of an easy way here. The only one I see is construct the 
double value
digit by digit. This mustn't be too different from 
decimal->string->double conversion.

>
>
>> @@ -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)) {
>
> And now it looks better than before, and less confusing, I do not have 
> complains here anymore...
>
>
>> 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)) {
>
> And here.
>
>> diag_set(ClientError, ER_SQL_TYPE_MISMATCH, mem_str(b),
>>                "integer, unsigned or double");
>>           return -1;
>
> Thanks,
> Timur

-- 
Serge Petrenko


^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [Tarantool-patches] [PATCH v1 2/4] sql: introduce field type decimal
  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
  0 siblings, 1 reply; 16+ messages in thread
From: Mergen Imeev via Tarantool-patches @ 2021-08-18 13:01 UTC (permalink / raw)
  To: Safin Timur; +Cc: tarantool-patches

Hi! Thank ypu fpr the review! My answers and diff below.

On Mon, Aug 16, 2021 at 10:22:42PM +0300, Safin Timur wrote:
> Please see below several notes...
> 
> On 16.08.2021 18:57, Mergen Imeev via Tarantool-patches wrote:
> > This patch introduces a decimal field type. However, implicit and
> > explicit casts and arithmetic operations for this type will be presented
> > in next few patches. Literals also will be introduced later.
> > 
> > Part of #4415
> > ---
> >   extra/mkkeywordhash.c                         |   2 +-
> >   src/box/sql/expr.c                            |   3 +
> >   src/box/sql/func.c                            |   4 +
> >   src/box/sql/mem.c                             | 173 +++++--
> >   src/box/sql/mem.h                             |  18 +-
> >   src/box/sql/parse.y                           |   1 +
> >   src/box/sql/sqlInt.h                          |   1 +
> >   test/sql-tap/CMakeLists.txt                   |   1 +
> >   test/sql-tap/decimal.c                        |  48 ++
> >   test/sql-tap/decimal.test.lua                 | 441 ++++++++++++++++++
> >   test/sql-tap/engine.cfg                       |   3 +
> >   .../gh-5913-segfault-on-select-uuid.test.lua  |  83 ----
> >   .../sql-tap/gh-6024-funcs-return-bin.test.lua |   8 +-
> >   13 files changed, 661 insertions(+), 125 deletions(-)
> >   create mode 100644 test/sql-tap/decimal.c
> >   create mode 100755 test/sql-tap/decimal.test.lua
> >   delete mode 100755 test/sql-tap/gh-5913-segfault-on-select-uuid.test.lua
> > 
> > diff --git a/extra/mkkeywordhash.c b/extra/mkkeywordhash.c
> > index 0d998506c..1c9d12295 100644
> > --- a/extra/mkkeywordhash.c
> > +++ b/extra/mkkeywordhash.c
> > @@ -196,7 +196,7 @@ static Keyword aKeywordTable[] = {
> >     { "CURRENT_TIMESTAMP",      "TK_STANDARD",    true  },
> >     { "DATE",                   "TK_STANDARD",    true  },
> >     { "DATETIME",               "TK_STANDARD",    true  },
> > -  { "DECIMAL",                "TK_STANDARD",    true  },
> > +  { "DECIMAL",                "TK_DECIMAL",     true  },
> 
> DEC is standard alias to DECIMAL. We should support that (similarly to
> INTEGER vs INT).
> 
Added.

> >     { "DECLARE",                "TK_STANDARD",    true  },
> >     { "DENSE_RANK",             "TK_STANDARD",    true  },
> >     { "DESCRIBE",               "TK_STANDARD",    true  },
> > diff --git a/src/box/sql/expr.c b/src/box/sql/expr.c
> > index c67a7091c..275dbc5ba 100644
> > --- a/src/box/sql/expr.c
> > +++ b/src/box/sql/expr.c
> 
> ...
> 
> > diff --git a/src/box/sql/mem.c b/src/box/sql/mem.c
> > index 066940fac..016f0e80b 100644
> > --- a/src/box/sql/mem.c
> > +++ b/src/box/sql/mem.c
> > @@ -1191,6 +1208,10 @@ mem_cast_explicit(struct Mem *mem, enum field_type
> > type)
> >   		return -1;
> >   	case FIELD_TYPE_NUMBER:
> >   		return mem_to_number(mem);
> > +	case FIELD_TYPE_DECIMAL:
> > +		if (mem->type == MEM_TYPE_DEC)
> > +			return 0;
> > +		return -1;
> 
> So CAST(expr as DECIMAL) will only work for DECIMALs? It should be rather
> behaving similar to other numeric types.
> 
Added in another patch.

> >   	case FIELD_TYPE_UUID:
> >   		if (mem->type == MEM_TYPE_UUID) {
> >   			mem->flags = 0;
> > @@ -1274,6 +1295,10 @@ mem_cast_implicit(struct Mem *mem, enum field_type
> > type)
> >   			return -1;
> >   		mem->flags = MEM_Number;
> >   		return 0;
> > +	case FIELD_TYPE_DECIMAL:
> > +		if (mem->type == MEM_TYPE_DEC)
> > +			return 0;
> > +		return -1;
> 
> Same question as above - implicit conversions to decimal should be
> numeric-like.
> 
> 
Added in another patch.

> >   	case FIELD_TYPE_MAP:
> >   		if (mem->type == MEM_TYPE_MAP)
> >   			return 0;
> > @@ -1595,12 +1620,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)) {
> > +	if (!mem_is_num(a) || mem_is_metatype(a) || a->type ==
> > MEM_TYPE_DEC) {
> >   		diag_set(ClientError, ER_SQL_TYPE_MISMATCH, mem_str(a),
> >   			 "integer, unsigned or double");
> >   		return -1;
> 
> I don't understant - we would raise if not a numeric (and decimal is part of
> numeric) or decimal specifically? So you do not want arithmetic types with
> decimals?
> 
> 
Added in another patch.

> >   	}
> > -	if (!mem_is_num(b) || mem_is_metatype(b)) {
> > +	if (!mem_is_num(b) || mem_is_metatype(b) || b->type ==
> > MEM_TYPE_DEC) {
> 
> The same confusion as above..
> 
Added in another patch.

> >   		diag_set(ClientError, ER_SQL_TYPE_MISMATCH, mem_str(b),
> >   			 "integer, unsigned or double");
> >   		return -1;
> > @@ -2926,26 +3040,10 @@ port_lua_get_vdbemem(struct port *base, uint32_t
> > *size)
> >   		case MP_EXT: {
> >   			assert(field.ext_type == MP_UUID ||
> >   			       field.ext_type == MP_DECIMAL);
> > -			char *buf;
> > -			uint32_t size;
> > -			uint32_t svp = region_used(&fiber()->gc);
> > -			if (field.ext_type == MP_UUID) {
> > +			if (field.ext_type == MP_UUID)
> >   				mem_set_uuid(&val[i], field.uuidval);
> > -				break;
> > -			} else {
> > -				size = mp_sizeof_decimal(field.decval);
> > -				buf = region_alloc(&fiber()->gc, size);
> > -				if (buf == NULL) {
> > -					diag_set(OutOfMemory, size,
> > -						 "region_alloc", "buf");
> > -					goto error;
> > -				}
> > -				mp_encode_decimal(buf, field.decval);
> > -			}
> > -			int rc = mem_copy_bin(&val[i], buf, size);
> > -			region_truncate(&fiber()->gc, svp);
> > -			if (rc != 0)
> > -				goto error;
> > +			else
> > +				mem_set_dec(&val[i], field.decval);
> 
> Nice! Now it's much compacter and is more readable than before!
> 
> > diff --git a/src/box/sql/parse.y b/src/box/sql/parse.y
> > index bd041e862..436c98cd9 100644
> > --- a/src/box/sql/parse.y
> > +++ b/src/box/sql/parse.y
> > @@ -1863,6 +1863,7 @@ number_typedef(A) ::= NUMBER . { A.type =
> > FIELD_TYPE_NUMBER; }
> >   number_typedef(A) ::= DOUBLE . { A.type = FIELD_TYPE_DOUBLE; }
> >   number_typedef(A) ::= INT|INTEGER_KW . { A.type = FIELD_TYPE_INTEGER; }
> >   number_typedef(A) ::= UNSIGNED . { A.type = FIELD_TYPE_UNSIGNED; }
> > +number_typedef(A) ::= DECIMAL . { A.type = FIELD_TYPE_DECIMAL; }
> 
> Here please add alias to DEC, as it was done with INT.
> 
Added, test fixed.

> > diff --git a/test/sql-tap/CMakeLists.txt b/test/sql-tap/CMakeLists.txt
> > index bd2b9f33f..87f23b2f7 100644
> > --- a/test/sql-tap/CMakeLists.txt
> > +++ b/test/sql-tap/CMakeLists.txt
> > @@ -2,3 +2,4 @@ include_directories(${MSGPUCK_INCLUDE_DIRS})
> >   build_module(gh-5938-wrong-string-length gh-5938-wrong-string-length.c)
> >   build_module(gh-6024-funcs-return-bin gh-6024-funcs-return-bin.c)
> >   build_module(sql_uuid sql_uuid.c)
> > +build_module(decimal decimal.c)
> > diff --git a/test/sql-tap/decimal.c b/test/sql-tap/decimal.c
> > new file mode 100644
> > index 000000000..4d9d1ce19
> > --- /dev/null
> > +++ b/test/sql-tap/decimal.c
> > @@ -0,0 +1,48 @@
> > +#include "msgpuck.h"
> > +#include "module.h"
> > +#include "mp_decimal.h"
> > +#include "mp_extension_types.h"
> > +
> > +enum {
> > +	BUF_SIZE = 512,
> > +};
> > +
> > +int
> > +is_dec(box_function_ctx_t *ctx, const char *args, const char *args_end)
> > +{
> > +	(void)args_end;
> > +	uint32_t arg_count = mp_decode_array(&args);
> > +	if (arg_count != 1) {
> > +		return box_error_set(__FILE__, __LINE__, ER_PROC_C,
> > +				     "invalid argument count");
> > +	}
> > +	bool is_uuid;
> > +	if (mp_typeof(*args) == MP_EXT) {
> > +		const char *str = args;
> > +		int8_t type;
> > +		mp_decode_extl(&str, &type);
> > +		is_uuid = type == MP_DECIMAL;
> > +	} else {
> > +		is_uuid = false;
> 
> Here we see remnants from copy-paste from uuid related code, I assume you
> meant that variable should be named `is_decimal`.
> 
True, fixed.

> > +	}
> > +
> > +	char res[BUF_SIZE];
> > +	memset(res, '\0', BUF_SIZE);
> > +	char *end = mp_encode_bool(res, is_uuid);
> > +	box_return_mp(ctx, res, end);
> > +	return 0;
> > +}
> > +
> > +int
> > diff --git a/test/sql-tap/engine.cfg b/test/sql-tap/engine.cfg
> > index 820c72b00..511d0a716 100644
> > --- a/test/sql-tap/engine.cfg
> > +++ b/test/sql-tap/engine.cfg
> > @@ -26,6 +26,9 @@
> >       "metatypes.test.lua": {
> >           "memtx": {"engine": "memtx"}
> >       },
> > +    "decimal.test.lua": {
> > +        "memtx": {"engine": "memtx"}
> > +    },
> 
> BTW, why this exception for the test?
> 
I see no reason to test both vinyl and memtx.

> >       "gh-4077-iproto-execute-no-bind.test.lua": {},
> >       "*": {
> >           "memtx": {"engine": "memtx"},
> 
> Thanks,
> Timur


Diff:


diff --git a/extra/mkkeywordhash.c b/extra/mkkeywordhash.c
index 1c9d12295..3e4200417 100644
--- a/extra/mkkeywordhash.c
+++ b/extra/mkkeywordhash.c
@@ -196,6 +196,7 @@ static Keyword aKeywordTable[] = {
   { "CURRENT_TIMESTAMP",      "TK_STANDARD",    true  },
   { "DATE",                   "TK_STANDARD",    true  },
   { "DATETIME",               "TK_STANDARD",    true  },
+  { "DEC",                    "TK_DECIMAL",     true  },
   { "DECIMAL",                "TK_DECIMAL",     true  },
   { "DECLARE",                "TK_STANDARD",    true  },
   { "DENSE_RANK",             "TK_STANDARD",    true  },
diff --git a/test/sql-tap/decimal.c b/test/sql-tap/decimal.c
index 4d9d1ce19..fd7c3e0c9 100644
--- a/test/sql-tap/decimal.c
+++ b/test/sql-tap/decimal.c
@@ -16,19 +16,19 @@ is_dec(box_function_ctx_t *ctx, const char *args, const char *args_end)
 		return box_error_set(__FILE__, __LINE__, ER_PROC_C,
 				     "invalid argument count");
 	}
-	bool is_uuid;
+	bool is_dec;
 	if (mp_typeof(*args) == MP_EXT) {
 		const char *str = args;
 		int8_t type;
 		mp_decode_extl(&str, &type);
-		is_uuid = type == MP_DECIMAL;
+		is_dec = type == MP_DECIMAL;
 	} else {
-		is_uuid = false;
+		is_dec = false;
 	}
 
 	char res[BUF_SIZE];
 	memset(res, '\0', BUF_SIZE);
-	char *end = mp_encode_bool(res, is_uuid);
+	char *end = mp_encode_bool(res, is_dec);
 	box_return_mp(ctx, res, end);
 	return 0;
 }
diff --git a/test/sql-tap/decimal.test.lua b/test/sql-tap/decimal.test.lua
index dd69ca370..10217a806 100755
--- a/test/sql-tap/decimal.test.lua
+++ b/test/sql-tap/decimal.test.lua
@@ -9,16 +9,26 @@ local dec = require("decimal")
 local dec1 = dec.new("111")
 local dec2 = dec.new("55555")
 local dec3 = dec.new("3333")
+local dec4 = dec.new("-13")
+local dec5 = dec.new("0")
+local dec6 = dec.new("-0")
 
 -- Check that it is possible to create spaces with DECIMAL field.
 test:do_execsql_test(
     "dec-1",
     [[
-        CREATE TABLE t1 (i INT PRIMARY KEY, u DECIMAL);
+        CREATE TABLE t0 (i INT PRIMARY KEY, u DEC);
+        CREATE TABLE t1 (i INT PRIMARY KEY, u DEC);
         CREATE TABLE t2 (u DECIMAL PRIMARY KEY);
     ]], {
     })
 
+box.space.T0:insert({1, dec1})
+box.space.T0:insert({2, dec2})
+box.space.T0:insert({3, dec3})
+box.space.T0:insert({4, dec4})
+box.space.T0:insert({5, dec5})
+box.space.T0:insert({6, dec6})
 box.space.T1:insert({1, dec1})
 box.space.T1:insert({2, dec2})
 box.space.T1:insert({3, dec3})
@@ -33,9 +43,9 @@ box.space.T2:insert({dec3})
 test:do_execsql_test(
     "dec-2.1.1",
     [[
-        SELECT * FROM t1;
+        SELECT * FROM t0;
     ]], {
-        1, dec1, 2, dec2, 3, dec3, 4, dec1, 5, dec1, 6, dec2
+        1, dec1, 2, dec2, 3, dec3, 4, dec4, 5, dec5, 6, dec6
     })
 
 test:do_execsql_test(
@@ -50,17 +60,17 @@ test:do_execsql_test(
 test:do_execsql_test(
     "dec-2.2.1",
     [[
-        SELECT * FROM t1 ORDER BY u;
+        SELECT * FROM t0 ORDER BY u;
     ]], {
-        1, dec1, 4, dec1, 5, dec1, 3, dec3, 2, dec2, 6, dec2
+        4, dec4, 5, dec5, 6, dec6, 1, dec1, 3, dec3, 2, dec2
     })
 
 test:do_execsql_test(
     "dec-2.2.2",
     [[
-        SELECT * FROM t1 ORDER BY u DESC;
+        SELECT * FROM t0 ORDER BY u DESC;
     ]], {
-        2, dec2, 6, dec2, 3, dec3, 1, dec1, 4, dec1, 5, dec1
+        2, dec2, 3, dec3, 1, dec1, 5, dec5, 6, dec6, 4, dec4
     })
 
 test:do_execsql_test(

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [Tarantool-patches] [PATCH v1 3/4] sql: introduce cast for decimal
  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
  0 siblings, 1 reply; 16+ messages in thread
From: Mergen Imeev via Tarantool-patches @ 2021-08-18 13:29 UTC (permalink / raw)
  To: Safin Timur; +Cc: tarantool-patches

Thank you for the review! My answer and diff below.

On Mon, Aug 16, 2021 at 10:34:16PM +0300, Safin Timur wrote:
> Oh, now I see the reasonable implicit and explicit casts table for decimals.
> I disavow my prior complain about missing directions for CAST(exprt AS
> DECIMAL).
> 
> Below there is minor complain though...
> 
> On 16.08.2021 18:57, Mergen Imeev via Tarantool-patches wrote:
> > This patch introduces implicit and explicit cast of the DECIMAL field in
> > SQL. Implicit cast to and from DECIMAL is precise. Explicit conversion
> > to decimal and back could lead to loss of precision.
> > 
> > Part of #4415
> > ---
> >   src/box/sql/mem.c             | 334 ++++++++++++++++++++++++++++++-
> >   test/sql-tap/decimal.test.lua | 366 +++++++++++++++++++++++++++++++++-
> >   2 files changed, 695 insertions(+), 5 deletions(-)
> > 
> > diff --git a/src/box/sql/mem.c b/src/box/sql/mem.c
> > index 016f0e80b..ff8b40d7f 100644
> > --- a/src/box/sql/mem.c
> > +++ b/src/box/sql/mem.c
> > @@ -678,6 +678,17 @@ int_to_double_forced(struct Mem *mem)
> >   	return CMP_OLD_NEW(i, d, int64_t);
> >   }
> > +static inline int
> > +int_to_dec(struct Mem *mem)
> > +{
> > +	assert(mem->type == MEM_TYPE_INT);
> > +	int64_t i = mem->u.i;
> > +	decimal_from_int64(&mem->u.d, i);
> > +	mem->type = MEM_TYPE_DEC;
> > +	mem->flags = 0;
> > +	return 0;
> > +}
> > +
> >   static inline int
> >   uint_to_double_precise(struct Mem *mem)
> >   {
> > @@ -709,6 +720,17 @@ uint_to_double_forced(struct Mem *mem)
> >   	return CMP_OLD_NEW(u, d, uint64_t);
> >   }
> > +static inline int
> > +uint_to_dec(struct Mem *mem)
> > +{
> > +	assert(mem->type == MEM_TYPE_UINT);
> > +	int64_t u = mem->u.u;
> > +	decimal_from_uint64(&mem->u.d, u);
> > +	mem->type = MEM_TYPE_DEC;
> > +	mem->flags = 0;
> > +	return 0;
> > +}
> > +
> >   static inline int
> >   int_to_str0(struct Mem *mem)
> >   {
> > @@ -855,6 +877,19 @@ str_to_double(struct Mem *mem)
> >   	return 0;
> >   }
> > +static inline int
> > +str_to_dec(struct Mem *mem)
> > +{
> > +	assert(mem->type == MEM_TYPE_STR);
> > +	decimal_t dec;
> > +	decimal_t *d;
> > +	d = decimal_from_string(&dec, mem->z);
> > +	if (d == NULL)
> > +		return -1;
> > +	mem_set_dec(mem, &dec);
> > +	return 0;
> > +}
> > +
> >   static inline int
> >   double_to_int(struct Mem *mem)
> >   {
> > @@ -988,6 +1023,69 @@ double_to_uint_forced(struct Mem *mem)
> >   	return res;
> >   }
> > +static inline int
> > +double_to_dec(struct Mem *mem)
> > +{
> > +	assert(mem->type == MEM_TYPE_DOUBLE);
> > +	double d = mem->u.r;
> > +	if (d >= 1e38 || d <= -1e38)
> > +		return -1;
> > +	decimal_from_double(&mem->u.d, d);
> > +	mem->type = MEM_TYPE_DEC;
> > +	mem->flags = 0;
> > +	return 0;
> > +}
> > +
> > +static inline int
> > +double_to_dec_precise(struct Mem *mem)
> > +{
> > +	assert(mem->type == MEM_TYPE_DOUBLE);
> > +	double d = mem->u.r;
> > +	if (d >= 1e38 || d <= -1e38)
> > +		return -1;
> > +	decimal_t dec;
> > +	decimal_from_double(&dec, d);
> > +	if (atof(decimal_str(&mem->u.d)) != d)
> > +		return -1;
> > +	mem->u.d = dec;
> > +	mem->type = MEM_TYPE_DEC;
> > +	mem->flags = 0;
> > +	return 0;
> > +}
> > +
> > +/**
> > + * Cast MEM with DOUBLE to DECIMAL. Doesn't fail. The return value is < 0
> > if
> > + * the original value is less than the result, > 0 if the original value
> > is
> > + * greater than the result, and 0 if the cast is precise.
> > + */
> > +static inline int
> > +double_to_dec_forced(struct Mem *mem)
> > +{
> > +	assert(mem->type == MEM_TYPE_DOUBLE);
> > +	double d = mem->u.r;
> > +	mem->type = MEM_TYPE_DEC;
> > +	mem->flags = 0;
> > +	if (d >= 1e38) {
> > +		const char *val =
> > "99999999999999999999999999999999999999";
> > +		assert(strlen(val) == 38);
> > +		decimal_from_string(&mem->u.d, val);
> > +		return 1;
> > +	}
> > +	if (d <= -1e38) {
> > +		const char *val =
> > "-99999999999999999999999999999999999999";
> > +		assert(strlen(val) == 39);
> > +		decimal_from_string(&mem->u.d, val);
> > +		return -1;
> > +	}
> > +	decimal_from_double(&mem->u.d, d);
> > +	double tmp = atof(decimal_str(&mem->u.d));
> > +	if (d > tmp)
> > +		return 1;
> > +	if (d < tmp)
> > +		return -1;
> > +	return 0;
> > +}
> > +
> >   static inline int
> >   double_to_str0(struct Mem *mem)
> >   {
> > @@ -1001,6 +1099,173 @@ double_to_str0(struct Mem *mem)
> >   	return 0;
> >   }
> > +static inline int
> > +dec_to_int(struct Mem *mem)
> > +{
> > +	assert(mem->type == MEM_TYPE_DEC);
> > +	if (decimal_is_neg(&mem->u.d)) {
> > +		int64_t i;
> > +		if (decimal_to_int64(&mem->u.d, &i) == NULL)
> > +			return -1;
> > +		assert(i < 0);
> > +		mem->u.i = i;
> > +		mem->type = MEM_TYPE_INT;
> > +		mem->flags = 0;
> > +		return 0;
> > +	}
> > +	uint64_t u;
> > +	if (decimal_to_uint64(&mem->u.d, &u) == NULL)
> > +		return -1;
> > +	mem->u.u = u;
> > +	mem->type = MEM_TYPE_UINT;
> > +	mem->flags = 0;
> > +	return 0;
> > +}
> > +
> > +static inline int
> > +dec_to_int_precise(struct Mem *mem)
> > +{
> > +	assert(mem->type == MEM_TYPE_DEC);
> > +	if (!decimal_is_int(&mem->u.d))
> > +		return -1;
> > +	return dec_to_int(mem);
> > +}
> > +
> > +static inline int
> > +dec_to_int_forced(struct Mem *mem)
> > +{
> > +	assert(mem->type == MEM_TYPE_DEC);
> > +	if (decimal_is_neg(&mem->u.d)) {
> > +		int64_t i;
> > +		mem->type = MEM_TYPE_INT;
> > +		mem->flags = 0;
> > +		if (decimal_to_int64(&mem->u.d, &i) == NULL) {
> > +			mem->u.i = INT64_MIN;
> > +			return -1;
> > +		}
> > +		assert(i < 0);
> > +		mem->u.i = i;
> > +		/*
> > +		 * Decimal is floored when cast to int, which means that
> > after
> > +		 * cast it becomes bigger if it was not integer.
> > +		 */
> > +		return decimal_is_int(&mem->u.d) ? 0 : -1;
> > +	}
> > +	uint64_t u;
> > +	mem->type = MEM_TYPE_UINT;
> > +	mem->flags = 0;
> > +	if (decimal_to_uint64(&mem->u.d, &u) == NULL) {
> > +		mem->u.u = UINT64_MAX;
> > +		return 1;
> > +	}
> > +	mem->u.u = u;
> > +	/*
> > +	 * Decimal is floored when cast to uint, which means that after
> > cast it
> > +	 * becomes less if it was not integer.
> > +	 */
> > +	return decimal_is_int(&mem->u.d) ? 0 : 1;
> > +}
> > +
> > +static inline int
> > +dec_to_uint(struct Mem *mem)
> > +{
> > +	assert(mem->type == MEM_TYPE_DEC);
> > +	uint64_t u;
> > +	if (decimal_to_uint64(&mem->u.d, &u) == NULL)
> > +		return -1;
> > +	mem->u.u = u;
> > +	mem->type = MEM_TYPE_UINT;
> > +	mem->flags = 0;
> > +	return 0;
> > +}
> > +
> > +static inline int
> > +dec_to_uint_precise(struct Mem *mem)
> > +{
> > +	assert(mem->type == MEM_TYPE_DEC);
> > +	if (!decimal_is_int(&mem->u.d))
> > +		return -1;
> > +	return dec_to_uint(mem);
> > +}
> > +
> > +static inline int
> > +dec_to_uint_forced(struct Mem *mem)
> > +{
> > +	assert(mem->type == MEM_TYPE_DEC);
> > +	uint64_t u;
> > +	mem->type = MEM_TYPE_UINT;
> > +	mem->flags = 0;
> > +	if (decimal_to_uint64(&mem->u.d, &u) == NULL) {
> > +		if (decimal_is_neg(&mem->u.d)) {
> > +			mem->u.u = 0;
> > +			return -1;
> > +		}
> > +		mem->u.u = UINT64_MAX;
> > +		return 1;
> > +	}
> > +	mem->u.u = u;
> > +	/*
> > +	 * Decimal is floored when cast to uint, which means that after
> > cast if
> > +	 * if was not integer it becomes less if it was positive, and move
> > if it
> > +	 * was negative.
> > +	 */
> > +	if (decimal_is_int(&mem->u.d))
> > +		return 0;
> > +	return decimal_is_neg(&mem->u.d) ? -1 : 1;
> > +}
> > +
> > +static inline int
> > +dec_to_double(struct Mem *mem)
> > +{
> > +	assert(mem->type == MEM_TYPE_DEC);
> > +	double r = atof(decimal_str(&mem->u.d));
> > +	mem->u.r = r;
> > +	mem->type = MEM_TYPE_DOUBLE;
> > +	mem->flags = 0;
> > +	return 0;
> > +}
> > +
> > +static inline int
> > +dec_to_double_precise(struct Mem *mem)
> > +{
> > +	assert(mem->type == MEM_TYPE_DEC);
> > +	double r = atof(decimal_str(&mem->u.d));
> > +	decimal_t d;
> > +	decimal_t *dec = decimal_from_double(&d, r);
> > +	if (dec == NULL || decimal_compare(dec, &mem->u.d) != 0)
> > +		return -1;
> > +	mem->u.r = r;
> > +	mem->type = MEM_TYPE_DOUBLE;
> > +	mem->flags = 0;
> > +	return 0;
> > +}
> > +
> > +static inline int
> > +dec_to_double_forced(struct Mem *mem)
> > +{
> > +	assert(mem->type == MEM_TYPE_DEC);
> > +	mem->type = MEM_TYPE_DOUBLE;
> > +	mem->flags = 0;
> > +	double r = atof(decimal_str(&mem->u.d));
> > +	int res;
> > +	decimal_t d;
> > +	if (r <= -1e38)
> > +		res = 1;
> > +	else if (r >= 1e38)
> > +		res = -1;
> > +	else
> > +		res = decimal_compare(&mem->u.d, decimal_from_double(&d,
> > r));
> > +	mem->u.r = r;
> > +	return res;
> > +}
> > +
> > +static inline int
> > +dec_to_str0(struct Mem *mem)
> > +{
> > +	assert(mem->type == MEM_TYPE_DEC);
> > +	return mem_copy_str0(mem, decimal_str(&mem->u.d));
> > +}
> > +
> >   static inline int
> >   bool_to_str0(struct Mem *mem)
> >   {
> > @@ -1053,6 +1318,8 @@ mem_to_int(struct Mem *mem)
> >   		return str_to_int(mem);
> >   	if (mem->type == MEM_TYPE_DOUBLE)
> >   		return double_to_int(mem);
> > +	if (mem->type == MEM_TYPE_DEC)
> > +		return dec_to_int(mem);
> >   	return -1;
> >   }
> > @@ -1068,6 +1335,8 @@ mem_to_int_precise(struct Mem *mem)
> >   		return str_to_int(mem);
> >   	if (mem->type == MEM_TYPE_DOUBLE)
> >   		return double_to_int_precise(mem);
> > +	if (mem->type == MEM_TYPE_DEC)
> > +		return dec_to_int(mem);
> >   	return -1;
> >   }
> > @@ -1083,6 +1352,8 @@ mem_to_double(struct Mem *mem)
> >   		return int_to_double(mem);
> >   	if (mem->type == MEM_TYPE_STR)
> >   		return str_to_double(mem);
> > +	if (mem->type == MEM_TYPE_DEC)
> > +		return dec_to_double(mem);
> >   	return -1;
> >   }
> > @@ -1129,6 +1400,8 @@ mem_to_str0(struct Mem *mem)
> >   		return array_to_str0(mem);
> >   	case MEM_TYPE_UUID:
> >   		return uuid_to_str0(mem);
> > +	case MEM_TYPE_DEC:
> > +		return dec_to_str0(mem);
> >   	default:
> >   		return -1;
> >   	}
> > @@ -1157,6 +1430,8 @@ mem_to_str(struct Mem *mem)
> >   		return array_to_str0(mem);
> >   	case MEM_TYPE_UUID:
> >   		return uuid_to_str0(mem);
> > +	case MEM_TYPE_DEC:
> > +		return dec_to_str0(mem);
> >   	default:
> >   		return -1;
> >   	}
> > @@ -1177,6 +1452,8 @@ mem_cast_explicit(struct Mem *mem, enum field_type
> > type)
> >   			return str_to_uint(mem);
> >   		case MEM_TYPE_DOUBLE:
> >   			return double_to_uint(mem);
> > +		case MEM_TYPE_DEC:
> > +			return dec_to_uint(mem);
> >   		default:
> >   			return -1;
> >   		}
> > @@ -1209,9 +1486,21 @@ mem_cast_explicit(struct Mem *mem, enum field_type
> > type)
> >   	case FIELD_TYPE_NUMBER:
> >   		return mem_to_number(mem);
> >   	case FIELD_TYPE_DECIMAL:
> > -		if (mem->type == MEM_TYPE_DEC)
> > +		switch (mem->type) {
> > +		case MEM_TYPE_INT:
> > +			return int_to_dec(mem);
> > +		case MEM_TYPE_UINT:
> > +			return uint_to_dec(mem);
> > +		case MEM_TYPE_STR:
> > +			return str_to_dec(mem);
> > +		case MEM_TYPE_DOUBLE:
> > +			return double_to_dec(mem);
> > +		case MEM_TYPE_DEC:
> > +			mem->flags = 0;
> >   			return 0;
> > -		return -1;
> > +		default:
> > +			return -1;
> > +		}
> 
> For consistency with majority of other numeric directions (mem_to_int(),
> mem_to_number(), mem_to_double()) I'd extract this code similarly to
> mem_to_decimal(). But this is up to you...
> 
> 
I will do this later, when I have time for some refactoring.

> >   	case FIELD_TYPE_UUID:
> >   		if (mem->type == MEM_TYPE_UUID) {
> >   			mem->flags = 0;
> ...
> 
> > @@ -1296,9 +1591,19 @@ mem_cast_implicit(struct Mem *mem, enum field_type
> > type)
> >   		mem->flags = MEM_Number;
> >   		return 0;
> >   	case FIELD_TYPE_DECIMAL:
> > -		if (mem->type == MEM_TYPE_DEC)
> > +		switch (mem->type) {
> > +		case MEM_TYPE_INT:
> > +			return int_to_dec(mem);
> > +		case MEM_TYPE_UINT:
> > +			return uint_to_dec(mem);
> > +		case MEM_TYPE_DOUBLE:
> > +			return double_to_dec_precise(mem);
> > +		case MEM_TYPE_DEC:
> > +			mem->flags = 0;
> >   			return 0;
> > -		return -1;
> > +		default:
> > +			return -1;
> > +		}
> >   	case FIELD_TYPE_MAP:
> >   		if (mem->type == MEM_TYPE_MAP)
> >   			return 0;
> > @@ -1343,6 +1648,8 @@ mem_cast_implicit_number(struct Mem *mem, enum
> > field_type type)
> >   			return -1;
> >   		case MEM_TYPE_DOUBLE:
> >   			return double_to_uint_forced(mem);
> > +		case MEM_TYPE_DEC:
> > +			return dec_to_uint_forced(mem);
> >   		default:
> >   			unreachable();
> >   		}
> > @@ -1353,6 +1660,8 @@ mem_cast_implicit_number(struct Mem *mem, enum
> > field_type type)
> >   			return int_to_double_forced(mem);
> >   		case MEM_TYPE_UINT:
> >   			return uint_to_double_forced(mem);
> > +		case MEM_TYPE_DEC:
> > +			return dec_to_double_forced(mem);
> >   		case MEM_TYPE_DOUBLE:
> >   			mem->flags = 0;
> >   			return 0;
> > @@ -1368,6 +1677,23 @@ mem_cast_implicit_number(struct Mem *mem, enum
> > field_type type)
> >   			return 0;
> >   		case MEM_TYPE_DOUBLE:
> >   			return double_to_int_forced(mem);
> > +		case MEM_TYPE_DEC:
> > +			return dec_to_int_forced(mem);
> > +		default:
> > +			unreachable();
> > +		}
> > +		break;
> > +	case FIELD_TYPE_DECIMAL:
> > +		switch (mem->type) {
> > +		case MEM_TYPE_INT:
> > +			return int_to_dec(mem);
> > +		case MEM_TYPE_UINT:
> > +			return uint_to_dec(mem);
> > +		case MEM_TYPE_DEC:
> > +			mem->flags = 0;
> > +			return 0;
> > +		case MEM_TYPE_DOUBLE:
> > +			return double_to_dec_forced(mem);
> >   		default:
> >   			unreachable();
> >   		}
> 
> Otherwise great patch, with the expected extension to explicit/implicit
> casts tables.
> 
> Thanks,
> Timur


Diff:


diff --git a/src/box/sql/mem.c b/src/box/sql/mem.c
index ff8b40d7f..92f671ab5 100644
--- a/src/box/sql/mem.c
+++ b/src/box/sql/mem.c
@@ -1028,9 +1028,10 @@ double_to_dec(struct Mem *mem)
 {
 	assert(mem->type == MEM_TYPE_DOUBLE);
 	double d = mem->u.r;
-	if (d >= 1e38 || d <= -1e38)
+	decimal_t dec;
+	if (decimal_from_double(&dec, d) == NULL)
 		return -1;
-	decimal_from_double(&mem->u.d, d);
+	mem->u.d = dec;
 	mem->type = MEM_TYPE_DEC;
 	mem->flags = 0;
 	return 0;
@@ -1041,11 +1042,9 @@ double_to_dec_precise(struct Mem *mem)
 {
 	assert(mem->type == MEM_TYPE_DOUBLE);
 	double d = mem->u.r;
-	if (d >= 1e38 || d <= -1e38)
-		return -1;
 	decimal_t dec;
-	decimal_from_double(&dec, d);
-	if (atof(decimal_str(&mem->u.d)) != d)
+	if (decimal_from_double(&dec, d) == NULL ||
+	    atof(decimal_str(&dec)) != d)
 		return -1;
 	mem->u.d = dec;
 	mem->type = MEM_TYPE_DEC;
@@ -1206,8 +1205,9 @@ dec_to_uint_forced(struct Mem *mem)
 	mem->u.u = u;
 	/*
 	 * Decimal is floored when cast to uint, which means that after cast if
-	 * if was not integer it becomes less if it was positive, and move if it
-	 * was negative.
+	 * it was not integer it becomes less if it was positive, and move if it
+	 * was negative. For example, DECIMAL value -1.5 becomes -1 after cast
+	 * to INTEGER and DECIMAL value 1.5 becomes 1 after cast to INTEGER.
 	 */
 	if (decimal_is_int(&mem->u.d))
 		return 0;

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [Tarantool-patches] [PATCH v1 4/4] sql: introduce decimal to arithmetic
  2021-08-16 19:48   ` 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
  1 sibling, 1 reply; 16+ messages in thread
From: Mergen Imeev via Tarantool-patches @ 2021-08-18 13:32 UTC (permalink / raw)
  To: Safin Timur; +Cc: tarantool-patches

Thank you for the review! My answer and diff below. There will be quite big
diff, but it is just change of description of one error.

On Mon, Aug 16, 2021 at 10:48:40PM +0300, Safin Timur wrote:
> 
> 
> On 16.08.2021 18:57, Mergen Imeev via Tarantool-patches wrote:
> > 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;
> > +	}
> 
> 1st question is - was it intentionally that you call here atof, while few
> lines belowe we use sqlAtoF?
> 
> 2nd complain is - it all looks that we miss decimal_to_double() function in
> src/core/decimal.c (where we do have decimal_from_double() but not the
> reverse direction). It will look more consistent if this implementation
> would be there. And, I stll believe, there is better way for converting
> decimal to double, than converting it to string, and then to double.
> 
> [Though quick navigation over decNumber API didn't reveal the direct
> approach. Serge, could you please recommend the easiest way here?]
> 
> 
I will think about this a bit later, for now I decided to not add additional
functions to decimal.c/.h.

> > @@ -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)) {
> 
> And now it looks better than before, and less confusing, I do not have
> complains here anymore...
> 
> 
> >   		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)) {
> 
> And here.
> 
> >   		diag_set(ClientError, ER_SQL_TYPE_MISMATCH, mem_str(b),
> >   			 "integer, unsigned or double");
> >   		return -1;
> 
> Thanks,
> Timur


Diff:


diff --git a/src/box/sql/mem.c b/src/box/sql/mem.c
index 077a8b51e..74febd182 100644
--- a/src/box/sql/mem.c
+++ b/src/box/sql/mem.c
@@ -2005,12 +2005,12 @@ check_types_numeric_arithmetic(const struct Mem *a, const struct Mem *b)
 {
 	if (!mem_is_num(a) || mem_is_metatype(a)) {
 		diag_set(ClientError, ER_SQL_TYPE_MISMATCH, mem_str(a),
-			 "integer, unsigned or double");
+			 "integer, decimal or double");
 		return -1;
 	}
 	if (!mem_is_num(b) || mem_is_metatype(b)) {
 		diag_set(ClientError, ER_SQL_TYPE_MISMATCH, mem_str(b),
-			 "integer, unsigned or double");
+			 "integer, decimal or double");
 		return -1;
 	}
 	return 0;
diff --git a/test/sql-tap/gh-5756-implicit-cast-in-arithmetic.test.lua b/test/sql-tap/gh-5756-implicit-cast-in-arithmetic.test.lua
index 3e4de6860..390a2d9b2 100755
--- a/test/sql-tap/gh-5756-implicit-cast-in-arithmetic.test.lua
+++ b/test/sql-tap/gh-5756-implicit-cast-in-arithmetic.test.lua
@@ -35,7 +35,7 @@ test:do_catchsql_test(
     [[
         SELECT 9 + '2';
     ]], {
-        1, "Type mismatch: can not convert string('2') to integer, unsigned or double"
+        1, "Type mismatch: can not convert string('2') to integer, decimal or double"
     })
 
 test:do_catchsql_test(
@@ -43,7 +43,7 @@ test:do_catchsql_test(
     [[
         SELECT 9 + x'32';
     ]], {
-        1, "Type mismatch: can not convert varbinary(x'32') to integer, unsigned or double"
+        1, "Type mismatch: can not convert varbinary(x'32') to integer, decimal or double"
     })
 
 test:do_catchsql_test(
@@ -51,7 +51,7 @@ test:do_catchsql_test(
     [[
         SELECT 9 + true;
     ]], {
-        1, "Type mismatch: can not convert boolean(TRUE) to integer, unsigned or double"
+        1, "Type mismatch: can not convert boolean(TRUE) to integer, decimal or double"
     })
 
 test:do_catchsql_test(
@@ -60,7 +60,7 @@ test:do_catchsql_test(
         SELECT 9 + CAST('11111111-1111-1111-1111-111111111111' AS UUID);
     ]], {
         1, "Type mismatch: can not convert "..
-           "uuid('11111111-1111-1111-1111-111111111111') to integer, unsigned or double"
+           "uuid('11111111-1111-1111-1111-111111111111') to integer, decimal or double"
     })
 
 test:do_execsql_test(
@@ -92,7 +92,7 @@ test:do_catchsql_test(
     [[
         SELECT 9 - '2';
     ]], {
-        1, "Type mismatch: can not convert string('2') to integer, unsigned or double"
+        1, "Type mismatch: can not convert string('2') to integer, decimal or double"
     })
 
 test:do_catchsql_test(
@@ -100,7 +100,7 @@ test:do_catchsql_test(
     [[
         SELECT 9 - x'32';
     ]], {
-        1, "Type mismatch: can not convert varbinary(x'32') to integer, unsigned or double"
+        1, "Type mismatch: can not convert varbinary(x'32') to integer, decimal or double"
     })
 
 test:do_catchsql_test(
@@ -108,7 +108,7 @@ test:do_catchsql_test(
     [[
         SELECT 9 - true;
     ]], {
-        1, "Type mismatch: can not convert boolean(TRUE) to integer, unsigned or double"
+        1, "Type mismatch: can not convert boolean(TRUE) to integer, decimal or double"
     })
 
 test:do_catchsql_test(
@@ -117,7 +117,7 @@ test:do_catchsql_test(
         SELECT 9 - CAST('11111111-1111-1111-1111-111111111111' AS UUID);
     ]], {
         1, "Type mismatch: can not convert "..
-           "uuid('11111111-1111-1111-1111-111111111111') to integer, unsigned or double"
+           "uuid('11111111-1111-1111-1111-111111111111') to integer, decimal or double"
     })
 
 test:do_execsql_test(
@@ -149,7 +149,7 @@ test:do_catchsql_test(
     [[
         SELECT 9 * '2';
     ]], {
-        1, "Type mismatch: can not convert string('2') to integer, unsigned or double"
+        1, "Type mismatch: can not convert string('2') to integer, decimal or double"
     })
 
 test:do_catchsql_test(
@@ -157,7 +157,7 @@ test:do_catchsql_test(
     [[
         SELECT 9 * x'32';
     ]], {
-        1, "Type mismatch: can not convert varbinary(x'32') to integer, unsigned or double"
+        1, "Type mismatch: can not convert varbinary(x'32') to integer, decimal or double"
     })
 
 test:do_catchsql_test(
@@ -165,7 +165,7 @@ test:do_catchsql_test(
     [[
         SELECT 9 * true;
     ]], {
-        1, "Type mismatch: can not convert boolean(TRUE) to integer, unsigned or double"
+        1, "Type mismatch: can not convert boolean(TRUE) to integer, decimal or double"
     })
 
 test:do_catchsql_test(
@@ -174,7 +174,7 @@ test:do_catchsql_test(
         SELECT 9 * CAST('11111111-1111-1111-1111-111111111111' AS UUID);
     ]], {
         1, "Type mismatch: can not convert "..
-           "uuid('11111111-1111-1111-1111-111111111111') to integer, unsigned or double"
+           "uuid('11111111-1111-1111-1111-111111111111') to integer, decimal or double"
     })
 
 test:do_execsql_test(
@@ -206,7 +206,7 @@ test:do_catchsql_test(
     [[
         SELECT 9 / '2';
     ]], {
-        1, "Type mismatch: can not convert string('2') to integer, unsigned or double"
+        1, "Type mismatch: can not convert string('2') to integer, decimal or double"
     })
 
 test:do_catchsql_test(
@@ -214,7 +214,7 @@ test:do_catchsql_test(
     [[
         SELECT 9 / x'32';
     ]], {
-        1, "Type mismatch: can not convert varbinary(x'32') to integer, unsigned or double"
+        1, "Type mismatch: can not convert varbinary(x'32') to integer, decimal or double"
     })
 
 test:do_catchsql_test(
@@ -222,7 +222,7 @@ test:do_catchsql_test(
     [[
         SELECT 9 / true;
     ]], {
-        1, "Type mismatch: can not convert boolean(TRUE) to integer, unsigned or double"
+        1, "Type mismatch: can not convert boolean(TRUE) to integer, decimal or double"
     })
 
 test:do_catchsql_test(
@@ -231,7 +231,7 @@ test:do_catchsql_test(
         SELECT 9 / CAST('11111111-1111-1111-1111-111111111111' AS UUID);
     ]], {
         1, "Type mismatch: can not convert "..
-           "uuid('11111111-1111-1111-1111-111111111111') to integer, unsigned or double"
+           "uuid('11111111-1111-1111-1111-111111111111') to integer, decimal or double"
     })
 
 test:do_execsql_test(
diff --git a/test/sql-tap/metatypes.test.lua b/test/sql-tap/metatypes.test.lua
index 349c670ba..3e4091675 100755
--- a/test/sql-tap/metatypes.test.lua
+++ b/test/sql-tap/metatypes.test.lua
@@ -96,7 +96,7 @@ test:do_catchsql_test(
     [[
         SELECT 1 + CAST(1 AS NUMBER);
     ]], {
-        1, "Type mismatch: can not convert number(1) to integer, unsigned or double"
+        1, "Type mismatch: can not convert number(1) to integer, decimal or double"
     })
 
 test:do_catchsql_test(
@@ -104,7 +104,7 @@ test:do_catchsql_test(
     [[
         SELECT CAST(1 AS SCALAR) * 1;
     ]], {
-        1, "Type mismatch: can not convert scalar(1) to integer, unsigned or double"
+        1, "Type mismatch: can not convert scalar(1) to integer, decimal or double"
     })
 
 -- Check that bitwise operations are prohibited for NUMBER and SCALAR values.
diff --git a/test/sql-tap/sql-errors.test.lua b/test/sql-tap/sql-errors.test.lua
index daf0ee643..ceb4ecccf 100755
--- a/test/sql-tap/sql-errors.test.lua
+++ b/test/sql-tap/sql-errors.test.lua
@@ -696,7 +696,7 @@ test:do_catchsql_test(
 		SELECT X'ff' + 1;
 	]], {
 		-- <sql-errors-2.1>
-		1, "Type mismatch: can not convert varbinary(x'FF') to integer, unsigned or double"
+		1, "Type mismatch: can not convert varbinary(x'FF') to integer, decimal or double"
 		-- </sql-errors-2.1>
 	})
 
@@ -706,7 +706,7 @@ test:do_catchsql_test(
 		SELECT X'ff' - 1;
 	]], {
 		-- <sql-errors-2.2>
-		1, "Type mismatch: can not convert varbinary(x'FF') to integer, unsigned or double"
+		1, "Type mismatch: can not convert varbinary(x'FF') to integer, decimal or double"
 		-- </sql-errors-2.2>
 	})
 
@@ -716,7 +716,7 @@ test:do_catchsql_test(
 		SELECT X'ff' * 1;
 	]], {
 		-- <sql-errors-2.3>
-		1, "Type mismatch: can not convert varbinary(x'FF') to integer, unsigned or double"
+		1, "Type mismatch: can not convert varbinary(x'FF') to integer, decimal or double"
 		-- </sql-errors-2.3>
 	})
 
@@ -726,7 +726,7 @@ test:do_catchsql_test(
 		SELECT X'ff' / 1;
 	]], {
 		-- <sql-errors-2.4>
-		1, "Type mismatch: can not convert varbinary(x'FF') to integer, unsigned or double"
+		1, "Type mismatch: can not convert varbinary(x'FF') to integer, decimal or double"
 		-- </sql-errors-2.4>
 	})
 
diff --git a/test/sql-tap/tkt-a8a0d2996a.test.lua b/test/sql-tap/tkt-a8a0d2996a.test.lua
index 72f57ec65..a40621f41 100755
--- a/test/sql-tap/tkt-a8a0d2996a.test.lua
+++ b/test/sql-tap/tkt-a8a0d2996a.test.lua
@@ -27,7 +27,7 @@ test:do_catchsql_test(
         SELECT typeof(x), typeof(y) FROM t WHERE 1=x+0 AND y=='1';
     ]], {
         -- <1.0>
-        1, "Type mismatch: can not convert string('1') to integer, unsigned or double"
+        1, "Type mismatch: can not convert string('1') to integer, decimal or double"
         -- </1.0>
     })
 
@@ -37,7 +37,7 @@ test:do_catchsql_test(
         SELECT typeof(x), typeof(y) FROM t WHERE 1=x-0 AND y=='1';
     ]], {
         -- <1.1>
-        1, "Type mismatch: can not convert string('1') to integer, unsigned or double"
+        1, "Type mismatch: can not convert string('1') to integer, decimal or double"
         -- </1.1>
     })
 
@@ -47,7 +47,7 @@ test:do_catchsql_test(
         SELECT typeof(x), typeof(y) FROM t WHERE 1=x*1 AND y=='1';
     ]], {
         -- <1.2>
-        1, "Type mismatch: can not convert string('1') to integer, unsigned or double"
+        1, "Type mismatch: can not convert string('1') to integer, decimal or double"
         -- </1.2>
     })
 
@@ -57,7 +57,7 @@ test:do_catchsql_test(
         SELECT typeof(x), typeof(y) FROM t WHERE 1=x/1 AND y=='1';
     ]], {
         -- <1.3>
-        1, "Type mismatch: can not convert string('1') to integer, unsigned or double"
+        1, "Type mismatch: can not convert string('1') to integer, decimal or double"
         -- </1.3>
     })
 
@@ -78,7 +78,7 @@ test:do_catchsql_test(
         SELECT typeof(x), typeof(y) FROM t WHERE 1=x+0 AND y=='1';
     ]], {
         -- <3.0>
-        1, "Type mismatch: can not convert string('1.0') to integer, unsigned or double"
+        1, "Type mismatch: can not convert string('1.0') to integer, decimal or double"
         -- </3.0>
     })
 
@@ -88,7 +88,7 @@ test:do_catchsql_test(
         SELECT typeof(x), typeof(y) FROM t WHERE 1=x-0 AND y=='1';
     ]], {
         -- <3.1>
-        1, "Type mismatch: can not convert string('1.0') to integer, unsigned or double"
+        1, "Type mismatch: can not convert string('1.0') to integer, decimal or double"
         -- </3.1>
     })
 
@@ -98,7 +98,7 @@ test:do_catchsql_test(
         SELECT typeof(x), typeof(y) FROM t WHERE 1=x*1 AND y=='1';
     ]], {
         -- <3.2>
-        1, "Type mismatch: can not convert string('1.0') to integer, unsigned or double"
+        1, "Type mismatch: can not convert string('1.0') to integer, decimal or double"
         -- </3.2>
     })
 
@@ -108,7 +108,7 @@ test:do_catchsql_test(
         SELECT typeof(x), typeof(y) FROM t WHERE 1=x/1 AND y=='1';
     ]], {
         -- <3.3>
-        1, "Type mismatch: can not convert string('1.0') to integer, unsigned or double"
+        1, "Type mismatch: can not convert string('1.0') to integer, decimal or double"
         -- </3.3>
     })
 
@@ -138,7 +138,7 @@ test:do_catchsql_test(
         SELECT '1.23e64'/'1.0000e+62';
     ]], {
         -- <4.1>
-        1, "Type mismatch: can not convert string('1.0000e+62') to integer, unsigned or double"
+        1, "Type mismatch: can not convert string('1.0000e+62') to integer, decimal or double"
         -- </4.1>
     })
 
diff --git a/test/sql-tap/uuid.test.lua b/test/sql-tap/uuid.test.lua
index 57e638046..177798cfa 100755
--- a/test/sql-tap/uuid.test.lua
+++ b/test/sql-tap/uuid.test.lua
@@ -957,7 +957,7 @@ test:do_catchsql_test(
     [[
         SELECT -u FROM t2;
     ]], {
-        1, "Type mismatch: can not convert uuid('11111111-1111-1111-1111-111111111111') to integer, unsigned or double"
+        1, "Type mismatch: can not convert uuid('11111111-1111-1111-1111-111111111111') to integer, decimal or double"
     })
 
 test:do_catchsql_test(
@@ -965,7 +965,7 @@ test:do_catchsql_test(
     [[
         SELECT u + 1 FROM t2;
     ]], {
-        1, "Type mismatch: can not convert uuid('11111111-1111-1111-1111-111111111111') to integer, unsigned or double"
+        1, "Type mismatch: can not convert uuid('11111111-1111-1111-1111-111111111111') to integer, decimal or double"
     })
 
 test:do_catchsql_test(
@@ -973,7 +973,7 @@ test:do_catchsql_test(
     [[
         SELECT u - 1 FROM t2;
     ]], {
-        1, "Type mismatch: can not convert uuid('11111111-1111-1111-1111-111111111111') to integer, unsigned or double"
+        1, "Type mismatch: can not convert uuid('11111111-1111-1111-1111-111111111111') to integer, decimal or double"
     })
 
 test:do_catchsql_test(
@@ -981,7 +981,7 @@ test:do_catchsql_test(
     [[
         SELECT u * 1 FROM t2;
     ]], {
-        1, "Type mismatch: can not convert uuid('11111111-1111-1111-1111-111111111111') to integer, unsigned or double"
+        1, "Type mismatch: can not convert uuid('11111111-1111-1111-1111-111111111111') to integer, decimal or double"
     })
 
 test:do_catchsql_test(
@@ -989,7 +989,7 @@ test:do_catchsql_test(
     [[
         SELECT u / 1 FROM t2;
     ]], {
-        1, "Type mismatch: can not convert uuid('11111111-1111-1111-1111-111111111111') to integer, unsigned or double"
+        1, "Type mismatch: can not convert uuid('11111111-1111-1111-1111-111111111111') to integer, decimal or double"
     })
 
 test:do_catchsql_test(
diff --git a/test/sql/boolean.result b/test/sql/boolean.result
index a9ce37e11..000142ebe 100644
--- a/test/sql/boolean.result
+++ b/test/sql/boolean.result
@@ -1131,98 +1131,98 @@ SELECT a, a1, a OR a1 FROM t, t6;
 SELECT -true;
  | ---
  | - null
- | - 'Type mismatch: can not convert boolean(TRUE) to integer, unsigned or double'
+ | - 'Type mismatch: can not convert boolean(TRUE) to integer, decimal or double'
  | ...
 SELECT -false;
  | ---
  | - null
- | - 'Type mismatch: can not convert boolean(FALSE) to integer, unsigned or double'
+ | - 'Type mismatch: can not convert boolean(FALSE) to integer, decimal or double'
  | ...
 SELECT -a FROM t;
  | ---
  | - null
- | - 'Type mismatch: can not convert boolean(FALSE) to integer, unsigned or double'
+ | - 'Type mismatch: can not convert boolean(FALSE) to integer, decimal or double'
  | ...
 
 SELECT true + true;
  | ---
  | - null
- | - 'Type mismatch: can not convert boolean(TRUE) to integer, unsigned or double'
+ | - 'Type mismatch: can not convert boolean(TRUE) to integer, decimal or double'
  | ...
 SELECT true + false;
  | ---
  | - null
- | - 'Type mismatch: can not convert boolean(FALSE) to integer, unsigned or double'
+ | - 'Type mismatch: can not convert boolean(FALSE) to integer, decimal or double'
  | ...
 SELECT false + true;
  | ---
  | - null
- | - 'Type mismatch: can not convert boolean(TRUE) to integer, unsigned or double'
+ | - 'Type mismatch: can not convert boolean(TRUE) to integer, decimal or double'
  | ...
 SELECT false + false;
  | ---
  | - null
- | - 'Type mismatch: can not convert boolean(FALSE) to integer, unsigned or double'
+ | - 'Type mismatch: can not convert boolean(FALSE) to integer, decimal or double'
  | ...
 SELECT true - true;
  | ---
  | - null
- | - 'Type mismatch: can not convert boolean(TRUE) to integer, unsigned or double'
+ | - 'Type mismatch: can not convert boolean(TRUE) to integer, decimal or double'
  | ...
 SELECT true - false;
  | ---
  | - null
- | - 'Type mismatch: can not convert boolean(FALSE) to integer, unsigned or double'
+ | - 'Type mismatch: can not convert boolean(FALSE) to integer, decimal or double'
  | ...
 SELECT false - true;
  | ---
  | - null
- | - 'Type mismatch: can not convert boolean(TRUE) to integer, unsigned or double'
+ | - 'Type mismatch: can not convert boolean(TRUE) to integer, decimal or double'
  | ...
 SELECT false - false;
  | ---
  | - null
- | - 'Type mismatch: can not convert boolean(FALSE) to integer, unsigned or double'
+ | - 'Type mismatch: can not convert boolean(FALSE) to integer, decimal or double'
  | ...
 SELECT true * true;
  | ---
  | - null
- | - 'Type mismatch: can not convert boolean(TRUE) to integer, unsigned or double'
+ | - 'Type mismatch: can not convert boolean(TRUE) to integer, decimal or double'
  | ...
 SELECT true * false;
  | ---
  | - null
- | - 'Type mismatch: can not convert boolean(FALSE) to integer, unsigned or double'
+ | - 'Type mismatch: can not convert boolean(FALSE) to integer, decimal or double'
  | ...
 SELECT false * true;
  | ---
  | - null
- | - 'Type mismatch: can not convert boolean(TRUE) to integer, unsigned or double'
+ | - 'Type mismatch: can not convert boolean(TRUE) to integer, decimal or double'
  | ...
 SELECT false * false;
  | ---
  | - null
- | - 'Type mismatch: can not convert boolean(FALSE) to integer, unsigned or double'
+ | - 'Type mismatch: can not convert boolean(FALSE) to integer, decimal or double'
  | ...
 SELECT true / true;
  | ---
  | - null
- | - 'Type mismatch: can not convert boolean(TRUE) to integer, unsigned or double'
+ | - 'Type mismatch: can not convert boolean(TRUE) to integer, decimal or double'
  | ...
 SELECT true / false;
  | ---
  | - null
- | - 'Type mismatch: can not convert boolean(FALSE) to integer, unsigned or double'
+ | - 'Type mismatch: can not convert boolean(FALSE) to integer, decimal or double'
  | ...
 SELECT false / true;
  | ---
  | - null
- | - 'Type mismatch: can not convert boolean(TRUE) to integer, unsigned or double'
+ | - 'Type mismatch: can not convert boolean(TRUE) to integer, decimal or double'
  | ...
 SELECT false / false;
  | ---
  | - null
- | - 'Type mismatch: can not convert boolean(FALSE) to integer, unsigned or double'
+ | - 'Type mismatch: can not convert boolean(FALSE) to integer, decimal or double'
  | ...
 SELECT true % true;
  | ---
@@ -1248,42 +1248,42 @@ SELECT false % false;
 SELECT a, true + a FROM t;
  | ---
  | - null
- | - 'Type mismatch: can not convert boolean(FALSE) to integer, unsigned or double'
+ | - 'Type mismatch: can not convert boolean(FALSE) to integer, decimal or double'
  | ...
 SELECT a, false + a FROM t;
  | ---
  | - null
- | - 'Type mismatch: can not convert boolean(FALSE) to integer, unsigned or double'
+ | - 'Type mismatch: can not convert boolean(FALSE) to integer, decimal or double'
  | ...
 SELECT a, true - a FROM t;
  | ---
  | - null
- | - 'Type mismatch: can not convert boolean(FALSE) to integer, unsigned or double'
+ | - 'Type mismatch: can not convert boolean(FALSE) to integer, decimal or double'
  | ...
 SELECT a, false - a FROM t;
  | ---
  | - null
- | - 'Type mismatch: can not convert boolean(FALSE) to integer, unsigned or double'
+ | - 'Type mismatch: can not convert boolean(FALSE) to integer, decimal or double'
  | ...
 SELECT a, true * a FROM t;
  | ---
  | - null
- | - 'Type mismatch: can not convert boolean(FALSE) to integer, unsigned or double'
+ | - 'Type mismatch: can not convert boolean(FALSE) to integer, decimal or double'
  | ...
 SELECT a, false * a FROM t;
  | ---
  | - null
- | - 'Type mismatch: can not convert boolean(FALSE) to integer, unsigned or double'
+ | - 'Type mismatch: can not convert boolean(FALSE) to integer, decimal or double'
  | ...
 SELECT a, true / a FROM t;
  | ---
  | - null
- | - 'Type mismatch: can not convert boolean(FALSE) to integer, unsigned or double'
+ | - 'Type mismatch: can not convert boolean(FALSE) to integer, decimal or double'
  | ...
 SELECT a, false / a FROM t;
  | ---
  | - null
- | - 'Type mismatch: can not convert boolean(FALSE) to integer, unsigned or double'
+ | - 'Type mismatch: can not convert boolean(FALSE) to integer, decimal or double'
  | ...
 SELECT a, true % a FROM t;
  | ---
@@ -1298,42 +1298,42 @@ SELECT a, false % a FROM t;
 SELECT a, a + true FROM t;
  | ---
  | - null
- | - 'Type mismatch: can not convert boolean(TRUE) to integer, unsigned or double'
+ | - 'Type mismatch: can not convert boolean(TRUE) to integer, decimal or double'
  | ...
 SELECT a, a + false FROM t;
  | ---
  | - null
- | - 'Type mismatch: can not convert boolean(FALSE) to integer, unsigned or double'
+ | - 'Type mismatch: can not convert boolean(FALSE) to integer, decimal or double'
  | ...
 SELECT a, a - true FROM t;
  | ---
  | - null
- | - 'Type mismatch: can not convert boolean(TRUE) to integer, unsigned or double'
+ | - 'Type mismatch: can not convert boolean(TRUE) to integer, decimal or double'
  | ...
 SELECT a, a - false FROM t;
  | ---
  | - null
- | - 'Type mismatch: can not convert boolean(FALSE) to integer, unsigned or double'
+ | - 'Type mismatch: can not convert boolean(FALSE) to integer, decimal or double'
  | ...
 SELECT a, a * true FROM t;
  | ---
  | - null
- | - 'Type mismatch: can not convert boolean(TRUE) to integer, unsigned or double'
+ | - 'Type mismatch: can not convert boolean(TRUE) to integer, decimal or double'
  | ...
 SELECT a, a * false FROM t;
  | ---
  | - null
- | - 'Type mismatch: can not convert boolean(FALSE) to integer, unsigned or double'
+ | - 'Type mismatch: can not convert boolean(FALSE) to integer, decimal or double'
  | ...
 SELECT a, a / true FROM t;
  | ---
  | - null
- | - 'Type mismatch: can not convert boolean(TRUE) to integer, unsigned or double'
+ | - 'Type mismatch: can not convert boolean(TRUE) to integer, decimal or double'
  | ...
 SELECT a, a / false FROM t;
  | ---
  | - null
- | - 'Type mismatch: can not convert boolean(FALSE) to integer, unsigned or double'
+ | - 'Type mismatch: can not convert boolean(FALSE) to integer, decimal or double'
  | ...
 SELECT a, a % true FROM t;
  | ---
@@ -1349,22 +1349,22 @@ SELECT a, a % false FROM t;
 SELECT a, a1, a + a1 FROM t, t6;
  | ---
  | - null
- | - 'Type mismatch: can not convert boolean(FALSE) to integer, unsigned or double'
+ | - 'Type mismatch: can not convert boolean(FALSE) to integer, decimal or double'
  | ...
 SELECT a, a1, a - a1 FROM t, t6;
  | ---
  | - null
- | - 'Type mismatch: can not convert boolean(FALSE) to integer, unsigned or double'
+ | - 'Type mismatch: can not convert boolean(FALSE) to integer, decimal or double'
  | ...
 SELECT a, a1, a * a1 FROM t, t6;
  | ---
  | - null
- | - 'Type mismatch: can not convert boolean(FALSE) to integer, unsigned or double'
+ | - 'Type mismatch: can not convert boolean(FALSE) to integer, decimal or double'
  | ...
 SELECT a, a1, a / a1 FROM t, t6;
  | ---
  | - null
- | - 'Type mismatch: can not convert boolean(FALSE) to integer, unsigned or double'
+ | - 'Type mismatch: can not convert boolean(FALSE) to integer, decimal or double'
  | ...
 SELECT a, a1, a % a1 FROM t, t6;
  | ---
@@ -2652,42 +2652,42 @@ SELECT a2, b, b OR a2 FROM t6, t7;
 SELECT true + 2;
  | ---
  | - null
- | - 'Type mismatch: can not convert boolean(TRUE) to integer, unsigned or double'
+ | - 'Type mismatch: can not convert boolean(TRUE) to integer, decimal or double'
  | ...
 SELECT false + 2;
  | ---
  | - null
- | - 'Type mismatch: can not convert boolean(FALSE) to integer, unsigned or double'
+ | - 'Type mismatch: can not convert boolean(FALSE) to integer, decimal or double'
  | ...
 SELECT true - 2;
  | ---
  | - null
- | - 'Type mismatch: can not convert boolean(TRUE) to integer, unsigned or double'
+ | - 'Type mismatch: can not convert boolean(TRUE) to integer, decimal or double'
  | ...
 SELECT false - 2;
  | ---
  | - null
- | - 'Type mismatch: can not convert boolean(FALSE) to integer, unsigned or double'
+ | - 'Type mismatch: can not convert boolean(FALSE) to integer, decimal or double'
  | ...
 SELECT true * 2;
  | ---
  | - null
- | - 'Type mismatch: can not convert boolean(TRUE) to integer, unsigned or double'
+ | - 'Type mismatch: can not convert boolean(TRUE) to integer, decimal or double'
  | ...
 SELECT false * 2;
  | ---
  | - null
- | - 'Type mismatch: can not convert boolean(FALSE) to integer, unsigned or double'
+ | - 'Type mismatch: can not convert boolean(FALSE) to integer, decimal or double'
  | ...
 SELECT true / 2;
  | ---
  | - null
- | - 'Type mismatch: can not convert boolean(TRUE) to integer, unsigned or double'
+ | - 'Type mismatch: can not convert boolean(TRUE) to integer, decimal or double'
  | ...
 SELECT false / 2;
  | ---
  | - null
- | - 'Type mismatch: can not convert boolean(FALSE) to integer, unsigned or double'
+ | - 'Type mismatch: can not convert boolean(FALSE) to integer, decimal or double'
  | ...
 SELECT true % 2;
  | ---
@@ -2702,42 +2702,42 @@ SELECT false % 2;
 SELECT 2 + true;
  | ---
  | - null
- | - 'Type mismatch: can not convert boolean(TRUE) to integer, unsigned or double'
+ | - 'Type mismatch: can not convert boolean(TRUE) to integer, decimal or double'
  | ...
 SELECT 2 + false;
  | ---
  | - null
- | - 'Type mismatch: can not convert boolean(FALSE) to integer, unsigned or double'
+ | - 'Type mismatch: can not convert boolean(FALSE) to integer, decimal or double'
  | ...
 SELECT 2 - true;
  | ---
  | - null
- | - 'Type mismatch: can not convert boolean(TRUE) to integer, unsigned or double'
+ | - 'Type mismatch: can not convert boolean(TRUE) to integer, decimal or double'
  | ...
 SELECT 2 - false;
  | ---
  | - null
- | - 'Type mismatch: can not convert boolean(FALSE) to integer, unsigned or double'
+ | - 'Type mismatch: can not convert boolean(FALSE) to integer, decimal or double'
  | ...
 SELECT 2 * true;
  | ---
  | - null
- | - 'Type mismatch: can not convert boolean(TRUE) to integer, unsigned or double'
+ | - 'Type mismatch: can not convert boolean(TRUE) to integer, decimal or double'
  | ...
 SELECT 2 * false;
  | ---
  | - null
- | - 'Type mismatch: can not convert boolean(FALSE) to integer, unsigned or double'
+ | - 'Type mismatch: can not convert boolean(FALSE) to integer, decimal or double'
  | ...
 SELECT 2 / true;
  | ---
  | - null
- | - 'Type mismatch: can not convert boolean(TRUE) to integer, unsigned or double'
+ | - 'Type mismatch: can not convert boolean(TRUE) to integer, decimal or double'
  | ...
 SELECT 2 / false;
  | ---
  | - null
- | - 'Type mismatch: can not convert boolean(FALSE) to integer, unsigned or double'
+ | - 'Type mismatch: can not convert boolean(FALSE) to integer, decimal or double'
  | ...
 SELECT 2 % true;
  | ---
@@ -2753,22 +2753,22 @@ SELECT 2 % false;
 SELECT a1, a1 + 2 FROM t6
  | ---
  | - null
- | - 'Type mismatch: can not convert boolean(FALSE) to integer, unsigned or double'
+ | - 'Type mismatch: can not convert boolean(FALSE) to integer, decimal or double'
  | ...
 SELECT a1, a1 - 2 FROM t6
  | ---
  | - null
- | - 'Type mismatch: can not convert boolean(FALSE) to integer, unsigned or double'
+ | - 'Type mismatch: can not convert boolean(FALSE) to integer, decimal or double'
  | ...
 SELECT a1, a1 * 2 FROM t6
  | ---
  | - null
- | - 'Type mismatch: can not convert boolean(FALSE) to integer, unsigned or double'
+ | - 'Type mismatch: can not convert boolean(FALSE) to integer, decimal or double'
  | ...
 SELECT a1, a1 / 2 FROM t6
  | ---
  | - null
- | - 'Type mismatch: can not convert boolean(FALSE) to integer, unsigned or double'
+ | - 'Type mismatch: can not convert boolean(FALSE) to integer, decimal or double'
  | ...
 SELECT a1, a1 % 2 FROM t6
  | ---
@@ -2778,22 +2778,22 @@ SELECT a1, a1 % 2 FROM t6
 SELECT a1, 2 + a1 FROM t6
  | ---
  | - null
- | - 'Type mismatch: can not convert boolean(FALSE) to integer, unsigned or double'
+ | - 'Type mismatch: can not convert boolean(FALSE) to integer, decimal or double'
  | ...
 SELECT a1, 2 - a1 FROM t6
  | ---
  | - null
- | - 'Type mismatch: can not convert boolean(FALSE) to integer, unsigned or double'
+ | - 'Type mismatch: can not convert boolean(FALSE) to integer, decimal or double'
  | ...
 SELECT a1, 2 * a1 FROM t6
  | ---
  | - null
- | - 'Type mismatch: can not convert boolean(FALSE) to integer, unsigned or double'
+ | - 'Type mismatch: can not convert boolean(FALSE) to integer, decimal or double'
  | ...
 SELECT a1, 2 / a1 FROM t6
  | ---
  | - null
- | - 'Type mismatch: can not convert boolean(FALSE) to integer, unsigned or double'
+ | - 'Type mismatch: can not convert boolean(FALSE) to integer, decimal or double'
  | ...
 SELECT a1, 2 % a1 FROM t6
  | ---
@@ -2803,22 +2803,22 @@ SELECT a1, 2 % a1 FROM t6
 SELECT a2, a2 + 2 FROM t6
  | ---
  | - null
- | - 'Type mismatch: can not convert boolean(TRUE) to integer, unsigned or double'
+ | - 'Type mismatch: can not convert boolean(TRUE) to integer, decimal or double'
  | ...
 SELECT a2, a2 - 2 FROM t6
  | ---
  | - null
- | - 'Type mismatch: can not convert boolean(TRUE) to integer, unsigned or double'
+ | - 'Type mismatch: can not convert boolean(TRUE) to integer, decimal or double'
  | ...
 SELECT a2, a2 * 2 FROM t6
  | ---
  | - null
- | - 'Type mismatch: can not convert boolean(TRUE) to integer, unsigned or double'
+ | - 'Type mismatch: can not convert boolean(TRUE) to integer, decimal or double'
  | ...
 SELECT a2, a2 / 2 FROM t6
  | ---
  | - null
- | - 'Type mismatch: can not convert boolean(TRUE) to integer, unsigned or double'
+ | - 'Type mismatch: can not convert boolean(TRUE) to integer, decimal or double'
  | ...
 SELECT a2, a2 % 2 FROM t6
  | ---
@@ -2828,22 +2828,22 @@ SELECT a2, a2 % 2 FROM t6
 SELECT a2, 2 + a2 FROM t6
  | ---
  | - null
- | - 'Type mismatch: can not convert boolean(TRUE) to integer, unsigned or double'
+ | - 'Type mismatch: can not convert boolean(TRUE) to integer, decimal or double'
  | ...
 SELECT a2, 2 - a2 FROM t6
  | ---
  | - null
- | - 'Type mismatch: can not convert boolean(TRUE) to integer, unsigned or double'
+ | - 'Type mismatch: can not convert boolean(TRUE) to integer, decimal or double'
  | ...
 SELECT a2, 2 * a2 FROM t6
  | ---
  | - null
- | - 'Type mismatch: can not convert boolean(TRUE) to integer, unsigned or double'
+ | - 'Type mismatch: can not convert boolean(TRUE) to integer, decimal or double'
  | ...
 SELECT a2, 2 / a2 FROM t6
  | ---
  | - null
- | - 'Type mismatch: can not convert boolean(TRUE) to integer, unsigned or double'
+ | - 'Type mismatch: can not convert boolean(TRUE) to integer, decimal or double'
  | ...
 SELECT a2, 2 % a2 FROM t6
  | ---
@@ -2854,42 +2854,42 @@ SELECT a2, 2 % a2 FROM t6
 SELECT b, true + b FROM t7;
  | ---
  | - null
- | - 'Type mismatch: can not convert boolean(TRUE) to integer, unsigned or double'
+ | - 'Type mismatch: can not convert boolean(TRUE) to integer, decimal or double'
  | ...
 SELECT b, false + b FROM t7;
  | ---
  | - null
- | - 'Type mismatch: can not convert boolean(FALSE) to integer, unsigned or double'
+ | - 'Type mismatch: can not convert boolean(FALSE) to integer, decimal or double'
  | ...
 SELECT b, true - b FROM t7;
  | ---
  | - null
- | - 'Type mismatch: can not convert boolean(TRUE) to integer, unsigned or double'
+ | - 'Type mismatch: can not convert boolean(TRUE) to integer, decimal or double'
  | ...
 SELECT b, false - b FROM t7;
  | ---
  | - null
- | - 'Type mismatch: can not convert boolean(FALSE) to integer, unsigned or double'
+ | - 'Type mismatch: can not convert boolean(FALSE) to integer, decimal or double'
  | ...
 SELECT b, true * b FROM t7;
  | ---
  | - null
- | - 'Type mismatch: can not convert boolean(TRUE) to integer, unsigned or double'
+ | - 'Type mismatch: can not convert boolean(TRUE) to integer, decimal or double'
  | ...
 SELECT b, false * b FROM t7;
  | ---
  | - null
- | - 'Type mismatch: can not convert boolean(FALSE) to integer, unsigned or double'
+ | - 'Type mismatch: can not convert boolean(FALSE) to integer, decimal or double'
  | ...
 SELECT b, true / b FROM t7;
  | ---
  | - null
- | - 'Type mismatch: can not convert boolean(TRUE) to integer, unsigned or double'
+ | - 'Type mismatch: can not convert boolean(TRUE) to integer, decimal or double'
  | ...
 SELECT b, false / b FROM t7;
  | ---
  | - null
- | - 'Type mismatch: can not convert boolean(FALSE) to integer, unsigned or double'
+ | - 'Type mismatch: can not convert boolean(FALSE) to integer, decimal or double'
  | ...
 SELECT b, true % b FROM t7;
  | ---
@@ -2904,42 +2904,42 @@ SELECT b, false % b FROM t7;
 SELECT b, b + true FROM t7;
  | ---
  | - null
- | - 'Type mismatch: can not convert boolean(TRUE) to integer, unsigned or double'
+ | - 'Type mismatch: can not convert boolean(TRUE) to integer, decimal or double'
  | ...
 SELECT b, b + false FROM t7;
  | ---
  | - null
- | - 'Type mismatch: can not convert boolean(FALSE) to integer, unsigned or double'
+ | - 'Type mismatch: can not convert boolean(FALSE) to integer, decimal or double'
  | ...
 SELECT b, b - true FROM t7;
  | ---
  | - null
- | - 'Type mismatch: can not convert boolean(TRUE) to integer, unsigned or double'
+ | - 'Type mismatch: can not convert boolean(TRUE) to integer, decimal or double'
  | ...
 SELECT b, b - false FROM t7;
  | ---
  | - null
- | - 'Type mismatch: can not convert boolean(FALSE) to integer, unsigned or double'
+ | - 'Type mismatch: can not convert boolean(FALSE) to integer, decimal or double'
  | ...
 SELECT b, b * true FROM t7;
  | ---
  | - null
- | - 'Type mismatch: can not convert boolean(TRUE) to integer, unsigned or double'
+ | - 'Type mismatch: can not convert boolean(TRUE) to integer, decimal or double'
  | ...
 SELECT b, b * false FROM t7;
  | ---
  | - null
- | - 'Type mismatch: can not convert boolean(FALSE) to integer, unsigned or double'
+ | - 'Type mismatch: can not convert boolean(FALSE) to integer, decimal or double'
  | ...
 SELECT b, b / true FROM t7;
  | ---
  | - null
- | - 'Type mismatch: can not convert boolean(TRUE) to integer, unsigned or double'
+ | - 'Type mismatch: can not convert boolean(TRUE) to integer, decimal or double'
  | ...
 SELECT b, b / false FROM t7;
  | ---
  | - null
- | - 'Type mismatch: can not convert boolean(FALSE) to integer, unsigned or double'
+ | - 'Type mismatch: can not convert boolean(FALSE) to integer, decimal or double'
  | ...
 SELECT b, b % true FROM t7;
  | ---
@@ -2955,22 +2955,22 @@ SELECT b, b % false FROM t7;
 SELECT a1, b, a1 + b FROM t6, t7;
  | ---
  | - null
- | - 'Type mismatch: can not convert boolean(FALSE) to integer, unsigned or double'
+ | - 'Type mismatch: can not convert boolean(FALSE) to integer, decimal or double'
  | ...
 SELECT a1, b, a1 - b FROM t6, t7;
  | ---
  | - null
- | - 'Type mismatch: can not convert boolean(FALSE) to integer, unsigned or double'
+ | - 'Type mismatch: can not convert boolean(FALSE) to integer, decimal or double'
  | ...
 SELECT a1, b, a1 * b FROM t6, t7;
  | ---
  | - null
- | - 'Type mismatch: can not convert boolean(FALSE) to integer, unsigned or double'
+ | - 'Type mismatch: can not convert boolean(FALSE) to integer, decimal or double'
  | ...
 SELECT a1, b, a1 / b FROM t6, t7;
  | ---
  | - null
- | - 'Type mismatch: can not convert boolean(FALSE) to integer, unsigned or double'
+ | - 'Type mismatch: can not convert boolean(FALSE) to integer, decimal or double'
  | ...
 SELECT a1, b, a1 % b FROM t6, t7;
  | ---
@@ -2980,22 +2980,22 @@ SELECT a1, b, a1 % b FROM t6, t7;
 SELECT a1, b, b + a1 FROM t6, t7;
  | ---
  | - null
- | - 'Type mismatch: can not convert boolean(FALSE) to integer, unsigned or double'
+ | - 'Type mismatch: can not convert boolean(FALSE) to integer, decimal or double'
  | ...
 SELECT a1, b, b - a1 FROM t6, t7;
  | ---
  | - null
- | - 'Type mismatch: can not convert boolean(FALSE) to integer, unsigned or double'
+ | - 'Type mismatch: can not convert boolean(FALSE) to integer, decimal or double'
  | ...
 SELECT a1, b, b * a1 FROM t6, t7;
  | ---
  | - null
- | - 'Type mismatch: can not convert boolean(FALSE) to integer, unsigned or double'
+ | - 'Type mismatch: can not convert boolean(FALSE) to integer, decimal or double'
  | ...
 SELECT a1, b, b / a1 FROM t6, t7;
  | ---
  | - null
- | - 'Type mismatch: can not convert boolean(FALSE) to integer, unsigned or double'
+ | - 'Type mismatch: can not convert boolean(FALSE) to integer, decimal or double'
  | ...
 SELECT a1, b, b % a1 FROM t6, t7;
  | ---
@@ -3005,22 +3005,22 @@ SELECT a1, b, b % a1 FROM t6, t7;
 SELECT a2, b, a2 + b FROM t6, t7;
  | ---
  | - null
- | - 'Type mismatch: can not convert boolean(TRUE) to integer, unsigned or double'
+ | - 'Type mismatch: can not convert boolean(TRUE) to integer, decimal or double'
  | ...
 SELECT a2, b, a2 - b FROM t6, t7;
  | ---
  | - null
- | - 'Type mismatch: can not convert boolean(TRUE) to integer, unsigned or double'
+ | - 'Type mismatch: can not convert boolean(TRUE) to integer, decimal or double'
  | ...
 SELECT a2, b, a2 * b FROM t6, t7;
  | ---
  | - null
- | - 'Type mismatch: can not convert boolean(TRUE) to integer, unsigned or double'
+ | - 'Type mismatch: can not convert boolean(TRUE) to integer, decimal or double'
  | ...
 SELECT a2, b, a2 / b FROM t6, t7;
  | ---
  | - null
- | - 'Type mismatch: can not convert boolean(TRUE) to integer, unsigned or double'
+ | - 'Type mismatch: can not convert boolean(TRUE) to integer, decimal or double'
  | ...
 SELECT a2, b, a2 % b FROM t6, t7;
  | ---
@@ -3030,22 +3030,22 @@ SELECT a2, b, a2 % b FROM t6, t7;
 SELECT a2, b, b + a2 FROM t6, t7;
  | ---
  | - null
- | - 'Type mismatch: can not convert boolean(TRUE) to integer, unsigned or double'
+ | - 'Type mismatch: can not convert boolean(TRUE) to integer, decimal or double'
  | ...
 SELECT a2, b, b - a2 FROM t6, t7;
  | ---
  | - null
- | - 'Type mismatch: can not convert boolean(TRUE) to integer, unsigned or double'
+ | - 'Type mismatch: can not convert boolean(TRUE) to integer, decimal or double'
  | ...
 SELECT a2, b, b * a2 FROM t6, t7;
  | ---
  | - null
- | - 'Type mismatch: can not convert boolean(TRUE) to integer, unsigned or double'
+ | - 'Type mismatch: can not convert boolean(TRUE) to integer, decimal or double'
  | ...
 SELECT a2, b, b / a2 FROM t6, t7;
  | ---
  | - null
- | - 'Type mismatch: can not convert boolean(TRUE) to integer, unsigned or double'
+ | - 'Type mismatch: can not convert boolean(TRUE) to integer, decimal or double'
  | ...
 SELECT a2, b, b % a2 FROM t6, t7;
  | ---
@@ -4121,42 +4121,42 @@ SELECT a2, c, c OR a2 FROM t6, t8;
 SELECT true + 2.3;
  | ---
  | - null
- | - 'Type mismatch: can not convert boolean(TRUE) to integer, unsigned or double'
+ | - 'Type mismatch: can not convert boolean(TRUE) to integer, decimal or double'
  | ...
 SELECT false + 2.3;
  | ---
  | - null
- | - 'Type mismatch: can not convert boolean(FALSE) to integer, unsigned or double'
+ | - 'Type mismatch: can not convert boolean(FALSE) to integer, decimal or double'
  | ...
 SELECT true - 2.3;
  | ---
  | - null
- | - 'Type mismatch: can not convert boolean(TRUE) to integer, unsigned or double'
+ | - 'Type mismatch: can not convert boolean(TRUE) to integer, decimal or double'
  | ...
 SELECT false - 2.3;
  | ---
  | - null
- | - 'Type mismatch: can not convert boolean(FALSE) to integer, unsigned or double'
+ | - 'Type mismatch: can not convert boolean(FALSE) to integer, decimal or double'
  | ...
 SELECT true * 2.3;
  | ---
  | - null
- | - 'Type mismatch: can not convert boolean(TRUE) to integer, unsigned or double'
+ | - 'Type mismatch: can not convert boolean(TRUE) to integer, decimal or double'
  | ...
 SELECT false * 2.3;
  | ---
  | - null
- | - 'Type mismatch: can not convert boolean(FALSE) to integer, unsigned or double'
+ | - 'Type mismatch: can not convert boolean(FALSE) to integer, decimal or double'
  | ...
 SELECT true / 2.3;
  | ---
  | - null
- | - 'Type mismatch: can not convert boolean(TRUE) to integer, unsigned or double'
+ | - 'Type mismatch: can not convert boolean(TRUE) to integer, decimal or double'
  | ...
 SELECT false / 2.3;
  | ---
  | - null
- | - 'Type mismatch: can not convert boolean(FALSE) to integer, unsigned or double'
+ | - 'Type mismatch: can not convert boolean(FALSE) to integer, decimal or double'
  | ...
 SELECT true % 2.3;
  | ---
@@ -4171,42 +4171,42 @@ SELECT false % 2.3;
 SELECT 2.3 + true;
  | ---
  | - null
- | - 'Type mismatch: can not convert boolean(TRUE) to integer, unsigned or double'
+ | - 'Type mismatch: can not convert boolean(TRUE) to integer, decimal or double'
  | ...
 SELECT 2.3 + false;
  | ---
  | - null
- | - 'Type mismatch: can not convert boolean(FALSE) to integer, unsigned or double'
+ | - 'Type mismatch: can not convert boolean(FALSE) to integer, decimal or double'
  | ...
 SELECT 2.3 - true;
  | ---
  | - null
- | - 'Type mismatch: can not convert boolean(TRUE) to integer, unsigned or double'
+ | - 'Type mismatch: can not convert boolean(TRUE) to integer, decimal or double'
  | ...
 SELECT 2.3 - false;
  | ---
  | - null
- | - 'Type mismatch: can not convert boolean(FALSE) to integer, unsigned or double'
+ | - 'Type mismatch: can not convert boolean(FALSE) to integer, decimal or double'
  | ...
 SELECT 2.3 * true;
  | ---
  | - null
- | - 'Type mismatch: can not convert boolean(TRUE) to integer, unsigned or double'
+ | - 'Type mismatch: can not convert boolean(TRUE) to integer, decimal or double'
  | ...
 SELECT 2.3 * false;
  | ---
  | - null
- | - 'Type mismatch: can not convert boolean(FALSE) to integer, unsigned or double'
+ | - 'Type mismatch: can not convert boolean(FALSE) to integer, decimal or double'
  | ...
 SELECT 2.3 / true;
  | ---
  | - null
- | - 'Type mismatch: can not convert boolean(TRUE) to integer, unsigned or double'
+ | - 'Type mismatch: can not convert boolean(TRUE) to integer, decimal or double'
  | ...
 SELECT 2.3 / false;
  | ---
  | - null
- | - 'Type mismatch: can not convert boolean(FALSE) to integer, unsigned or double'
+ | - 'Type mismatch: can not convert boolean(FALSE) to integer, decimal or double'
  | ...
 SELECT 2.3 % true;
  | ---
@@ -4222,22 +4222,22 @@ SELECT 2.3 % false;
 SELECT a1, a1 + 2.3 FROM t6
  | ---
  | - null
- | - 'Type mismatch: can not convert boolean(FALSE) to integer, unsigned or double'
+ | - 'Type mismatch: can not convert boolean(FALSE) to integer, decimal or double'
  | ...
 SELECT a1, a1 - 2.3 FROM t6
  | ---
  | - null
- | - 'Type mismatch: can not convert boolean(FALSE) to integer, unsigned or double'
+ | - 'Type mismatch: can not convert boolean(FALSE) to integer, decimal or double'
  | ...
 SELECT a1, a1 * 2.3 FROM t6
  | ---
  | - null
- | - 'Type mismatch: can not convert boolean(FALSE) to integer, unsigned or double'
+ | - 'Type mismatch: can not convert boolean(FALSE) to integer, decimal or double'
  | ...
 SELECT a1, a1 / 2.3 FROM t6
  | ---
  | - null
- | - 'Type mismatch: can not convert boolean(FALSE) to integer, unsigned or double'
+ | - 'Type mismatch: can not convert boolean(FALSE) to integer, decimal or double'
  | ...
 SELECT a1, a1 % 2.3 FROM t6
  | ---
@@ -4247,22 +4247,22 @@ SELECT a1, a1 % 2.3 FROM t6
 SELECT a1, 2.3 + a1 FROM t6
  | ---
  | - null
- | - 'Type mismatch: can not convert boolean(FALSE) to integer, unsigned or double'
+ | - 'Type mismatch: can not convert boolean(FALSE) to integer, decimal or double'
  | ...
 SELECT a1, 2.3 - a1 FROM t6
  | ---
  | - null
- | - 'Type mismatch: can not convert boolean(FALSE) to integer, unsigned or double'
+ | - 'Type mismatch: can not convert boolean(FALSE) to integer, decimal or double'
  | ...
 SELECT a1, 2.3 * a1 FROM t6
  | ---
  | - null
- | - 'Type mismatch: can not convert boolean(FALSE) to integer, unsigned or double'
+ | - 'Type mismatch: can not convert boolean(FALSE) to integer, decimal or double'
  | ...
 SELECT a1, 2.3 / a1 FROM t6
  | ---
  | - null
- | - 'Type mismatch: can not convert boolean(FALSE) to integer, unsigned or double'
+ | - 'Type mismatch: can not convert boolean(FALSE) to integer, decimal or double'
  | ...
 SELECT a1, 2.3 % a1 FROM t6
  | ---
@@ -4272,22 +4272,22 @@ SELECT a1, 2.3 % a1 FROM t6
 SELECT a2, a2 + 2.3 FROM t6
  | ---
  | - null
- | - 'Type mismatch: can not convert boolean(TRUE) to integer, unsigned or double'
+ | - 'Type mismatch: can not convert boolean(TRUE) to integer, decimal or double'
  | ...
 SELECT a2, a2 - 2.3 FROM t6
  | ---
  | - null
- | - 'Type mismatch: can not convert boolean(TRUE) to integer, unsigned or double'
+ | - 'Type mismatch: can not convert boolean(TRUE) to integer, decimal or double'
  | ...
 SELECT a2, a2 * 2.3 FROM t6
  | ---
  | - null
- | - 'Type mismatch: can not convert boolean(TRUE) to integer, unsigned or double'
+ | - 'Type mismatch: can not convert boolean(TRUE) to integer, decimal or double'
  | ...
 SELECT a2, a2 / 2.3 FROM t6
  | ---
  | - null
- | - 'Type mismatch: can not convert boolean(TRUE) to integer, unsigned or double'
+ | - 'Type mismatch: can not convert boolean(TRUE) to integer, decimal or double'
  | ...
 SELECT a2, a2 % 2.3 FROM t6
  | ---
@@ -4297,22 +4297,22 @@ SELECT a2, a2 % 2.3 FROM t6
 SELECT a2, 2.3 + a2 FROM t6
  | ---
  | - null
- | - 'Type mismatch: can not convert boolean(TRUE) to integer, unsigned or double'
+ | - 'Type mismatch: can not convert boolean(TRUE) to integer, decimal or double'
  | ...
 SELECT a2, 2.3 - a2 FROM t6
  | ---
  | - null
- | - 'Type mismatch: can not convert boolean(TRUE) to integer, unsigned or double'
+ | - 'Type mismatch: can not convert boolean(TRUE) to integer, decimal or double'
  | ...
 SELECT a2, 2.3 * a2 FROM t6
  | ---
  | - null
- | - 'Type mismatch: can not convert boolean(TRUE) to integer, unsigned or double'
+ | - 'Type mismatch: can not convert boolean(TRUE) to integer, decimal or double'
  | ...
 SELECT a2, 2.3 / a2 FROM t6
  | ---
  | - null
- | - 'Type mismatch: can not convert boolean(TRUE) to integer, unsigned or double'
+ | - 'Type mismatch: can not convert boolean(TRUE) to integer, decimal or double'
  | ...
 SELECT a2, 2.3 % a2 FROM t6
  | ---
@@ -4323,42 +4323,42 @@ SELECT a2, 2.3 % a2 FROM t6
 SELECT c, true + c FROM t8;
  | ---
  | - null
- | - 'Type mismatch: can not convert boolean(TRUE) to integer, unsigned or double'
+ | - 'Type mismatch: can not convert boolean(TRUE) to integer, decimal or double'
  | ...
 SELECT c, false + c FROM t8;
  | ---
  | - null
- | - 'Type mismatch: can not convert boolean(FALSE) to integer, unsigned or double'
+ | - 'Type mismatch: can not convert boolean(FALSE) to integer, decimal or double'
  | ...
 SELECT c, true - c FROM t8;
  | ---
  | - null
- | - 'Type mismatch: can not convert boolean(TRUE) to integer, unsigned or double'
+ | - 'Type mismatch: can not convert boolean(TRUE) to integer, decimal or double'
  | ...
 SELECT c, false - c FROM t8;
  | ---
  | - null
- | - 'Type mismatch: can not convert boolean(FALSE) to integer, unsigned or double'
+ | - 'Type mismatch: can not convert boolean(FALSE) to integer, decimal or double'
  | ...
 SELECT c, true * c FROM t8;
  | ---
  | - null
- | - 'Type mismatch: can not convert boolean(TRUE) to integer, unsigned or double'
+ | - 'Type mismatch: can not convert boolean(TRUE) to integer, decimal or double'
  | ...
 SELECT c, false * c FROM t8;
  | ---
  | - null
- | - 'Type mismatch: can not convert boolean(FALSE) to integer, unsigned or double'
+ | - 'Type mismatch: can not convert boolean(FALSE) to integer, decimal or double'
  | ...
 SELECT c, true / c FROM t8;
  | ---
  | - null
- | - 'Type mismatch: can not convert boolean(TRUE) to integer, unsigned or double'
+ | - 'Type mismatch: can not convert boolean(TRUE) to integer, decimal or double'
  | ...
 SELECT c, false / c FROM t8;
  | ---
  | - null
- | - 'Type mismatch: can not convert boolean(FALSE) to integer, unsigned or double'
+ | - 'Type mismatch: can not convert boolean(FALSE) to integer, decimal or double'
  | ...
 SELECT c, true % c FROM t8;
  | ---
@@ -4373,42 +4373,42 @@ SELECT c, false % c FROM t8;
 SELECT c, c + true FROM t8;
  | ---
  | - null
- | - 'Type mismatch: can not convert boolean(TRUE) to integer, unsigned or double'
+ | - 'Type mismatch: can not convert boolean(TRUE) to integer, decimal or double'
  | ...
 SELECT c, c + false FROM t8;
  | ---
  | - null
- | - 'Type mismatch: can not convert boolean(FALSE) to integer, unsigned or double'
+ | - 'Type mismatch: can not convert boolean(FALSE) to integer, decimal or double'
  | ...
 SELECT c, c - true FROM t8;
  | ---
  | - null
- | - 'Type mismatch: can not convert boolean(TRUE) to integer, unsigned or double'
+ | - 'Type mismatch: can not convert boolean(TRUE) to integer, decimal or double'
  | ...
 SELECT c, c - false FROM t8;
  | ---
  | - null
- | - 'Type mismatch: can not convert boolean(FALSE) to integer, unsigned or double'
+ | - 'Type mismatch: can not convert boolean(FALSE) to integer, decimal or double'
  | ...
 SELECT c, c * true FROM t8;
  | ---
  | - null
- | - 'Type mismatch: can not convert boolean(TRUE) to integer, unsigned or double'
+ | - 'Type mismatch: can not convert boolean(TRUE) to integer, decimal or double'
  | ...
 SELECT c, c * false FROM t8;
  | ---
  | - null
- | - 'Type mismatch: can not convert boolean(FALSE) to integer, unsigned or double'
+ | - 'Type mismatch: can not convert boolean(FALSE) to integer, decimal or double'
  | ...
 SELECT c, c / true FROM t8;
  | ---
  | - null
- | - 'Type mismatch: can not convert boolean(TRUE) to integer, unsigned or double'
+ | - 'Type mismatch: can not convert boolean(TRUE) to integer, decimal or double'
  | ...
 SELECT c, c / false FROM t8;
  | ---
  | - null
- | - 'Type mismatch: can not convert boolean(FALSE) to integer, unsigned or double'
+ | - 'Type mismatch: can not convert boolean(FALSE) to integer, decimal or double'
  | ...
 SELECT c, c % true FROM t8;
  | ---
@@ -4424,22 +4424,22 @@ SELECT c, c % false FROM t8;
 SELECT a1, c, a1 + c FROM t6, t8;
  | ---
  | - null
- | - 'Type mismatch: can not convert boolean(FALSE) to integer, unsigned or double'
+ | - 'Type mismatch: can not convert boolean(FALSE) to integer, decimal or double'
  | ...
 SELECT a1, c, a1 - c FROM t6, t8;
  | ---
  | - null
- | - 'Type mismatch: can not convert boolean(FALSE) to integer, unsigned or double'
+ | - 'Type mismatch: can not convert boolean(FALSE) to integer, decimal or double'
  | ...
 SELECT a1, c, a1 * c FROM t6, t8;
  | ---
  | - null
- | - 'Type mismatch: can not convert boolean(FALSE) to integer, unsigned or double'
+ | - 'Type mismatch: can not convert boolean(FALSE) to integer, decimal or double'
  | ...
 SELECT a1, c, a1 / c FROM t6, t8;
  | ---
  | - null
- | - 'Type mismatch: can not convert boolean(FALSE) to integer, unsigned or double'
+ | - 'Type mismatch: can not convert boolean(FALSE) to integer, decimal or double'
  | ...
 SELECT a1, c, a1 % c FROM t6, t8;
  | ---
@@ -4449,22 +4449,22 @@ SELECT a1, c, a1 % c FROM t6, t8;
 SELECT a1, c, c + a1 FROM t6, t8;
  | ---
  | - null
- | - 'Type mismatch: can not convert boolean(FALSE) to integer, unsigned or double'
+ | - 'Type mismatch: can not convert boolean(FALSE) to integer, decimal or double'
  | ...
 SELECT a1, c, c - a1 FROM t6, t8;
  | ---
  | - null
- | - 'Type mismatch: can not convert boolean(FALSE) to integer, unsigned or double'
+ | - 'Type mismatch: can not convert boolean(FALSE) to integer, decimal or double'
  | ...
 SELECT a1, c, c * a1 FROM t6, t8;
  | ---
  | - null
- | - 'Type mismatch: can not convert boolean(FALSE) to integer, unsigned or double'
+ | - 'Type mismatch: can not convert boolean(FALSE) to integer, decimal or double'
  | ...
 SELECT a1, c, c / a1 FROM t6, t8;
  | ---
  | - null
- | - 'Type mismatch: can not convert boolean(FALSE) to integer, unsigned or double'
+ | - 'Type mismatch: can not convert boolean(FALSE) to integer, decimal or double'
  | ...
 SELECT a1, c, c % a1 FROM t6, t8;
  | ---
@@ -4474,22 +4474,22 @@ SELECT a1, c, c % a1 FROM t6, t8;
 SELECT a2, c, a2 + c FROM t6, t8;
  | ---
  | - null
- | - 'Type mismatch: can not convert boolean(TRUE) to integer, unsigned or double'
+ | - 'Type mismatch: can not convert boolean(TRUE) to integer, decimal or double'
  | ...
 SELECT a2, c, a2 - c FROM t6, t8;
  | ---
  | - null
- | - 'Type mismatch: can not convert boolean(TRUE) to integer, unsigned or double'
+ | - 'Type mismatch: can not convert boolean(TRUE) to integer, decimal or double'
  | ...
 SELECT a2, c, a2 * c FROM t6, t8;
  | ---
  | - null
- | - 'Type mismatch: can not convert boolean(TRUE) to integer, unsigned or double'
+ | - 'Type mismatch: can not convert boolean(TRUE) to integer, decimal or double'
  | ...
 SELECT a2, c, a2 / c FROM t6, t8;
  | ---
  | - null
- | - 'Type mismatch: can not convert boolean(TRUE) to integer, unsigned or double'
+ | - 'Type mismatch: can not convert boolean(TRUE) to integer, decimal or double'
  | ...
 SELECT a2, c, a2 % c FROM t6, t8;
  | ---
@@ -4499,22 +4499,22 @@ SELECT a2, c, a2 % c FROM t6, t8;
 SELECT a2, c, c + a2 FROM t6, t8;
  | ---
  | - null
- | - 'Type mismatch: can not convert boolean(TRUE) to integer, unsigned or double'
+ | - 'Type mismatch: can not convert boolean(TRUE) to integer, decimal or double'
  | ...
 SELECT a2, c, c - a2 FROM t6, t8;
  | ---
  | - null
- | - 'Type mismatch: can not convert boolean(TRUE) to integer, unsigned or double'
+ | - 'Type mismatch: can not convert boolean(TRUE) to integer, decimal or double'
  | ...
 SELECT a2, c, c * a2 FROM t6, t8;
  | ---
  | - null
- | - 'Type mismatch: can not convert boolean(TRUE) to integer, unsigned or double'
+ | - 'Type mismatch: can not convert boolean(TRUE) to integer, decimal or double'
  | ...
 SELECT a2, c, c / a2 FROM t6, t8;
  | ---
  | - null
- | - 'Type mismatch: can not convert boolean(TRUE) to integer, unsigned or double'
+ | - 'Type mismatch: can not convert boolean(TRUE) to integer, decimal or double'
  | ...
 SELECT a2, c, c % a2 FROM t6, t8;
  | ---
diff --git a/test/sql/types.result b/test/sql/types.result
index 68bdcd62e..4d49318c9 100644
--- a/test/sql/types.result
+++ b/test/sql/types.result
@@ -311,7 +311,7 @@ box.execute('SELECT 1 + 1.1;')
 box.execute('SELECT \'9223372036854\' + 1;')
 ---
 - null
-- 'Type mismatch: can not convert string(''9223372036854'') to integer, unsigned or
+- 'Type mismatch: can not convert string(''9223372036854'') to integer, decimal or
   double'
 ...
 -- Fix BOOLEAN bindings.

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [Tarantool-patches] [PATCH v1 2/4] sql: introduce field type decimal
  2021-08-18 13:01     ` Mergen Imeev via Tarantool-patches
@ 2021-08-18 16:52       ` Safin Timur via Tarantool-patches
  0 siblings, 0 replies; 16+ messages in thread
From: Safin Timur via Tarantool-patches @ 2021-08-18 16:52 UTC (permalink / raw)
  To: Mergen Imeev; +Cc: tarantool-patches

LGTM.

(Though this apparent interdependency between patches is a good 
indication that they better to live in a single patch, but I do not insist)

Timur

On 18.08.2021 16:01, Mergen Imeev wrote:
> Hi! Thank ypu fpr the review! My answers and diff below.
> 
> On Mon, Aug 16, 2021 at 10:22:42PM +0300, Safin Timur wrote:
>> Please see below several notes...
>>
>> On 16.08.2021 18:57, Mergen Imeev via Tarantool-patches wrote:
>>> This patch introduces a decimal field type. However, implicit and
>>> explicit casts and arithmetic operations for this type will be presented
>>> in next few patches. Literals also will be introduced later.
>>>
>>> Part of #4415
>>> ---
>>>    extra/mkkeywordhash.c                         |   2 +-
>>>    src/box/sql/expr.c                            |   3 +
>>>    src/box/sql/func.c                            |   4 +
>>>    src/box/sql/mem.c                             | 173 +++++--
>>>    src/box/sql/mem.h                             |  18 +-
>>>    src/box/sql/parse.y                           |   1 +
>>>    src/box/sql/sqlInt.h                          |   1 +
>>>    test/sql-tap/CMakeLists.txt                   |   1 +
>>>    test/sql-tap/decimal.c                        |  48 ++
>>>    test/sql-tap/decimal.test.lua                 | 441 ++++++++++++++++++
>>>    test/sql-tap/engine.cfg                       |   3 +
>>>    .../gh-5913-segfault-on-select-uuid.test.lua  |  83 ----
>>>    .../sql-tap/gh-6024-funcs-return-bin.test.lua |   8 +-
>>>    13 files changed, 661 insertions(+), 125 deletions(-)
>>>    create mode 100644 test/sql-tap/decimal.c
>>>    create mode 100755 test/sql-tap/decimal.test.lua
>>>    delete mode 100755 test/sql-tap/gh-5913-segfault-on-select-uuid.test.lua
>>>
>>> diff --git a/extra/mkkeywordhash.c b/extra/mkkeywordhash.c
>>> index 0d998506c..1c9d12295 100644
>>> --- a/extra/mkkeywordhash.c
>>> +++ b/extra/mkkeywordhash.c
>>> @@ -196,7 +196,7 @@ static Keyword aKeywordTable[] = {
>>>      { "CURRENT_TIMESTAMP",      "TK_STANDARD",    true  },
>>>      { "DATE",                   "TK_STANDARD",    true  },
>>>      { "DATETIME",               "TK_STANDARD",    true  },
>>> -  { "DECIMAL",                "TK_STANDARD",    true  },
>>> +  { "DECIMAL",                "TK_DECIMAL",     true  },
>>
>> DEC is standard alias to DECIMAL. We should support that (similarly to
>> INTEGER vs INT).
>>
> Added.
> 
>>>      { "DECLARE",                "TK_STANDARD",    true  },
>>>      { "DENSE_RANK",             "TK_STANDARD",    true  },
>>>      { "DESCRIBE",               "TK_STANDARD",    true  },
>>> diff --git a/src/box/sql/expr.c b/src/box/sql/expr.c
>>> index c67a7091c..275dbc5ba 100644
>>> --- a/src/box/sql/expr.c
>>> +++ b/src/box/sql/expr.c
>>
>> ...
>>
>>> diff --git a/src/box/sql/mem.c b/src/box/sql/mem.c
>>> index 066940fac..016f0e80b 100644
>>> --- a/src/box/sql/mem.c
>>> +++ b/src/box/sql/mem.c
>>> @@ -1191,6 +1208,10 @@ mem_cast_explicit(struct Mem *mem, enum field_type
>>> type)
>>>    		return -1;
>>>    	case FIELD_TYPE_NUMBER:
>>>    		return mem_to_number(mem);
>>> +	case FIELD_TYPE_DECIMAL:
>>> +		if (mem->type == MEM_TYPE_DEC)
>>> +			return 0;
>>> +		return -1;
>>
>> So CAST(expr as DECIMAL) will only work for DECIMALs? It should be rather
>> behaving similar to other numeric types.
>>
> Added in another patch.
> 
>>>    	case FIELD_TYPE_UUID:
>>>    		if (mem->type == MEM_TYPE_UUID) {
>>>    			mem->flags = 0;
>>> @@ -1274,6 +1295,10 @@ mem_cast_implicit(struct Mem *mem, enum field_type
>>> type)
>>>    			return -1;
>>>    		mem->flags = MEM_Number;
>>>    		return 0;
>>> +	case FIELD_TYPE_DECIMAL:
>>> +		if (mem->type == MEM_TYPE_DEC)
>>> +			return 0;
>>> +		return -1;
>>
>> Same question as above - implicit conversions to decimal should be
>> numeric-like.
>>
>>
> Added in another patch.
> 
>>>    	case FIELD_TYPE_MAP:
>>>    		if (mem->type == MEM_TYPE_MAP)
>>>    			return 0;
>>> @@ -1595,12 +1620,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)) {
>>> +	if (!mem_is_num(a) || mem_is_metatype(a) || a->type ==
>>> MEM_TYPE_DEC) {
>>>    		diag_set(ClientError, ER_SQL_TYPE_MISMATCH, mem_str(a),
>>>    			 "integer, unsigned or double");
>>>    		return -1;
>>
>> I don't understant - we would raise if not a numeric (and decimal is part of
>> numeric) or decimal specifically? So you do not want arithmetic types with
>> decimals?
>>
>>
> Added in another patch.
> 
>>>    	}
>>> -	if (!mem_is_num(b) || mem_is_metatype(b)) {
>>> +	if (!mem_is_num(b) || mem_is_metatype(b) || b->type ==
>>> MEM_TYPE_DEC) {
>>
>> The same confusion as above..
>>
> Added in another patch.
> 
>>>    		diag_set(ClientError, ER_SQL_TYPE_MISMATCH, mem_str(b),
>>>    			 "integer, unsigned or double");
>>>    		return -1;
>>> @@ -2926,26 +3040,10 @@ port_lua_get_vdbemem(struct port *base, uint32_t
>>> *size)
>>>    		case MP_EXT: {
>>>    			assert(field.ext_type == MP_UUID ||
>>>    			       field.ext_type == MP_DECIMAL);
>>> -			char *buf;
>>> -			uint32_t size;
>>> -			uint32_t svp = region_used(&fiber()->gc);
>>> -			if (field.ext_type == MP_UUID) {
>>> +			if (field.ext_type == MP_UUID)
>>>    				mem_set_uuid(&val[i], field.uuidval);
>>> -				break;
>>> -			} else {
>>> -				size = mp_sizeof_decimal(field.decval);
>>> -				buf = region_alloc(&fiber()->gc, size);
>>> -				if (buf == NULL) {
>>> -					diag_set(OutOfMemory, size,
>>> -						 "region_alloc", "buf");
>>> -					goto error;
>>> -				}
>>> -				mp_encode_decimal(buf, field.decval);
>>> -			}
>>> -			int rc = mem_copy_bin(&val[i], buf, size);
>>> -			region_truncate(&fiber()->gc, svp);
>>> -			if (rc != 0)
>>> -				goto error;
>>> +			else
>>> +				mem_set_dec(&val[i], field.decval);
>>
>> Nice! Now it's much compacter and is more readable than before!
>>
>>> diff --git a/src/box/sql/parse.y b/src/box/sql/parse.y
>>> index bd041e862..436c98cd9 100644
>>> --- a/src/box/sql/parse.y
>>> +++ b/src/box/sql/parse.y
>>> @@ -1863,6 +1863,7 @@ number_typedef(A) ::= NUMBER . { A.type =
>>> FIELD_TYPE_NUMBER; }
>>>    number_typedef(A) ::= DOUBLE . { A.type = FIELD_TYPE_DOUBLE; }
>>>    number_typedef(A) ::= INT|INTEGER_KW . { A.type = FIELD_TYPE_INTEGER; }
>>>    number_typedef(A) ::= UNSIGNED . { A.type = FIELD_TYPE_UNSIGNED; }
>>> +number_typedef(A) ::= DECIMAL . { A.type = FIELD_TYPE_DECIMAL; }
>>
>> Here please add alias to DEC, as it was done with INT.
>>
> Added, test fixed.
> 
>>> diff --git a/test/sql-tap/CMakeLists.txt b/test/sql-tap/CMakeLists.txt
>>> index bd2b9f33f..87f23b2f7 100644
>>> --- a/test/sql-tap/CMakeLists.txt
>>> +++ b/test/sql-tap/CMakeLists.txt
>>> @@ -2,3 +2,4 @@ include_directories(${MSGPUCK_INCLUDE_DIRS})
>>>    build_module(gh-5938-wrong-string-length gh-5938-wrong-string-length.c)
>>>    build_module(gh-6024-funcs-return-bin gh-6024-funcs-return-bin.c)
>>>    build_module(sql_uuid sql_uuid.c)
>>> +build_module(decimal decimal.c)
>>> diff --git a/test/sql-tap/decimal.c b/test/sql-tap/decimal.c
>>> new file mode 100644
>>> index 000000000..4d9d1ce19
>>> --- /dev/null
>>> +++ b/test/sql-tap/decimal.c
>>> @@ -0,0 +1,48 @@
>>> +#include "msgpuck.h"
>>> +#include "module.h"
>>> +#include "mp_decimal.h"
>>> +#include "mp_extension_types.h"
>>> +
>>> +enum {
>>> +	BUF_SIZE = 512,
>>> +};
>>> +
>>> +int
>>> +is_dec(box_function_ctx_t *ctx, const char *args, const char *args_end)
>>> +{
>>> +	(void)args_end;
>>> +	uint32_t arg_count = mp_decode_array(&args);
>>> +	if (arg_count != 1) {
>>> +		return box_error_set(__FILE__, __LINE__, ER_PROC_C,
>>> +				     "invalid argument count");
>>> +	}
>>> +	bool is_uuid;
>>> +	if (mp_typeof(*args) == MP_EXT) {
>>> +		const char *str = args;
>>> +		int8_t type;
>>> +		mp_decode_extl(&str, &type);
>>> +		is_uuid = type == MP_DECIMAL;
>>> +	} else {
>>> +		is_uuid = false;
>>
>> Here we see remnants from copy-paste from uuid related code, I assume you
>> meant that variable should be named `is_decimal`.
>>
> True, fixed.
> 
>>> +	}
>>> +
>>> +	char res[BUF_SIZE];
>>> +	memset(res, '\0', BUF_SIZE);
>>> +	char *end = mp_encode_bool(res, is_uuid);
>>> +	box_return_mp(ctx, res, end);
>>> +	return 0;
>>> +}
>>> +
>>> +int
>>> diff --git a/test/sql-tap/engine.cfg b/test/sql-tap/engine.cfg
>>> index 820c72b00..511d0a716 100644
>>> --- a/test/sql-tap/engine.cfg
>>> +++ b/test/sql-tap/engine.cfg
>>> @@ -26,6 +26,9 @@
>>>        "metatypes.test.lua": {
>>>            "memtx": {"engine": "memtx"}
>>>        },
>>> +    "decimal.test.lua": {
>>> +        "memtx": {"engine": "memtx"}
>>> +    },
>>
>> BTW, why this exception for the test?
>>
> I see no reason to test both vinyl and memtx.
> 
>>>        "gh-4077-iproto-execute-no-bind.test.lua": {},
>>>        "*": {
>>>            "memtx": {"engine": "memtx"},
>>
>> Thanks,
>> Timur
> 
> 
> Diff:
> 
> 
> diff --git a/extra/mkkeywordhash.c b/extra/mkkeywordhash.c
> index 1c9d12295..3e4200417 100644
> --- a/extra/mkkeywordhash.c
> +++ b/extra/mkkeywordhash.c
> @@ -196,6 +196,7 @@ static Keyword aKeywordTable[] = {
>     { "CURRENT_TIMESTAMP",      "TK_STANDARD",    true  },
>     { "DATE",                   "TK_STANDARD",    true  },
>     { "DATETIME",               "TK_STANDARD",    true  },
> +  { "DEC",                    "TK_DECIMAL",     true  },
>     { "DECIMAL",                "TK_DECIMAL",     true  },
>     { "DECLARE",                "TK_STANDARD",    true  },
>     { "DENSE_RANK",             "TK_STANDARD",    true  },
> diff --git a/test/sql-tap/decimal.c b/test/sql-tap/decimal.c
> index 4d9d1ce19..fd7c3e0c9 100644
> --- a/test/sql-tap/decimal.c
> +++ b/test/sql-tap/decimal.c
> @@ -16,19 +16,19 @@ is_dec(box_function_ctx_t *ctx, const char *args, const char *args_end)
>   		return box_error_set(__FILE__, __LINE__, ER_PROC_C,
>   				     "invalid argument count");
>   	}
> -	bool is_uuid;
> +	bool is_dec;
>   	if (mp_typeof(*args) == MP_EXT) {
>   		const char *str = args;
>   		int8_t type;
>   		mp_decode_extl(&str, &type);
> -		is_uuid = type == MP_DECIMAL;
> +		is_dec = type == MP_DECIMAL;
>   	} else {
> -		is_uuid = false;
> +		is_dec = false;
>   	}
>   
>   	char res[BUF_SIZE];
>   	memset(res, '\0', BUF_SIZE);
> -	char *end = mp_encode_bool(res, is_uuid);
> +	char *end = mp_encode_bool(res, is_dec);
>   	box_return_mp(ctx, res, end);
>   	return 0;
>   }
> diff --git a/test/sql-tap/decimal.test.lua b/test/sql-tap/decimal.test.lua
> index dd69ca370..10217a806 100755
> --- a/test/sql-tap/decimal.test.lua
> +++ b/test/sql-tap/decimal.test.lua
> @@ -9,16 +9,26 @@ local dec = require("decimal")
>   local dec1 = dec.new("111")
>   local dec2 = dec.new("55555")
>   local dec3 = dec.new("3333")
> +local dec4 = dec.new("-13")
> +local dec5 = dec.new("0")
> +local dec6 = dec.new("-0")
>   
>   -- Check that it is possible to create spaces with DECIMAL field.
>   test:do_execsql_test(
>       "dec-1",
>       [[
> -        CREATE TABLE t1 (i INT PRIMARY KEY, u DECIMAL);
> +        CREATE TABLE t0 (i INT PRIMARY KEY, u DEC);
> +        CREATE TABLE t1 (i INT PRIMARY KEY, u DEC);
>           CREATE TABLE t2 (u DECIMAL PRIMARY KEY);
>       ]], {
>       })
>   
> +box.space.T0:insert({1, dec1})
> +box.space.T0:insert({2, dec2})
> +box.space.T0:insert({3, dec3})
> +box.space.T0:insert({4, dec4})
> +box.space.T0:insert({5, dec5})
> +box.space.T0:insert({6, dec6})
>   box.space.T1:insert({1, dec1})
>   box.space.T1:insert({2, dec2})
>   box.space.T1:insert({3, dec3})
> @@ -33,9 +43,9 @@ box.space.T2:insert({dec3})
>   test:do_execsql_test(
>       "dec-2.1.1",
>       [[
> -        SELECT * FROM t1;
> +        SELECT * FROM t0;
>       ]], {
> -        1, dec1, 2, dec2, 3, dec3, 4, dec1, 5, dec1, 6, dec2
> +        1, dec1, 2, dec2, 3, dec3, 4, dec4, 5, dec5, 6, dec6
>       })
>   
>   test:do_execsql_test(
> @@ -50,17 +60,17 @@ test:do_execsql_test(
>   test:do_execsql_test(
>       "dec-2.2.1",
>       [[
> -        SELECT * FROM t1 ORDER BY u;
> +        SELECT * FROM t0 ORDER BY u;
>       ]], {
> -        1, dec1, 4, dec1, 5, dec1, 3, dec3, 2, dec2, 6, dec2
> +        4, dec4, 5, dec5, 6, dec6, 1, dec1, 3, dec3, 2, dec2
>       })
>   
>   test:do_execsql_test(
>       "dec-2.2.2",
>       [[
> -        SELECT * FROM t1 ORDER BY u DESC;
> +        SELECT * FROM t0 ORDER BY u DESC;
>       ]], {
> -        2, dec2, 6, dec2, 3, dec3, 1, dec1, 4, dec1, 5, dec1
> +        2, dec2, 3, dec3, 1, dec1, 5, dec5, 6, dec6, 4, dec4
>       })
>   
>   test:do_execsql_test(
> 

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [Tarantool-patches] [PATCH v1 3/4] sql: introduce cast for decimal
  2021-08-18 13:29     ` Mergen Imeev via Tarantool-patches
@ 2021-08-18 16:53       ` Safin Timur via Tarantool-patches
  0 siblings, 0 replies; 16+ messages in thread
From: Safin Timur via Tarantool-patches @ 2021-08-18 16:53 UTC (permalink / raw)
  To: Mergen Imeev; +Cc: tarantool-patches

LGTM

On 18.08.2021 16:29, Mergen Imeev wrote:
> Thank you for the review! My answer and diff below.
> 
> On Mon, Aug 16, 2021 at 10:34:16PM +0300, Safin Timur wrote:
>> Oh, now I see the reasonable implicit and explicit casts table for decimals.
>> I disavow my prior complain about missing directions for CAST(exprt AS
>> DECIMAL).
>>
>> Below there is minor complain though...
>>
>> On 16.08.2021 18:57, Mergen Imeev via Tarantool-patches wrote:
>>> This patch introduces implicit and explicit cast of the DECIMAL field in
>>> SQL. Implicit cast to and from DECIMAL is precise. Explicit conversion
>>> to decimal and back could lead to loss of precision.
>>>
>>> Part of #4415
>>> ---
>>>    src/box/sql/mem.c             | 334 ++++++++++++++++++++++++++++++-
>>>    test/sql-tap/decimal.test.lua | 366 +++++++++++++++++++++++++++++++++-
>>>    2 files changed, 695 insertions(+), 5 deletions(-)
>>>
>>> diff --git a/src/box/sql/mem.c b/src/box/sql/mem.c
>>> index 016f0e80b..ff8b40d7f 100644
>>> --- a/src/box/sql/mem.c
>>> +++ b/src/box/sql/mem.c
>>> @@ -678,6 +678,17 @@ int_to_double_forced(struct Mem *mem)
>>>    	return CMP_OLD_NEW(i, d, int64_t);
>>>    }
>>> +static inline int
>>> +int_to_dec(struct Mem *mem)
>>> +{
>>> +	assert(mem->type == MEM_TYPE_INT);
>>> +	int64_t i = mem->u.i;
>>> +	decimal_from_int64(&mem->u.d, i);
>>> +	mem->type = MEM_TYPE_DEC;
>>> +	mem->flags = 0;
>>> +	return 0;
>>> +}
>>> +
>>>    static inline int
>>>    uint_to_double_precise(struct Mem *mem)
>>>    {
>>> @@ -709,6 +720,17 @@ uint_to_double_forced(struct Mem *mem)
>>>    	return CMP_OLD_NEW(u, d, uint64_t);
>>>    }
>>> +static inline int
>>> +uint_to_dec(struct Mem *mem)
>>> +{
>>> +	assert(mem->type == MEM_TYPE_UINT);
>>> +	int64_t u = mem->u.u;
>>> +	decimal_from_uint64(&mem->u.d, u);
>>> +	mem->type = MEM_TYPE_DEC;
>>> +	mem->flags = 0;
>>> +	return 0;
>>> +}
>>> +
>>>    static inline int
>>>    int_to_str0(struct Mem *mem)
>>>    {
>>> @@ -855,6 +877,19 @@ str_to_double(struct Mem *mem)
>>>    	return 0;
>>>    }
>>> +static inline int
>>> +str_to_dec(struct Mem *mem)
>>> +{
>>> +	assert(mem->type == MEM_TYPE_STR);
>>> +	decimal_t dec;
>>> +	decimal_t *d;
>>> +	d = decimal_from_string(&dec, mem->z);
>>> +	if (d == NULL)
>>> +		return -1;
>>> +	mem_set_dec(mem, &dec);
>>> +	return 0;
>>> +}
>>> +
>>>    static inline int
>>>    double_to_int(struct Mem *mem)
>>>    {
>>> @@ -988,6 +1023,69 @@ double_to_uint_forced(struct Mem *mem)
>>>    	return res;
>>>    }
>>> +static inline int
>>> +double_to_dec(struct Mem *mem)
>>> +{
>>> +	assert(mem->type == MEM_TYPE_DOUBLE);
>>> +	double d = mem->u.r;
>>> +	if (d >= 1e38 || d <= -1e38)
>>> +		return -1;
>>> +	decimal_from_double(&mem->u.d, d);
>>> +	mem->type = MEM_TYPE_DEC;
>>> +	mem->flags = 0;
>>> +	return 0;
>>> +}
>>> +
>>> +static inline int
>>> +double_to_dec_precise(struct Mem *mem)
>>> +{
>>> +	assert(mem->type == MEM_TYPE_DOUBLE);
>>> +	double d = mem->u.r;
>>> +	if (d >= 1e38 || d <= -1e38)
>>> +		return -1;
>>> +	decimal_t dec;
>>> +	decimal_from_double(&dec, d);
>>> +	if (atof(decimal_str(&mem->u.d)) != d)
>>> +		return -1;
>>> +	mem->u.d = dec;
>>> +	mem->type = MEM_TYPE_DEC;
>>> +	mem->flags = 0;
>>> +	return 0;
>>> +}
>>> +
>>> +/**
>>> + * Cast MEM with DOUBLE to DECIMAL. Doesn't fail. The return value is < 0
>>> if
>>> + * the original value is less than the result, > 0 if the original value
>>> is
>>> + * greater than the result, and 0 if the cast is precise.
>>> + */
>>> +static inline int
>>> +double_to_dec_forced(struct Mem *mem)
>>> +{
>>> +	assert(mem->type == MEM_TYPE_DOUBLE);
>>> +	double d = mem->u.r;
>>> +	mem->type = MEM_TYPE_DEC;
>>> +	mem->flags = 0;
>>> +	if (d >= 1e38) {
>>> +		const char *val =
>>> "99999999999999999999999999999999999999";
>>> +		assert(strlen(val) == 38);
>>> +		decimal_from_string(&mem->u.d, val);
>>> +		return 1;
>>> +	}
>>> +	if (d <= -1e38) {
>>> +		const char *val =
>>> "-99999999999999999999999999999999999999";
>>> +		assert(strlen(val) == 39);
>>> +		decimal_from_string(&mem->u.d, val);
>>> +		return -1;
>>> +	}
>>> +	decimal_from_double(&mem->u.d, d);
>>> +	double tmp = atof(decimal_str(&mem->u.d));
>>> +	if (d > tmp)
>>> +		return 1;
>>> +	if (d < tmp)
>>> +		return -1;
>>> +	return 0;
>>> +}
>>> +
>>>    static inline int
>>>    double_to_str0(struct Mem *mem)
>>>    {
>>> @@ -1001,6 +1099,173 @@ double_to_str0(struct Mem *mem)
>>>    	return 0;
>>>    }
>>> +static inline int
>>> +dec_to_int(struct Mem *mem)
>>> +{
>>> +	assert(mem->type == MEM_TYPE_DEC);
>>> +	if (decimal_is_neg(&mem->u.d)) {
>>> +		int64_t i;
>>> +		if (decimal_to_int64(&mem->u.d, &i) == NULL)
>>> +			return -1;
>>> +		assert(i < 0);
>>> +		mem->u.i = i;
>>> +		mem->type = MEM_TYPE_INT;
>>> +		mem->flags = 0;
>>> +		return 0;
>>> +	}
>>> +	uint64_t u;
>>> +	if (decimal_to_uint64(&mem->u.d, &u) == NULL)
>>> +		return -1;
>>> +	mem->u.u = u;
>>> +	mem->type = MEM_TYPE_UINT;
>>> +	mem->flags = 0;
>>> +	return 0;
>>> +}
>>> +
>>> +static inline int
>>> +dec_to_int_precise(struct Mem *mem)
>>> +{
>>> +	assert(mem->type == MEM_TYPE_DEC);
>>> +	if (!decimal_is_int(&mem->u.d))
>>> +		return -1;
>>> +	return dec_to_int(mem);
>>> +}
>>> +
>>> +static inline int
>>> +dec_to_int_forced(struct Mem *mem)
>>> +{
>>> +	assert(mem->type == MEM_TYPE_DEC);
>>> +	if (decimal_is_neg(&mem->u.d)) {
>>> +		int64_t i;
>>> +		mem->type = MEM_TYPE_INT;
>>> +		mem->flags = 0;
>>> +		if (decimal_to_int64(&mem->u.d, &i) == NULL) {
>>> +			mem->u.i = INT64_MIN;
>>> +			return -1;
>>> +		}
>>> +		assert(i < 0);
>>> +		mem->u.i = i;
>>> +		/*
>>> +		 * Decimal is floored when cast to int, which means that
>>> after
>>> +		 * cast it becomes bigger if it was not integer.
>>> +		 */
>>> +		return decimal_is_int(&mem->u.d) ? 0 : -1;
>>> +	}
>>> +	uint64_t u;
>>> +	mem->type = MEM_TYPE_UINT;
>>> +	mem->flags = 0;
>>> +	if (decimal_to_uint64(&mem->u.d, &u) == NULL) {
>>> +		mem->u.u = UINT64_MAX;
>>> +		return 1;
>>> +	}
>>> +	mem->u.u = u;
>>> +	/*
>>> +	 * Decimal is floored when cast to uint, which means that after
>>> cast it
>>> +	 * becomes less if it was not integer.
>>> +	 */
>>> +	return decimal_is_int(&mem->u.d) ? 0 : 1;
>>> +}
>>> +
>>> +static inline int
>>> +dec_to_uint(struct Mem *mem)
>>> +{
>>> +	assert(mem->type == MEM_TYPE_DEC);
>>> +	uint64_t u;
>>> +	if (decimal_to_uint64(&mem->u.d, &u) == NULL)
>>> +		return -1;
>>> +	mem->u.u = u;
>>> +	mem->type = MEM_TYPE_UINT;
>>> +	mem->flags = 0;
>>> +	return 0;
>>> +}
>>> +
>>> +static inline int
>>> +dec_to_uint_precise(struct Mem *mem)
>>> +{
>>> +	assert(mem->type == MEM_TYPE_DEC);
>>> +	if (!decimal_is_int(&mem->u.d))
>>> +		return -1;
>>> +	return dec_to_uint(mem);
>>> +}
>>> +
>>> +static inline int
>>> +dec_to_uint_forced(struct Mem *mem)
>>> +{
>>> +	assert(mem->type == MEM_TYPE_DEC);
>>> +	uint64_t u;
>>> +	mem->type = MEM_TYPE_UINT;
>>> +	mem->flags = 0;
>>> +	if (decimal_to_uint64(&mem->u.d, &u) == NULL) {
>>> +		if (decimal_is_neg(&mem->u.d)) {
>>> +			mem->u.u = 0;
>>> +			return -1;
>>> +		}
>>> +		mem->u.u = UINT64_MAX;
>>> +		return 1;
>>> +	}
>>> +	mem->u.u = u;
>>> +	/*
>>> +	 * Decimal is floored when cast to uint, which means that after
>>> cast if
>>> +	 * if was not integer it becomes less if it was positive, and move
>>> if it
>>> +	 * was negative.
>>> +	 */
>>> +	if (decimal_is_int(&mem->u.d))
>>> +		return 0;
>>> +	return decimal_is_neg(&mem->u.d) ? -1 : 1;
>>> +}
>>> +
>>> +static inline int
>>> +dec_to_double(struct Mem *mem)
>>> +{
>>> +	assert(mem->type == MEM_TYPE_DEC);
>>> +	double r = atof(decimal_str(&mem->u.d));
>>> +	mem->u.r = r;
>>> +	mem->type = MEM_TYPE_DOUBLE;
>>> +	mem->flags = 0;
>>> +	return 0;
>>> +}
>>> +
>>> +static inline int
>>> +dec_to_double_precise(struct Mem *mem)
>>> +{
>>> +	assert(mem->type == MEM_TYPE_DEC);
>>> +	double r = atof(decimal_str(&mem->u.d));
>>> +	decimal_t d;
>>> +	decimal_t *dec = decimal_from_double(&d, r);
>>> +	if (dec == NULL || decimal_compare(dec, &mem->u.d) != 0)
>>> +		return -1;
>>> +	mem->u.r = r;
>>> +	mem->type = MEM_TYPE_DOUBLE;
>>> +	mem->flags = 0;
>>> +	return 0;
>>> +}
>>> +
>>> +static inline int
>>> +dec_to_double_forced(struct Mem *mem)
>>> +{
>>> +	assert(mem->type == MEM_TYPE_DEC);
>>> +	mem->type = MEM_TYPE_DOUBLE;
>>> +	mem->flags = 0;
>>> +	double r = atof(decimal_str(&mem->u.d));
>>> +	int res;
>>> +	decimal_t d;
>>> +	if (r <= -1e38)
>>> +		res = 1;
>>> +	else if (r >= 1e38)
>>> +		res = -1;
>>> +	else
>>> +		res = decimal_compare(&mem->u.d, decimal_from_double(&d,
>>> r));
>>> +	mem->u.r = r;
>>> +	return res;
>>> +}
>>> +
>>> +static inline int
>>> +dec_to_str0(struct Mem *mem)
>>> +{
>>> +	assert(mem->type == MEM_TYPE_DEC);
>>> +	return mem_copy_str0(mem, decimal_str(&mem->u.d));
>>> +}
>>> +
>>>    static inline int
>>>    bool_to_str0(struct Mem *mem)
>>>    {
>>> @@ -1053,6 +1318,8 @@ mem_to_int(struct Mem *mem)
>>>    		return str_to_int(mem);
>>>    	if (mem->type == MEM_TYPE_DOUBLE)
>>>    		return double_to_int(mem);
>>> +	if (mem->type == MEM_TYPE_DEC)
>>> +		return dec_to_int(mem);
>>>    	return -1;
>>>    }
>>> @@ -1068,6 +1335,8 @@ mem_to_int_precise(struct Mem *mem)
>>>    		return str_to_int(mem);
>>>    	if (mem->type == MEM_TYPE_DOUBLE)
>>>    		return double_to_int_precise(mem);
>>> +	if (mem->type == MEM_TYPE_DEC)
>>> +		return dec_to_int(mem);
>>>    	return -1;
>>>    }
>>> @@ -1083,6 +1352,8 @@ mem_to_double(struct Mem *mem)
>>>    		return int_to_double(mem);
>>>    	if (mem->type == MEM_TYPE_STR)
>>>    		return str_to_double(mem);
>>> +	if (mem->type == MEM_TYPE_DEC)
>>> +		return dec_to_double(mem);
>>>    	return -1;
>>>    }
>>> @@ -1129,6 +1400,8 @@ mem_to_str0(struct Mem *mem)
>>>    		return array_to_str0(mem);
>>>    	case MEM_TYPE_UUID:
>>>    		return uuid_to_str0(mem);
>>> +	case MEM_TYPE_DEC:
>>> +		return dec_to_str0(mem);
>>>    	default:
>>>    		return -1;
>>>    	}
>>> @@ -1157,6 +1430,8 @@ mem_to_str(struct Mem *mem)
>>>    		return array_to_str0(mem);
>>>    	case MEM_TYPE_UUID:
>>>    		return uuid_to_str0(mem);
>>> +	case MEM_TYPE_DEC:
>>> +		return dec_to_str0(mem);
>>>    	default:
>>>    		return -1;
>>>    	}
>>> @@ -1177,6 +1452,8 @@ mem_cast_explicit(struct Mem *mem, enum field_type
>>> type)
>>>    			return str_to_uint(mem);
>>>    		case MEM_TYPE_DOUBLE:
>>>    			return double_to_uint(mem);
>>> +		case MEM_TYPE_DEC:
>>> +			return dec_to_uint(mem);
>>>    		default:
>>>    			return -1;
>>>    		}
>>> @@ -1209,9 +1486,21 @@ mem_cast_explicit(struct Mem *mem, enum field_type
>>> type)
>>>    	case FIELD_TYPE_NUMBER:
>>>    		return mem_to_number(mem);
>>>    	case FIELD_TYPE_DECIMAL:
>>> -		if (mem->type == MEM_TYPE_DEC)
>>> +		switch (mem->type) {
>>> +		case MEM_TYPE_INT:
>>> +			return int_to_dec(mem);
>>> +		case MEM_TYPE_UINT:
>>> +			return uint_to_dec(mem);
>>> +		case MEM_TYPE_STR:
>>> +			return str_to_dec(mem);
>>> +		case MEM_TYPE_DOUBLE:
>>> +			return double_to_dec(mem);
>>> +		case MEM_TYPE_DEC:
>>> +			mem->flags = 0;
>>>    			return 0;
>>> -		return -1;
>>> +		default:
>>> +			return -1;
>>> +		}
>>
>> For consistency with majority of other numeric directions (mem_to_int(),
>> mem_to_number(), mem_to_double()) I'd extract this code similarly to
>> mem_to_decimal(). But this is up to you...
>>
>>
> I will do this later, when I have time for some refactoring.
> 
>>>    	case FIELD_TYPE_UUID:
>>>    		if (mem->type == MEM_TYPE_UUID) {
>>>    			mem->flags = 0;
>> ...
>>
>>> @@ -1296,9 +1591,19 @@ mem_cast_implicit(struct Mem *mem, enum field_type
>>> type)
>>>    		mem->flags = MEM_Number;
>>>    		return 0;
>>>    	case FIELD_TYPE_DECIMAL:
>>> -		if (mem->type == MEM_TYPE_DEC)
>>> +		switch (mem->type) {
>>> +		case MEM_TYPE_INT:
>>> +			return int_to_dec(mem);
>>> +		case MEM_TYPE_UINT:
>>> +			return uint_to_dec(mem);
>>> +		case MEM_TYPE_DOUBLE:
>>> +			return double_to_dec_precise(mem);
>>> +		case MEM_TYPE_DEC:
>>> +			mem->flags = 0;
>>>    			return 0;
>>> -		return -1;
>>> +		default:
>>> +			return -1;
>>> +		}
>>>    	case FIELD_TYPE_MAP:
>>>    		if (mem->type == MEM_TYPE_MAP)
>>>    			return 0;
>>> @@ -1343,6 +1648,8 @@ mem_cast_implicit_number(struct Mem *mem, enum
>>> field_type type)
>>>    			return -1;
>>>    		case MEM_TYPE_DOUBLE:
>>>    			return double_to_uint_forced(mem);
>>> +		case MEM_TYPE_DEC:
>>> +			return dec_to_uint_forced(mem);
>>>    		default:
>>>    			unreachable();
>>>    		}
>>> @@ -1353,6 +1660,8 @@ mem_cast_implicit_number(struct Mem *mem, enum
>>> field_type type)
>>>    			return int_to_double_forced(mem);
>>>    		case MEM_TYPE_UINT:
>>>    			return uint_to_double_forced(mem);
>>> +		case MEM_TYPE_DEC:
>>> +			return dec_to_double_forced(mem);
>>>    		case MEM_TYPE_DOUBLE:
>>>    			mem->flags = 0;
>>>    			return 0;
>>> @@ -1368,6 +1677,23 @@ mem_cast_implicit_number(struct Mem *mem, enum
>>> field_type type)
>>>    			return 0;
>>>    		case MEM_TYPE_DOUBLE:
>>>    			return double_to_int_forced(mem);
>>> +		case MEM_TYPE_DEC:
>>> +			return dec_to_int_forced(mem);
>>> +		default:
>>> +			unreachable();
>>> +		}
>>> +		break;
>>> +	case FIELD_TYPE_DECIMAL:
>>> +		switch (mem->type) {
>>> +		case MEM_TYPE_INT:
>>> +			return int_to_dec(mem);
>>> +		case MEM_TYPE_UINT:
>>> +			return uint_to_dec(mem);
>>> +		case MEM_TYPE_DEC:
>>> +			mem->flags = 0;
>>> +			return 0;
>>> +		case MEM_TYPE_DOUBLE:
>>> +			return double_to_dec_forced(mem);
>>>    		default:
>>>    			unreachable();
>>>    		}
>>
>> Otherwise great patch, with the expected extension to explicit/implicit
>> casts tables.
>>
>> Thanks,
>> Timur
> 
> 
> Diff:
> 
> 
> diff --git a/src/box/sql/mem.c b/src/box/sql/mem.c
> index ff8b40d7f..92f671ab5 100644
> --- a/src/box/sql/mem.c
> +++ b/src/box/sql/mem.c
> @@ -1028,9 +1028,10 @@ double_to_dec(struct Mem *mem)
>   {
>   	assert(mem->type == MEM_TYPE_DOUBLE);
>   	double d = mem->u.r;
> -	if (d >= 1e38 || d <= -1e38)
> +	decimal_t dec;
> +	if (decimal_from_double(&dec, d) == NULL)
>   		return -1;
> -	decimal_from_double(&mem->u.d, d);
> +	mem->u.d = dec;
>   	mem->type = MEM_TYPE_DEC;
>   	mem->flags = 0;
>   	return 0;
> @@ -1041,11 +1042,9 @@ double_to_dec_precise(struct Mem *mem)
>   {
>   	assert(mem->type == MEM_TYPE_DOUBLE);
>   	double d = mem->u.r;
> -	if (d >= 1e38 || d <= -1e38)
> -		return -1;
>   	decimal_t dec;
> -	decimal_from_double(&dec, d);
> -	if (atof(decimal_str(&mem->u.d)) != d)
> +	if (decimal_from_double(&dec, d) == NULL ||
> +	    atof(decimal_str(&dec)) != d)
>   		return -1;
>   	mem->u.d = dec;
>   	mem->type = MEM_TYPE_DEC;
> @@ -1206,8 +1205,9 @@ dec_to_uint_forced(struct Mem *mem)
>   	mem->u.u = u;
>   	/*
>   	 * Decimal is floored when cast to uint, which means that after cast if
> -	 * if was not integer it becomes less if it was positive, and move if it
> -	 * was negative.
> +	 * it was not integer it becomes less if it was positive, and move if it
> +	 * was negative. For example, DECIMAL value -1.5 becomes -1 after cast
> +	 * to INTEGER and DECIMAL value 1.5 becomes 1 after cast to INTEGER.
>   	 */
>   	if (decimal_is_int(&mem->u.d))
>   		return 0;
> 

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [Tarantool-patches] [PATCH v1 4/4] sql: introduce decimal to arithmetic
  2021-08-18 13:32     ` Mergen Imeev via Tarantool-patches
@ 2021-08-18 16:53       ` Safin Timur via Tarantool-patches
  0 siblings, 0 replies; 16+ messages in thread
From: Safin Timur via Tarantool-patches @ 2021-08-18 16:53 UTC (permalink / raw)
  To: Mergen Imeev; +Cc: tarantool-patches

LGTM

On 18.08.2021 16:32, Mergen Imeev wrote:
> Thank you for the review! My answer and diff below. There will be quite big
> diff, but it is just change of description of one error.
> 
> On Mon, Aug 16, 2021 at 10:48:40PM +0300, Safin Timur wrote:
>>
>>
>> On 16.08.2021 18:57, Mergen Imeev via Tarantool-patches wrote:
>>> 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;
>>> +	}
>>
>> 1st question is - was it intentionally that you call here atof, while few
>> lines belowe we use sqlAtoF?
>>
>> 2nd complain is - it all looks that we miss decimal_to_double() function in
>> src/core/decimal.c (where we do have decimal_from_double() but not the
>> reverse direction). It will look more consistent if this implementation
>> would be there. And, I stll believe, there is better way for converting
>> decimal to double, than converting it to string, and then to double.
>>
>> [Though quick navigation over decNumber API didn't reveal the direct
>> approach. Serge, could you please recommend the easiest way here?]
>>
>>
> I will think about this a bit later, for now I decided to not add additional
> functions to decimal.c/.h.
> 
>>> @@ -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)) {
>>
>> And now it looks better than before, and less confusing, I do not have
>> complains here anymore...
>>
>>
>>>    		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)) {
>>
>> And here.
>>
>>>    		diag_set(ClientError, ER_SQL_TYPE_MISMATCH, mem_str(b),
>>>    			 "integer, unsigned or double");
>>>    		return -1;
>>
>> Thanks,
>> Timur
> 
> 
> Diff:
> 
> 
> diff --git a/src/box/sql/mem.c b/src/box/sql/mem.c
> index 077a8b51e..74febd182 100644
> --- a/src/box/sql/mem.c
> +++ b/src/box/sql/mem.c
> @@ -2005,12 +2005,12 @@ check_types_numeric_arithmetic(const struct Mem *a, const struct Mem *b)
>   {
>   	if (!mem_is_num(a) || mem_is_metatype(a)) {
>   		diag_set(ClientError, ER_SQL_TYPE_MISMATCH, mem_str(a),
> -			 "integer, unsigned or double");
> +			 "integer, decimal or double");
>   		return -1;
>   	}
>   	if (!mem_is_num(b) || mem_is_metatype(b)) {
>   		diag_set(ClientError, ER_SQL_TYPE_MISMATCH, mem_str(b),
> -			 "integer, unsigned or double");
> +			 "integer, decimal or double");
>   		return -1;
>   	}
>   	return 0;
> diff --git a/test/sql-tap/gh-5756-implicit-cast-in-arithmetic.test.lua b/test/sql-tap/gh-5756-implicit-cast-in-arithmetic.test.lua
> index 3e4de6860..390a2d9b2 100755
> --- a/test/sql-tap/gh-5756-implicit-cast-in-arithmetic.test.lua
> +++ b/test/sql-tap/gh-5756-implicit-cast-in-arithmetic.test.lua
> @@ -35,7 +35,7 @@ test:do_catchsql_test(
>       [[
>           SELECT 9 + '2';
>       ]], {
> -        1, "Type mismatch: can not convert string('2') to integer, unsigned or double"
> +        1, "Type mismatch: can not convert string('2') to integer, decimal or double"
>       })
>   
>   test:do_catchsql_test(
> @@ -43,7 +43,7 @@ test:do_catchsql_test(
>       [[
>           SELECT 9 + x'32';
>       ]], {
> -        1, "Type mismatch: can not convert varbinary(x'32') to integer, unsigned or double"
> +        1, "Type mismatch: can not convert varbinary(x'32') to integer, decimal or double"
>       })
>   
>   test:do_catchsql_test(
> @@ -51,7 +51,7 @@ test:do_catchsql_test(
>       [[
>           SELECT 9 + true;
>       ]], {
> -        1, "Type mismatch: can not convert boolean(TRUE) to integer, unsigned or double"
> +        1, "Type mismatch: can not convert boolean(TRUE) to integer, decimal or double"
>       })
>   
>   test:do_catchsql_test(
> @@ -60,7 +60,7 @@ test:do_catchsql_test(
>           SELECT 9 + CAST('11111111-1111-1111-1111-111111111111' AS UUID);
>       ]], {
>           1, "Type mismatch: can not convert "..
> -           "uuid('11111111-1111-1111-1111-111111111111') to integer, unsigned or double"
> +           "uuid('11111111-1111-1111-1111-111111111111') to integer, decimal or double"
>       })
>   
>   test:do_execsql_test(
> @@ -92,7 +92,7 @@ test:do_catchsql_test(
>       [[
>           SELECT 9 - '2';
>       ]], {
> -        1, "Type mismatch: can not convert string('2') to integer, unsigned or double"
> +        1, "Type mismatch: can not convert string('2') to integer, decimal or double"
>       })
>   
>   test:do_catchsql_test(
> @@ -100,7 +100,7 @@ test:do_catchsql_test(
>       [[
>           SELECT 9 - x'32';
>       ]], {
> -        1, "Type mismatch: can not convert varbinary(x'32') to integer, unsigned or double"
> +        1, "Type mismatch: can not convert varbinary(x'32') to integer, decimal or double"
>       })
>   
>   test:do_catchsql_test(
> @@ -108,7 +108,7 @@ test:do_catchsql_test(
>       [[
>           SELECT 9 - true;
>       ]], {
> -        1, "Type mismatch: can not convert boolean(TRUE) to integer, unsigned or double"
> +        1, "Type mismatch: can not convert boolean(TRUE) to integer, decimal or double"
>       })
>   
>   test:do_catchsql_test(
> @@ -117,7 +117,7 @@ test:do_catchsql_test(
>           SELECT 9 - CAST('11111111-1111-1111-1111-111111111111' AS UUID);
>       ]], {
>           1, "Type mismatch: can not convert "..
> -           "uuid('11111111-1111-1111-1111-111111111111') to integer, unsigned or double"
> +           "uuid('11111111-1111-1111-1111-111111111111') to integer, decimal or double"
>       })
>   
>   test:do_execsql_test(
> @@ -149,7 +149,7 @@ test:do_catchsql_test(
>       [[
>           SELECT 9 * '2';
>       ]], {
> -        1, "Type mismatch: can not convert string('2') to integer, unsigned or double"
> +        1, "Type mismatch: can not convert string('2') to integer, decimal or double"
>       })
>   
>   test:do_catchsql_test(
> @@ -157,7 +157,7 @@ test:do_catchsql_test(
>       [[
>           SELECT 9 * x'32';
>       ]], {
> -        1, "Type mismatch: can not convert varbinary(x'32') to integer, unsigned or double"
> +        1, "Type mismatch: can not convert varbinary(x'32') to integer, decimal or double"
>       })
>   
>   test:do_catchsql_test(
> @@ -165,7 +165,7 @@ test:do_catchsql_test(
>       [[
>           SELECT 9 * true;
>       ]], {
> -        1, "Type mismatch: can not convert boolean(TRUE) to integer, unsigned or double"
> +        1, "Type mismatch: can not convert boolean(TRUE) to integer, decimal or double"
>       })
>   
>   test:do_catchsql_test(
> @@ -174,7 +174,7 @@ test:do_catchsql_test(
>           SELECT 9 * CAST('11111111-1111-1111-1111-111111111111' AS UUID);
>       ]], {
>           1, "Type mismatch: can not convert "..
> -           "uuid('11111111-1111-1111-1111-111111111111') to integer, unsigned or double"
> +           "uuid('11111111-1111-1111-1111-111111111111') to integer, decimal or double"
>       })
>   
>   test:do_execsql_test(
> @@ -206,7 +206,7 @@ test:do_catchsql_test(
>       [[
>           SELECT 9 / '2';
>       ]], {
> -        1, "Type mismatch: can not convert string('2') to integer, unsigned or double"
> +        1, "Type mismatch: can not convert string('2') to integer, decimal or double"
>       })
>   
>   test:do_catchsql_test(
> @@ -214,7 +214,7 @@ test:do_catchsql_test(
>       [[
>           SELECT 9 / x'32';
>       ]], {
> -        1, "Type mismatch: can not convert varbinary(x'32') to integer, unsigned or double"
> +        1, "Type mismatch: can not convert varbinary(x'32') to integer, decimal or double"
>       })
>   
>   test:do_catchsql_test(
> @@ -222,7 +222,7 @@ test:do_catchsql_test(
>       [[
>           SELECT 9 / true;
>       ]], {
> -        1, "Type mismatch: can not convert boolean(TRUE) to integer, unsigned or double"
> +        1, "Type mismatch: can not convert boolean(TRUE) to integer, decimal or double"
>       })
>   
>   test:do_catchsql_test(
> @@ -231,7 +231,7 @@ test:do_catchsql_test(
>           SELECT 9 / CAST('11111111-1111-1111-1111-111111111111' AS UUID);
>       ]], {
>           1, "Type mismatch: can not convert "..
> -           "uuid('11111111-1111-1111-1111-111111111111') to integer, unsigned or double"
> +           "uuid('11111111-1111-1111-1111-111111111111') to integer, decimal or double"
>       })
>   
>   test:do_execsql_test(
> diff --git a/test/sql-tap/metatypes.test.lua b/test/sql-tap/metatypes.test.lua
> index 349c670ba..3e4091675 100755
> --- a/test/sql-tap/metatypes.test.lua
> +++ b/test/sql-tap/metatypes.test.lua
> @@ -96,7 +96,7 @@ test:do_catchsql_test(
>       [[
>           SELECT 1 + CAST(1 AS NUMBER);
>       ]], {
> -        1, "Type mismatch: can not convert number(1) to integer, unsigned or double"
> +        1, "Type mismatch: can not convert number(1) to integer, decimal or double"
>       })
>   
>   test:do_catchsql_test(
> @@ -104,7 +104,7 @@ test:do_catchsql_test(
>       [[
>           SELECT CAST(1 AS SCALAR) * 1;
>       ]], {
> -        1, "Type mismatch: can not convert scalar(1) to integer, unsigned or double"
> +        1, "Type mismatch: can not convert scalar(1) to integer, decimal or double"
>       })
>   
>   -- Check that bitwise operations are prohibited for NUMBER and SCALAR values.
> diff --git a/test/sql-tap/sql-errors.test.lua b/test/sql-tap/sql-errors.test.lua
> index daf0ee643..ceb4ecccf 100755
> --- a/test/sql-tap/sql-errors.test.lua
> +++ b/test/sql-tap/sql-errors.test.lua
> @@ -696,7 +696,7 @@ test:do_catchsql_test(
>   		SELECT X'ff' + 1;
>   	]], {
>   		-- <sql-errors-2.1>
> -		1, "Type mismatch: can not convert varbinary(x'FF') to integer, unsigned or double"
> +		1, "Type mismatch: can not convert varbinary(x'FF') to integer, decimal or double"
>   		-- </sql-errors-2.1>
>   	})
>   
> @@ -706,7 +706,7 @@ test:do_catchsql_test(
>   		SELECT X'ff' - 1;
>   	]], {
>   		-- <sql-errors-2.2>
> -		1, "Type mismatch: can not convert varbinary(x'FF') to integer, unsigned or double"
> +		1, "Type mismatch: can not convert varbinary(x'FF') to integer, decimal or double"
>   		-- </sql-errors-2.2>
>   	})
>   
> @@ -716,7 +716,7 @@ test:do_catchsql_test(
>   		SELECT X'ff' * 1;
>   	]], {
>   		-- <sql-errors-2.3>
> -		1, "Type mismatch: can not convert varbinary(x'FF') to integer, unsigned or double"
> +		1, "Type mismatch: can not convert varbinary(x'FF') to integer, decimal or double"
>   		-- </sql-errors-2.3>
>   	})
>   
> @@ -726,7 +726,7 @@ test:do_catchsql_test(
>   		SELECT X'ff' / 1;
>   	]], {
>   		-- <sql-errors-2.4>
> -		1, "Type mismatch: can not convert varbinary(x'FF') to integer, unsigned or double"
> +		1, "Type mismatch: can not convert varbinary(x'FF') to integer, decimal or double"
>   		-- </sql-errors-2.4>
>   	})
>   
> diff --git a/test/sql-tap/tkt-a8a0d2996a.test.lua b/test/sql-tap/tkt-a8a0d2996a.test.lua
> index 72f57ec65..a40621f41 100755
> --- a/test/sql-tap/tkt-a8a0d2996a.test.lua
> +++ b/test/sql-tap/tkt-a8a0d2996a.test.lua
> @@ -27,7 +27,7 @@ test:do_catchsql_test(
>           SELECT typeof(x), typeof(y) FROM t WHERE 1=x+0 AND y=='1';
>       ]], {
>           -- <1.0>
> -        1, "Type mismatch: can not convert string('1') to integer, unsigned or double"
> +        1, "Type mismatch: can not convert string('1') to integer, decimal or double"
>           -- </1.0>
>       })
>   
> @@ -37,7 +37,7 @@ test:do_catchsql_test(
>           SELECT typeof(x), typeof(y) FROM t WHERE 1=x-0 AND y=='1';
>       ]], {
>           -- <1.1>
> -        1, "Type mismatch: can not convert string('1') to integer, unsigned or double"
> +        1, "Type mismatch: can not convert string('1') to integer, decimal or double"
>           -- </1.1>
>       })
>   
> @@ -47,7 +47,7 @@ test:do_catchsql_test(
>           SELECT typeof(x), typeof(y) FROM t WHERE 1=x*1 AND y=='1';
>       ]], {
>           -- <1.2>
> -        1, "Type mismatch: can not convert string('1') to integer, unsigned or double"
> +        1, "Type mismatch: can not convert string('1') to integer, decimal or double"
>           -- </1.2>
>       })
>   
> @@ -57,7 +57,7 @@ test:do_catchsql_test(
>           SELECT typeof(x), typeof(y) FROM t WHERE 1=x/1 AND y=='1';
>       ]], {
>           -- <1.3>
> -        1, "Type mismatch: can not convert string('1') to integer, unsigned or double"
> +        1, "Type mismatch: can not convert string('1') to integer, decimal or double"
>           -- </1.3>
>       })
>   
> @@ -78,7 +78,7 @@ test:do_catchsql_test(
>           SELECT typeof(x), typeof(y) FROM t WHERE 1=x+0 AND y=='1';
>       ]], {
>           -- <3.0>
> -        1, "Type mismatch: can not convert string('1.0') to integer, unsigned or double"
> +        1, "Type mismatch: can not convert string('1.0') to integer, decimal or double"
>           -- </3.0>
>       })
>   
> @@ -88,7 +88,7 @@ test:do_catchsql_test(
>           SELECT typeof(x), typeof(y) FROM t WHERE 1=x-0 AND y=='1';
>       ]], {
>           -- <3.1>
> -        1, "Type mismatch: can not convert string('1.0') to integer, unsigned or double"
> +        1, "Type mismatch: can not convert string('1.0') to integer, decimal or double"
>           -- </3.1>
>       })
>   
> @@ -98,7 +98,7 @@ test:do_catchsql_test(
>           SELECT typeof(x), typeof(y) FROM t WHERE 1=x*1 AND y=='1';
>       ]], {
>           -- <3.2>
> -        1, "Type mismatch: can not convert string('1.0') to integer, unsigned or double"
> +        1, "Type mismatch: can not convert string('1.0') to integer, decimal or double"
>           -- </3.2>
>       })
>   
> @@ -108,7 +108,7 @@ test:do_catchsql_test(
>           SELECT typeof(x), typeof(y) FROM t WHERE 1=x/1 AND y=='1';
>       ]], {
>           -- <3.3>
> -        1, "Type mismatch: can not convert string('1.0') to integer, unsigned or double"
> +        1, "Type mismatch: can not convert string('1.0') to integer, decimal or double"
>           -- </3.3>
>       })
>   
> @@ -138,7 +138,7 @@ test:do_catchsql_test(
>           SELECT '1.23e64'/'1.0000e+62';
>       ]], {
>           -- <4.1>
> -        1, "Type mismatch: can not convert string('1.0000e+62') to integer, unsigned or double"
> +        1, "Type mismatch: can not convert string('1.0000e+62') to integer, decimal or double"
>           -- </4.1>
>       })
>   
> diff --git a/test/sql-tap/uuid.test.lua b/test/sql-tap/uuid.test.lua
> index 57e638046..177798cfa 100755
> --- a/test/sql-tap/uuid.test.lua
> +++ b/test/sql-tap/uuid.test.lua
> @@ -957,7 +957,7 @@ test:do_catchsql_test(
>       [[
>           SELECT -u FROM t2;
>       ]], {
> -        1, "Type mismatch: can not convert uuid('11111111-1111-1111-1111-111111111111') to integer, unsigned or double"
> +        1, "Type mismatch: can not convert uuid('11111111-1111-1111-1111-111111111111') to integer, decimal or double"
>       })
>   
>   test:do_catchsql_test(
> @@ -965,7 +965,7 @@ test:do_catchsql_test(
>       [[
>           SELECT u + 1 FROM t2;
>       ]], {
> -        1, "Type mismatch: can not convert uuid('11111111-1111-1111-1111-111111111111') to integer, unsigned or double"
> +        1, "Type mismatch: can not convert uuid('11111111-1111-1111-1111-111111111111') to integer, decimal or double"
>       })
>   
>   test:do_catchsql_test(
> @@ -973,7 +973,7 @@ test:do_catchsql_test(
>       [[
>           SELECT u - 1 FROM t2;
>       ]], {
> -        1, "Type mismatch: can not convert uuid('11111111-1111-1111-1111-111111111111') to integer, unsigned or double"
> +        1, "Type mismatch: can not convert uuid('11111111-1111-1111-1111-111111111111') to integer, decimal or double"
>       })
>   
>   test:do_catchsql_test(
> @@ -981,7 +981,7 @@ test:do_catchsql_test(
>       [[
>           SELECT u * 1 FROM t2;
>       ]], {
> -        1, "Type mismatch: can not convert uuid('11111111-1111-1111-1111-111111111111') to integer, unsigned or double"
> +        1, "Type mismatch: can not convert uuid('11111111-1111-1111-1111-111111111111') to integer, decimal or double"
>       })
>   
>   test:do_catchsql_test(
> @@ -989,7 +989,7 @@ test:do_catchsql_test(
>       [[
>           SELECT u / 1 FROM t2;
>       ]], {
> -        1, "Type mismatch: can not convert uuid('11111111-1111-1111-1111-111111111111') to integer, unsigned or double"
> +        1, "Type mismatch: can not convert uuid('11111111-1111-1111-1111-111111111111') to integer, decimal or double"
>       })
>   
>   test:do_catchsql_test(
> diff --git a/test/sql/boolean.result b/test/sql/boolean.result
> index a9ce37e11..000142ebe 100644
> --- a/test/sql/boolean.result
> +++ b/test/sql/boolean.result
> @@ -1131,98 +1131,98 @@ SELECT a, a1, a OR a1 FROM t, t6;
>   SELECT -true;
>    | ---
>    | - null
> - | - 'Type mismatch: can not convert boolean(TRUE) to integer, unsigned or double'
> + | - 'Type mismatch: can not convert boolean(TRUE) to integer, decimal or double'
>    | ...
>   SELECT -false;
>    | ---
>    | - null
> - | - 'Type mismatch: can not convert boolean(FALSE) to integer, unsigned or double'
> + | - 'Type mismatch: can not convert boolean(FALSE) to integer, decimal or double'
>    | ...
>   SELECT -a FROM t;
>    | ---
>    | - null
> - | - 'Type mismatch: can not convert boolean(FALSE) to integer, unsigned or double'
> + | - 'Type mismatch: can not convert boolean(FALSE) to integer, decimal or double'
>    | ...
>   
>   SELECT true + true;
>    | ---
>    | - null
> - | - 'Type mismatch: can not convert boolean(TRUE) to integer, unsigned or double'
> + | - 'Type mismatch: can not convert boolean(TRUE) to integer, decimal or double'
>    | ...
>   SELECT true + false;
>    | ---
>    | - null
> - | - 'Type mismatch: can not convert boolean(FALSE) to integer, unsigned or double'
> + | - 'Type mismatch: can not convert boolean(FALSE) to integer, decimal or double'
>    | ...
>   SELECT false + true;
>    | ---
>    | - null
> - | - 'Type mismatch: can not convert boolean(TRUE) to integer, unsigned or double'
> + | - 'Type mismatch: can not convert boolean(TRUE) to integer, decimal or double'
>    | ...
>   SELECT false + false;
>    | ---
>    | - null
> - | - 'Type mismatch: can not convert boolean(FALSE) to integer, unsigned or double'
> + | - 'Type mismatch: can not convert boolean(FALSE) to integer, decimal or double'
>    | ...
>   SELECT true - true;
>    | ---
>    | - null
> - | - 'Type mismatch: can not convert boolean(TRUE) to integer, unsigned or double'
> + | - 'Type mismatch: can not convert boolean(TRUE) to integer, decimal or double'
>    | ...
>   SELECT true - false;
>    | ---
>    | - null
> - | - 'Type mismatch: can not convert boolean(FALSE) to integer, unsigned or double'
> + | - 'Type mismatch: can not convert boolean(FALSE) to integer, decimal or double'
>    | ...
>   SELECT false - true;
>    | ---
>    | - null
> - | - 'Type mismatch: can not convert boolean(TRUE) to integer, unsigned or double'
> + | - 'Type mismatch: can not convert boolean(TRUE) to integer, decimal or double'
>    | ...
>   SELECT false - false;
>    | ---
>    | - null
> - | - 'Type mismatch: can not convert boolean(FALSE) to integer, unsigned or double'
> + | - 'Type mismatch: can not convert boolean(FALSE) to integer, decimal or double'
>    | ...
>   SELECT true * true;
>    | ---
>    | - null
> - | - 'Type mismatch: can not convert boolean(TRUE) to integer, unsigned or double'
> + | - 'Type mismatch: can not convert boolean(TRUE) to integer, decimal or double'
>    | ...
>   SELECT true * false;
>    | ---
>    | - null
> - | - 'Type mismatch: can not convert boolean(FALSE) to integer, unsigned or double'
> + | - 'Type mismatch: can not convert boolean(FALSE) to integer, decimal or double'
>    | ...
>   SELECT false * true;
>    | ---
>    | - null
> - | - 'Type mismatch: can not convert boolean(TRUE) to integer, unsigned or double'
> + | - 'Type mismatch: can not convert boolean(TRUE) to integer, decimal or double'
>    | ...
>   SELECT false * false;
>    | ---
>    | - null
> - | - 'Type mismatch: can not convert boolean(FALSE) to integer, unsigned or double'
> + | - 'Type mismatch: can not convert boolean(FALSE) to integer, decimal or double'
>    | ...
>   SELECT true / true;
>    | ---
>    | - null
> - | - 'Type mismatch: can not convert boolean(TRUE) to integer, unsigned or double'
> + | - 'Type mismatch: can not convert boolean(TRUE) to integer, decimal or double'
>    | ...
>   SELECT true / false;
>    | ---
>    | - null
> - | - 'Type mismatch: can not convert boolean(FALSE) to integer, unsigned or double'
> + | - 'Type mismatch: can not convert boolean(FALSE) to integer, decimal or double'
>    | ...
>   SELECT false / true;
>    | ---
>    | - null
> - | - 'Type mismatch: can not convert boolean(TRUE) to integer, unsigned or double'
> + | - 'Type mismatch: can not convert boolean(TRUE) to integer, decimal or double'
>    | ...
>   SELECT false / false;
>    | ---
>    | - null
> - | - 'Type mismatch: can not convert boolean(FALSE) to integer, unsigned or double'
> + | - 'Type mismatch: can not convert boolean(FALSE) to integer, decimal or double'
>    | ...
>   SELECT true % true;
>    | ---
> @@ -1248,42 +1248,42 @@ SELECT false % false;
>   SELECT a, true + a FROM t;
>    | ---
>    | - null
> - | - 'Type mismatch: can not convert boolean(FALSE) to integer, unsigned or double'
> + | - 'Type mismatch: can not convert boolean(FALSE) to integer, decimal or double'
>    | ...
>   SELECT a, false + a FROM t;
>    | ---
>    | - null
> - | - 'Type mismatch: can not convert boolean(FALSE) to integer, unsigned or double'
> + | - 'Type mismatch: can not convert boolean(FALSE) to integer, decimal or double'
>    | ...
>   SELECT a, true - a FROM t;
>    | ---
>    | - null
> - | - 'Type mismatch: can not convert boolean(FALSE) to integer, unsigned or double'
> + | - 'Type mismatch: can not convert boolean(FALSE) to integer, decimal or double'
>    | ...
>   SELECT a, false - a FROM t;
>    | ---
>    | - null
> - | - 'Type mismatch: can not convert boolean(FALSE) to integer, unsigned or double'
> + | - 'Type mismatch: can not convert boolean(FALSE) to integer, decimal or double'
>    | ...
>   SELECT a, true * a FROM t;
>    | ---
>    | - null
> - | - 'Type mismatch: can not convert boolean(FALSE) to integer, unsigned or double'
> + | - 'Type mismatch: can not convert boolean(FALSE) to integer, decimal or double'
>    | ...
>   SELECT a, false * a FROM t;
>    | ---
>    | - null
> - | - 'Type mismatch: can not convert boolean(FALSE) to integer, unsigned or double'
> + | - 'Type mismatch: can not convert boolean(FALSE) to integer, decimal or double'
>    | ...
>   SELECT a, true / a FROM t;
>    | ---
>    | - null
> - | - 'Type mismatch: can not convert boolean(FALSE) to integer, unsigned or double'
> + | - 'Type mismatch: can not convert boolean(FALSE) to integer, decimal or double'
>    | ...
>   SELECT a, false / a FROM t;
>    | ---
>    | - null
> - | - 'Type mismatch: can not convert boolean(FALSE) to integer, unsigned or double'
> + | - 'Type mismatch: can not convert boolean(FALSE) to integer, decimal or double'
>    | ...
>   SELECT a, true % a FROM t;
>    | ---
> @@ -1298,42 +1298,42 @@ SELECT a, false % a FROM t;
>   SELECT a, a + true FROM t;
>    | ---
>    | - null
> - | - 'Type mismatch: can not convert boolean(TRUE) to integer, unsigned or double'
> + | - 'Type mismatch: can not convert boolean(TRUE) to integer, decimal or double'
>    | ...
>   SELECT a, a + false FROM t;
>    | ---
>    | - null
> - | - 'Type mismatch: can not convert boolean(FALSE) to integer, unsigned or double'
> + | - 'Type mismatch: can not convert boolean(FALSE) to integer, decimal or double'
>    | ...
>   SELECT a, a - true FROM t;
>    | ---
>    | - null
> - | - 'Type mismatch: can not convert boolean(TRUE) to integer, unsigned or double'
> + | - 'Type mismatch: can not convert boolean(TRUE) to integer, decimal or double'
>    | ...
>   SELECT a, a - false FROM t;
>    | ---
>    | - null
> - | - 'Type mismatch: can not convert boolean(FALSE) to integer, unsigned or double'
> + | - 'Type mismatch: can not convert boolean(FALSE) to integer, decimal or double'
>    | ...
>   SELECT a, a * true FROM t;
>    | ---
>    | - null
> - | - 'Type mismatch: can not convert boolean(TRUE) to integer, unsigned or double'
> + | - 'Type mismatch: can not convert boolean(TRUE) to integer, decimal or double'
>    | ...
>   SELECT a, a * false FROM t;
>    | ---
>    | - null
> - | - 'Type mismatch: can not convert boolean(FALSE) to integer, unsigned or double'
> + | - 'Type mismatch: can not convert boolean(FALSE) to integer, decimal or double'
>    | ...
>   SELECT a, a / true FROM t;
>    | ---
>    | - null
> - | - 'Type mismatch: can not convert boolean(TRUE) to integer, unsigned or double'
> + | - 'Type mismatch: can not convert boolean(TRUE) to integer, decimal or double'
>    | ...
>   SELECT a, a / false FROM t;
>    | ---
>    | - null
> - | - 'Type mismatch: can not convert boolean(FALSE) to integer, unsigned or double'
> + | - 'Type mismatch: can not convert boolean(FALSE) to integer, decimal or double'
>    | ...
>   SELECT a, a % true FROM t;
>    | ---
> @@ -1349,22 +1349,22 @@ SELECT a, a % false FROM t;
>   SELECT a, a1, a + a1 FROM t, t6;
>    | ---
>    | - null
> - | - 'Type mismatch: can not convert boolean(FALSE) to integer, unsigned or double'
> + | - 'Type mismatch: can not convert boolean(FALSE) to integer, decimal or double'
>    | ...
>   SELECT a, a1, a - a1 FROM t, t6;
>    | ---
>    | - null
> - | - 'Type mismatch: can not convert boolean(FALSE) to integer, unsigned or double'
> + | - 'Type mismatch: can not convert boolean(FALSE) to integer, decimal or double'
>    | ...
>   SELECT a, a1, a * a1 FROM t, t6;
>    | ---
>    | - null
> - | - 'Type mismatch: can not convert boolean(FALSE) to integer, unsigned or double'
> + | - 'Type mismatch: can not convert boolean(FALSE) to integer, decimal or double'
>    | ...
>   SELECT a, a1, a / a1 FROM t, t6;
>    | ---
>    | - null
> - | - 'Type mismatch: can not convert boolean(FALSE) to integer, unsigned or double'
> + | - 'Type mismatch: can not convert boolean(FALSE) to integer, decimal or double'
>    | ...
>   SELECT a, a1, a % a1 FROM t, t6;
>    | ---
> @@ -2652,42 +2652,42 @@ SELECT a2, b, b OR a2 FROM t6, t7;
>   SELECT true + 2;
>    | ---
>    | - null
> - | - 'Type mismatch: can not convert boolean(TRUE) to integer, unsigned or double'
> + | - 'Type mismatch: can not convert boolean(TRUE) to integer, decimal or double'
>    | ...
>   SELECT false + 2;
>    | ---
>    | - null
> - | - 'Type mismatch: can not convert boolean(FALSE) to integer, unsigned or double'
> + | - 'Type mismatch: can not convert boolean(FALSE) to integer, decimal or double'
>    | ...
>   SELECT true - 2;
>    | ---
>    | - null
> - | - 'Type mismatch: can not convert boolean(TRUE) to integer, unsigned or double'
> + | - 'Type mismatch: can not convert boolean(TRUE) to integer, decimal or double'
>    | ...
>   SELECT false - 2;
>    | ---
>    | - null
> - | - 'Type mismatch: can not convert boolean(FALSE) to integer, unsigned or double'
> + | - 'Type mismatch: can not convert boolean(FALSE) to integer, decimal or double'
>    | ...
>   SELECT true * 2;
>    | ---
>    | - null
> - | - 'Type mismatch: can not convert boolean(TRUE) to integer, unsigned or double'
> + | - 'Type mismatch: can not convert boolean(TRUE) to integer, decimal or double'
>    | ...
>   SELECT false * 2;
>    | ---
>    | - null
> - | - 'Type mismatch: can not convert boolean(FALSE) to integer, unsigned or double'
> + | - 'Type mismatch: can not convert boolean(FALSE) to integer, decimal or double'
>    | ...
>   SELECT true / 2;
>    | ---
>    | - null
> - | - 'Type mismatch: can not convert boolean(TRUE) to integer, unsigned or double'
> + | - 'Type mismatch: can not convert boolean(TRUE) to integer, decimal or double'
>    | ...
>   SELECT false / 2;
>    | ---
>    | - null
> - | - 'Type mismatch: can not convert boolean(FALSE) to integer, unsigned or double'
> + | - 'Type mismatch: can not convert boolean(FALSE) to integer, decimal or double'
>    | ...
>   SELECT true % 2;
>    | ---
> @@ -2702,42 +2702,42 @@ SELECT false % 2;
>   SELECT 2 + true;
>    | ---
>    | - null
> - | - 'Type mismatch: can not convert boolean(TRUE) to integer, unsigned or double'
> + | - 'Type mismatch: can not convert boolean(TRUE) to integer, decimal or double'
>    | ...
>   SELECT 2 + false;
>    | ---
>    | - null
> - | - 'Type mismatch: can not convert boolean(FALSE) to integer, unsigned or double'
> + | - 'Type mismatch: can not convert boolean(FALSE) to integer, decimal or double'
>    | ...
>   SELECT 2 - true;
>    | ---
>    | - null
> - | - 'Type mismatch: can not convert boolean(TRUE) to integer, unsigned or double'
> + | - 'Type mismatch: can not convert boolean(TRUE) to integer, decimal or double'
>    | ...
>   SELECT 2 - false;
>    | ---
>    | - null
> - | - 'Type mismatch: can not convert boolean(FALSE) to integer, unsigned or double'
> + | - 'Type mismatch: can not convert boolean(FALSE) to integer, decimal or double'
>    | ...
>   SELECT 2 * true;
>    | ---
>    | - null
> - | - 'Type mismatch: can not convert boolean(TRUE) to integer, unsigned or double'
> + | - 'Type mismatch: can not convert boolean(TRUE) to integer, decimal or double'
>    | ...
>   SELECT 2 * false;
>    | ---
>    | - null
> - | - 'Type mismatch: can not convert boolean(FALSE) to integer, unsigned or double'
> + | - 'Type mismatch: can not convert boolean(FALSE) to integer, decimal or double'
>    | ...
>   SELECT 2 / true;
>    | ---
>    | - null
> - | - 'Type mismatch: can not convert boolean(TRUE) to integer, unsigned or double'
> + | - 'Type mismatch: can not convert boolean(TRUE) to integer, decimal or double'
>    | ...
>   SELECT 2 / false;
>    | ---
>    | - null
> - | - 'Type mismatch: can not convert boolean(FALSE) to integer, unsigned or double'
> + | - 'Type mismatch: can not convert boolean(FALSE) to integer, decimal or double'
>    | ...
>   SELECT 2 % true;
>    | ---
> @@ -2753,22 +2753,22 @@ SELECT 2 % false;
>   SELECT a1, a1 + 2 FROM t6
>    | ---
>    | - null
> - | - 'Type mismatch: can not convert boolean(FALSE) to integer, unsigned or double'
> + | - 'Type mismatch: can not convert boolean(FALSE) to integer, decimal or double'
>    | ...
>   SELECT a1, a1 - 2 FROM t6
>    | ---
>    | - null
> - | - 'Type mismatch: can not convert boolean(FALSE) to integer, unsigned or double'
> + | - 'Type mismatch: can not convert boolean(FALSE) to integer, decimal or double'
>    | ...
>   SELECT a1, a1 * 2 FROM t6
>    | ---
>    | - null
> - | - 'Type mismatch: can not convert boolean(FALSE) to integer, unsigned or double'
> + | - 'Type mismatch: can not convert boolean(FALSE) to integer, decimal or double'
>    | ...
>   SELECT a1, a1 / 2 FROM t6
>    | ---
>    | - null
> - | - 'Type mismatch: can not convert boolean(FALSE) to integer, unsigned or double'
> + | - 'Type mismatch: can not convert boolean(FALSE) to integer, decimal or double'
>    | ...
>   SELECT a1, a1 % 2 FROM t6
>    | ---
> @@ -2778,22 +2778,22 @@ SELECT a1, a1 % 2 FROM t6
>   SELECT a1, 2 + a1 FROM t6
>    | ---
>    | - null
> - | - 'Type mismatch: can not convert boolean(FALSE) to integer, unsigned or double'
> + | - 'Type mismatch: can not convert boolean(FALSE) to integer, decimal or double'
>    | ...
>   SELECT a1, 2 - a1 FROM t6
>    | ---
>    | - null
> - | - 'Type mismatch: can not convert boolean(FALSE) to integer, unsigned or double'
> + | - 'Type mismatch: can not convert boolean(FALSE) to integer, decimal or double'
>    | ...
>   SELECT a1, 2 * a1 FROM t6
>    | ---
>    | - null
> - | - 'Type mismatch: can not convert boolean(FALSE) to integer, unsigned or double'
> + | - 'Type mismatch: can not convert boolean(FALSE) to integer, decimal or double'
>    | ...
>   SELECT a1, 2 / a1 FROM t6
>    | ---
>    | - null
> - | - 'Type mismatch: can not convert boolean(FALSE) to integer, unsigned or double'
> + | - 'Type mismatch: can not convert boolean(FALSE) to integer, decimal or double'
>    | ...
>   SELECT a1, 2 % a1 FROM t6
>    | ---
> @@ -2803,22 +2803,22 @@ SELECT a1, 2 % a1 FROM t6
>   SELECT a2, a2 + 2 FROM t6
>    | ---
>    | - null
> - | - 'Type mismatch: can not convert boolean(TRUE) to integer, unsigned or double'
> + | - 'Type mismatch: can not convert boolean(TRUE) to integer, decimal or double'
>    | ...
>   SELECT a2, a2 - 2 FROM t6
>    | ---
>    | - null
> - | - 'Type mismatch: can not convert boolean(TRUE) to integer, unsigned or double'
> + | - 'Type mismatch: can not convert boolean(TRUE) to integer, decimal or double'
>    | ...
>   SELECT a2, a2 * 2 FROM t6
>    | ---
>    | - null
> - | - 'Type mismatch: can not convert boolean(TRUE) to integer, unsigned or double'
> + | - 'Type mismatch: can not convert boolean(TRUE) to integer, decimal or double'
>    | ...
>   SELECT a2, a2 / 2 FROM t6
>    | ---
>    | - null
> - | - 'Type mismatch: can not convert boolean(TRUE) to integer, unsigned or double'
> + | - 'Type mismatch: can not convert boolean(TRUE) to integer, decimal or double'
>    | ...
>   SELECT a2, a2 % 2 FROM t6
>    | ---
> @@ -2828,22 +2828,22 @@ SELECT a2, a2 % 2 FROM t6
>   SELECT a2, 2 + a2 FROM t6
>    | ---
>    | - null
> - | - 'Type mismatch: can not convert boolean(TRUE) to integer, unsigned or double'
> + | - 'Type mismatch: can not convert boolean(TRUE) to integer, decimal or double'
>    | ...
>   SELECT a2, 2 - a2 FROM t6
>    | ---
>    | - null
> - | - 'Type mismatch: can not convert boolean(TRUE) to integer, unsigned or double'
> + | - 'Type mismatch: can not convert boolean(TRUE) to integer, decimal or double'
>    | ...
>   SELECT a2, 2 * a2 FROM t6
>    | ---
>    | - null
> - | - 'Type mismatch: can not convert boolean(TRUE) to integer, unsigned or double'
> + | - 'Type mismatch: can not convert boolean(TRUE) to integer, decimal or double'
>    | ...
>   SELECT a2, 2 / a2 FROM t6
>    | ---
>    | - null
> - | - 'Type mismatch: can not convert boolean(TRUE) to integer, unsigned or double'
> + | - 'Type mismatch: can not convert boolean(TRUE) to integer, decimal or double'
>    | ...
>   SELECT a2, 2 % a2 FROM t6
>    | ---
> @@ -2854,42 +2854,42 @@ SELECT a2, 2 % a2 FROM t6
>   SELECT b, true + b FROM t7;
>    | ---
>    | - null
> - | - 'Type mismatch: can not convert boolean(TRUE) to integer, unsigned or double'
> + | - 'Type mismatch: can not convert boolean(TRUE) to integer, decimal or double'
>    | ...
>   SELECT b, false + b FROM t7;
>    | ---
>    | - null
> - | - 'Type mismatch: can not convert boolean(FALSE) to integer, unsigned or double'
> + | - 'Type mismatch: can not convert boolean(FALSE) to integer, decimal or double'
>    | ...
>   SELECT b, true - b FROM t7;
>    | ---
>    | - null
> - | - 'Type mismatch: can not convert boolean(TRUE) to integer, unsigned or double'
> + | - 'Type mismatch: can not convert boolean(TRUE) to integer, decimal or double'
>    | ...
>   SELECT b, false - b FROM t7;
>    | ---
>    | - null
> - | - 'Type mismatch: can not convert boolean(FALSE) to integer, unsigned or double'
> + | - 'Type mismatch: can not convert boolean(FALSE) to integer, decimal or double'
>    | ...
>   SELECT b, true * b FROM t7;
>    | ---
>    | - null
> - | - 'Type mismatch: can not convert boolean(TRUE) to integer, unsigned or double'
> + | - 'Type mismatch: can not convert boolean(TRUE) to integer, decimal or double'
>    | ...
>   SELECT b, false * b FROM t7;
>    | ---
>    | - null
> - | - 'Type mismatch: can not convert boolean(FALSE) to integer, unsigned or double'
> + | - 'Type mismatch: can not convert boolean(FALSE) to integer, decimal or double'
>    | ...
>   SELECT b, true / b FROM t7;
>    | ---
>    | - null
> - | - 'Type mismatch: can not convert boolean(TRUE) to integer, unsigned or double'
> + | - 'Type mismatch: can not convert boolean(TRUE) to integer, decimal or double'
>    | ...
>   SELECT b, false / b FROM t7;
>    | ---
>    | - null
> - | - 'Type mismatch: can not convert boolean(FALSE) to integer, unsigned or double'
> + | - 'Type mismatch: can not convert boolean(FALSE) to integer, decimal or double'
>    | ...
>   SELECT b, true % b FROM t7;
>    | ---
> @@ -2904,42 +2904,42 @@ SELECT b, false % b FROM t7;
>   SELECT b, b + true FROM t7;
>    | ---
>    | - null
> - | - 'Type mismatch: can not convert boolean(TRUE) to integer, unsigned or double'
> + | - 'Type mismatch: can not convert boolean(TRUE) to integer, decimal or double'
>    | ...
>   SELECT b, b + false FROM t7;
>    | ---
>    | - null
> - | - 'Type mismatch: can not convert boolean(FALSE) to integer, unsigned or double'
> + | - 'Type mismatch: can not convert boolean(FALSE) to integer, decimal or double'
>    | ...
>   SELECT b, b - true FROM t7;
>    | ---
>    | - null
> - | - 'Type mismatch: can not convert boolean(TRUE) to integer, unsigned or double'
> + | - 'Type mismatch: can not convert boolean(TRUE) to integer, decimal or double'
>    | ...
>   SELECT b, b - false FROM t7;
>    | ---
>    | - null
> - | - 'Type mismatch: can not convert boolean(FALSE) to integer, unsigned or double'
> + | - 'Type mismatch: can not convert boolean(FALSE) to integer, decimal or double'
>    | ...
>   SELECT b, b * true FROM t7;
>    | ---
>    | - null
> - | - 'Type mismatch: can not convert boolean(TRUE) to integer, unsigned or double'
> + | - 'Type mismatch: can not convert boolean(TRUE) to integer, decimal or double'
>    | ...
>   SELECT b, b * false FROM t7;
>    | ---
>    | - null
> - | - 'Type mismatch: can not convert boolean(FALSE) to integer, unsigned or double'
> + | - 'Type mismatch: can not convert boolean(FALSE) to integer, decimal or double'
>    | ...
>   SELECT b, b / true FROM t7;
>    | ---
>    | - null
> - | - 'Type mismatch: can not convert boolean(TRUE) to integer, unsigned or double'
> + | - 'Type mismatch: can not convert boolean(TRUE) to integer, decimal or double'
>    | ...
>   SELECT b, b / false FROM t7;
>    | ---
>    | - null
> - | - 'Type mismatch: can not convert boolean(FALSE) to integer, unsigned or double'
> + | - 'Type mismatch: can not convert boolean(FALSE) to integer, decimal or double'
>    | ...
>   SELECT b, b % true FROM t7;
>    | ---
> @@ -2955,22 +2955,22 @@ SELECT b, b % false FROM t7;
>   SELECT a1, b, a1 + b FROM t6, t7;
>    | ---
>    | - null
> - | - 'Type mismatch: can not convert boolean(FALSE) to integer, unsigned or double'
> + | - 'Type mismatch: can not convert boolean(FALSE) to integer, decimal or double'
>    | ...
>   SELECT a1, b, a1 - b FROM t6, t7;
>    | ---
>    | - null
> - | - 'Type mismatch: can not convert boolean(FALSE) to integer, unsigned or double'
> + | - 'Type mismatch: can not convert boolean(FALSE) to integer, decimal or double'
>    | ...
>   SELECT a1, b, a1 * b FROM t6, t7;
>    | ---
>    | - null
> - | - 'Type mismatch: can not convert boolean(FALSE) to integer, unsigned or double'
> + | - 'Type mismatch: can not convert boolean(FALSE) to integer, decimal or double'
>    | ...
>   SELECT a1, b, a1 / b FROM t6, t7;
>    | ---
>    | - null
> - | - 'Type mismatch: can not convert boolean(FALSE) to integer, unsigned or double'
> + | - 'Type mismatch: can not convert boolean(FALSE) to integer, decimal or double'
>    | ...
>   SELECT a1, b, a1 % b FROM t6, t7;
>    | ---
> @@ -2980,22 +2980,22 @@ SELECT a1, b, a1 % b FROM t6, t7;
>   SELECT a1, b, b + a1 FROM t6, t7;
>    | ---
>    | - null
> - | - 'Type mismatch: can not convert boolean(FALSE) to integer, unsigned or double'
> + | - 'Type mismatch: can not convert boolean(FALSE) to integer, decimal or double'
>    | ...
>   SELECT a1, b, b - a1 FROM t6, t7;
>    | ---
>    | - null
> - | - 'Type mismatch: can not convert boolean(FALSE) to integer, unsigned or double'
> + | - 'Type mismatch: can not convert boolean(FALSE) to integer, decimal or double'
>    | ...
>   SELECT a1, b, b * a1 FROM t6, t7;
>    | ---
>    | - null
> - | - 'Type mismatch: can not convert boolean(FALSE) to integer, unsigned or double'
> + | - 'Type mismatch: can not convert boolean(FALSE) to integer, decimal or double'
>    | ...
>   SELECT a1, b, b / a1 FROM t6, t7;
>    | ---
>    | - null
> - | - 'Type mismatch: can not convert boolean(FALSE) to integer, unsigned or double'
> + | - 'Type mismatch: can not convert boolean(FALSE) to integer, decimal or double'
>    | ...
>   SELECT a1, b, b % a1 FROM t6, t7;
>    | ---
> @@ -3005,22 +3005,22 @@ SELECT a1, b, b % a1 FROM t6, t7;
>   SELECT a2, b, a2 + b FROM t6, t7;
>    | ---
>    | - null
> - | - 'Type mismatch: can not convert boolean(TRUE) to integer, unsigned or double'
> + | - 'Type mismatch: can not convert boolean(TRUE) to integer, decimal or double'
>    | ...
>   SELECT a2, b, a2 - b FROM t6, t7;
>    | ---
>    | - null
> - | - 'Type mismatch: can not convert boolean(TRUE) to integer, unsigned or double'
> + | - 'Type mismatch: can not convert boolean(TRUE) to integer, decimal or double'
>    | ...
>   SELECT a2, b, a2 * b FROM t6, t7;
>    | ---
>    | - null
> - | - 'Type mismatch: can not convert boolean(TRUE) to integer, unsigned or double'
> + | - 'Type mismatch: can not convert boolean(TRUE) to integer, decimal or double'
>    | ...
>   SELECT a2, b, a2 / b FROM t6, t7;
>    | ---
>    | - null
> - | - 'Type mismatch: can not convert boolean(TRUE) to integer, unsigned or double'
> + | - 'Type mismatch: can not convert boolean(TRUE) to integer, decimal or double'
>    | ...
>   SELECT a2, b, a2 % b FROM t6, t7;
>    | ---
> @@ -3030,22 +3030,22 @@ SELECT a2, b, a2 % b FROM t6, t7;
>   SELECT a2, b, b + a2 FROM t6, t7;
>    | ---
>    | - null
> - | - 'Type mismatch: can not convert boolean(TRUE) to integer, unsigned or double'
> + | - 'Type mismatch: can not convert boolean(TRUE) to integer, decimal or double'
>    | ...
>   SELECT a2, b, b - a2 FROM t6, t7;
>    | ---
>    | - null
> - | - 'Type mismatch: can not convert boolean(TRUE) to integer, unsigned or double'
> + | - 'Type mismatch: can not convert boolean(TRUE) to integer, decimal or double'
>    | ...
>   SELECT a2, b, b * a2 FROM t6, t7;
>    | ---
>    | - null
> - | - 'Type mismatch: can not convert boolean(TRUE) to integer, unsigned or double'
> + | - 'Type mismatch: can not convert boolean(TRUE) to integer, decimal or double'
>    | ...
>   SELECT a2, b, b / a2 FROM t6, t7;
>    | ---
>    | - null
> - | - 'Type mismatch: can not convert boolean(TRUE) to integer, unsigned or double'
> + | - 'Type mismatch: can not convert boolean(TRUE) to integer, decimal or double'
>    | ...
>   SELECT a2, b, b % a2 FROM t6, t7;
>    | ---
> @@ -4121,42 +4121,42 @@ SELECT a2, c, c OR a2 FROM t6, t8;
>   SELECT true + 2.3;
>    | ---
>    | - null
> - | - 'Type mismatch: can not convert boolean(TRUE) to integer, unsigned or double'
> + | - 'Type mismatch: can not convert boolean(TRUE) to integer, decimal or double'
>    | ...
>   SELECT false + 2.3;
>    | ---
>    | - null
> - | - 'Type mismatch: can not convert boolean(FALSE) to integer, unsigned or double'
> + | - 'Type mismatch: can not convert boolean(FALSE) to integer, decimal or double'
>    | ...
>   SELECT true - 2.3;
>    | ---
>    | - null
> - | - 'Type mismatch: can not convert boolean(TRUE) to integer, unsigned or double'
> + | - 'Type mismatch: can not convert boolean(TRUE) to integer, decimal or double'
>    | ...
>   SELECT false - 2.3;
>    | ---
>    | - null
> - | - 'Type mismatch: can not convert boolean(FALSE) to integer, unsigned or double'
> + | - 'Type mismatch: can not convert boolean(FALSE) to integer, decimal or double'
>    | ...
>   SELECT true * 2.3;
>    | ---
>    | - null
> - | - 'Type mismatch: can not convert boolean(TRUE) to integer, unsigned or double'
> + | - 'Type mismatch: can not convert boolean(TRUE) to integer, decimal or double'
>    | ...
>   SELECT false * 2.3;
>    | ---
>    | - null
> - | - 'Type mismatch: can not convert boolean(FALSE) to integer, unsigned or double'
> + | - 'Type mismatch: can not convert boolean(FALSE) to integer, decimal or double'
>    | ...
>   SELECT true / 2.3;
>    | ---
>    | - null
> - | - 'Type mismatch: can not convert boolean(TRUE) to integer, unsigned or double'
> + | - 'Type mismatch: can not convert boolean(TRUE) to integer, decimal or double'
>    | ...
>   SELECT false / 2.3;
>    | ---
>    | - null
> - | - 'Type mismatch: can not convert boolean(FALSE) to integer, unsigned or double'
> + | - 'Type mismatch: can not convert boolean(FALSE) to integer, decimal or double'
>    | ...
>   SELECT true % 2.3;
>    | ---
> @@ -4171,42 +4171,42 @@ SELECT false % 2.3;
>   SELECT 2.3 + true;
>    | ---
>    | - null
> - | - 'Type mismatch: can not convert boolean(TRUE) to integer, unsigned or double'
> + | - 'Type mismatch: can not convert boolean(TRUE) to integer, decimal or double'
>    | ...
>   SELECT 2.3 + false;
>    | ---
>    | - null
> - | - 'Type mismatch: can not convert boolean(FALSE) to integer, unsigned or double'
> + | - 'Type mismatch: can not convert boolean(FALSE) to integer, decimal or double'
>    | ...
>   SELECT 2.3 - true;
>    | ---
>    | - null
> - | - 'Type mismatch: can not convert boolean(TRUE) to integer, unsigned or double'
> + | - 'Type mismatch: can not convert boolean(TRUE) to integer, decimal or double'
>    | ...
>   SELECT 2.3 - false;
>    | ---
>    | - null
> - | - 'Type mismatch: can not convert boolean(FALSE) to integer, unsigned or double'
> + | - 'Type mismatch: can not convert boolean(FALSE) to integer, decimal or double'
>    | ...
>   SELECT 2.3 * true;
>    | ---
>    | - null
> - | - 'Type mismatch: can not convert boolean(TRUE) to integer, unsigned or double'
> + | - 'Type mismatch: can not convert boolean(TRUE) to integer, decimal or double'
>    | ...
>   SELECT 2.3 * false;
>    | ---
>    | - null
> - | - 'Type mismatch: can not convert boolean(FALSE) to integer, unsigned or double'
> + | - 'Type mismatch: can not convert boolean(FALSE) to integer, decimal or double'
>    | ...
>   SELECT 2.3 / true;
>    | ---
>    | - null
> - | - 'Type mismatch: can not convert boolean(TRUE) to integer, unsigned or double'
> + | - 'Type mismatch: can not convert boolean(TRUE) to integer, decimal or double'
>    | ...
>   SELECT 2.3 / false;
>    | ---
>    | - null
> - | - 'Type mismatch: can not convert boolean(FALSE) to integer, unsigned or double'
> + | - 'Type mismatch: can not convert boolean(FALSE) to integer, decimal or double'
>    | ...
>   SELECT 2.3 % true;
>    | ---
> @@ -4222,22 +4222,22 @@ SELECT 2.3 % false;
>   SELECT a1, a1 + 2.3 FROM t6
>    | ---
>    | - null
> - | - 'Type mismatch: can not convert boolean(FALSE) to integer, unsigned or double'
> + | - 'Type mismatch: can not convert boolean(FALSE) to integer, decimal or double'
>    | ...
>   SELECT a1, a1 - 2.3 FROM t6
>    | ---
>    | - null
> - | - 'Type mismatch: can not convert boolean(FALSE) to integer, unsigned or double'
> + | - 'Type mismatch: can not convert boolean(FALSE) to integer, decimal or double'
>    | ...
>   SELECT a1, a1 * 2.3 FROM t6
>    | ---
>    | - null
> - | - 'Type mismatch: can not convert boolean(FALSE) to integer, unsigned or double'
> + | - 'Type mismatch: can not convert boolean(FALSE) to integer, decimal or double'
>    | ...
>   SELECT a1, a1 / 2.3 FROM t6
>    | ---
>    | - null
> - | - 'Type mismatch: can not convert boolean(FALSE) to integer, unsigned or double'
> + | - 'Type mismatch: can not convert boolean(FALSE) to integer, decimal or double'
>    | ...
>   SELECT a1, a1 % 2.3 FROM t6
>    | ---
> @@ -4247,22 +4247,22 @@ SELECT a1, a1 % 2.3 FROM t6
>   SELECT a1, 2.3 + a1 FROM t6
>    | ---
>    | - null
> - | - 'Type mismatch: can not convert boolean(FALSE) to integer, unsigned or double'
> + | - 'Type mismatch: can not convert boolean(FALSE) to integer, decimal or double'
>    | ...
>   SELECT a1, 2.3 - a1 FROM t6
>    | ---
>    | - null
> - | - 'Type mismatch: can not convert boolean(FALSE) to integer, unsigned or double'
> + | - 'Type mismatch: can not convert boolean(FALSE) to integer, decimal or double'
>    | ...
>   SELECT a1, 2.3 * a1 FROM t6
>    | ---
>    | - null
> - | - 'Type mismatch: can not convert boolean(FALSE) to integer, unsigned or double'
> + | - 'Type mismatch: can not convert boolean(FALSE) to integer, decimal or double'
>    | ...
>   SELECT a1, 2.3 / a1 FROM t6
>    | ---
>    | - null
> - | - 'Type mismatch: can not convert boolean(FALSE) to integer, unsigned or double'
> + | - 'Type mismatch: can not convert boolean(FALSE) to integer, decimal or double'
>    | ...
>   SELECT a1, 2.3 % a1 FROM t6
>    | ---
> @@ -4272,22 +4272,22 @@ SELECT a1, 2.3 % a1 FROM t6
>   SELECT a2, a2 + 2.3 FROM t6
>    | ---
>    | - null
> - | - 'Type mismatch: can not convert boolean(TRUE) to integer, unsigned or double'
> + | - 'Type mismatch: can not convert boolean(TRUE) to integer, decimal or double'
>    | ...
>   SELECT a2, a2 - 2.3 FROM t6
>    | ---
>    | - null
> - | - 'Type mismatch: can not convert boolean(TRUE) to integer, unsigned or double'
> + | - 'Type mismatch: can not convert boolean(TRUE) to integer, decimal or double'
>    | ...
>   SELECT a2, a2 * 2.3 FROM t6
>    | ---
>    | - null
> - | - 'Type mismatch: can not convert boolean(TRUE) to integer, unsigned or double'
> + | - 'Type mismatch: can not convert boolean(TRUE) to integer, decimal or double'
>    | ...
>   SELECT a2, a2 / 2.3 FROM t6
>    | ---
>    | - null
> - | - 'Type mismatch: can not convert boolean(TRUE) to integer, unsigned or double'
> + | - 'Type mismatch: can not convert boolean(TRUE) to integer, decimal or double'
>    | ...
>   SELECT a2, a2 % 2.3 FROM t6
>    | ---
> @@ -4297,22 +4297,22 @@ SELECT a2, a2 % 2.3 FROM t6
>   SELECT a2, 2.3 + a2 FROM t6
>    | ---
>    | - null
> - | - 'Type mismatch: can not convert boolean(TRUE) to integer, unsigned or double'
> + | - 'Type mismatch: can not convert boolean(TRUE) to integer, decimal or double'
>    | ...
>   SELECT a2, 2.3 - a2 FROM t6
>    | ---
>    | - null
> - | - 'Type mismatch: can not convert boolean(TRUE) to integer, unsigned or double'
> + | - 'Type mismatch: can not convert boolean(TRUE) to integer, decimal or double'
>    | ...
>   SELECT a2, 2.3 * a2 FROM t6
>    | ---
>    | - null
> - | - 'Type mismatch: can not convert boolean(TRUE) to integer, unsigned or double'
> + | - 'Type mismatch: can not convert boolean(TRUE) to integer, decimal or double'
>    | ...
>   SELECT a2, 2.3 / a2 FROM t6
>    | ---
>    | - null
> - | - 'Type mismatch: can not convert boolean(TRUE) to integer, unsigned or double'
> + | - 'Type mismatch: can not convert boolean(TRUE) to integer, decimal or double'
>    | ...
>   SELECT a2, 2.3 % a2 FROM t6
>    | ---
> @@ -4323,42 +4323,42 @@ SELECT a2, 2.3 % a2 FROM t6
>   SELECT c, true + c FROM t8;
>    | ---
>    | - null
> - | - 'Type mismatch: can not convert boolean(TRUE) to integer, unsigned or double'
> + | - 'Type mismatch: can not convert boolean(TRUE) to integer, decimal or double'
>    | ...
>   SELECT c, false + c FROM t8;
>    | ---
>    | - null
> - | - 'Type mismatch: can not convert boolean(FALSE) to integer, unsigned or double'
> + | - 'Type mismatch: can not convert boolean(FALSE) to integer, decimal or double'
>    | ...
>   SELECT c, true - c FROM t8;
>    | ---
>    | - null
> - | - 'Type mismatch: can not convert boolean(TRUE) to integer, unsigned or double'
> + | - 'Type mismatch: can not convert boolean(TRUE) to integer, decimal or double'
>    | ...
>   SELECT c, false - c FROM t8;
>    | ---
>    | - null
> - | - 'Type mismatch: can not convert boolean(FALSE) to integer, unsigned or double'
> + | - 'Type mismatch: can not convert boolean(FALSE) to integer, decimal or double'
>    | ...
>   SELECT c, true * c FROM t8;
>    | ---
>    | - null
> - | - 'Type mismatch: can not convert boolean(TRUE) to integer, unsigned or double'
> + | - 'Type mismatch: can not convert boolean(TRUE) to integer, decimal or double'
>    | ...
>   SELECT c, false * c FROM t8;
>    | ---
>    | - null
> - | - 'Type mismatch: can not convert boolean(FALSE) to integer, unsigned or double'
> + | - 'Type mismatch: can not convert boolean(FALSE) to integer, decimal or double'
>    | ...
>   SELECT c, true / c FROM t8;
>    | ---
>    | - null
> - | - 'Type mismatch: can not convert boolean(TRUE) to integer, unsigned or double'
> + | - 'Type mismatch: can not convert boolean(TRUE) to integer, decimal or double'
>    | ...
>   SELECT c, false / c FROM t8;
>    | ---
>    | - null
> - | - 'Type mismatch: can not convert boolean(FALSE) to integer, unsigned or double'
> + | - 'Type mismatch: can not convert boolean(FALSE) to integer, decimal or double'
>    | ...
>   SELECT c, true % c FROM t8;
>    | ---
> @@ -4373,42 +4373,42 @@ SELECT c, false % c FROM t8;
>   SELECT c, c + true FROM t8;
>    | ---
>    | - null
> - | - 'Type mismatch: can not convert boolean(TRUE) to integer, unsigned or double'
> + | - 'Type mismatch: can not convert boolean(TRUE) to integer, decimal or double'
>    | ...
>   SELECT c, c + false FROM t8;
>    | ---
>    | - null
> - | - 'Type mismatch: can not convert boolean(FALSE) to integer, unsigned or double'
> + | - 'Type mismatch: can not convert boolean(FALSE) to integer, decimal or double'
>    | ...
>   SELECT c, c - true FROM t8;
>    | ---
>    | - null
> - | - 'Type mismatch: can not convert boolean(TRUE) to integer, unsigned or double'
> + | - 'Type mismatch: can not convert boolean(TRUE) to integer, decimal or double'
>    | ...
>   SELECT c, c - false FROM t8;
>    | ---
>    | - null
> - | - 'Type mismatch: can not convert boolean(FALSE) to integer, unsigned or double'
> + | - 'Type mismatch: can not convert boolean(FALSE) to integer, decimal or double'
>    | ...
>   SELECT c, c * true FROM t8;
>    | ---
>    | - null
> - | - 'Type mismatch: can not convert boolean(TRUE) to integer, unsigned or double'
> + | - 'Type mismatch: can not convert boolean(TRUE) to integer, decimal or double'
>    | ...
>   SELECT c, c * false FROM t8;
>    | ---
>    | - null
> - | - 'Type mismatch: can not convert boolean(FALSE) to integer, unsigned or double'
> + | - 'Type mismatch: can not convert boolean(FALSE) to integer, decimal or double'
>    | ...
>   SELECT c, c / true FROM t8;
>    | ---
>    | - null
> - | - 'Type mismatch: can not convert boolean(TRUE) to integer, unsigned or double'
> + | - 'Type mismatch: can not convert boolean(TRUE) to integer, decimal or double'
>    | ...
>   SELECT c, c / false FROM t8;
>    | ---
>    | - null
> - | - 'Type mismatch: can not convert boolean(FALSE) to integer, unsigned or double'
> + | - 'Type mismatch: can not convert boolean(FALSE) to integer, decimal or double'
>    | ...
>   SELECT c, c % true FROM t8;
>    | ---
> @@ -4424,22 +4424,22 @@ SELECT c, c % false FROM t8;
>   SELECT a1, c, a1 + c FROM t6, t8;
>    | ---
>    | - null
> - | - 'Type mismatch: can not convert boolean(FALSE) to integer, unsigned or double'
> + | - 'Type mismatch: can not convert boolean(FALSE) to integer, decimal or double'
>    | ...
>   SELECT a1, c, a1 - c FROM t6, t8;
>    | ---
>    | - null
> - | - 'Type mismatch: can not convert boolean(FALSE) to integer, unsigned or double'
> + | - 'Type mismatch: can not convert boolean(FALSE) to integer, decimal or double'
>    | ...
>   SELECT a1, c, a1 * c FROM t6, t8;
>    | ---
>    | - null
> - | - 'Type mismatch: can not convert boolean(FALSE) to integer, unsigned or double'
> + | - 'Type mismatch: can not convert boolean(FALSE) to integer, decimal or double'
>    | ...
>   SELECT a1, c, a1 / c FROM t6, t8;
>    | ---
>    | - null
> - | - 'Type mismatch: can not convert boolean(FALSE) to integer, unsigned or double'
> + | - 'Type mismatch: can not convert boolean(FALSE) to integer, decimal or double'
>    | ...
>   SELECT a1, c, a1 % c FROM t6, t8;
>    | ---
> @@ -4449,22 +4449,22 @@ SELECT a1, c, a1 % c FROM t6, t8;
>   SELECT a1, c, c + a1 FROM t6, t8;
>    | ---
>    | - null
> - | - 'Type mismatch: can not convert boolean(FALSE) to integer, unsigned or double'
> + | - 'Type mismatch: can not convert boolean(FALSE) to integer, decimal or double'
>    | ...
>   SELECT a1, c, c - a1 FROM t6, t8;
>    | ---
>    | - null
> - | - 'Type mismatch: can not convert boolean(FALSE) to integer, unsigned or double'
> + | - 'Type mismatch: can not convert boolean(FALSE) to integer, decimal or double'
>    | ...
>   SELECT a1, c, c * a1 FROM t6, t8;
>    | ---
>    | - null
> - | - 'Type mismatch: can not convert boolean(FALSE) to integer, unsigned or double'
> + | - 'Type mismatch: can not convert boolean(FALSE) to integer, decimal or double'
>    | ...
>   SELECT a1, c, c / a1 FROM t6, t8;
>    | ---
>    | - null
> - | - 'Type mismatch: can not convert boolean(FALSE) to integer, unsigned or double'
> + | - 'Type mismatch: can not convert boolean(FALSE) to integer, decimal or double'
>    | ...
>   SELECT a1, c, c % a1 FROM t6, t8;
>    | ---
> @@ -4474,22 +4474,22 @@ SELECT a1, c, c % a1 FROM t6, t8;
>   SELECT a2, c, a2 + c FROM t6, t8;
>    | ---
>    | - null
> - | - 'Type mismatch: can not convert boolean(TRUE) to integer, unsigned or double'
> + | - 'Type mismatch: can not convert boolean(TRUE) to integer, decimal or double'
>    | ...
>   SELECT a2, c, a2 - c FROM t6, t8;
>    | ---
>    | - null
> - | - 'Type mismatch: can not convert boolean(TRUE) to integer, unsigned or double'
> + | - 'Type mismatch: can not convert boolean(TRUE) to integer, decimal or double'
>    | ...
>   SELECT a2, c, a2 * c FROM t6, t8;
>    | ---
>    | - null
> - | - 'Type mismatch: can not convert boolean(TRUE) to integer, unsigned or double'
> + | - 'Type mismatch: can not convert boolean(TRUE) to integer, decimal or double'
>    | ...
>   SELECT a2, c, a2 / c FROM t6, t8;
>    | ---
>    | - null
> - | - 'Type mismatch: can not convert boolean(TRUE) to integer, unsigned or double'
> + | - 'Type mismatch: can not convert boolean(TRUE) to integer, decimal or double'
>    | ...
>   SELECT a2, c, a2 % c FROM t6, t8;
>    | ---
> @@ -4499,22 +4499,22 @@ SELECT a2, c, a2 % c FROM t6, t8;
>   SELECT a2, c, c + a2 FROM t6, t8;
>    | ---
>    | - null
> - | - 'Type mismatch: can not convert boolean(TRUE) to integer, unsigned or double'
> + | - 'Type mismatch: can not convert boolean(TRUE) to integer, decimal or double'
>    | ...
>   SELECT a2, c, c - a2 FROM t6, t8;
>    | ---
>    | - null
> - | - 'Type mismatch: can not convert boolean(TRUE) to integer, unsigned or double'
> + | - 'Type mismatch: can not convert boolean(TRUE) to integer, decimal or double'
>    | ...
>   SELECT a2, c, c * a2 FROM t6, t8;
>    | ---
>    | - null
> - | - 'Type mismatch: can not convert boolean(TRUE) to integer, unsigned or double'
> + | - 'Type mismatch: can not convert boolean(TRUE) to integer, decimal or double'
>    | ...
>   SELECT a2, c, c / a2 FROM t6, t8;
>    | ---
>    | - null
> - | - 'Type mismatch: can not convert boolean(TRUE) to integer, unsigned or double'
> + | - 'Type mismatch: can not convert boolean(TRUE) to integer, decimal or double'
>    | ...
>   SELECT a2, c, c % a2 FROM t6, t8;
>    | ---
> diff --git a/test/sql/types.result b/test/sql/types.result
> index 68bdcd62e..4d49318c9 100644
> --- a/test/sql/types.result
> +++ b/test/sql/types.result
> @@ -311,7 +311,7 @@ box.execute('SELECT 1 + 1.1;')
>   box.execute('SELECT \'9223372036854\' + 1;')
>   ---
>   - null
> -- 'Type mismatch: can not convert string(''9223372036854'') to integer, unsigned or
> +- 'Type mismatch: can not convert string(''9223372036854'') to integer, decimal or
>     double'
>   ...
>   -- Fix BOOLEAN bindings.
> 

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [Tarantool-patches] [PATCH v1 1/4] decimal: introduce decimal_is_neg()
  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
  0 siblings, 0 replies; 16+ messages in thread
From: Safin Timur via Tarantool-patches @ 2021-08-18 16:54 UTC (permalink / raw)
  To: imeevma, korablev; +Cc: tarantool-patches

LGTM, though...

On 16.08.2021 18:57, Mergen Imeev via Tarantool-patches wrote:
> This patch introduces function decimal_is_neg() which checks that
> decimal is less than zero.
> 
> Needed for #4415
> ---
>   src/lib/core/decimal.c   |  6 ++++++
>   src/lib/core/decimal.h   |  4 ++++
>   test/unit/decimal.c      |  7 ++++++-
>   test/unit/decimal.result | 10 +++++++++-
>   4 files changed, 25 insertions(+), 2 deletions(-)
> 
> diff --git a/src/lib/core/decimal.c b/src/lib/core/decimal.c
> index 6d2ccb96f..7543ef44d 100644
> --- a/src/lib/core/decimal.c
> +++ b/src/lib/core/decimal.c
> @@ -115,6 +115,12 @@ decimal_is_int(decimal_t *dec)
>   	return decNumberIsInt(dec);
>   }
>   
> +bool
> +decimal_is_neg(const decimal_t *dec)
> +{
> +	return decNumberIsNegative(dec) && !decNumberIsZero(dec);
> +}
> +

^^ here was a good place for decimal_to_string()

>   decimal_t *
>   decimal_from_string(decimal_t *dec, const char *str)
>   {
> diff --git a/src/lib/core/decimal.h b/src/lib/core/decimal.h
> index aeafd2c68..15a576648 100644
> --- a/src/lib/core/decimal.h
> +++ b/src/lib/core/decimal.h
> @@ -81,6 +81,10 @@ decimal_zero(decimal_t *dec);
>   bool
>   decimal_is_int(decimal_t *dec);
>   
> +/** @return true if the decimal is negative, false otherwise. */
> +bool
> +decimal_is_neg(const decimal_t *dec);
> +
>   /**
>    * Initialize a decimal with a value from the string.
>    *
> diff --git a/test/unit/decimal.c b/test/unit/decimal.c
> index aea646e15..328b1b668 100644
> --- a/test/unit/decimal.c
> +++ b/test/unit/decimal.c
> @@ -336,7 +336,7 @@ test_mp_print(void)
>   int
>   main(void)
>   {
> -	plan(304);
> +	plan(312);
>   
>   	dectest(314, 271, uint64, uint64_t);
>   	dectest(65535, 23456, uint64, uint64_t);
> @@ -416,5 +416,10 @@ main(void)
>   	dectest_is(is_int, 1.0000, true);
>   	dectest_is(is_int, 1.0000001, false);
>   
> +	dectest_is(is_neg, 1, false);
> +	dectest_is(is_neg, -1, true);
> +	dectest_is(is_neg, 0, false);
> +	dectest_is(is_neg, -0, false);
> +
>   	return check_plan();
>   }
> diff --git a/test/unit/decimal.result b/test/unit/decimal.result
> index b7da2d2ce..ceaaf718a 100644
> --- a/test/unit/decimal.result
> +++ b/test/unit/decimal.result
> @@ -1,4 +1,4 @@
> -1..304
> +1..312
>   ok 1 - decimal(314)
>   ok 2 - decimal(271)
>   ok 3 - decimal(314) + decimal(271)
> @@ -747,3 +747,11 @@ ok 301 - decimal_from_string(1.0000)
>   ok 302 - decimal_is_int(1.0000) - expected true
>   ok 303 - decimal_from_string(1.0000001)
>   ok 304 - decimal_is_int(1.0000001) - expected false
> +ok 305 - decimal_from_string(1)
> +ok 306 - decimal_is_neg(1) - expected false
> +ok 307 - decimal_from_string(-1)
> +ok 308 - decimal_is_neg(-1) - expected true
> +ok 309 - decimal_from_string(0)
> +ok 310 - decimal_is_neg(0) - expected false
> +ok 311 - decimal_from_string(-0)
> +ok 312 - decimal_is_neg(-0) - expected false
> 

^ permalink raw reply	[flat|nested] 16+ messages in thread

end of thread, other threads:[~2021-08-18 16:55 UTC | newest]

Thread overview: 16+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
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 ` [Tarantool-patches] [PATCH v1 4/4] sql: introduce decimal to arithmetic Mergen Imeev via Tarantool-patches
2021-08-16 19:48   ` 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

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox