Tarantool development patches archive
 help / color / mirror / Atom feed
* [Tarantool-patches] [PATCH v1 0/3] Introduce field type ANY to SQL
@ 2021-10-28 10:52 Mergen Imeev via Tarantool-patches
  2021-10-28 10:52 ` [Tarantool-patches] [PATCH v1 1/3] sql: properly set INTEGER type in struct Expr Mergen Imeev via Tarantool-patches
                   ` (3 more replies)
  0 siblings, 4 replies; 6+ messages in thread
From: Mergen Imeev via Tarantool-patches @ 2021-10-28 10:52 UTC (permalink / raw)
  To: v.shpilevoy; +Cc: tarantool-patches

This patch-set introduces field type ANY to SQL.

https://github.com/tarantool/tarantool/issues/3174
https://github.com/tarantool/tarantool/tree/imeevma/gh-3174-introduce-field-type-any

Mergen Imeev (3):
  sql: properly set INTEGER type in struct Expr
  sql: use field_type_MAX instead of FIELD_TYPE_ANY
  sql: introduce field type ANY

 extra/mkkeywordhash.c           |   3 +-
 src/box/sql/expr.c              |   2 +
 src/box/sql/func.c              |  24 +-
 src/box/sql/mem.c               |  51 ++-
 src/box/sql/mem.h               |   4 +-
 src/box/sql/parse.y             |   3 +-
 src/box/sql/resolve.c           |   1 +
 src/box/sql/select.c            |   1 +
 src/box/sql/vdbe.c              |   4 +-
 test/sql-tap/metatypes.test.lua | 568 ++++++++++++++++++++++++++++++--
 10 files changed, 609 insertions(+), 52 deletions(-)

-- 
2.25.1


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

* [Tarantool-patches] [PATCH v1 1/3] sql: properly set INTEGER type in struct Expr
  2021-10-28 10:52 [Tarantool-patches] [PATCH v1 0/3] Introduce field type ANY to SQL Mergen Imeev via Tarantool-patches
@ 2021-10-28 10:52 ` Mergen Imeev via Tarantool-patches
  2021-10-28 10:52 ` [Tarantool-patches] [PATCH v1 2/3] sql: use field_type_MAX instead of FIELD_TYPE_ANY Mergen Imeev via Tarantool-patches
                   ` (2 subsequent siblings)
  3 siblings, 0 replies; 6+ messages in thread
From: Mergen Imeev via Tarantool-patches @ 2021-10-28 10:52 UTC (permalink / raw)
  To: v.shpilevoy; +Cc: tarantool-patches

Prior to this patch, in some cases a struct Expr value with an opcode
TK_INTEGER could have field type ANY. This patch sets such values to the
INTEGER field type.

Part of #3174
---
 src/box/sql/expr.c    | 2 ++
 src/box/sql/resolve.c | 1 +
 src/box/sql/select.c  | 1 +
 3 files changed, 4 insertions(+)

diff --git a/src/box/sql/expr.c b/src/box/sql/expr.c
index db8355f33..d3fde9501 100644
--- a/src/box/sql/expr.c
+++ b/src/box/sql/expr.c
@@ -1010,6 +1010,7 @@ sql_expr_new_int(struct sql *db, int value)
 {
 	struct Expr *e = sql_expr_new_empty(db, TK_INTEGER, 0);
 	if (e != NULL) {
+		e->type = FIELD_TYPE_INTEGER;
 		e->flags |= EP_IntValue;
 		e->u.iValue = value;
 	}
@@ -3886,6 +3887,7 @@ sqlExprCodeTarget(Parse * pParse, Expr * pExpr, int target)
 				return target;
 			} else {
 				tempX.op = TK_INTEGER;
+				tempX.type = FIELD_TYPE_INTEGER;
 				tempX.flags = EP_IntValue | EP_TokenOnly;
 				tempX.u.iValue = 0;
 				r1 = sqlExprCodeTemp(pParse, &tempX,
diff --git a/src/box/sql/resolve.c b/src/box/sql/resolve.c
index 21fe124d7..22b4e6799 100644
--- a/src/box/sql/resolve.c
+++ b/src/box/sql/resolve.c
@@ -928,6 +928,7 @@ resolveCompoundOrderBy(Parse * pParse,	/* Parsing context.  Leave error messages
 				}
 				pNew->flags |= EP_IntValue;
 				pNew->u.iValue = iCol;
+				pNew->type = FIELD_TYPE_INTEGER;
 				if (pItem->pExpr == pE) {
 					pItem->pExpr = pNew;
 				} else {
diff --git a/src/box/sql/select.c b/src/box/sql/select.c
index 8ca967108..2f97d121d 100644
--- a/src/box/sql/select.c
+++ b/src/box/sql/select.c
@@ -3566,6 +3566,7 @@ multiSelectOrderBy(Parse * pParse,	/* Parsing context */
 				}
 				pNew->flags |= EP_IntValue;
 				pNew->u.iValue = i;
+				pNew->type = FIELD_TYPE_INTEGER;
 				pOrderBy = sql_expr_list_append(pParse->db,
 								pOrderBy, pNew);
 				if (pOrderBy)
-- 
2.25.1


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

* [Tarantool-patches] [PATCH v1 2/3] sql: use field_type_MAX instead of FIELD_TYPE_ANY
  2021-10-28 10:52 [Tarantool-patches] [PATCH v1 0/3] Introduce field type ANY to SQL Mergen Imeev via Tarantool-patches
  2021-10-28 10:52 ` [Tarantool-patches] [PATCH v1 1/3] sql: properly set INTEGER type in struct Expr Mergen Imeev via Tarantool-patches
@ 2021-10-28 10:52 ` Mergen Imeev via Tarantool-patches
  2021-10-28 10:52 ` [Tarantool-patches] [PATCH v1 3/3] sql: introduce field type ANY Mergen Imeev via Tarantool-patches
  2021-11-09 23:48 ` [Tarantool-patches] [PATCH v1 0/3] Introduce field type ANY to SQL Vladislav Shpilevoy via Tarantool-patches
  3 siblings, 0 replies; 6+ messages in thread
From: Mergen Imeev via Tarantool-patches @ 2021-10-28 10:52 UTC (permalink / raw)
  To: v.shpilevoy; +Cc: tarantool-patches

After this patch, the built-in SQL function implementation definitions
will use field_type_MAX to indicate that they accept values of any type,
instead of FIELD_TYPE_ANY.

Part of #3174
---
 src/box/sql/func.c | 22 +++++++++++-----------
 src/box/sql/mem.c  |  2 +-
 2 files changed, 12 insertions(+), 12 deletions(-)

diff --git a/src/box/sql/func.c b/src/box/sql/func.c
index 0cf17572b..63d7536d9 100644
--- a/src/box/sql/func.c
+++ b/src/box/sql/func.c
@@ -1937,10 +1937,10 @@ static struct sql_func_definition definitions[] = {
 	{"CHAR", -1, {FIELD_TYPE_INTEGER}, FIELD_TYPE_STRING, charFunc, NULL},
 	{"CHAR_LENGTH", 1, {FIELD_TYPE_STRING}, FIELD_TYPE_INTEGER, lengthFunc,
 	 NULL},
-	{"COALESCE", -1, {FIELD_TYPE_ANY}, FIELD_TYPE_SCALAR, sql_builtin_stub,
+	{"COALESCE", -1, {field_type_MAX}, FIELD_TYPE_SCALAR, sql_builtin_stub,
 	 NULL},
 	{"COUNT", 0, {}, FIELD_TYPE_INTEGER, step_count, fin_count},
-	{"COUNT", 1, {FIELD_TYPE_ANY}, FIELD_TYPE_INTEGER, step_count,
+	{"COUNT", 1, {field_type_MAX}, FIELD_TYPE_INTEGER, step_count,
 	 fin_count},
 
 	{"GREATEST", -1, {FIELD_TYPE_INTEGER}, FIELD_TYPE_INTEGER, minmaxFunc,
@@ -1967,7 +1967,7 @@ static struct sql_func_definition definitions[] = {
 	 FIELD_TYPE_VARBINARY, step_group_concat, NULL},
 
 	{"HEX", 1, {FIELD_TYPE_VARBINARY}, FIELD_TYPE_STRING, hexFunc, NULL},
-	{"IFNULL", 2, {FIELD_TYPE_ANY, FIELD_TYPE_ANY}, FIELD_TYPE_SCALAR,
+	{"IFNULL", 2, {field_type_MAX, field_type_MAX}, FIELD_TYPE_SCALAR,
 	 sql_builtin_stub, NULL},
 
 	{"LEAST", -1, {FIELD_TYPE_INTEGER}, FIELD_TYPE_INTEGER, minmaxFunc,
@@ -1988,9 +1988,9 @@ static struct sql_func_definition definitions[] = {
 	 FIELD_TYPE_BOOLEAN, likeFunc, NULL},
 	{"LIKE", 3, {FIELD_TYPE_STRING, FIELD_TYPE_STRING, FIELD_TYPE_STRING},
 	 FIELD_TYPE_BOOLEAN, likeFunc, NULL},
-	{"LIKELIHOOD", 2, {FIELD_TYPE_ANY, FIELD_TYPE_DOUBLE},
+	{"LIKELIHOOD", 2, {field_type_MAX, FIELD_TYPE_DOUBLE},
 	 FIELD_TYPE_BOOLEAN, sql_builtin_stub, NULL},
-	{"LIKELY", 1, {FIELD_TYPE_ANY}, FIELD_TYPE_BOOLEAN, sql_builtin_stub,
+	{"LIKELY", 1, {field_type_MAX}, FIELD_TYPE_BOOLEAN, sql_builtin_stub,
 	 NULL},
 	{"LOWER", 1, {FIELD_TYPE_STRING}, FIELD_TYPE_STRING, LowerICUFunc,
 	 NULL},
@@ -2013,13 +2013,13 @@ static struct sql_func_definition definitions[] = {
 	{"MIN", 1, {FIELD_TYPE_STRING}, FIELD_TYPE_STRING, step_minmax, NULL},
 	{"MIN", 1, {FIELD_TYPE_SCALAR}, FIELD_TYPE_SCALAR, step_minmax, NULL},
 
-	{"NULLIF", 2, {FIELD_TYPE_ANY, FIELD_TYPE_ANY}, FIELD_TYPE_SCALAR,
+	{"NULLIF", 2, {field_type_MAX, field_type_MAX}, FIELD_TYPE_SCALAR,
 	 nullifFunc, NULL},
 	{"POSITION", 2, {FIELD_TYPE_STRING, FIELD_TYPE_STRING},
 	 FIELD_TYPE_INTEGER, position_func, NULL},
-	{"PRINTF", -1, {FIELD_TYPE_ANY}, FIELD_TYPE_STRING, printfFunc, 
+	{"PRINTF", -1, {field_type_MAX}, FIELD_TYPE_STRING, printfFunc,
 	 NULL},
-	{"QUOTE", 1, {FIELD_TYPE_ANY}, FIELD_TYPE_STRING, quoteFunc, NULL},
+	{"QUOTE", 1, {field_type_MAX}, FIELD_TYPE_STRING, quoteFunc, NULL},
 	{"RANDOM", 0, {}, FIELD_TYPE_INTEGER, randomFunc, NULL},
 	{"RANDOMBLOB", 1, {FIELD_TYPE_INTEGER}, FIELD_TYPE_VARBINARY,
 	 randomBlob, NULL},
@@ -2062,10 +2062,10 @@ static struct sql_func_definition definitions[] = {
 	 {FIELD_TYPE_VARBINARY, FIELD_TYPE_INTEGER, FIELD_TYPE_VARBINARY},
 	 FIELD_TYPE_VARBINARY, trim_func, NULL},
 
-	{"TYPEOF", 1, {FIELD_TYPE_ANY}, FIELD_TYPE_STRING, typeofFunc, NULL},
+	{"TYPEOF", 1, {field_type_MAX}, FIELD_TYPE_STRING, typeofFunc, NULL},
 	{"UNICODE", 1, {FIELD_TYPE_STRING}, FIELD_TYPE_INTEGER, unicodeFunc,
 	 NULL},
-	{"UNLIKELY", 1, {FIELD_TYPE_ANY}, FIELD_TYPE_BOOLEAN, sql_builtin_stub,
+	{"UNLIKELY", 1, {field_type_MAX}, FIELD_TYPE_BOOLEAN, sql_builtin_stub,
 	 NULL},
 	{"UPPER", 1, {FIELD_TYPE_STRING}, FIELD_TYPE_STRING, UpperICUFunc,
 	 NULL},
@@ -2112,7 +2112,7 @@ built_in_func_put(struct sql_func_dictionary *dict)
 static inline bool
 is_exact(int op, enum field_type a, enum field_type b)
 {
-	return op == TK_NULL || a == b || a == FIELD_TYPE_ANY ||
+	return op == TK_NULL || a == b || a == field_type_MAX ||
 	       (a == FIELD_TYPE_INTEGER && b == FIELD_TYPE_UNSIGNED);
 }
 
diff --git a/src/box/sql/mem.c b/src/box/sql/mem.c
index dc629aee3..f648c5d62 100644
--- a/src/box/sql/mem.c
+++ b/src/box/sql/mem.c
@@ -1489,7 +1489,7 @@ mem_cast_explicit(struct Mem *mem, enum field_type type)
 int
 mem_cast_implicit(struct Mem *mem, enum field_type type)
 {
-	if (mem->type == MEM_TYPE_NULL || type == FIELD_TYPE_ANY)
+	if (mem->type == MEM_TYPE_NULL || type == field_type_MAX)
 		return 0;
 	if ((mem->flags & MEM_Scalar) != 0 && type != FIELD_TYPE_SCALAR)
 		return -1;
-- 
2.25.1


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

* [Tarantool-patches] [PATCH v1 3/3] sql: introduce field type ANY
  2021-10-28 10:52 [Tarantool-patches] [PATCH v1 0/3] Introduce field type ANY to SQL Mergen Imeev via Tarantool-patches
  2021-10-28 10:52 ` [Tarantool-patches] [PATCH v1 1/3] sql: properly set INTEGER type in struct Expr Mergen Imeev via Tarantool-patches
  2021-10-28 10:52 ` [Tarantool-patches] [PATCH v1 2/3] sql: use field_type_MAX instead of FIELD_TYPE_ANY Mergen Imeev via Tarantool-patches
@ 2021-10-28 10:52 ` Mergen Imeev via Tarantool-patches
  2021-11-09 23:48 ` [Tarantool-patches] [PATCH v1 0/3] Introduce field type ANY to SQL Vladislav Shpilevoy via Tarantool-patches
  3 siblings, 0 replies; 6+ messages in thread
From: Mergen Imeev via Tarantool-patches @ 2021-10-28 10:52 UTC (permalink / raw)
  To: v.shpilevoy; +Cc: tarantool-patches

Closes #3174

@TarantoolBot document
Title: Field type ANY in SQL

Properties of type ANY in SQL:
1) typeof() with an argument of type ANY returns "any";
2) any value of any type can be implicitly and explicitly cast to the ANY type;
3) a value of type ANY cannot be implicitly cast to any other type;
4) a value of type ANY cannot participate in arithmetic, bitwise, comparison,
   and concationation operations.
---
 extra/mkkeywordhash.c           |   3 +-
 src/box/sql/func.c              |   4 +-
 src/box/sql/mem.c               |  49 ++-
 src/box/sql/mem.h               |   4 +-
 src/box/sql/parse.y             |   3 +-
 src/box/sql/vdbe.c              |   4 +-
 test/sql-tap/metatypes.test.lua | 568 ++++++++++++++++++++++++++++++--
 7 files changed, 594 insertions(+), 41 deletions(-)

diff --git a/extra/mkkeywordhash.c b/extra/mkkeywordhash.c
index 3e4200417..aaeb7d51b 100644
--- a/extra/mkkeywordhash.c
+++ b/extra/mkkeywordhash.c
@@ -176,10 +176,11 @@ static Keyword aKeywordTable[] = {
   { "VALUES",                 "TK_VALUES",      true  },
   { "VARBINARY",              "TK_VARBINARY",   true  },
   { "VIEW",                   "TK_VIEW",        true  },
+  { "WILDCARD",               "TK_STANDARD",    true  },
   { "WITH",                   "TK_WITH",        true  },
   { "WHEN",                   "TK_WHEN",        true  },
   { "WHERE",                  "TK_WHERE",       true  },
-  { "ANY",                    "TK_STANDARD",    true  },
+  { "ANY",                    "TK_ANY",         true  },
   { "ASENSITIVE",             "TK_STANDARD",    true  },
   { "BLOB",                   "TK_STANDARD",    true  },
   { "BINARY",                 "TK_ID",          true  },
diff --git a/src/box/sql/func.c b/src/box/sql/func.c
index 63d7536d9..43f6fc40a 100644
--- a/src/box/sql/func.c
+++ b/src/box/sql/func.c
@@ -318,6 +318,8 @@ typeofFunc(struct sql_context *context, int argc, struct Mem *argv)
 {
 	(void)argc;
 	const char *z = 0;
+	if ((argv[0].flags & MEM_Any) != 0)
+		return mem_set_str0_static(context->pOut, "any");
 	if ((argv[0].flags & MEM_Number) != 0)
 		return mem_set_str0_static(context->pOut, "number");
 	if ((argv[0].flags & MEM_Scalar) != 0)
@@ -2013,7 +2015,7 @@ static struct sql_func_definition definitions[] = {
 	{"MIN", 1, {FIELD_TYPE_STRING}, FIELD_TYPE_STRING, step_minmax, NULL},
 	{"MIN", 1, {FIELD_TYPE_SCALAR}, FIELD_TYPE_SCALAR, step_minmax, NULL},
 
-	{"NULLIF", 2, {field_type_MAX, field_type_MAX}, FIELD_TYPE_SCALAR,
+	{"NULLIF", 2, {FIELD_TYPE_SCALAR, FIELD_TYPE_SCALAR}, FIELD_TYPE_SCALAR,
 	 nullifFunc, NULL},
 	{"POSITION", 2, {FIELD_TYPE_STRING, FIELD_TYPE_STRING},
 	 FIELD_TYPE_INTEGER, position_func, NULL},
diff --git a/src/box/sql/mem.c b/src/box/sql/mem.c
index f648c5d62..244415e02 100644
--- a/src/box/sql/mem.c
+++ b/src/box/sql/mem.c
@@ -727,7 +727,7 @@ str_to_bin(struct Mem *mem)
 {
 	assert(mem->type == MEM_TYPE_STR);
 	mem->type = MEM_TYPE_BIN;
-	mem->flags &= ~(MEM_Term | MEM_Scalar);
+	mem->flags &= ~(MEM_Term | MEM_Scalar | MEM_Any);
 	return 0;
 }
 
@@ -774,7 +774,7 @@ bin_to_str(struct Mem *mem)
 {
 	assert(mem->type == MEM_TYPE_BIN);
 	mem->type = MEM_TYPE_STR;
-	mem->flags &= ~MEM_Scalar;
+	mem->flags &= ~(MEM_Scalar | MEM_Any);
 	return 0;
 }
 
@@ -1376,7 +1376,7 @@ mem_to_str(struct Mem *mem)
 	assert(mem->type < MEM_TYPE_INVALID);
 	switch (mem->type) {
 	case MEM_TYPE_STR:
-		mem->flags &= ~MEM_Scalar;
+		mem->flags &= ~(MEM_Scalar | MEM_Any);
 		return 0;
 	case MEM_TYPE_INT:
 	case MEM_TYPE_UINT:
@@ -1440,7 +1440,7 @@ mem_cast_explicit(struct Mem *mem, enum field_type type)
 		if (mem->type == MEM_TYPE_STR)
 			return str_to_bin(mem);
 		if (mem_is_bytes(mem)) {
-			mem->flags &= ~MEM_Scalar;
+			mem->flags &= ~(MEM_Scalar | MEM_Any);
 			return 0;
 		}
 		if (mem->type == MEM_TYPE_UUID)
@@ -1478,7 +1478,11 @@ mem_cast_explicit(struct Mem *mem, enum field_type type)
 		if ((mem->type & (MEM_TYPE_MAP | MEM_TYPE_ARRAY)) != 0)
 			return -1;
 		mem->flags |= MEM_Scalar;
-		mem->flags &= ~MEM_Number;
+		mem->flags &= ~(MEM_Number | MEM_Any);
+		return 0;
+	case FIELD_TYPE_ANY:
+		mem->flags |= MEM_Any;
+		mem->flags &= ~(MEM_Number | MEM_Scalar);
 		return 0;
 	default:
 		break;
@@ -1491,11 +1495,21 @@ mem_cast_implicit(struct Mem *mem, enum field_type type)
 {
 	if (mem->type == MEM_TYPE_NULL || type == field_type_MAX)
 		return 0;
-	if ((mem->flags & MEM_Scalar) != 0 && type != FIELD_TYPE_SCALAR)
-		return -1;
-	if ((mem->flags & MEM_Number) != 0 && type != FIELD_TYPE_SCALAR &&
-	    type != FIELD_TYPE_NUMBER)
-		return -1;
+	if (mem_is_metatype(mem) && type != FIELD_TYPE_ANY) {
+		if ((mem->flags & MEM_Number) != 0) {
+			if (type != FIELD_TYPE_NUMBER &&
+			    type != FIELD_TYPE_SCALAR &&
+			    type != FIELD_TYPE_ANY)
+				return -1;
+		} else if ((mem->flags & MEM_Scalar) != 0) {
+			if (type != FIELD_TYPE_SCALAR &&
+			    type != FIELD_TYPE_ANY)
+				return -1;
+		} else {
+			if (type != FIELD_TYPE_ANY)
+				return -1;
+		}
+	}
 	switch (type) {
 	case FIELD_TYPE_UNSIGNED:
 		if (mem->type == MEM_TYPE_UINT) {
@@ -1509,7 +1523,7 @@ mem_cast_implicit(struct Mem *mem, enum field_type type)
 		return -1;
 	case FIELD_TYPE_STRING:
 		if (mem->type == MEM_TYPE_STR) {
-			mem->flags &= ~MEM_Scalar;
+			mem->flags &= ~(MEM_Scalar | MEM_Any);
 			return 0;
 		}
 		return -1;
@@ -1544,7 +1558,7 @@ mem_cast_implicit(struct Mem *mem, enum field_type type)
 	case FIELD_TYPE_VARBINARY:
 		if ((mem->type & (MEM_TYPE_BIN | MEM_TYPE_MAP |
 				  MEM_TYPE_ARRAY)) != 0) {
-			mem->flags &= ~MEM_Scalar;
+			mem->flags &= ~(MEM_Scalar | MEM_Any);
 			return 0;
 		}
 		return -1;
@@ -1579,7 +1593,7 @@ mem_cast_implicit(struct Mem *mem, enum field_type type)
 		if ((mem->type & (MEM_TYPE_MAP | MEM_TYPE_ARRAY)) != 0)
 			return -1;
 		mem->flags |= MEM_Scalar;
-		mem->flags &= ~MEM_Number;
+		mem->flags &= ~(MEM_Number | MEM_Any);
 		return 0;
 	case FIELD_TYPE_UUID:
 		if (mem->type != MEM_TYPE_UUID)
@@ -1587,6 +1601,8 @@ mem_cast_implicit(struct Mem *mem, enum field_type type)
 		mem->flags = 0;
 		return 0;
 	case FIELD_TYPE_ANY:
+		mem->flags |= MEM_Any;
+		mem->flags &= ~(MEM_Number | MEM_Scalar);
 		return 0;
 	default:
 		break;
@@ -2540,6 +2556,11 @@ mem_cmp(const struct Mem *a, const struct Mem *b, int *result,
 			*result = 1;
 		return 0;
 	}
+	if (((a->flags | b->flags) & MEM_Any) != 0) {
+		diag_set(ClientError, ER_SQL_TYPE_MISMATCH, mem_str(a),
+			 "comparable type");
+		return -1;
+	}
 	if (((a->flags | b->flags) & MEM_Scalar) != 0) {
 		*result = mem_cmp_scalar(a, b, coll);
 		return 0;
@@ -2575,6 +2596,8 @@ char *
 mem_type_to_str(const struct Mem *p)
 {
 	assert(p != NULL);
+	if ((p->flags & MEM_Any) != 0)
+		return "any";
 	if ((p->flags & MEM_Scalar) != 0)
 		return "scalar";
 	if ((p->flags & MEM_Number) != 0)
diff --git a/src/box/sql/mem.h b/src/box/sql/mem.h
index 242f910db..a8b132479 100644
--- a/src/box/sql/mem.h
+++ b/src/box/sql/mem.h
@@ -98,6 +98,8 @@ struct Mem {
 #define MEM_Number    0x0001
 /** MEM is of SCALAR meta-type. */
 #define MEM_Scalar    0x0002
+/** MEM is of ANY meta-type. */
+#define MEM_Any       0x0004
 #define MEM_Cleared   0x0200	/* NULL set by OP_Null, not from data */
 
 /* Whenever Mem contains a valid string or blob representation, one of
@@ -145,7 +147,7 @@ mem_is_num(const struct Mem *mem)
 static inline bool
 mem_is_metatype(const struct Mem *mem)
 {
-	return (mem->flags & (MEM_Number | MEM_Scalar)) != 0;
+	return (mem->flags & (MEM_Number | MEM_Scalar | MEM_Any)) != 0;
 }
 
 static inline bool
diff --git a/src/box/sql/parse.y b/src/box/sql/parse.y
index 337df4916..2ed0ab09f 100644
--- a/src/box/sql/parse.y
+++ b/src/box/sql/parse.y
@@ -270,7 +270,7 @@ columnlist ::= tcons.
   QUERY KEY OFFSET RAISE RELEASE REPLACE RESTRICT
   RENAME CTIME_KW IF ENABLE DISABLE UUID
   .
-%wildcard ANY.
+%wildcard WILDCARD.
 
 
 // And "ids" is an identifer-or-string.
@@ -1837,6 +1837,7 @@ typedef(A) ::= BOOL . { A.type = FIELD_TYPE_BOOLEAN; }
 typedef(A) ::= BOOLEAN . { A.type = FIELD_TYPE_BOOLEAN; }
 typedef(A) ::= VARBINARY . { A.type = FIELD_TYPE_VARBINARY; }
 typedef(A) ::= UUID . { A.type = FIELD_TYPE_UUID; }
+typedef(A) ::= ANY . { A.type = FIELD_TYPE_ANY; }
 
 /**
  * Time-like types are temporary disabled, until they are
diff --git a/src/box/sql/vdbe.c b/src/box/sql/vdbe.c
index 628fcb7be..323cbf04c 100644
--- a/src/box/sql/vdbe.c
+++ b/src/box/sql/vdbe.c
@@ -1922,7 +1922,9 @@ case OP_Column: {
 	/* Currently PSEUDO cursor does not have info about field types. */
 	if (pC->eCurType == CURTYPE_TARANTOOL)
 		field_type = pC->uc.pCursor->space->def->fields[p2].type;
-	if (field_type == FIELD_TYPE_SCALAR)
+	if (field_type == FIELD_TYPE_ANY)
+		pDest->flags |= MEM_Any;
+	else if (field_type == FIELD_TYPE_SCALAR)
 		pDest->flags |= MEM_Scalar;
 	else if (field_type == FIELD_TYPE_NUMBER)
 		pDest->flags |= MEM_Number;
diff --git a/test/sql-tap/metatypes.test.lua b/test/sql-tap/metatypes.test.lua
index 3e4091675..3d60ef268 100755
--- a/test/sql-tap/metatypes.test.lua
+++ b/test/sql-tap/metatypes.test.lua
@@ -1,32 +1,51 @@
 #!/usr/bin/env tarantool
 local test = require("sqltester")
-test:plan(19)
+test:plan(80)
 
 -- Check that SCALAR and NUMBER meta-types works as intended.
-box.execute([[CREATE TABLE t (i INT PRIMARY KEY, s SCALAR, n NUMBER);]])
+box.execute([[CREATE TABLE t (i INT PRIMARY KEY, s SCALAR, n NUMBER, a ANY);]])
+box.execute([[CREATE TABLE t1 (id INTEGER PRIMARY KEY AUTOINCREMENT, a ANY,
+                               g UNSIGNED, t STRING, n NUMBER, f DOUBLE,
+                               i INTEGER, b BOOLEAN, v VARBINARY, s SCALAR,
+                               d DECIMAL, u UUID);]])
+box.execute([[INSERT INTO t1(id) VALUES(NULL);]])
 
 --
 -- Check that implicit cast from numeric types to NUMBER and from scalar types
 -- to SCALAR works properly.
 --
-local uuid = [[CAST('11111111-1111-1111-1111-111111111111' AS UUID)]]
+local uuid_str = [[11111111-1111-1111-1111-111111111111]]
+local uuid = require('uuid').fromstr(uuid_str)
+local dec = require('decimal').new(1.5)
 test:do_execsql_test(
     "metatypes-1.1",
     [[
-        INSERT INTO t VALUES(1, 1, 1);
-        INSERT INTO t VALUES(2, 2.0, 2.0);
+        INSERT INTO t VALUES(1, 1, 1, 1);
+        INSERT INTO t VALUES(2, 2.0, 2.0, 2.0);
         INSERT INTO t(i, s) VALUES(3, '3');
         INSERT INTO t(i, s) VALUES(4, true);
         INSERT INTO t(i, s) VALUES(5, x'35');
-        INSERT INTO t(i, s) VALUES(6, ]]..uuid..[[);
+        INSERT INTO t(i, s) VALUES(6, CAST(']]..uuid_str..[[' AS UUID));
+        INSERT INTO t(i, a) VALUES(7, '3');
+        INSERT INTO t(i, a) VALUES(8, true);
+        INSERT INTO t(i, a) VALUES(9, x'35');
+        INSERT INTO t(i, a) VALUES(10, CAST(']]..uuid_str..[[' AS UUID));
+        INSERT INTO t(i, a) VALUES(11, 3e0);
+        INSERT INTO t(i, a) VALUES(12, CAST(1.5 AS DECIMAL));
         SELECT * FROM t;
     ]], {
-        1,1,1,
-        2,2,2,
-        3,"3","",
-        4,true,"",
-        5,"5","",
-        6,require('uuid').fromstr('11111111-1111-1111-1111-111111111111'),""
+        1, 1, 1, 1,
+        2, 2, 2, 2,
+        3, "3", "", "",
+        4, true, "", "",
+        5, "5", "", "",
+        6, uuid, "", "",
+        7, "", "", "3",
+        8, "", "", true,
+        9, "", "", "5",
+        10, "", "", uuid,
+        11, "", "", 3,
+        12, "", "", dec
     })
 
 -- Check that typeof() returns right result.
@@ -35,7 +54,8 @@ test:do_execsql_test(
     [[
         SELECT typeof(s) FROM t;
     ]], {
-        "scalar","scalar","scalar","scalar","scalar","scalar"
+        "scalar", "scalar", "scalar", "scalar", "scalar", "scalar",
+        "NULL", "NULL", "NULL", "NULL", "NULL", "NULL"
     })
 
 test:do_execsql_test(
@@ -43,7 +63,17 @@ test:do_execsql_test(
     [[
         SELECT typeof(n) FROM t;
     ]], {
-        "number","number","NULL","NULL","NULL","NULL"
+        "number", "number", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL",
+        "NULL", "NULL", "NULL", "NULL"
+    })
+
+test:do_execsql_test(
+    "metatypes-1.4",
+    [[
+        SELECT typeof(a) FROM t;
+    ]], {
+        "any", "any", "NULL", "NULL", "NULL", "NULL", "any", "any", "any",
+        "any", "any", "any"
     })
 
 --
@@ -90,7 +120,98 @@ test:do_catchsql_test(
         1, "Type mismatch: can not convert number(11) to integer"
     })
 
--- Check that arithmetic operations are prohibited for NUMBER and SCALAR values.
+-- Check that implicit cast from ANY to any other type is prohibited.
+test:do_execsql_test(
+    "metatypes-2.6",
+    [[
+        INSERT INTO t1(a) VALUES(CAST(1 AS ANY));
+        SELECT * FROM t1;
+    ]], {
+        1, "", "", "", "", "", "", "", "", "", "", "",
+        2, 1, "", "", "", "", "", "", "", "", "", ""
+    })
+
+test:do_catchsql_test(
+    "metatypes-2.7",
+    [[
+        INSERT INTO t1(g) VALUES(CAST(1 AS ANY));
+    ]], {
+        1, "Type mismatch: can not convert any(1) to unsigned"
+    })
+
+test:do_catchsql_test(
+    "metatypes-2.8",
+    [[
+        INSERT INTO t1(t) VALUES(CAST(1 AS ANY));
+    ]], {
+        1, "Type mismatch: can not convert any(1) to string"
+    })
+
+test:do_catchsql_test(
+    "metatypes-2.9",
+    [[
+        INSERT INTO t1(n) VALUES(CAST(1 AS ANY));
+    ]], {
+        1, "Type mismatch: can not convert any(1) to number"
+    })
+
+test:do_catchsql_test(
+    "metatypes-2.10",
+    [[
+        INSERT INTO t1(f) VALUES(CAST(1 AS ANY));
+    ]], {
+        1, "Type mismatch: can not convert any(1) to double"
+    })
+
+test:do_catchsql_test(
+    "metatypes-2.11",
+    [[
+        INSERT INTO t1(i) VALUES(CAST(1 AS ANY));
+    ]], {
+        1, "Type mismatch: can not convert any(1) to integer"
+    })
+
+test:do_catchsql_test(
+    "metatypes-2.12",
+    [[
+        INSERT INTO t1(b) VALUES(CAST(1 AS ANY));
+    ]], {
+        1, "Type mismatch: can not convert any(1) to boolean"
+    })
+
+test:do_catchsql_test(
+    "metatypes-2.13",
+    [[
+        INSERT INTO t1(v) VALUES(CAST(1 AS ANY));
+    ]], {
+        1, "Type mismatch: can not convert any(1) to varbinary"
+    })
+
+test:do_catchsql_test(
+    "metatypes-2.14",
+    [[
+        INSERT INTO t1(s) VALUES(CAST(1 AS ANY));
+    ]], {
+        1, "Type mismatch: can not convert any(1) to scalar"
+    })
+
+test:do_catchsql_test(
+    "metatypes-2.15",
+    [[
+        INSERT INTO t1(d) VALUES(CAST(1 AS ANY));
+    ]], {
+        1, "Type mismatch: can not convert any(1) to decimal"
+    })
+
+test:do_catchsql_test(
+    "metatypes-2.16",
+    [[
+        INSERT INTO t1(u) VALUES(CAST(1 AS ANY));
+    ]], {
+        1, "Type mismatch: can not convert any(1) to uuid"
+    })
+
+-- Check that arithmetic operations are prohibited for NUMBER, SCALAR and ANY.
 test:do_catchsql_test(
     "metatypes-3.1",
     [[
@@ -107,7 +228,15 @@ test:do_catchsql_test(
         1, "Type mismatch: can not convert scalar(1) to integer, decimal or double"
     })
 
--- Check that bitwise operations are prohibited for NUMBER and SCALAR values.
+test:do_catchsql_test(
+    "metatypes-3.3",
+    [[
+        SELECT CAST(1 AS ANY) - 1;
+    ]], {
+        1, "Type mismatch: can not convert any(1) to integer, decimal or double"
+    })
+
+-- Check that bitwise operations are prohibited for NUMBER, SCALAR and ANY.
 test:do_catchsql_test(
     "metatypes-4.1",
     [[
@@ -124,7 +253,15 @@ test:do_catchsql_test(
         1, "Type mismatch: can not convert scalar(1) to unsigned"
     })
 
--- Check that concatination is prohibited for SCALAR values.
+test:do_catchsql_test(
+    "metatypes-4.3",
+    [[
+        SELECT CAST(1 AS ANY) | 1;
+    ]], {
+        1, "Type mismatch: can not convert any(1) to unsigned"
+    })
+
+-- Check that concatination is prohibited for SCALAR and ANY.
 test:do_catchsql_test(
     "metatypes-5",
     [[
@@ -133,13 +270,21 @@ test:do_catchsql_test(
         1, "Inconsistent types: expected string or varbinary got scalar('asd')"
     })
 
+test:do_catchsql_test(
+    "metatypes-5",
+    [[
+        SELECT CAST('asd' AS ANY) || 'dsa';
+    ]], {
+        1, "Inconsistent types: expected string or varbinary got any('asd')"
+    })
+
 -- Check that SCALAR values can be compared to values of any other scalar type.
 test:do_execsql_test(
     "metatypes-6.1",
     [[
         SELECT s > false FROM t;
     ]], {
-        true, true, true, true, true, true
+        true, true, true, true, true, true, "", "", "", "", "", ""
     })
 
 test:do_execsql_test(
@@ -147,7 +292,7 @@ test:do_execsql_test(
     [[
         SELECT s = 1 FROM t;
     ]], {
-        true, false, false, false, false, false
+        true, false, false, false, false, false, "", "", "", "", "", ""
     })
 
 test:do_execsql_test(
@@ -155,7 +300,7 @@ test:do_execsql_test(
     [[
         SELECT s != 1.5 FROM t;
     ]], {
-        true, true, true, true, true, true
+        true, true, true, true, true, true, "", "", "", "", "", ""
     })
 
 test:do_execsql_test(
@@ -163,7 +308,7 @@ test:do_execsql_test(
     [[
         SELECT s <= 'abc' FROM t;
     ]], {
-        true, true, true, true, false, false
+        true, true, true, true, false, false, "", "", "", "", "", ""
     })
 
 test:do_execsql_test(
@@ -171,7 +316,7 @@ test:do_execsql_test(
     [[
         SELECT s < x'10' FROM t;
     ]], {
-        true, true, true, true, false, false
+        true, true, true, true, false, false, "", "", "", "", "", ""
     })
 
 test:do_execsql_test(
@@ -179,9 +324,386 @@ test:do_execsql_test(
     [[
         SELECT s > CAST('11111111-1111-1111-1111-111111111110' AS UUID) FROM t;
     ]], {
-        false, false, false, false, false, true
+        false, false, false, false, false, true, "", "", "", "", "", ""
+    })
+
+-- Check that ANY values cannot be compared to values of any other scalar type.
+test:do_catchsql_test(
+    "metatypes-7.1",
+    [[
+        SELECT a > false FROM t;
+    ]], {
+        1, "Type mismatch: can not convert any(1) to comparable type"
+    })
+
+test:do_catchsql_test(
+    "metatypes-7.2",
+    [[
+        SELECT a = 1 FROM t;
+    ]], {
+        1, "Type mismatch: can not convert any(1) to comparable type"
+    })
+
+test:do_catchsql_test(
+    "metatypes-7.3",
+    [[
+        SELECT a != 1.5 FROM t;
+    ]], {
+        1, "Type mismatch: can not convert any(1) to comparable type"
+    })
+
+test:do_catchsql_test(
+    "metatypes-7.4",
+    [[
+        SELECT a <= 'abc' FROM t;
+    ]], {
+        1, "Type mismatch: can not convert any(1) to comparable type"
+    })
+
+test:do_catchsql_test(
+    "metatypes-7.5",
+    [[
+        SELECT a < x'10' FROM t;
+    ]], {
+        1, "Type mismatch: can not convert any(1) to comparable type"
+    })
+
+test:do_catchsql_test(
+    "metatypes-7.6",
+    [[
+        SELECT a > CAST('11111111-1111-1111-1111-111111111110' AS UUID) FROM t;
+    ]], {
+        1, "Type mismatch: can not convert any(1) to comparable type"
+    })
+
+test:do_catchsql_test(
+    "metatypes-7.7",
+    [[
+        SELECT a >= CAST(1 AS DECIMAL) FROM t;
+    ]], {
+        1, "Type mismatch: can not convert any(1) to comparable type"
+    })
+
+test:do_catchsql_test(
+    "metatypes-7.8",
+    [[
+        SELECT a = CAST(1 AS NUMBER) FROM t;
+    ]], {
+        1, "Type mismatch: can not convert any(1) to comparable type"
     })
 
+test:do_catchsql_test(
+    "metatypes-7.9",
+    [[
+        SELECT a != CAST(1 AS SCALAR) FROM t;
+    ]], {
+        1, "Type mismatch: can not convert any(1) to comparable type"
+    })
+
+-- Make sure the SQL built-in functions work correctly with ANY.
+test:do_catchsql_test(
+    "metatypes-8.1",
+    [[
+        SELECT ABS(a) FROM t;
+    ]], {
+        1, "Type mismatch: can not convert any(1) to integer"
+    })
+
+test:do_catchsql_test(
+    "metatypes-8.2",
+    [[
+        SELECT AVG(a) FROM t;
+    ]], {
+        1, "Type mismatch: can not convert any(1) to integer"
+    })
+
+test:do_catchsql_test(
+    "metatypes-8.3",
+    [[
+        SELECT CHAR(a) FROM t;
+    ]], {
+        1, "Type mismatch: can not convert any(1) to integer"
+    })
+
+test:do_catchsql_test(
+    "metatypes-8.4",
+    [[
+        SELECT CHARACTER_LENGTH(a) FROM t;
+    ]], {
+        1, "Type mismatch: can not convert any(1) to string"
+    })
+
+test:do_catchsql_test(
+    "metatypes-8.5",
+    [[
+        SELECT CHAR_LENGTH(a) FROM t;
+    ]], {
+        1, "Type mismatch: can not convert any(1) to string"
+    })
+
+test:do_execsql_test(
+    "metatypes-8.6",
+    [[
+        SELECT COALESCE(s, a) FROM t;
+    ]], {
+        1, 2, "3", true, "5", uuid, "3", true, "5", uuid, 3, dec
+    })
+
+test:do_execsql_test(
+    "metatypes-8.7",
+    [[
+        SELECT COUNT(a) FROM t;
+    ]], {
+        8
+    })
+
+test:do_catchsql_test(
+    "metatypes-8.8",
+    [[
+        SELECT GREATEST(s, a) FROM t;
+    ]], {
+        1, "Type mismatch: can not convert any(1) to scalar"
+    })
+
+test:do_catchsql_test(
+    "metatypes-8.9",
+    [[
+        SELECT GROUP_CONCAT(a) FROM t;
+    ]], {
+        1, "Type mismatch: can not convert any(1) to string"
+    })
+
+test:do_catchsql_test(
+    "metatypes-8.10",
+    [[
+        SELECT HEX(a) FROM t;
+    ]], {
+        1, "Type mismatch: can not convert any(1) to varbinary"
+    })
+
+test:do_execsql_test(
+    "metatypes-8.11",
+    [[
+        SELECT IFNULL(s, a) FROM t;
+    ]], {
+        1, 2, "3", true, "5", uuid, "3", true, "5", uuid, 3, dec
+    })
+
+test:do_catchsql_test(
+    "metatypes-8.12",
+    [[
+        SELECT LEAST(s, a) FROM t;
+    ]], {
+        1, "Type mismatch: can not convert any(1) to scalar"
+    })
+
+test:do_catchsql_test(
+    "metatypes-8.13",
+    [[
+        SELECT LENGTH(a) FROM t;
+    ]], {
+        1, "Type mismatch: can not convert any(1) to string"
+    })
+
+test:do_catchsql_test(
+    "metatypes-8.14",
+    [[
+        SELECT s LIKE a FROM t;
+    ]], {
+        1, [[Failed to execute SQL statement: wrong arguments for function ]]..
+           [[LIKE()]]
+    })
+
+test:do_execsql_test(
+    "metatypes-8.15",
+    [[
+        SELECT LIKELIHOOD(a, 0.5) FROM t;
+    ]], {
+        1, 2, "", "", "", "", "3", true, "5", uuid, 3, dec
+    })
+
+test:do_execsql_test(
+    "metatypes-8.16",
+    [[
+        SELECT LIKELY(a) FROM t;
+    ]], {
+        1, 2, "", "", "", "", "3", true, "5", uuid, 3, dec
+    })
+
+test:do_catchsql_test(
+    "metatypes-8.17",
+    [[
+        SELECT LOWER(a) FROM t;
+    ]], {
+        1, "Type mismatch: can not convert any(1) to string"
+    })
+
+test:do_catchsql_test(
+    "metatypes-8.18",
+    [[
+        SELECT MAX(a) FROM t;
+    ]], {
+        1, "Type mismatch: can not convert any(1) to scalar"
+    })
+
+test:do_catchsql_test(
+    "metatypes-8.19",
+    [[
+        SELECT MIN(a) FROM t;
+    ]], {
+        1, "Type mismatch: can not convert any(1) to scalar"
+    })
+
+test:do_catchsql_test(
+    "metatypes-8.20",
+    [[
+        SELECT NULLIF(s, a) FROM t;
+    ]], {
+        1, "Type mismatch: can not convert any(1) to scalar"
+    })
+
+test:do_catchsql_test(
+    "metatypes-8.21",
+    [[
+        SELECT POSITION(s, a) FROM t;
+    ]], {
+        1, [[Failed to execute SQL statement: wrong arguments for function ]]..
+           [[POSITION()]]
+    })
+
+test:do_execsql_test(
+    "metatypes-8.22",
+    [[
+        SELECT PRINTF(a) FROM t;
+    ]], {
+        "1", "2.0", "", "", "", "", "3", "TRUE", "5",
+        "11111111-1111-1111-1111-111111111111", "3.0", "1.5"
+    })
+
+test:do_execsql_test(
+    "metatypes-8.23",
+    [[
+        SELECT QUOTE(a) FROM t;
+    ]], {
+        1, 2, "NULL", "NULL", "NULL", "NULL", "'3'", "TRUE", "X'35'",
+        "11111111-1111-1111-1111-111111111111", 3, dec
+    })
+
+test:do_catchsql_test(
+    "metatypes-8.24",
+    [[
+        SELECT RANDOMBLOB(a) FROM t;
+    ]], {
+        1, "Type mismatch: can not convert any(1) to integer"
+    })
+
+test:do_catchsql_test(
+    "metatypes-8.25",
+    [[
+        SELECT REPLACE(s, n, a) FROM t;
+    ]], {
+        1, [[Failed to execute SQL statement: wrong arguments for function ]]..
+           [[REPLACE()]]
+    })
+
+test:do_catchsql_test(
+    "metatypes-8.26",
+    [[
+        SELECT ROUND(a) FROM t;
+    ]], {
+        1, "Type mismatch: can not convert any(1) to double"
+    })
+
+test:do_catchsql_test(
+    "metatypes-8.27",
+    [[
+        SELECT SOUNDEX(a) FROM t;
+    ]], {
+        1, "Type mismatch: can not convert any(1) to string"
+    })
+
+test:do_catchsql_test(
+    "metatypes-8.28",
+    [[
+        SELECT SUBSTR(a, 1, 1) FROM t;
+    ]], {
+        1, "Type mismatch: can not convert any(1) to string"
+    })
+
+test:do_catchsql_test(
+    "metatypes-8.29",
+    [[
+        SELECT SUM(a) FROM t;
+    ]], {
+        1, "Type mismatch: can not convert any(1) to integer"
+    })
+
+test:do_catchsql_test(
+    "metatypes-8.30",
+    [[
+        SELECT TOTAL(a) FROM t;
+    ]], {
+        1, "Type mismatch: can not convert any(1) to integer"
+    })
+
+test:do_catchsql_test(
+    "metatypes-8.31",
+    [[
+        SELECT TRIM(a) FROM t;
+    ]], {
+        1, "Type mismatch: can not convert any(1) to string"
+    })
+
+test:do_execsql_test(
+    "metatypes-8.32",
+    [[
+        SELECT TYPEOF(a) FROM t;
+    ]], {
+        "any", "any", "NULL", "NULL", "NULL", "NULL", "any", "any", "any",
+        "any", "any", "any"
+    })
+
+test:do_catchsql_test(
+    "metatypes-8.33",
+    [[
+        SELECT UNICODE(a) FROM t;
+    ]], {
+        1, "Type mismatch: can not convert any(1) to string"
+    })
+
+test:do_execsql_test(
+    "metatypes-8.34",
+    [[
+        SELECT UNLIKELY(a) FROM t;
+    ]], {
+        1, 2, "", "", "", "", "3", true, "5", uuid, 3, dec
+    })
+
+test:do_catchsql_test(
+    "metatypes-8.35",
+    [[
+        SELECT UPPER(a) FROM t;
+    ]], {
+        1, "Type mismatch: can not convert any(1) to string"
+    })
+
+test:do_catchsql_test(
+    "metatypes-8.36",
+    [[
+        SELECT UUID(a) FROM t;
+    ]], {
+        1, "Type mismatch: can not convert any(1) to integer"
+    })
+
+test:do_catchsql_test(
+    "metatypes-8.37",
+    [[
+        SELECT ZEROBLOB(a) FROM t;
+    ]], {
+        1, "Type mismatch: can not convert any(1) to integer"
+    })
+
+
 box.execute([[DROP TABLE t;]])
 
 test:finish_test()
-- 
2.25.1


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

* Re: [Tarantool-patches] [PATCH v1 0/3] Introduce field type ANY to SQL
  2021-10-28 10:52 [Tarantool-patches] [PATCH v1 0/3] Introduce field type ANY to SQL Mergen Imeev via Tarantool-patches
                   ` (2 preceding siblings ...)
  2021-10-28 10:52 ` [Tarantool-patches] [PATCH v1 3/3] sql: introduce field type ANY Mergen Imeev via Tarantool-patches
@ 2021-11-09 23:48 ` Vladislav Shpilevoy via Tarantool-patches
  3 siblings, 0 replies; 6+ messages in thread
From: Vladislav Shpilevoy via Tarantool-patches @ 2021-11-09 23:48 UTC (permalink / raw)
  To: imeevma; +Cc: tarantool-patches

Hi! Thanks for the patchset!

LGTM.

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

* [Tarantool-patches] [PATCH v1 3/3] sql: introduce field type ANY
  2021-11-11 10:53 Mergen Imeev via Tarantool-patches
@ 2021-11-11 10:53 ` Mergen Imeev via Tarantool-patches
  0 siblings, 0 replies; 6+ messages in thread
From: Mergen Imeev via Tarantool-patches @ 2021-11-11 10:53 UTC (permalink / raw)
  To: kyukhin; +Cc: tarantool-patches

Closes #3174

@TarantoolBot document
Title: Field type ANY in SQL

Properties of type ANY in SQL:
1) typeof() with an argument of type ANY returns "any";
2) any value of any type can be implicitly and explicitly cast to the ANY type;
3) a value of type ANY cannot be implicitly cast to any other type;
4) a value of type ANY cannot participate in arithmetic, bitwise, comparison,
   and concationation operations.
---
 extra/mkkeywordhash.c           |   3 +-
 src/box/sql/func.c              |   4 +-
 src/box/sql/mem.c               |  49 ++-
 src/box/sql/mem.h               |   4 +-
 src/box/sql/parse.y             |   3 +-
 src/box/sql/vdbe.c              |   4 +-
 test/sql-tap/metatypes.test.lua | 568 ++++++++++++++++++++++++++++++--
 7 files changed, 594 insertions(+), 41 deletions(-)

diff --git a/extra/mkkeywordhash.c b/extra/mkkeywordhash.c
index 3e4200417..aaeb7d51b 100644
--- a/extra/mkkeywordhash.c
+++ b/extra/mkkeywordhash.c
@@ -176,10 +176,11 @@ static Keyword aKeywordTable[] = {
   { "VALUES",                 "TK_VALUES",      true  },
   { "VARBINARY",              "TK_VARBINARY",   true  },
   { "VIEW",                   "TK_VIEW",        true  },
+  { "WILDCARD",               "TK_STANDARD",    true  },
   { "WITH",                   "TK_WITH",        true  },
   { "WHEN",                   "TK_WHEN",        true  },
   { "WHERE",                  "TK_WHERE",       true  },
-  { "ANY",                    "TK_STANDARD",    true  },
+  { "ANY",                    "TK_ANY",         true  },
   { "ASENSITIVE",             "TK_STANDARD",    true  },
   { "BLOB",                   "TK_STANDARD",    true  },
   { "BINARY",                 "TK_ID",          true  },
diff --git a/src/box/sql/func.c b/src/box/sql/func.c
index 63d7536d9..43f6fc40a 100644
--- a/src/box/sql/func.c
+++ b/src/box/sql/func.c
@@ -318,6 +318,8 @@ typeofFunc(struct sql_context *context, int argc, struct Mem *argv)
 {
 	(void)argc;
 	const char *z = 0;
+	if ((argv[0].flags & MEM_Any) != 0)
+		return mem_set_str0_static(context->pOut, "any");
 	if ((argv[0].flags & MEM_Number) != 0)
 		return mem_set_str0_static(context->pOut, "number");
 	if ((argv[0].flags & MEM_Scalar) != 0)
@@ -2013,7 +2015,7 @@ static struct sql_func_definition definitions[] = {
 	{"MIN", 1, {FIELD_TYPE_STRING}, FIELD_TYPE_STRING, step_minmax, NULL},
 	{"MIN", 1, {FIELD_TYPE_SCALAR}, FIELD_TYPE_SCALAR, step_minmax, NULL},
 
-	{"NULLIF", 2, {field_type_MAX, field_type_MAX}, FIELD_TYPE_SCALAR,
+	{"NULLIF", 2, {FIELD_TYPE_SCALAR, FIELD_TYPE_SCALAR}, FIELD_TYPE_SCALAR,
 	 nullifFunc, NULL},
 	{"POSITION", 2, {FIELD_TYPE_STRING, FIELD_TYPE_STRING},
 	 FIELD_TYPE_INTEGER, position_func, NULL},
diff --git a/src/box/sql/mem.c b/src/box/sql/mem.c
index f648c5d62..244415e02 100644
--- a/src/box/sql/mem.c
+++ b/src/box/sql/mem.c
@@ -727,7 +727,7 @@ str_to_bin(struct Mem *mem)
 {
 	assert(mem->type == MEM_TYPE_STR);
 	mem->type = MEM_TYPE_BIN;
-	mem->flags &= ~(MEM_Term | MEM_Scalar);
+	mem->flags &= ~(MEM_Term | MEM_Scalar | MEM_Any);
 	return 0;
 }
 
@@ -774,7 +774,7 @@ bin_to_str(struct Mem *mem)
 {
 	assert(mem->type == MEM_TYPE_BIN);
 	mem->type = MEM_TYPE_STR;
-	mem->flags &= ~MEM_Scalar;
+	mem->flags &= ~(MEM_Scalar | MEM_Any);
 	return 0;
 }
 
@@ -1376,7 +1376,7 @@ mem_to_str(struct Mem *mem)
 	assert(mem->type < MEM_TYPE_INVALID);
 	switch (mem->type) {
 	case MEM_TYPE_STR:
-		mem->flags &= ~MEM_Scalar;
+		mem->flags &= ~(MEM_Scalar | MEM_Any);
 		return 0;
 	case MEM_TYPE_INT:
 	case MEM_TYPE_UINT:
@@ -1440,7 +1440,7 @@ mem_cast_explicit(struct Mem *mem, enum field_type type)
 		if (mem->type == MEM_TYPE_STR)
 			return str_to_bin(mem);
 		if (mem_is_bytes(mem)) {
-			mem->flags &= ~MEM_Scalar;
+			mem->flags &= ~(MEM_Scalar | MEM_Any);
 			return 0;
 		}
 		if (mem->type == MEM_TYPE_UUID)
@@ -1478,7 +1478,11 @@ mem_cast_explicit(struct Mem *mem, enum field_type type)
 		if ((mem->type & (MEM_TYPE_MAP | MEM_TYPE_ARRAY)) != 0)
 			return -1;
 		mem->flags |= MEM_Scalar;
-		mem->flags &= ~MEM_Number;
+		mem->flags &= ~(MEM_Number | MEM_Any);
+		return 0;
+	case FIELD_TYPE_ANY:
+		mem->flags |= MEM_Any;
+		mem->flags &= ~(MEM_Number | MEM_Scalar);
 		return 0;
 	default:
 		break;
@@ -1491,11 +1495,21 @@ mem_cast_implicit(struct Mem *mem, enum field_type type)
 {
 	if (mem->type == MEM_TYPE_NULL || type == field_type_MAX)
 		return 0;
-	if ((mem->flags & MEM_Scalar) != 0 && type != FIELD_TYPE_SCALAR)
-		return -1;
-	if ((mem->flags & MEM_Number) != 0 && type != FIELD_TYPE_SCALAR &&
-	    type != FIELD_TYPE_NUMBER)
-		return -1;
+	if (mem_is_metatype(mem) && type != FIELD_TYPE_ANY) {
+		if ((mem->flags & MEM_Number) != 0) {
+			if (type != FIELD_TYPE_NUMBER &&
+			    type != FIELD_TYPE_SCALAR &&
+			    type != FIELD_TYPE_ANY)
+				return -1;
+		} else if ((mem->flags & MEM_Scalar) != 0) {
+			if (type != FIELD_TYPE_SCALAR &&
+			    type != FIELD_TYPE_ANY)
+				return -1;
+		} else {
+			if (type != FIELD_TYPE_ANY)
+				return -1;
+		}
+	}
 	switch (type) {
 	case FIELD_TYPE_UNSIGNED:
 		if (mem->type == MEM_TYPE_UINT) {
@@ -1509,7 +1523,7 @@ mem_cast_implicit(struct Mem *mem, enum field_type type)
 		return -1;
 	case FIELD_TYPE_STRING:
 		if (mem->type == MEM_TYPE_STR) {
-			mem->flags &= ~MEM_Scalar;
+			mem->flags &= ~(MEM_Scalar | MEM_Any);
 			return 0;
 		}
 		return -1;
@@ -1544,7 +1558,7 @@ mem_cast_implicit(struct Mem *mem, enum field_type type)
 	case FIELD_TYPE_VARBINARY:
 		if ((mem->type & (MEM_TYPE_BIN | MEM_TYPE_MAP |
 				  MEM_TYPE_ARRAY)) != 0) {
-			mem->flags &= ~MEM_Scalar;
+			mem->flags &= ~(MEM_Scalar | MEM_Any);
 			return 0;
 		}
 		return -1;
@@ -1579,7 +1593,7 @@ mem_cast_implicit(struct Mem *mem, enum field_type type)
 		if ((mem->type & (MEM_TYPE_MAP | MEM_TYPE_ARRAY)) != 0)
 			return -1;
 		mem->flags |= MEM_Scalar;
-		mem->flags &= ~MEM_Number;
+		mem->flags &= ~(MEM_Number | MEM_Any);
 		return 0;
 	case FIELD_TYPE_UUID:
 		if (mem->type != MEM_TYPE_UUID)
@@ -1587,6 +1601,8 @@ mem_cast_implicit(struct Mem *mem, enum field_type type)
 		mem->flags = 0;
 		return 0;
 	case FIELD_TYPE_ANY:
+		mem->flags |= MEM_Any;
+		mem->flags &= ~(MEM_Number | MEM_Scalar);
 		return 0;
 	default:
 		break;
@@ -2540,6 +2556,11 @@ mem_cmp(const struct Mem *a, const struct Mem *b, int *result,
 			*result = 1;
 		return 0;
 	}
+	if (((a->flags | b->flags) & MEM_Any) != 0) {
+		diag_set(ClientError, ER_SQL_TYPE_MISMATCH, mem_str(a),
+			 "comparable type");
+		return -1;
+	}
 	if (((a->flags | b->flags) & MEM_Scalar) != 0) {
 		*result = mem_cmp_scalar(a, b, coll);
 		return 0;
@@ -2575,6 +2596,8 @@ char *
 mem_type_to_str(const struct Mem *p)
 {
 	assert(p != NULL);
+	if ((p->flags & MEM_Any) != 0)
+		return "any";
 	if ((p->flags & MEM_Scalar) != 0)
 		return "scalar";
 	if ((p->flags & MEM_Number) != 0)
diff --git a/src/box/sql/mem.h b/src/box/sql/mem.h
index 242f910db..a8b132479 100644
--- a/src/box/sql/mem.h
+++ b/src/box/sql/mem.h
@@ -98,6 +98,8 @@ struct Mem {
 #define MEM_Number    0x0001
 /** MEM is of SCALAR meta-type. */
 #define MEM_Scalar    0x0002
+/** MEM is of ANY meta-type. */
+#define MEM_Any       0x0004
 #define MEM_Cleared   0x0200	/* NULL set by OP_Null, not from data */
 
 /* Whenever Mem contains a valid string or blob representation, one of
@@ -145,7 +147,7 @@ mem_is_num(const struct Mem *mem)
 static inline bool
 mem_is_metatype(const struct Mem *mem)
 {
-	return (mem->flags & (MEM_Number | MEM_Scalar)) != 0;
+	return (mem->flags & (MEM_Number | MEM_Scalar | MEM_Any)) != 0;
 }
 
 static inline bool
diff --git a/src/box/sql/parse.y b/src/box/sql/parse.y
index ae480a5d1..4b32f33f5 100644
--- a/src/box/sql/parse.y
+++ b/src/box/sql/parse.y
@@ -270,7 +270,7 @@ columnlist ::= tcons.
   QUERY KEY OFFSET RAISE RELEASE REPLACE RESTRICT
   RENAME CTIME_KW IF ENABLE DISABLE UUID
   .
-%wildcard ANY.
+%wildcard WILDCARD.
 
 
 // And "ids" is an identifer-or-string.
@@ -1841,6 +1841,7 @@ typedef(A) ::= BOOL . { A.type = FIELD_TYPE_BOOLEAN; }
 typedef(A) ::= BOOLEAN . { A.type = FIELD_TYPE_BOOLEAN; }
 typedef(A) ::= VARBINARY . { A.type = FIELD_TYPE_VARBINARY; }
 typedef(A) ::= UUID . { A.type = FIELD_TYPE_UUID; }
+typedef(A) ::= ANY . { A.type = FIELD_TYPE_ANY; }
 
 /**
  * Time-like types are temporary disabled, until they are
diff --git a/src/box/sql/vdbe.c b/src/box/sql/vdbe.c
index 7b1203bc8..0c4e38557 100644
--- a/src/box/sql/vdbe.c
+++ b/src/box/sql/vdbe.c
@@ -1934,7 +1934,9 @@ case OP_Column: {
 	/* Currently PSEUDO cursor does not have info about field types. */
 	if (pC->eCurType == CURTYPE_TARANTOOL)
 		field_type = pC->uc.pCursor->space->def->fields[p2].type;
-	if (field_type == FIELD_TYPE_SCALAR)
+	if (field_type == FIELD_TYPE_ANY)
+		pDest->flags |= MEM_Any;
+	else if (field_type == FIELD_TYPE_SCALAR)
 		pDest->flags |= MEM_Scalar;
 	else if (field_type == FIELD_TYPE_NUMBER)
 		pDest->flags |= MEM_Number;
diff --git a/test/sql-tap/metatypes.test.lua b/test/sql-tap/metatypes.test.lua
index dfa5bbab4..12c3ce06d 100755
--- a/test/sql-tap/metatypes.test.lua
+++ b/test/sql-tap/metatypes.test.lua
@@ -1,32 +1,51 @@
 #!/usr/bin/env tarantool
 local test = require("sqltester")
-test:plan(19)
+test:plan(80)
 
 -- Check that SCALAR and NUMBER meta-types works as intended.
-box.execute([[CREATE TABLE t (i INT PRIMARY KEY, s SCALAR, n NUMBER);]])
+box.execute([[CREATE TABLE t (i INT PRIMARY KEY, s SCALAR, n NUMBER, a ANY);]])
+box.execute([[CREATE TABLE t1 (id INTEGER PRIMARY KEY AUTOINCREMENT, a ANY,
+                               g UNSIGNED, t STRING, n NUMBER, f DOUBLE,
+                               i INTEGER, b BOOLEAN, v VARBINARY, s SCALAR,
+                               d DECIMAL, u UUID);]])
+box.execute([[INSERT INTO t1(id) VALUES(NULL);]])
 
 --
 -- Check that implicit cast from numeric types to NUMBER and from scalar types
 -- to SCALAR works properly.
 --
-local uuid = [[CAST('11111111-1111-1111-1111-111111111111' AS UUID)]]
+local uuid_str = [[11111111-1111-1111-1111-111111111111]]
+local uuid = require('uuid').fromstr(uuid_str)
+local dec = require('decimal').new(1.5)
 test:do_execsql_test(
     "metatypes-1.1",
     [[
-        INSERT INTO t VALUES(1, 1, 1);
-        INSERT INTO t VALUES(2, 2e0, 2e0);
+        INSERT INTO t VALUES(1, 1, 1, 1);
+        INSERT INTO t VALUES(2, 2e0, 2e0, 2e0);
         INSERT INTO t(i, s) VALUES(3, '3');
         INSERT INTO t(i, s) VALUES(4, true);
         INSERT INTO t(i, s) VALUES(5, x'35');
-        INSERT INTO t(i, s) VALUES(6, ]]..uuid..[[);
+        INSERT INTO t(i, s) VALUES(6, CAST(']]..uuid_str..[[' AS UUID));
+        INSERT INTO t(i, a) VALUES(7, '3');
+        INSERT INTO t(i, a) VALUES(8, true);
+        INSERT INTO t(i, a) VALUES(9, x'35');
+        INSERT INTO t(i, a) VALUES(10, CAST(']]..uuid_str..[[' AS UUID));
+        INSERT INTO t(i, a) VALUES(11, 3e0);
+        INSERT INTO t(i, a) VALUES(12, CAST(1.5 AS DECIMAL));
         SELECT * FROM t;
     ]], {
-        1,1,1,
-        2,2,2,
-        3,"3","",
-        4,true,"",
-        5,"5","",
-        6,require('uuid').fromstr('11111111-1111-1111-1111-111111111111'),""
+        1, 1, 1, 1,
+        2, 2, 2, 2,
+        3, "3", "", "",
+        4, true, "", "",
+        5, "5", "", "",
+        6, uuid, "", "",
+        7, "", "", "3",
+        8, "", "", true,
+        9, "", "", "5",
+        10, "", "", uuid,
+        11, "", "", 3,
+        12, "", "", dec
     })
 
 -- Check that typeof() returns right result.
@@ -35,7 +54,8 @@ test:do_execsql_test(
     [[
         SELECT typeof(s) FROM t;
     ]], {
-        "scalar","scalar","scalar","scalar","scalar","scalar"
+        "scalar", "scalar", "scalar", "scalar", "scalar", "scalar",
+        "NULL", "NULL", "NULL", "NULL", "NULL", "NULL"
     })
 
 test:do_execsql_test(
@@ -43,7 +63,17 @@ test:do_execsql_test(
     [[
         SELECT typeof(n) FROM t;
     ]], {
-        "number","number","NULL","NULL","NULL","NULL"
+        "number", "number", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL",
+        "NULL", "NULL", "NULL", "NULL"
+    })
+
+test:do_execsql_test(
+    "metatypes-1.4",
+    [[
+        SELECT typeof(a) FROM t;
+    ]], {
+        "any", "any", "NULL", "NULL", "NULL", "NULL", "any", "any", "any",
+        "any", "any", "any"
     })
 
 --
@@ -90,7 +120,98 @@ test:do_catchsql_test(
         1, "Type mismatch: can not convert number(11) to integer"
     })
 
--- Check that arithmetic operations are prohibited for NUMBER and SCALAR values.
+-- Check that implicit cast from ANY to any other type is prohibited.
+test:do_execsql_test(
+    "metatypes-2.6",
+    [[
+        INSERT INTO t1(a) VALUES(CAST(1 AS ANY));
+        SELECT * FROM t1;
+    ]], {
+        1, "", "", "", "", "", "", "", "", "", "", "",
+        2, 1, "", "", "", "", "", "", "", "", "", ""
+    })
+
+test:do_catchsql_test(
+    "metatypes-2.7",
+    [[
+        INSERT INTO t1(g) VALUES(CAST(1 AS ANY));
+    ]], {
+        1, "Type mismatch: can not convert any(1) to unsigned"
+    })
+
+test:do_catchsql_test(
+    "metatypes-2.8",
+    [[
+        INSERT INTO t1(t) VALUES(CAST(1 AS ANY));
+    ]], {
+        1, "Type mismatch: can not convert any(1) to string"
+    })
+
+test:do_catchsql_test(
+    "metatypes-2.9",
+    [[
+        INSERT INTO t1(n) VALUES(CAST(1 AS ANY));
+    ]], {
+        1, "Type mismatch: can not convert any(1) to number"
+    })
+
+test:do_catchsql_test(
+    "metatypes-2.10",
+    [[
+        INSERT INTO t1(f) VALUES(CAST(1 AS ANY));
+    ]], {
+        1, "Type mismatch: can not convert any(1) to double"
+    })
+
+test:do_catchsql_test(
+    "metatypes-2.11",
+    [[
+        INSERT INTO t1(i) VALUES(CAST(1 AS ANY));
+    ]], {
+        1, "Type mismatch: can not convert any(1) to integer"
+    })
+
+test:do_catchsql_test(
+    "metatypes-2.12",
+    [[
+        INSERT INTO t1(b) VALUES(CAST(1 AS ANY));
+    ]], {
+        1, "Type mismatch: can not convert any(1) to boolean"
+    })
+
+test:do_catchsql_test(
+    "metatypes-2.13",
+    [[
+        INSERT INTO t1(v) VALUES(CAST(1 AS ANY));
+    ]], {
+        1, "Type mismatch: can not convert any(1) to varbinary"
+    })
+
+test:do_catchsql_test(
+    "metatypes-2.14",
+    [[
+        INSERT INTO t1(s) VALUES(CAST(1 AS ANY));
+    ]], {
+        1, "Type mismatch: can not convert any(1) to scalar"
+    })
+
+test:do_catchsql_test(
+    "metatypes-2.15",
+    [[
+        INSERT INTO t1(d) VALUES(CAST(1 AS ANY));
+    ]], {
+        1, "Type mismatch: can not convert any(1) to decimal"
+    })
+
+test:do_catchsql_test(
+    "metatypes-2.16",
+    [[
+        INSERT INTO t1(u) VALUES(CAST(1 AS ANY));
+    ]], {
+        1, "Type mismatch: can not convert any(1) to uuid"
+    })
+
+-- Check that arithmetic operations are prohibited for NUMBER, SCALAR and ANY.
 test:do_catchsql_test(
     "metatypes-3.1",
     [[
@@ -107,7 +228,15 @@ test:do_catchsql_test(
         1, "Type mismatch: can not convert scalar(1) to integer, decimal or double"
     })
 
--- Check that bitwise operations are prohibited for NUMBER and SCALAR values.
+test:do_catchsql_test(
+    "metatypes-3.3",
+    [[
+        SELECT CAST(1 AS ANY) - 1;
+    ]], {
+        1, "Type mismatch: can not convert any(1) to integer, decimal or double"
+    })
+
+-- Check that bitwise operations are prohibited for NUMBER, SCALAR and ANY.
 test:do_catchsql_test(
     "metatypes-4.1",
     [[
@@ -124,7 +253,15 @@ test:do_catchsql_test(
         1, "Type mismatch: can not convert scalar(1) to unsigned"
     })
 
--- Check that concatination is prohibited for SCALAR values.
+test:do_catchsql_test(
+    "metatypes-4.3",
+    [[
+        SELECT CAST(1 AS ANY) | 1;
+    ]], {
+        1, "Type mismatch: can not convert any(1) to unsigned"
+    })
+
+-- Check that concatination is prohibited for SCALAR and ANY.
 test:do_catchsql_test(
     "metatypes-5",
     [[
@@ -133,13 +270,21 @@ test:do_catchsql_test(
         1, "Inconsistent types: expected string or varbinary got scalar('asd')"
     })
 
+test:do_catchsql_test(
+    "metatypes-5",
+    [[
+        SELECT CAST('asd' AS ANY) || 'dsa';
+    ]], {
+        1, "Inconsistent types: expected string or varbinary got any('asd')"
+    })
+
 -- Check that SCALAR values can be compared to values of any other scalar type.
 test:do_execsql_test(
     "metatypes-6.1",
     [[
         SELECT s > false FROM t;
     ]], {
-        true, true, true, true, true, true
+        true, true, true, true, true, true, "", "", "", "", "", ""
     })
 
 test:do_execsql_test(
@@ -147,7 +292,7 @@ test:do_execsql_test(
     [[
         SELECT s = 1 FROM t;
     ]], {
-        true, false, false, false, false, false
+        true, false, false, false, false, false, "", "", "", "", "", ""
     })
 
 test:do_execsql_test(
@@ -155,7 +300,7 @@ test:do_execsql_test(
     [[
         SELECT s != 1.5 FROM t;
     ]], {
-        true, true, true, true, true, true
+        true, true, true, true, true, true, "", "", "", "", "", ""
     })
 
 test:do_execsql_test(
@@ -163,7 +308,7 @@ test:do_execsql_test(
     [[
         SELECT s <= 'abc' FROM t;
     ]], {
-        true, true, true, true, false, false
+        true, true, true, true, false, false, "", "", "", "", "", ""
     })
 
 test:do_execsql_test(
@@ -171,7 +316,7 @@ test:do_execsql_test(
     [[
         SELECT s < x'10' FROM t;
     ]], {
-        true, true, true, true, false, false
+        true, true, true, true, false, false, "", "", "", "", "", ""
     })
 
 test:do_execsql_test(
@@ -179,9 +324,386 @@ test:do_execsql_test(
     [[
         SELECT s > CAST('11111111-1111-1111-1111-111111111110' AS UUID) FROM t;
     ]], {
-        false, false, false, false, false, true
+        false, false, false, false, false, true, "", "", "", "", "", ""
+    })
+
+-- Check that ANY values cannot be compared to values of any other scalar type.
+test:do_catchsql_test(
+    "metatypes-7.1",
+    [[
+        SELECT a > false FROM t;
+    ]], {
+        1, "Type mismatch: can not convert any(1) to comparable type"
+    })
+
+test:do_catchsql_test(
+    "metatypes-7.2",
+    [[
+        SELECT a = 1 FROM t;
+    ]], {
+        1, "Type mismatch: can not convert any(1) to comparable type"
+    })
+
+test:do_catchsql_test(
+    "metatypes-7.3",
+    [[
+        SELECT a != 1.5 FROM t;
+    ]], {
+        1, "Type mismatch: can not convert any(1) to comparable type"
+    })
+
+test:do_catchsql_test(
+    "metatypes-7.4",
+    [[
+        SELECT a <= 'abc' FROM t;
+    ]], {
+        1, "Type mismatch: can not convert any(1) to comparable type"
+    })
+
+test:do_catchsql_test(
+    "metatypes-7.5",
+    [[
+        SELECT a < x'10' FROM t;
+    ]], {
+        1, "Type mismatch: can not convert any(1) to comparable type"
+    })
+
+test:do_catchsql_test(
+    "metatypes-7.6",
+    [[
+        SELECT a > CAST('11111111-1111-1111-1111-111111111110' AS UUID) FROM t;
+    ]], {
+        1, "Type mismatch: can not convert any(1) to comparable type"
+    })
+
+test:do_catchsql_test(
+    "metatypes-7.7",
+    [[
+        SELECT a >= CAST(1 AS DECIMAL) FROM t;
+    ]], {
+        1, "Type mismatch: can not convert any(1) to comparable type"
+    })
+
+test:do_catchsql_test(
+    "metatypes-7.8",
+    [[
+        SELECT a = CAST(1 AS NUMBER) FROM t;
+    ]], {
+        1, "Type mismatch: can not convert any(1) to comparable type"
     })
 
+test:do_catchsql_test(
+    "metatypes-7.9",
+    [[
+        SELECT a != CAST(1 AS SCALAR) FROM t;
+    ]], {
+        1, "Type mismatch: can not convert any(1) to comparable type"
+    })
+
+-- Make sure the SQL built-in functions work correctly with ANY.
+test:do_catchsql_test(
+    "metatypes-8.1",
+    [[
+        SELECT ABS(a) FROM t;
+    ]], {
+        1, "Type mismatch: can not convert any(1) to integer"
+    })
+
+test:do_catchsql_test(
+    "metatypes-8.2",
+    [[
+        SELECT AVG(a) FROM t;
+    ]], {
+        1, "Type mismatch: can not convert any(1) to integer"
+    })
+
+test:do_catchsql_test(
+    "metatypes-8.3",
+    [[
+        SELECT CHAR(a) FROM t;
+    ]], {
+        1, "Type mismatch: can not convert any(1) to integer"
+    })
+
+test:do_catchsql_test(
+    "metatypes-8.4",
+    [[
+        SELECT CHARACTER_LENGTH(a) FROM t;
+    ]], {
+        1, "Type mismatch: can not convert any(1) to string"
+    })
+
+test:do_catchsql_test(
+    "metatypes-8.5",
+    [[
+        SELECT CHAR_LENGTH(a) FROM t;
+    ]], {
+        1, "Type mismatch: can not convert any(1) to string"
+    })
+
+test:do_execsql_test(
+    "metatypes-8.6",
+    [[
+        SELECT COALESCE(s, a) FROM t;
+    ]], {
+        1, 2, "3", true, "5", uuid, "3", true, "5", uuid, 3, dec
+    })
+
+test:do_execsql_test(
+    "metatypes-8.7",
+    [[
+        SELECT COUNT(a) FROM t;
+    ]], {
+        8
+    })
+
+test:do_catchsql_test(
+    "metatypes-8.8",
+    [[
+        SELECT GREATEST(s, a) FROM t;
+    ]], {
+        1, "Type mismatch: can not convert any(1) to scalar"
+    })
+
+test:do_catchsql_test(
+    "metatypes-8.9",
+    [[
+        SELECT GROUP_CONCAT(a) FROM t;
+    ]], {
+        1, "Type mismatch: can not convert any(1) to string"
+    })
+
+test:do_catchsql_test(
+    "metatypes-8.10",
+    [[
+        SELECT HEX(a) FROM t;
+    ]], {
+        1, "Type mismatch: can not convert any(1) to varbinary"
+    })
+
+test:do_execsql_test(
+    "metatypes-8.11",
+    [[
+        SELECT IFNULL(s, a) FROM t;
+    ]], {
+        1, 2, "3", true, "5", uuid, "3", true, "5", uuid, 3, dec
+    })
+
+test:do_catchsql_test(
+    "metatypes-8.12",
+    [[
+        SELECT LEAST(s, a) FROM t;
+    ]], {
+        1, "Type mismatch: can not convert any(1) to scalar"
+    })
+
+test:do_catchsql_test(
+    "metatypes-8.13",
+    [[
+        SELECT LENGTH(a) FROM t;
+    ]], {
+        1, "Type mismatch: can not convert any(1) to string"
+    })
+
+test:do_catchsql_test(
+    "metatypes-8.14",
+    [[
+        SELECT s LIKE a FROM t;
+    ]], {
+        1, [[Failed to execute SQL statement: wrong arguments for function ]]..
+           [[LIKE()]]
+    })
+
+test:do_execsql_test(
+    "metatypes-8.15",
+    [[
+        SELECT LIKELIHOOD(a, 0.5e0) FROM t;
+    ]], {
+        1, 2, "", "", "", "", "3", true, "5", uuid, 3, dec
+    })
+
+test:do_execsql_test(
+    "metatypes-8.16",
+    [[
+        SELECT LIKELY(a) FROM t;
+    ]], {
+        1, 2, "", "", "", "", "3", true, "5", uuid, 3, dec
+    })
+
+test:do_catchsql_test(
+    "metatypes-8.17",
+    [[
+        SELECT LOWER(a) FROM t;
+    ]], {
+        1, "Type mismatch: can not convert any(1) to string"
+    })
+
+test:do_catchsql_test(
+    "metatypes-8.18",
+    [[
+        SELECT MAX(a) FROM t;
+    ]], {
+        1, "Type mismatch: can not convert any(1) to scalar"
+    })
+
+test:do_catchsql_test(
+    "metatypes-8.19",
+    [[
+        SELECT MIN(a) FROM t;
+    ]], {
+        1, "Type mismatch: can not convert any(1) to scalar"
+    })
+
+test:do_catchsql_test(
+    "metatypes-8.20",
+    [[
+        SELECT NULLIF(s, a) FROM t;
+    ]], {
+        1, "Type mismatch: can not convert any(1) to scalar"
+    })
+
+test:do_catchsql_test(
+    "metatypes-8.21",
+    [[
+        SELECT POSITION(s, a) FROM t;
+    ]], {
+        1, [[Failed to execute SQL statement: wrong arguments for function ]]..
+           [[POSITION()]]
+    })
+
+test:do_execsql_test(
+    "metatypes-8.22",
+    [[
+        SELECT PRINTF(a) FROM t;
+    ]], {
+        "1", "2.0", "", "", "", "", "3", "TRUE", "5",
+        "11111111-1111-1111-1111-111111111111", "3.0", "1.5"
+    })
+
+test:do_execsql_test(
+    "metatypes-8.23",
+    [[
+        SELECT QUOTE(a) FROM t;
+    ]], {
+        1, 2, "NULL", "NULL", "NULL", "NULL", "'3'", "TRUE", "X'35'",
+        "11111111-1111-1111-1111-111111111111", 3, dec
+    })
+
+test:do_catchsql_test(
+    "metatypes-8.24",
+    [[
+        SELECT RANDOMBLOB(a) FROM t;
+    ]], {
+        1, "Type mismatch: can not convert any(1) to integer"
+    })
+
+test:do_catchsql_test(
+    "metatypes-8.25",
+    [[
+        SELECT REPLACE(s, n, a) FROM t;
+    ]], {
+        1, [[Failed to execute SQL statement: wrong arguments for function ]]..
+           [[REPLACE()]]
+    })
+
+test:do_catchsql_test(
+    "metatypes-8.26",
+    [[
+        SELECT ROUND(a) FROM t;
+    ]], {
+        1, "Type mismatch: can not convert any(1) to double"
+    })
+
+test:do_catchsql_test(
+    "metatypes-8.27",
+    [[
+        SELECT SOUNDEX(a) FROM t;
+    ]], {
+        1, "Type mismatch: can not convert any(1) to string"
+    })
+
+test:do_catchsql_test(
+    "metatypes-8.28",
+    [[
+        SELECT SUBSTR(a, 1, 1) FROM t;
+    ]], {
+        1, "Type mismatch: can not convert any(1) to string"
+    })
+
+test:do_catchsql_test(
+    "metatypes-8.29",
+    [[
+        SELECT SUM(a) FROM t;
+    ]], {
+        1, "Type mismatch: can not convert any(1) to integer"
+    })
+
+test:do_catchsql_test(
+    "metatypes-8.30",
+    [[
+        SELECT TOTAL(a) FROM t;
+    ]], {
+        1, "Type mismatch: can not convert any(1) to integer"
+    })
+
+test:do_catchsql_test(
+    "metatypes-8.31",
+    [[
+        SELECT TRIM(a) FROM t;
+    ]], {
+        1, "Type mismatch: can not convert any(1) to string"
+    })
+
+test:do_execsql_test(
+    "metatypes-8.32",
+    [[
+        SELECT TYPEOF(a) FROM t;
+    ]], {
+        "any", "any", "NULL", "NULL", "NULL", "NULL", "any", "any", "any",
+        "any", "any", "any"
+    })
+
+test:do_catchsql_test(
+    "metatypes-8.33",
+    [[
+        SELECT UNICODE(a) FROM t;
+    ]], {
+        1, "Type mismatch: can not convert any(1) to string"
+    })
+
+test:do_execsql_test(
+    "metatypes-8.34",
+    [[
+        SELECT UNLIKELY(a) FROM t;
+    ]], {
+        1, 2, "", "", "", "", "3", true, "5", uuid, 3, dec
+    })
+
+test:do_catchsql_test(
+    "metatypes-8.35",
+    [[
+        SELECT UPPER(a) FROM t;
+    ]], {
+        1, "Type mismatch: can not convert any(1) to string"
+    })
+
+test:do_catchsql_test(
+    "metatypes-8.36",
+    [[
+        SELECT UUID(a) FROM t;
+    ]], {
+        1, "Type mismatch: can not convert any(1) to integer"
+    })
+
+test:do_catchsql_test(
+    "metatypes-8.37",
+    [[
+        SELECT ZEROBLOB(a) FROM t;
+    ]], {
+        1, "Type mismatch: can not convert any(1) to integer"
+    })
+
+
 box.execute([[DROP TABLE t;]])
 
 test:finish_test()
-- 
2.25.1


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

end of thread, other threads:[~2021-11-11 11:02 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-10-28 10:52 [Tarantool-patches] [PATCH v1 0/3] Introduce field type ANY to SQL Mergen Imeev via Tarantool-patches
2021-10-28 10:52 ` [Tarantool-patches] [PATCH v1 1/3] sql: properly set INTEGER type in struct Expr Mergen Imeev via Tarantool-patches
2021-10-28 10:52 ` [Tarantool-patches] [PATCH v1 2/3] sql: use field_type_MAX instead of FIELD_TYPE_ANY Mergen Imeev via Tarantool-patches
2021-10-28 10:52 ` [Tarantool-patches] [PATCH v1 3/3] sql: introduce field type ANY Mergen Imeev via Tarantool-patches
2021-11-09 23:48 ` [Tarantool-patches] [PATCH v1 0/3] Introduce field type ANY to SQL Vladislav Shpilevoy via Tarantool-patches
2021-11-11 10:53 Mergen Imeev via Tarantool-patches
2021-11-11 10:53 ` [Tarantool-patches] [PATCH v1 3/3] sql: introduce field type ANY Mergen Imeev 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