* [Tarantool-patches] [PATCH v4 1/5] sql: set field_type in mem_set_*() functions
2020-07-13 5:32 [Tarantool-patches] [PATCH v4 0/5] Change implicit cast for assignment imeevma
@ 2020-07-13 5:32 ` imeevma
2020-07-13 10:36 ` Nikita Pettik
2020-07-13 5:33 ` [Tarantool-patches] [PATCH v4 2/5] sql: move diag setting to sql_func_by_signature() imeevma
` (3 subsequent siblings)
4 siblings, 1 reply; 11+ messages in thread
From: imeevma @ 2020-07-13 5:32 UTC (permalink / raw)
To: korablev, tsafin, tarantool-patches
After this patch, the mem_set _*() functions will set the mem field type
along with its MEM type flag.
---
src/box/sql/vdbemem.c | 10 ++++++++++
1 file changed, 10 insertions(+)
diff --git a/src/box/sql/vdbemem.c b/src/box/sql/vdbemem.c
index 2e0d0bc3b..8e9ebf7ab 100644
--- a/src/box/sql/vdbemem.c
+++ b/src/box/sql/vdbemem.c
@@ -829,6 +829,7 @@ mem_set_bool(struct Mem *mem, bool value)
sqlVdbeMemSetNull(mem);
mem->u.b = value;
mem->flags = MEM_Bool;
+ mem->field_type = FIELD_TYPE_BOOLEAN;
}
void
@@ -839,6 +840,7 @@ mem_set_i64(struct Mem *mem, int64_t value)
mem->u.i = value;
int flag = value < 0 ? MEM_Int : MEM_UInt;
MemSetTypeFlag(mem, flag);
+ mem->field_type = FIELD_TYPE_INTEGER;
}
void
@@ -848,6 +850,7 @@ mem_set_u64(struct Mem *mem, uint64_t value)
sqlVdbeMemSetNull(mem);
mem->u.u = value;
MemSetTypeFlag(mem, MEM_UInt);
+ mem->field_type = FIELD_TYPE_UNSIGNED;
}
void
@@ -863,6 +866,7 @@ mem_set_int(struct Mem *mem, int64_t value, bool is_neg)
mem->u.u = value;
MemSetTypeFlag(mem, MEM_UInt);
}
+ mem->field_type = FIELD_TYPE_INTEGER;
}
void
@@ -873,6 +877,7 @@ mem_set_double(struct Mem *mem, double value)
return;
mem->u.r = value;
MemSetTypeFlag(mem, MEM_Real);
+ mem->field_type = FIELD_TYPE_DOUBLE;
}
/*
@@ -1068,6 +1073,11 @@ sqlVdbeMemSetStr(Mem * pMem, /* Memory cell to set to string value */
pMem->n = nByte;
pMem->flags = flags;
+ assert((pMem->flags & (MEM_Str | MEM_Blob)) != 0);
+ if ((pMem->flags & MEM_Str) != 0)
+ pMem->field_type = FIELD_TYPE_STRING;
+ else
+ pMem->field_type = FIELD_TYPE_VARBINARY;
if (nByte > iLimit) {
diag_set(ClientError, ER_SQL_EXECUTE, "string or binary string"\
--
2.25.1
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [Tarantool-patches] [PATCH v4 1/5] sql: set field_type in mem_set_*() functions
2020-07-13 5:32 ` [Tarantool-patches] [PATCH v4 1/5] sql: set field_type in mem_set_*() functions imeevma
@ 2020-07-13 10:36 ` Nikita Pettik
0 siblings, 0 replies; 11+ messages in thread
From: Nikita Pettik @ 2020-07-13 10:36 UTC (permalink / raw)
To: imeevma; +Cc: tarantool-patches
On 13 Jul 08:32, imeevma@tarantool.org wrote:
> After this patch, the mem_set _*() functions will set the mem field type
> along with its MEM type flag.
Nice. All tests seem passing. So what was the problem in this
refactoring? I 'member you said that it couldn't be done with ease.
LGTM but I would put justification in commit message.
> ---
> src/box/sql/vdbemem.c | 10 ++++++++++
> 1 file changed, 10 insertions(+)
>
> diff --git a/src/box/sql/vdbemem.c b/src/box/sql/vdbemem.c
> index 2e0d0bc3b..8e9ebf7ab 100644
> --- a/src/box/sql/vdbemem.c
> +++ b/src/box/sql/vdbemem.c
> @@ -829,6 +829,7 @@ mem_set_bool(struct Mem *mem, bool value)
> sqlVdbeMemSetNull(mem);
> mem->u.b = value;
> mem->flags = MEM_Bool;
> + mem->field_type = FIELD_TYPE_BOOLEAN;
> }
>
> void
> @@ -839,6 +840,7 @@ mem_set_i64(struct Mem *mem, int64_t value)
> mem->u.i = value;
> int flag = value < 0 ? MEM_Int : MEM_UInt;
> MemSetTypeFlag(mem, flag);
> + mem->field_type = FIELD_TYPE_INTEGER;
> }
>
> void
> @@ -848,6 +850,7 @@ mem_set_u64(struct Mem *mem, uint64_t value)
> sqlVdbeMemSetNull(mem);
> mem->u.u = value;
> MemSetTypeFlag(mem, MEM_UInt);
> + mem->field_type = FIELD_TYPE_UNSIGNED;
> }
>
> void
> @@ -863,6 +866,7 @@ mem_set_int(struct Mem *mem, int64_t value, bool is_neg)
> mem->u.u = value;
> MemSetTypeFlag(mem, MEM_UInt);
> }
> + mem->field_type = FIELD_TYPE_INTEGER;
> }
>
> void
> @@ -873,6 +877,7 @@ mem_set_double(struct Mem *mem, double value)
> return;
> mem->u.r = value;
> MemSetTypeFlag(mem, MEM_Real);
> + mem->field_type = FIELD_TYPE_DOUBLE;
> }
>
> /*
> @@ -1068,6 +1073,11 @@ sqlVdbeMemSetStr(Mem * pMem, /* Memory cell to set to string value */
>
> pMem->n = nByte;
> pMem->flags = flags;
> + assert((pMem->flags & (MEM_Str | MEM_Blob)) != 0);
> + if ((pMem->flags & MEM_Str) != 0)
> + pMem->field_type = FIELD_TYPE_STRING;
> + else
> + pMem->field_type = FIELD_TYPE_VARBINARY;
>
> if (nByte > iLimit) {
> diag_set(ClientError, ER_SQL_EXECUTE, "string or binary string"\
> --
> 2.25.1
>
^ permalink raw reply [flat|nested] 11+ messages in thread
* [Tarantool-patches] [PATCH v4 2/5] sql: move diag setting to sql_func_by_signature()
2020-07-13 5:32 [Tarantool-patches] [PATCH v4 0/5] Change implicit cast for assignment imeevma
2020-07-13 5:32 ` [Tarantool-patches] [PATCH v4 1/5] sql: set field_type in mem_set_*() functions imeevma
@ 2020-07-13 5:33 ` imeevma
2020-07-13 10:58 ` Nikita Pettik
2020-07-13 5:33 ` [Tarantool-patches] [PATCH v4 3/5] sql: check number of arguments in sql_func_by_signature() imeevma
` (2 subsequent siblings)
4 siblings, 1 reply; 11+ messages in thread
From: imeevma @ 2020-07-13 5:33 UTC (permalink / raw)
To: korablev, tsafin, tarantool-patches
After this patch, the sql_func_by_signature() function will check the
found function and set diag if something is wrong.
Needed for #4159
---
src/box/sql/expr.c | 2 --
src/box/sql/func.c | 18 +++++++++++++++---
src/box/sql/resolve.c | 23 ++---------------------
src/box/sql/sqlInt.h | 7 +++++--
4 files changed, 22 insertions(+), 28 deletions(-)
diff --git a/src/box/sql/expr.c b/src/box/sql/expr.c
index bc2182446..d0620b98c 100644
--- a/src/box/sql/expr.c
+++ b/src/box/sql/expr.c
@@ -3978,8 +3978,6 @@ sqlExprCodeTarget(Parse * pParse, Expr * pExpr, int target)
zId = pExpr->u.zToken;
struct func *func = sql_func_by_signature(zId, nFarg);
if (func == NULL) {
- diag_set(ClientError, ER_NO_SUCH_FUNCTION,
- zId);
pParse->is_aborted = true;
break;
}
diff --git a/src/box/sql/func.c b/src/box/sql/func.c
index 487cdafe1..4bbe4d4b7 100644
--- a/src/box/sql/func.c
+++ b/src/box/sql/func.c
@@ -2185,11 +2185,23 @@ struct func *
sql_func_by_signature(const char *name, int argc)
{
struct func *base = func_by_name(name, strlen(name));
- if (base == NULL || !base->def->exports.sql)
+ if (base == NULL) {
+ diag_set(ClientError, ER_NO_SUCH_FUNCTION, name);
return NULL;
-
- if (base->def->param_count != -1 && base->def->param_count != argc)
+ }
+ if (!base->def->exports.sql) {
+ diag_set(ClientError, ER_SQL_PARSER_GENERIC,
+ tt_sprintf("function %s() is not available in SQL",
+ name));
+ return NULL;
+ }
+ int param_count = base->def->param_count;
+ if (param_count != -1 && param_count != argc) {
+ const char *err = tt_sprintf("%d", param_count);
+ diag_set(ClientError, ER_FUNC_WRONG_ARG_COUNT,
+ base->def->name, err, argc);
return NULL;
+ }
return base;
}
diff --git a/src/box/sql/resolve.c b/src/box/sql/resolve.c
index 6f625dc18..10c77c491 100644
--- a/src/box/sql/resolve.c
+++ b/src/box/sql/resolve.c
@@ -598,32 +598,13 @@ resolveExprStep(Walker * pWalker, Expr * pExpr)
assert(!ExprHasProperty(pExpr, EP_xIsSelect));
zId = pExpr->u.zToken;
nId = sqlStrlen30(zId);
- struct func *func = func_by_name(zId, nId);
+ struct func *func = sql_func_by_signature(zId, n);
if (func == NULL) {
- diag_set(ClientError, ER_NO_SUCH_FUNCTION, zId);
- pParse->is_aborted = true;
- pNC->nErr++;
- return WRC_Abort;
- }
- if (!func->def->exports.sql) {
- diag_set(ClientError, ER_SQL_PARSER_GENERIC,
- tt_sprintf("function %.*s() is not "
- "available in SQL",
- nId, zId));
- pParse->is_aborted = true;
- pNC->nErr++;
- return WRC_Abort;
- }
- if (func->def->param_count != -1 &&
- func->def->param_count != n) {
- uint32_t argc = func->def->param_count;
- const char *err = tt_sprintf("%d", argc);
- diag_set(ClientError, ER_FUNC_WRONG_ARG_COUNT,
- func->def->name, err, n);
pParse->is_aborted = true;
pNC->nErr++;
return WRC_Abort;
}
+ assert(func->def->exports.sql);
bool is_agg = func->def->aggregate ==
FUNC_AGGREGATE_GROUP;
assert(!is_agg || func->def->language ==
diff --git a/src/box/sql/sqlInt.h b/src/box/sql/sqlInt.h
index 37283e506..58a65acc1 100644
--- a/src/box/sql/sqlInt.h
+++ b/src/box/sql/sqlInt.h
@@ -4441,8 +4441,11 @@ sql_func_flag_is_set(struct func *func, uint16_t flag)
* export field set true and have exactly the same signature
* are returned.
*
- * Returns not NULL function pointer when a valid and exported
- * to SQL engine function is found and NULL otherwise.
+ * @param name Name of the function to find.
+ * @param argc Number of arguments of the function.
+ *
+ * @retval not NULL function pointer when a function is found.
+ * @retval NULL on error and sets a diag.
*/
struct func *
sql_func_by_signature(const char *name, int argc);
--
2.25.1
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [Tarantool-patches] [PATCH v4 2/5] sql: move diag setting to sql_func_by_signature()
2020-07-13 5:33 ` [Tarantool-patches] [PATCH v4 2/5] sql: move diag setting to sql_func_by_signature() imeevma
@ 2020-07-13 10:58 ` Nikita Pettik
0 siblings, 0 replies; 11+ messages in thread
From: Nikita Pettik @ 2020-07-13 10:58 UTC (permalink / raw)
To: imeevma; +Cc: tarantool-patches
On 13 Jul 08:33, imeevma@tarantool.org wrote:
> After this patch, the sql_func_by_signature() function will check the
> found function and set diag if something is wrong.
Why we are doing this refactoring? I've bee begging you to provide
more descriptive commit messages for past two years.
> Needed for #4159
> ---
> src/box/sql/expr.c | 2 --
> src/box/sql/func.c | 18 +++++++++++++++---
> src/box/sql/resolve.c | 23 ++---------------------
> src/box/sql/sqlInt.h | 7 +++++--
> 4 files changed, 22 insertions(+), 28 deletions(-)
>
> diff --git a/src/box/sql/expr.c b/src/box/sql/expr.c
> index bc2182446..d0620b98c 100644
> --- a/src/box/sql/expr.c
> +++ b/src/box/sql/expr.c
> @@ -3978,8 +3978,6 @@ sqlExprCodeTarget(Parse * pParse, Expr * pExpr, int target)
> zId = pExpr->u.zToken;
> struct func *func = sql_func_by_signature(zId, nFarg);
> if (func == NULL) {
> - diag_set(ClientError, ER_NO_SUCH_FUNCTION,
> - zId);
> pParse->is_aborted = true;
> break;
> }
> diff --git a/src/box/sql/func.c b/src/box/sql/func.c
> index 487cdafe1..4bbe4d4b7 100644
> --- a/src/box/sql/func.c
> +++ b/src/box/sql/func.c
> @@ -2185,11 +2185,23 @@ struct func *
> sql_func_by_signature(const char *name, int argc)
> {
> struct func *base = func_by_name(name, strlen(name));
> - if (base == NULL || !base->def->exports.sql)
> + if (base == NULL) {
> + diag_set(ClientError, ER_NO_SUCH_FUNCTION, name);
> return NULL;
> -
> - if (base->def->param_count != -1 && base->def->param_count != argc)
> + }
> + if (!base->def->exports.sql) {
> + diag_set(ClientError, ER_SQL_PARSER_GENERIC,
> + tt_sprintf("function %s() is not available in SQL",
> + name));
> + return NULL;
> + }
> + int param_count = base->def->param_count;
> + if (param_count != -1 && param_count != argc) {
> + const char *err = tt_sprintf("%d", param_count);
> + diag_set(ClientError, ER_FUNC_WRONG_ARG_COUNT,
> + base->def->name, err, argc);
> return NULL;
> + }
> return base;
> }
>
> diff --git a/src/box/sql/resolve.c b/src/box/sql/resolve.c
> index 6f625dc18..10c77c491 100644
> --- a/src/box/sql/resolve.c
> +++ b/src/box/sql/resolve.c
> @@ -598,32 +598,13 @@ resolveExprStep(Walker * pWalker, Expr * pExpr)
> assert(!ExprHasProperty(pExpr, EP_xIsSelect));
> zId = pExpr->u.zToken;
> nId = sqlStrlen30(zId);
> - struct func *func = func_by_name(zId, nId);
> + struct func *func = sql_func_by_signature(zId, n);
> if (func == NULL) {
> - diag_set(ClientError, ER_NO_SUCH_FUNCTION, zId);
> - pParse->is_aborted = true;
> - pNC->nErr++;
> - return WRC_Abort;
> - }
> - if (!func->def->exports.sql) {
> - diag_set(ClientError, ER_SQL_PARSER_GENERIC,
> - tt_sprintf("function %.*s() is not "
> - "available in SQL",
> - nId, zId));
> - pParse->is_aborted = true;
> - pNC->nErr++;
> - return WRC_Abort;
> - }
> - if (func->def->param_count != -1 &&
> - func->def->param_count != n) {
> - uint32_t argc = func->def->param_count;
> - const char *err = tt_sprintf("%d", argc);
> - diag_set(ClientError, ER_FUNC_WRONG_ARG_COUNT,
> - func->def->name, err, n);
> pParse->is_aborted = true;
> pNC->nErr++;
> return WRC_Abort;
> }
> + assert(func->def->exports.sql);
> bool is_agg = func->def->aggregate ==
> FUNC_AGGREGATE_GROUP;
> assert(!is_agg || func->def->language ==
> diff --git a/src/box/sql/sqlInt.h b/src/box/sql/sqlInt.h
> index 37283e506..58a65acc1 100644
> --- a/src/box/sql/sqlInt.h
> +++ b/src/box/sql/sqlInt.h
> @@ -4441,8 +4441,11 @@ sql_func_flag_is_set(struct func *func, uint16_t flag)
> * export field set true and have exactly the same signature
> * are returned.
> *
> - * Returns not NULL function pointer when a valid and exported
> - * to SQL engine function is found and NULL otherwise.
> + * @param name Name of the function to find.
> + * @param argc Number of arguments of the function.
> + *
> + * @retval not NULL function pointer when a function is found.
> + * @retval NULL on error and sets a diag.
> */
> struct func *
> sql_func_by_signature(const char *name, int argc);
> --
> 2.25.1
>
^ permalink raw reply [flat|nested] 11+ messages in thread
* [Tarantool-patches] [PATCH v4 3/5] sql: check number of arguments in sql_func_by_signature()
2020-07-13 5:32 [Tarantool-patches] [PATCH v4 0/5] Change implicit cast for assignment imeevma
2020-07-13 5:32 ` [Tarantool-patches] [PATCH v4 1/5] sql: set field_type in mem_set_*() functions imeevma
2020-07-13 5:33 ` [Tarantool-patches] [PATCH v4 2/5] sql: move diag setting to sql_func_by_signature() imeevma
@ 2020-07-13 5:33 ` imeevma
2020-07-13 12:21 ` Nikita Pettik
2020-07-13 5:33 ` [Tarantool-patches] [PATCH v4 4/5] sql: change implicit cast for assignment imeevma
2020-07-13 5:33 ` [Tarantool-patches] [PATCH v4 5/5] sql: properly check arguments of functions imeevma
4 siblings, 1 reply; 11+ messages in thread
From: imeevma @ 2020-07-13 5:33 UTC (permalink / raw)
To: korablev, tsafin, tarantool-patches
After this patch, the number of function arguments will always be
checked in the sql_func_by_signature() function. This was not the
case for some of the built-in functions.
Part of #4159
---
src/box/sql/expr.c | 18 +-
src/box/sql/func.c | 208 +++--
src/box/sql/sqlInt.h | 13 +
test/sql-tap/func.test.lua | 8 +-
test/sql-tap/func2.test.lua | 18 +-
test/sql-tap/func5.test.lua | 6 +-
test/sql-tap/select1.test.lua | 4 +-
test/sql/collation.result | 2 +-
test/sql/gh-4159-function-argumens.result | 821 ++++++++++++++++++++
test/sql/gh-4159-function-argumens.test.sql | 193 +++++
10 files changed, 1212 insertions(+), 79 deletions(-)
create mode 100644 test/sql/gh-4159-function-argumens.result
create mode 100644 test/sql/gh-4159-function-argumens.test.sql
diff --git a/src/box/sql/expr.c b/src/box/sql/expr.c
index d0620b98c..7aee240a3 100644
--- a/src/box/sql/expr.c
+++ b/src/box/sql/expr.c
@@ -3987,14 +3987,7 @@ sqlExprCodeTarget(Parse * pParse, Expr * pExpr, int target)
*/
if (sql_func_flag_is_set(func, SQL_FUNC_COALESCE)) {
int endCoalesce = sqlVdbeMakeLabel(v);
- if (nFarg < 2) {
- diag_set(ClientError,
- ER_FUNC_WRONG_ARG_COUNT,
- func->def->name,
- "at least two", nFarg);
- pParse->is_aborted = true;
- break;
- }
+ assert(nFarg >= 2);
sqlExprCode(pParse, pFarg->a[0].pExpr,
target);
for (i = 1; i < nFarg; i++) {
@@ -4017,14 +4010,7 @@ sqlExprCodeTarget(Parse * pParse, Expr * pExpr, int target)
* of the first argument.
*/
if (sql_func_flag_is_set(func, SQL_FUNC_UNLIKELY)) {
- if (nFarg < 1) {
- diag_set(ClientError,
- ER_FUNC_WRONG_ARG_COUNT,
- func->def->name,
- "at least one", nFarg);
- pParse->is_aborted = true;
- break;
- }
+ assert(nFarg == 1 || nFarg == 2);
return sqlExprCodeTarget(pParse,
pFarg->a[0].pExpr,
target);
diff --git a/src/box/sql/func.c b/src/box/sql/func.c
index 4bbe4d4b7..9d4c26081 100644
--- a/src/box/sql/func.c
+++ b/src/box/sql/func.c
@@ -383,17 +383,12 @@ error:
static void
minmaxFunc(sql_context * context, int argc, sql_value ** argv)
{
+ assert(argc > 1);
int i;
int iBest;
struct coll *pColl;
struct func *func = context->func;
int mask = sql_func_flag_is_set(func, SQL_FUNC_MAX) ? -1 : 0;
- if (argc < 2) {
- diag_set(ClientError, ER_FUNC_WRONG_ARG_COUNT,
- mask ? "GREATEST" : "LEAST", "at least two", argc);
- context->is_aborted = true;
- return;
- }
pColl = sqlGetFuncCollSeq(context);
assert(mask == -1 || mask == 0);
iBest = 0;
@@ -722,6 +717,7 @@ printfFunc(sql_context * context, int argc, sql_value ** argv)
static void
substrFunc(sql_context * context, int argc, sql_value ** argv)
{
+ assert(argc == 2 || argc == 3);
const unsigned char *z;
const unsigned char *z2;
int len;
@@ -729,12 +725,6 @@ substrFunc(sql_context * context, int argc, sql_value ** argv)
i64 p1, p2;
int negP2 = 0;
- if (argc != 2 && argc != 3) {
- diag_set(ClientError, ER_FUNC_WRONG_ARG_COUNT, "SUBSTR",
- "1 or 2", argc);
- context->is_aborted = true;
- return;
- }
if (sql_value_is_null(argv[1])
|| (argc == 3 && sql_value_is_null(argv[2]))
) {
@@ -828,14 +818,9 @@ substrFunc(sql_context * context, int argc, sql_value ** argv)
static void
roundFunc(sql_context * context, int argc, sql_value ** argv)
{
+ assert(argc == 1 || argc == 2);
int n = 0;
double r;
- if (argc != 1 && argc != 2) {
- diag_set(ClientError, ER_FUNC_WRONG_ARG_COUNT, "ROUND",
- "1 or 2", argc);
- context->is_aborted = true;
- return;
- }
if (argc == 2) {
if (sql_value_is_null(argv[1]))
return;
@@ -1226,14 +1211,9 @@ sql_utf8_pattern_compare(const char *pattern,
static void
likeFunc(sql_context *context, int argc, sql_value **argv)
{
+ assert(argc == 2 || argc == 3);
u32 escape = SQL_END_OF_STRING;
int nPat;
- if (argc != 2 && argc != 3) {
- diag_set(ClientError, ER_FUNC_WRONG_ARG_COUNT,
- "LIKE", "2 or 3", argc);
- context->is_aborted = true;
- return;
- }
sql *db = sql_context_db_handle(context);
int rhs_type = sql_value_type(argv[0]);
int lhs_type = sql_value_type(argv[1]);
@@ -1838,6 +1818,7 @@ trim_func_three_args(struct sql_context *context, sql_value *arg1,
static void
trim_func(struct sql_context *context, int argc, sql_value **argv)
{
+ assert(argc == 1 || argc == 2 || argc == 3);
switch (argc) {
case 1:
trim_func_one_arg(context, argv[0]);
@@ -1849,9 +1830,7 @@ trim_func(struct sql_context *context, int argc, sql_value **argv)
trim_func_three_args(context, argv[0], argv[1], argv[2]);
break;
default:
- diag_set(ClientError, ER_FUNC_WRONG_ARG_COUNT, "TRIM",
- "1 or 2 or 3", argc);
- context->is_aborted = true;
+ unreachable();
}
}
@@ -2030,13 +2009,8 @@ struct CountCtx {
static void
countStep(sql_context * context, int argc, sql_value ** argv)
{
+ assert(argc == 0 || argc == 1);
CountCtx *p;
- if (argc != 0 && argc != 1) {
- diag_set(ClientError, ER_FUNC_WRONG_ARG_COUNT,
- "COUNT", "0 or 1", argc);
- context->is_aborted = true;
- return;
- }
p = sql_aggregate_context(context, sizeof(*p));
if ((argc == 0 || ! sql_value_is_null(argv[0])) && p) {
p->n++;
@@ -2111,16 +2085,11 @@ minMaxFinalize(sql_context * context)
static void
groupConcatStep(sql_context * context, int argc, sql_value ** argv)
{
+ assert(argc == 1 || argc == 2);
const char *zVal;
StrAccum *pAccum;
const char *zSep;
int nVal, nSep;
- if (argc != 1 && argc != 2) {
- diag_set(ClientError, ER_FUNC_WRONG_ARG_COUNT,
- "GROUP_CONCAT", "1 or 2", argc);
- context->is_aborted = true;
- return;
- }
if (sql_value_is_null(argv[0]))
return;
pAccum =
@@ -2195,11 +2164,28 @@ sql_func_by_signature(const char *name, int argc)
name));
return NULL;
}
- int param_count = base->def->param_count;
- if (param_count != -1 && param_count != argc) {
- const char *err = tt_sprintf("%d", param_count);
- diag_set(ClientError, ER_FUNC_WRONG_ARG_COUNT,
- base->def->name, err, argc);
+ if (base->def->language != FUNC_LANGUAGE_SQL_BUILTIN) {
+ int param_count = base->def->param_count;
+ if (param_count != -1 && param_count != argc) {
+ const char *err = tt_sprintf("%d", param_count);
+ diag_set(ClientError, ER_FUNC_WRONG_ARG_COUNT,
+ base->def->name, err, argc);
+ return NULL;
+ }
+ return base;
+ }
+ struct func_sql_builtin *func = (struct func_sql_builtin *)base;
+ uint32_t arg_c = (uint32_t)argc;
+ if (func->args.min_count > arg_c || func->args.max_count < arg_c) {
+ const char *err;
+ uint32_t min = func->args.min_count;
+ uint32_t max = func->args.max_count;
+ if (min != max)
+ err = tt_sprintf("from %d to %d", min, max);
+ else
+ err = tt_sprintf("%d", min);
+ diag_set(ClientError, ER_FUNC_WRONG_ARG_COUNT, base->def->name,
+ err, argc);
return NULL;
}
return base;
@@ -2242,12 +2228,16 @@ static struct {
/** Members below are related to struct func_def. */
bool is_deterministic;
int param_count;
+ uint32_t min_count;
+ uint32_t max_count;
enum field_type returns;
enum func_aggregate aggregate;
bool export_to_sql;
} sql_builtins[] = {
{.name = "ABS",
.param_count = 1,
+ .min_count = 1,
+ .max_count = 1,
.returns = FIELD_TYPE_NUMBER,
.aggregate = FUNC_AGGREGATE_NONE,
.is_deterministic = true,
@@ -2258,6 +2248,8 @@ static struct {
}, {
.name = "AVG",
.param_count = 1,
+ .min_count = 1,
+ .max_count = 1,
.returns = FIELD_TYPE_NUMBER,
.is_deterministic = false,
.aggregate = FUNC_AGGREGATE_GROUP,
@@ -2270,6 +2262,8 @@ static struct {
.call = sql_builtin_stub,
.export_to_sql = false,
.param_count = -1,
+ .min_count = 0,
+ .max_count = 0,
.returns = FIELD_TYPE_ANY,
.aggregate = FUNC_AGGREGATE_NONE,
.is_deterministic = false,
@@ -2280,6 +2274,8 @@ static struct {
.call = sql_builtin_stub,
.export_to_sql = false,
.param_count = -1,
+ .min_count = 0,
+ .max_count = 0,
.returns = FIELD_TYPE_ANY,
.aggregate = FUNC_AGGREGATE_NONE,
.is_deterministic = false,
@@ -2288,6 +2284,8 @@ static struct {
}, {
.name = "CHAR",
.param_count = -1,
+ .min_count = 0,
+ .max_count = SQL_MAX_FUNCTION_ARG,
.returns = FIELD_TYPE_STRING,
.is_deterministic = true,
.aggregate = FUNC_AGGREGATE_NONE,
@@ -2298,6 +2296,8 @@ static struct {
}, {
.name = "CHARACTER_LENGTH",
.param_count = 1,
+ .min_count = 1,
+ .max_count = 1,
.returns = FIELD_TYPE_INTEGER,
.aggregate = FUNC_AGGREGATE_NONE,
.is_deterministic = true,
@@ -2308,6 +2308,8 @@ static struct {
}, {
.name = "CHAR_LENGTH",
.param_count = 1,
+ .min_count = 1,
+ .max_count = 1,
.returns = FIELD_TYPE_INTEGER,
.aggregate = FUNC_AGGREGATE_NONE,
.is_deterministic = true,
@@ -2318,6 +2320,8 @@ static struct {
}, {
.name = "COALESCE",
.param_count = -1,
+ .min_count = 2,
+ .max_count = SQL_MAX_FUNCTION_ARG,
.returns = FIELD_TYPE_SCALAR,
.aggregate = FUNC_AGGREGATE_NONE,
.is_deterministic = true,
@@ -2328,6 +2332,8 @@ static struct {
}, {
.name = "COUNT",
.param_count = -1,
+ .min_count = 0,
+ .max_count = 1,
.returns = FIELD_TYPE_INTEGER,
.aggregate = FUNC_AGGREGATE_GROUP,
.is_deterministic = false,
@@ -2340,6 +2346,8 @@ static struct {
.call = sql_builtin_stub,
.export_to_sql = false,
.param_count = -1,
+ .min_count = 0,
+ .max_count = 0,
.returns = FIELD_TYPE_ANY,
.aggregate = FUNC_AGGREGATE_NONE,
.is_deterministic = false,
@@ -2350,6 +2358,8 @@ static struct {
.call = sql_builtin_stub,
.export_to_sql = false,
.param_count = -1,
+ .min_count = 0,
+ .max_count = 0,
.returns = FIELD_TYPE_ANY,
.aggregate = FUNC_AGGREGATE_NONE,
.is_deterministic = false,
@@ -2360,6 +2370,8 @@ static struct {
.call = sql_builtin_stub,
.export_to_sql = false,
.param_count = -1,
+ .min_count = 0,
+ .max_count = 0,
.returns = FIELD_TYPE_ANY,
.aggregate = FUNC_AGGREGATE_NONE,
.is_deterministic = false,
@@ -2370,6 +2382,8 @@ static struct {
.call = sql_builtin_stub,
.export_to_sql = false,
.param_count = -1,
+ .min_count = 0,
+ .max_count = 0,
.returns = FIELD_TYPE_ANY,
.aggregate = FUNC_AGGREGATE_NONE,
.is_deterministic = false,
@@ -2380,6 +2394,8 @@ static struct {
.call = sql_builtin_stub,
.export_to_sql = false,
.param_count = -1,
+ .min_count = 0,
+ .max_count = 0,
.returns = FIELD_TYPE_ANY,
.aggregate = FUNC_AGGREGATE_NONE,
.is_deterministic = false,
@@ -2390,6 +2406,8 @@ static struct {
.call = sql_builtin_stub,
.export_to_sql = false,
.param_count = -1,
+ .min_count = 0,
+ .max_count = 0,
.returns = FIELD_TYPE_ANY,
.aggregate = FUNC_AGGREGATE_NONE,
.is_deterministic = false,
@@ -2400,6 +2418,8 @@ static struct {
.call = sql_builtin_stub,
.export_to_sql = false,
.param_count = -1,
+ .min_count = 0,
+ .max_count = 0,
.returns = FIELD_TYPE_ANY,
.aggregate = FUNC_AGGREGATE_NONE,
.is_deterministic = false,
@@ -2410,6 +2430,8 @@ static struct {
.call = sql_builtin_stub,
.export_to_sql = false,
.param_count = -1,
+ .min_count = 0,
+ .max_count = 0,
.returns = FIELD_TYPE_ANY,
.aggregate = FUNC_AGGREGATE_NONE,
.is_deterministic = false,
@@ -2420,6 +2442,8 @@ static struct {
.call = sql_builtin_stub,
.export_to_sql = false,
.param_count = -1,
+ .min_count = 0,
+ .max_count = 0,
.returns = FIELD_TYPE_ANY,
.aggregate = FUNC_AGGREGATE_NONE,
.is_deterministic = false,
@@ -2430,6 +2454,8 @@ static struct {
.call = sql_builtin_stub,
.export_to_sql = false,
.param_count = -1,
+ .min_count = 0,
+ .max_count = 0,
.returns = FIELD_TYPE_ANY,
.aggregate = FUNC_AGGREGATE_NONE,
.is_deterministic = false,
@@ -2440,6 +2466,8 @@ static struct {
.call = sql_builtin_stub,
.export_to_sql = false,
.param_count = -1,
+ .min_count = 0,
+ .max_count = 0,
.returns = FIELD_TYPE_ANY,
.aggregate = FUNC_AGGREGATE_NONE,
.is_deterministic = false,
@@ -2448,6 +2476,8 @@ static struct {
}, {
.name = "GREATEST",
.param_count = -1,
+ .min_count = 2,
+ .max_count = SQL_MAX_FUNCTION_ARG,
.returns = FIELD_TYPE_SCALAR,
.aggregate = FUNC_AGGREGATE_NONE,
.is_deterministic = true,
@@ -2458,6 +2488,8 @@ static struct {
}, {
.name = "GROUP_CONCAT",
.param_count = -1,
+ .min_count = 1,
+ .max_count = 2,
.returns = FIELD_TYPE_STRING,
.aggregate = FUNC_AGGREGATE_GROUP,
.is_deterministic = false,
@@ -2468,6 +2500,8 @@ static struct {
}, {
.name = "HEX",
.param_count = 1,
+ .min_count = 1,
+ .max_count = 1,
.returns = FIELD_TYPE_STRING,
.aggregate = FUNC_AGGREGATE_NONE,
.is_deterministic = true,
@@ -2478,6 +2512,8 @@ static struct {
}, {
.name = "IFNULL",
.param_count = 2,
+ .min_count = 2,
+ .max_count = 2,
.returns = FIELD_TYPE_INTEGER,
.aggregate = FUNC_AGGREGATE_NONE,
.is_deterministic = true,
@@ -2490,6 +2526,8 @@ static struct {
.call = sql_builtin_stub,
.export_to_sql = false,
.param_count = -1,
+ .min_count = 0,
+ .max_count = 0,
.returns = FIELD_TYPE_ANY,
.aggregate = FUNC_AGGREGATE_NONE,
.is_deterministic = false,
@@ -2498,6 +2536,8 @@ static struct {
}, {
.name = "LEAST",
.param_count = -1,
+ .min_count = 2,
+ .max_count = SQL_MAX_FUNCTION_ARG,
.returns = FIELD_TYPE_SCALAR,
.aggregate = FUNC_AGGREGATE_NONE,
.is_deterministic = true,
@@ -2508,6 +2548,8 @@ static struct {
}, {
.name = "LENGTH",
.param_count = 1,
+ .min_count = 1,
+ .max_count = 1,
.returns = FIELD_TYPE_INTEGER,
.aggregate = FUNC_AGGREGATE_NONE,
.is_deterministic = true,
@@ -2520,6 +2562,8 @@ static struct {
.call = sql_builtin_stub,
.export_to_sql = false,
.param_count = -1,
+ .min_count = 0,
+ .max_count = 0,
.returns = FIELD_TYPE_ANY,
.aggregate = FUNC_AGGREGATE_NONE,
.is_deterministic = false,
@@ -2528,6 +2572,8 @@ static struct {
}, {
.name = "LIKE",
.param_count = -1,
+ .min_count = 2,
+ .max_count = 3,
.returns = FIELD_TYPE_INTEGER,
.aggregate = FUNC_AGGREGATE_NONE,
.is_deterministic = true,
@@ -2538,6 +2584,8 @@ static struct {
}, {
.name = "LIKELIHOOD",
.param_count = 2,
+ .min_count = 2,
+ .max_count = 2,
.returns = FIELD_TYPE_BOOLEAN,
.aggregate = FUNC_AGGREGATE_NONE,
.is_deterministic = true,
@@ -2548,6 +2596,8 @@ static struct {
}, {
.name = "LIKELY",
.param_count = 1,
+ .min_count = 1,
+ .max_count = 1,
.returns = FIELD_TYPE_BOOLEAN,
.aggregate = FUNC_AGGREGATE_NONE,
.is_deterministic = true,
@@ -2560,6 +2610,8 @@ static struct {
.call = sql_builtin_stub,
.export_to_sql = false,
.param_count = -1,
+ .min_count = 0,
+ .max_count = 0,
.returns = FIELD_TYPE_ANY,
.aggregate = FUNC_AGGREGATE_NONE,
.is_deterministic = false,
@@ -2568,6 +2620,8 @@ static struct {
}, {
.name = "LOWER",
.param_count = 1,
+ .min_count = 1,
+ .max_count = 1,
.returns = FIELD_TYPE_STRING,
.aggregate = FUNC_AGGREGATE_NONE,
.is_deterministic = true,
@@ -2578,6 +2632,8 @@ static struct {
}, {
.name = "MAX",
.param_count = 1,
+ .min_count = 1,
+ .max_count = 1,
.returns = FIELD_TYPE_SCALAR,
.aggregate = FUNC_AGGREGATE_GROUP,
.is_deterministic = false,
@@ -2588,6 +2644,8 @@ static struct {
}, {
.name = "MIN",
.param_count = 1,
+ .min_count = 1,
+ .max_count = 1,
.returns = FIELD_TYPE_SCALAR,
.aggregate = FUNC_AGGREGATE_GROUP,
.is_deterministic = false,
@@ -2600,6 +2658,8 @@ static struct {
.call = sql_builtin_stub,
.export_to_sql = false,
.param_count = -1,
+ .min_count = 0,
+ .max_count = 0,
.returns = FIELD_TYPE_ANY,
.aggregate = FUNC_AGGREGATE_NONE,
.is_deterministic = false,
@@ -2608,6 +2668,8 @@ static struct {
}, {
.name = "NULLIF",
.param_count = 2,
+ .min_count = 2,
+ .max_count = 2,
.returns = FIELD_TYPE_SCALAR,
.aggregate = FUNC_AGGREGATE_NONE,
.is_deterministic = true,
@@ -2620,6 +2682,8 @@ static struct {
.call = sql_builtin_stub,
.export_to_sql = false,
.param_count = -1,
+ .min_count = 0,
+ .max_count = 0,
.returns = FIELD_TYPE_ANY,
.aggregate = FUNC_AGGREGATE_NONE,
.is_deterministic = false,
@@ -2628,6 +2692,8 @@ static struct {
}, {
.name = "POSITION",
.param_count = 2,
+ .min_count = 2,
+ .max_count = 2,
.returns = FIELD_TYPE_INTEGER,
.aggregate = FUNC_AGGREGATE_NONE,
.is_deterministic = true,
@@ -2640,6 +2706,8 @@ static struct {
.call = sql_builtin_stub,
.export_to_sql = false,
.param_count = -1,
+ .min_count = 0,
+ .max_count = 0,
.returns = FIELD_TYPE_ANY,
.aggregate = FUNC_AGGREGATE_NONE,
.is_deterministic = false,
@@ -2648,6 +2716,8 @@ static struct {
}, {
.name = "PRINTF",
.param_count = -1,
+ .min_count = 0,
+ .max_count = SQL_MAX_FUNCTION_ARG,
.returns = FIELD_TYPE_STRING,
.aggregate = FUNC_AGGREGATE_NONE,
.is_deterministic = true,
@@ -2658,6 +2728,8 @@ static struct {
}, {
.name = "QUOTE",
.param_count = 1,
+ .min_count = 1,
+ .max_count = 1,
.returns = FIELD_TYPE_STRING,
.aggregate = FUNC_AGGREGATE_NONE,
.is_deterministic = true,
@@ -2668,6 +2740,8 @@ static struct {
}, {
.name = "RANDOM",
.param_count = 0,
+ .min_count = 0,
+ .max_count = 0,
.returns = FIELD_TYPE_INTEGER,
.aggregate = FUNC_AGGREGATE_NONE,
.is_deterministic = false,
@@ -2678,6 +2752,8 @@ static struct {
}, {
.name = "RANDOMBLOB",
.param_count = 1,
+ .min_count = 1,
+ .max_count = 1,
.returns = FIELD_TYPE_VARBINARY,
.aggregate = FUNC_AGGREGATE_NONE,
.is_deterministic = false,
@@ -2688,6 +2764,8 @@ static struct {
}, {
.name = "REPLACE",
.param_count = 3,
+ .min_count = 3,
+ .max_count = 3,
.returns = FIELD_TYPE_STRING,
.aggregate = FUNC_AGGREGATE_NONE,
.is_deterministic = true,
@@ -2698,6 +2776,8 @@ static struct {
}, {
.name = "ROUND",
.param_count = -1,
+ .min_count = 1,
+ .max_count = 2,
.returns = FIELD_TYPE_INTEGER,
.aggregate = FUNC_AGGREGATE_NONE,
.is_deterministic = true,
@@ -2708,6 +2788,8 @@ static struct {
}, {
.name = "ROW_COUNT",
.param_count = 0,
+ .min_count = 0,
+ .max_count = 0,
.returns = FIELD_TYPE_INTEGER,
.aggregate = FUNC_AGGREGATE_NONE,
.is_deterministic = true,
@@ -2720,6 +2802,8 @@ static struct {
.call = sql_builtin_stub,
.export_to_sql = false,
.param_count = -1,
+ .min_count = 0,
+ .max_count = 0,
.returns = FIELD_TYPE_ANY,
.aggregate = FUNC_AGGREGATE_NONE,
.is_deterministic = false,
@@ -2728,6 +2812,8 @@ static struct {
}, {
.name = "SOUNDEX",
.param_count = 1,
+ .min_count = 1,
+ .max_count = 1,
.returns = FIELD_TYPE_STRING,
.aggregate = FUNC_AGGREGATE_NONE,
.is_deterministic = true,
@@ -2740,6 +2826,8 @@ static struct {
.call = sql_builtin_stub,
.export_to_sql = false,
.param_count = -1,
+ .min_count = 0,
+ .max_count = 0,
.returns = FIELD_TYPE_ANY,
.aggregate = FUNC_AGGREGATE_NONE,
.is_deterministic = false,
@@ -2750,6 +2838,8 @@ static struct {
.call = sql_builtin_stub,
.export_to_sql = false,
.param_count = -1,
+ .min_count = 0,
+ .max_count = 0,
.returns = FIELD_TYPE_ANY,
.aggregate = FUNC_AGGREGATE_NONE,
.is_deterministic = false,
@@ -2758,6 +2848,8 @@ static struct {
}, {
.name = "SUBSTR",
.param_count = -1,
+ .min_count = 2,
+ .max_count = 3,
.returns = FIELD_TYPE_STRING,
.aggregate = FUNC_AGGREGATE_NONE,
.is_deterministic = true,
@@ -2768,6 +2860,8 @@ static struct {
}, {
.name = "SUM",
.param_count = 1,
+ .min_count = 1,
+ .max_count = 1,
.returns = FIELD_TYPE_NUMBER,
.aggregate = FUNC_AGGREGATE_GROUP,
.is_deterministic = false,
@@ -2780,6 +2874,8 @@ static struct {
.call = sql_builtin_stub,
.export_to_sql = false,
.param_count = -1,
+ .min_count = 0,
+ .max_count = 0,
.returns = FIELD_TYPE_ANY,
.aggregate = FUNC_AGGREGATE_NONE,
.is_deterministic = false,
@@ -2788,6 +2884,8 @@ static struct {
}, {
.name = "TOTAL",
.param_count = 1,
+ .min_count = 1,
+ .max_count = 1,
.returns = FIELD_TYPE_NUMBER,
.aggregate = FUNC_AGGREGATE_GROUP,
.is_deterministic = false,
@@ -2798,6 +2896,8 @@ static struct {
}, {
.name = "TRIM",
.param_count = -1,
+ .min_count = 1,
+ .max_count = 3,
.returns = FIELD_TYPE_STRING,
.aggregate = FUNC_AGGREGATE_NONE,
.is_deterministic = true,
@@ -2808,6 +2908,8 @@ static struct {
}, {
.name = "TYPEOF",
.param_count = 1,
+ .min_count = 1,
+ .max_count = 1,
.returns = FIELD_TYPE_STRING,
.aggregate = FUNC_AGGREGATE_NONE,
.is_deterministic = true,
@@ -2818,6 +2920,8 @@ static struct {
}, {
.name = "UNICODE",
.param_count = 1,
+ .min_count = 1,
+ .max_count = 1,
.returns = FIELD_TYPE_STRING,
.aggregate = FUNC_AGGREGATE_NONE,
.is_deterministic = true,
@@ -2828,6 +2932,8 @@ static struct {
}, {
.name = "UNLIKELY",
.param_count = 1,
+ .min_count = 1,
+ .max_count = 1,
.returns = FIELD_TYPE_BOOLEAN,
.aggregate = FUNC_AGGREGATE_NONE,
.is_deterministic = true,
@@ -2838,6 +2944,8 @@ static struct {
}, {
.name = "UPPER",
.param_count = 1,
+ .min_count = 1,
+ .max_count = 1,
.returns = FIELD_TYPE_STRING,
.aggregate = FUNC_AGGREGATE_NONE,
.is_deterministic = true,
@@ -2848,6 +2956,8 @@ static struct {
}, {
.name = "VERSION",
.param_count = 0,
+ .min_count = 0,
+ .max_count = 0,
.returns = FIELD_TYPE_STRING,
.aggregate = FUNC_AGGREGATE_NONE,
.is_deterministic = true,
@@ -2858,6 +2968,8 @@ static struct {
}, {
.name = "ZEROBLOB",
.param_count = 1,
+ .min_count = 1,
+ .max_count = 1,
.returns = FIELD_TYPE_VARBINARY,
.aggregate = FUNC_AGGREGATE_NONE,
.is_deterministic = true,
@@ -2870,6 +2982,8 @@ static struct {
.call = sql_builtin_stub,
.export_to_sql = false,
.param_count = -1,
+ .min_count = 0,
+ .max_count = 0,
.returns = FIELD_TYPE_ANY,
.aggregate = FUNC_AGGREGATE_NONE,
.is_deterministic = false,
@@ -2880,6 +2994,8 @@ static struct {
.call = sql_builtin_stub,
.export_to_sql = false,
.param_count = -1,
+ .min_count = 0,
+ .max_count = 0,
.returns = FIELD_TYPE_ANY,
.aggregate = FUNC_AGGREGATE_NONE,
.is_deterministic = false,
@@ -2890,6 +3006,8 @@ static struct {
.call = sql_builtin_stub,
.export_to_sql = false,
.param_count = -1,
+ .min_count = 0,
+ .max_count = 0,
.returns = FIELD_TYPE_ANY,
.aggregate = FUNC_AGGREGATE_NONE,
.is_deterministic = false,
@@ -2941,6 +3059,8 @@ func_sql_builtin_new(struct func_def *def)
func->flags = sql_builtins[idx].flags;
func->call = sql_builtins[idx].call;
func->finalize = sql_builtins[idx].finalize;
+ func->args.min_count = sql_builtins[idx].min_count;
+ func->args.max_count = sql_builtins[idx].max_count;
def->param_count = sql_builtins[idx].param_count;
def->is_deterministic = sql_builtins[idx].is_deterministic;
def->returns = sql_builtins[idx].returns;
diff --git a/src/box/sql/sqlInt.h b/src/box/sql/sqlInt.h
index 58a65acc1..6af9d7473 100644
--- a/src/box/sql/sqlInt.h
+++ b/src/box/sql/sqlInt.h
@@ -4397,11 +4397,24 @@ Expr *sqlExprForVectorField(Parse *, Expr *, int);
*/
extern int sqlSubProgramsRemaining;
+/**
+ * A structure that contains additional information about
+ * arguments to built-in SQL functions.
+ */
+struct sql_builtin_func_args {
+ /** Min number of arguments. */
+ uint32_t min_count;
+ /** Max number of arguments. */
+ uint32_t max_count;
+};
+
struct func_sql_builtin {
/** Function object base class. */
struct func base;
/** A bitmask of SQL flags. */
uint16_t flags;
+ /** Information about arguments to built-in functions. */
+ struct sql_builtin_func_args args;
/**
* A VDBE-memory-compatible call method.
* SQL built-ins don't use func base class "call"
diff --git a/test/sql-tap/func.test.lua b/test/sql-tap/func.test.lua
index 3c088920f..1d3ef9e2a 100755
--- a/test/sql-tap/func.test.lua
+++ b/test/sql-tap/func.test.lua
@@ -428,7 +428,7 @@ test:do_catchsql_test(
SELECT round(a,b,c) FROM t1
]], {
-- <func-4.5>
- 1, "Wrong number of arguments is passed to ROUND(): expected 1 or 2, got 3"
+ 1, "Wrong number of arguments is passed to ROUND(): expected from 1 to 2, got 3"
-- </func-4.5>
})
@@ -488,7 +488,7 @@ test:do_catchsql_test(
SELECT round() FROM t1 ORDER BY a
]], {
-- <func-4.11>
- 1, "Wrong number of arguments is passed to ROUND(): expected 1 or 2, got 0"
+ 1, "Wrong number of arguments is passed to ROUND(): expected from 1 to 2, got 0"
-- </func-4.11>
})
@@ -2540,7 +2540,7 @@ test:do_catchsql_test(
SELECT coalesce()
]], {
-- <func-27.1>
- 1, "Wrong number of arguments is passed to COALESCE(): expected at least two, got 0"
+ 1, "Wrong number of arguments is passed to COALESCE(): expected from 2 to 127, got 0"
-- </func-27.1>
})
@@ -2550,7 +2550,7 @@ test:do_catchsql_test(
SELECT coalesce(1)
]], {
-- <func-27.2>
- 1, "Wrong number of arguments is passed to COALESCE(): expected at least two, got 1"
+ 1, "Wrong number of arguments is passed to COALESCE(): expected from 2 to 127, got 1"
-- </func-27.2>
})
diff --git a/test/sql-tap/func2.test.lua b/test/sql-tap/func2.test.lua
index c9bd3665f..4bfee7150 100755
--- a/test/sql-tap/func2.test.lua
+++ b/test/sql-tap/func2.test.lua
@@ -50,7 +50,7 @@ test:do_catchsql_test(
SELECT SUBSTR()
]], {
-- <func2-1.2.1>
- 1, "Wrong number of arguments is passed to SUBSTR(): expected 1 or 2, got 0"
+ 1, "Wrong number of arguments is passed to SUBSTR(): expected from 2 to 3, got 0"
-- </func2-1.2.1>
})
@@ -60,7 +60,7 @@ test:do_catchsql_test(
SELECT SUBSTR('Supercalifragilisticexpialidocious')
]], {
-- <func2-1.2.2>
- 1, "Wrong number of arguments is passed to SUBSTR(): expected 1 or 2, got 1"
+ 1, "Wrong number of arguments is passed to SUBSTR(): expected from 2 to 3, got 1"
-- </func2-1.2.2>
})
@@ -70,7 +70,7 @@ test:do_catchsql_test(
SELECT SUBSTR('Supercalifragilisticexpialidocious', 1,1,1)
]], {
-- <func2-1.2.3>
- 1, "Wrong number of arguments is passed to SUBSTR(): expected 1 or 2, got 4"
+ 1, "Wrong number of arguments is passed to SUBSTR(): expected from 2 to 3, got 4"
-- </func2-1.2.3>
})
@@ -673,7 +673,7 @@ if ("ሴ" ~= "u1234")
SELECT SUBSTR()
]], {
-- <func2-2.1.2>
- 1, "Wrong number of arguments is passed to SUBSTR(): expected 1 or 2, got 0"
+ 1, "Wrong number of arguments is passed to SUBSTR(): expected from 2 to 3, got 0"
-- </func2-2.1.2>
})
@@ -683,7 +683,7 @@ if ("ሴ" ~= "u1234")
SELECT SUBSTR('hiሴho')
]], {
-- <func2-2.1.3>
- 1, "Wrong number of arguments is passed to SUBSTR(): expected 1 or 2, got 1"
+ 1, "Wrong number of arguments is passed to SUBSTR(): expected from 2 to 3, got 1"
-- </func2-2.1.3>
})
@@ -693,7 +693,7 @@ if ("ሴ" ~= "u1234")
SELECT SUBSTR('hiሴho', 1,1,1)
]], {
-- <func2-2.1.4>
- 1, "Wrong number of arguments is passed to SUBSTR(): expected 1 or 2, got 4"
+ 1, "Wrong number of arguments is passed to SUBSTR(): expected from 2 to 3, got 4"
-- </func2-2.1.4>
})
@@ -1038,7 +1038,7 @@ test:do_catchsql_test(
SELECT SUBSTR()
]], {
-- <func2-3.1.2>
- 1, "Wrong number of arguments is passed to SUBSTR(): expected 1 or 2, got 0"
+ 1, "Wrong number of arguments is passed to SUBSTR(): expected from 2 to 3, got 0"
-- </func2-3.1.2>
})
@@ -1048,7 +1048,7 @@ test:do_catchsql_test(
SELECT SUBSTR(x'1234')
]], {
-- <func2-3.1.3>
- 1, "Wrong number of arguments is passed to SUBSTR(): expected 1 or 2, got 1"
+ 1, "Wrong number of arguments is passed to SUBSTR(): expected from 2 to 3, got 1"
-- </func2-3.1.3>
})
@@ -1058,7 +1058,7 @@ test:do_catchsql_test(
SELECT SUBSTR(x'1234', 1,1,1)
]], {
-- <func2-3.1.4>
- 1, "Wrong number of arguments is passed to SUBSTR(): expected 1 or 2, got 4"
+ 1, "Wrong number of arguments is passed to SUBSTR(): expected from 2 to 3, got 4"
-- </func2-3.1.4>
})
diff --git a/test/sql-tap/func5.test.lua b/test/sql-tap/func5.test.lua
index 8329e1735..08c40fd02 100755
--- a/test/sql-tap/func5.test.lua
+++ b/test/sql-tap/func5.test.lua
@@ -276,19 +276,19 @@ test:do_catchsql_test(
"func-5-5.1",
[[
SELECT LEAST(false);
- ]], { 1, "Wrong number of arguments is passed to LEAST(): expected at least two, got 1" } )
+ ]], { 1, "Wrong number of arguments is passed to LEAST(): expected from 2 to 127, got 1" } )
test:do_catchsql_test(
"func-5-5.2",
[[
SELECT GREATEST('abc');
- ]], { 1, "Wrong number of arguments is passed to GREATEST(): expected at least two, got 1" } )
+ ]], { 1, "Wrong number of arguments is passed to GREATEST(): expected from 2 to 127, got 1" } )
test:do_catchsql_test(
"func-5-5.3",
[[
SELECT LEAST();
- ]], { 1, "Wrong number of arguments is passed to LEAST(): expected at least two, got 0" } )
+ ]], { 1, "Wrong number of arguments is passed to LEAST(): expected from 2 to 127, got 0" } )
box.func.COUNTER1:drop()
box.func.COUNTER2:drop()
diff --git a/test/sql-tap/select1.test.lua b/test/sql-tap/select1.test.lua
index fbebfab37..6248abb65 100755
--- a/test/sql-tap/select1.test.lua
+++ b/test/sql-tap/select1.test.lua
@@ -250,7 +250,7 @@ test:do_catchsql_test(
SELECT count(f1,f2) FROM test1
]], {
-- <select1-2.1>
- 1, "Wrong number of arguments is passed to COUNT(): expected 0 or 1, got 2"
+ 1, "Wrong number of arguments is passed to COUNT(): expected from 0 to 1, got 2"
-- </select1-2.1>
})
@@ -697,7 +697,7 @@ test:do_catchsql_test(
SELECT f1 FROM test1 WHERE count(f1,f2)!=11
]], {
-- <select1-3.9>
- 1, "misuse of aggregate function COUNT()"
+ 1, "Wrong number of arguments is passed to COUNT(): expected from 0 to 1, got 2"
-- </select1-3.9>
})
diff --git a/test/sql/collation.result b/test/sql/collation.result
index 4e4c27ef0..9c22c7191 100644
--- a/test/sql/collation.result
+++ b/test/sql/collation.result
@@ -298,7 +298,7 @@ box.execute("SELECT * FROM t WHERE a COLLATE \"binary\" = c COLLATE \"unicode\";
box.execute("SELECT * FROM t WHERE a COLLATE \"binary\" = substr();")
---
- null
-- 'Wrong number of arguments is passed to SUBSTR(): expected 1 or 2, got 0'
+- 'Wrong number of arguments is passed to SUBSTR(): expected from 2 to 3, got 0'
...
-- Compound queries perform implicit comparisons between values.
-- Hence, rules for collations compatibilities are the same.
diff --git a/test/sql/gh-4159-function-argumens.result b/test/sql/gh-4159-function-argumens.result
new file mode 100644
index 000000000..48bd550a4
--- /dev/null
+++ b/test/sql/gh-4159-function-argumens.result
@@ -0,0 +1,821 @@
+-- test-run result file version 2
+-- Function abs().
+SELECT abs();
+ | ---
+ | - null
+ | - 'Wrong number of arguments is passed to ABS(): expected 1, got 0'
+ | ...
+SELECT abs(1);
+ | ---
+ | - metadata:
+ | - name: abs(1)
+ | type: number
+ | rows:
+ | - [1]
+ | ...
+SELECT abs(1, 2);
+ | ---
+ | - null
+ | - 'Wrong number of arguments is passed to ABS(): expected 1, got 2'
+ | ...
+
+-- Function char().
+SELECT char();
+ | ---
+ | - metadata:
+ | - name: char()
+ | type: string
+ | rows:
+ | - ['']
+ | ...
+SELECT char(1);
+ | ---
+ | - metadata:
+ | - name: char(1)
+ | type: string
+ | rows:
+ | - ["\x01"]
+ | ...
+SELECT char(1, 2);
+ | ---
+ | - metadata:
+ | - name: char(1, 2)
+ | type: string
+ | rows:
+ | - ["\x01\x02"]
+ | ...
+
+-- Function character_length().
+SELECT character_length();
+ | ---
+ | - null
+ | - 'Wrong number of arguments is passed to CHARACTER_LENGTH(): expected 1, got 0'
+ | ...
+SELECT character_length('1');
+ | ---
+ | - metadata:
+ | - name: character_length('1')
+ | type: integer
+ | rows:
+ | - [1]
+ | ...
+SELECT character_length('1', '2');
+ | ---
+ | - null
+ | - 'Wrong number of arguments is passed to CHARACTER_LENGTH(): expected 1, got 2'
+ | ...
+
+-- Function char_length().
+SELECT char_length();
+ | ---
+ | - null
+ | - 'Wrong number of arguments is passed to CHAR_LENGTH(): expected 1, got 0'
+ | ...
+SELECT char_length('1');
+ | ---
+ | - metadata:
+ | - name: char_length('1')
+ | type: integer
+ | rows:
+ | - [1]
+ | ...
+SELECT char_length('1', '2');
+ | ---
+ | - null
+ | - 'Wrong number of arguments is passed to CHAR_LENGTH(): expected 1, got 2'
+ | ...
+
+-- Function coalesce().
+SELECT coalesce();
+ | ---
+ | - null
+ | - 'Wrong number of arguments is passed to COALESCE(): expected from 2 to 127, got
+ | 0'
+ | ...
+SELECT coalesce('1');
+ | ---
+ | - null
+ | - 'Wrong number of arguments is passed to COALESCE(): expected from 2 to 127, got
+ | 1'
+ | ...
+SELECT coalesce('1', '2');
+ | ---
+ | - metadata:
+ | - name: coalesce('1', '2')
+ | type: scalar
+ | rows:
+ | - ['1']
+ | ...
+SELECT coalesce('1', '2', '3');
+ | ---
+ | - metadata:
+ | - name: coalesce('1', '2', '3')
+ | type: scalar
+ | rows:
+ | - ['1']
+ | ...
+
+-- Function greatest().
+SELECT greatest();
+ | ---
+ | - null
+ | - 'Wrong number of arguments is passed to GREATEST(): expected from 2 to 127, got
+ | 0'
+ | ...
+SELECT greatest('1');
+ | ---
+ | - null
+ | - 'Wrong number of arguments is passed to GREATEST(): expected from 2 to 127, got
+ | 1'
+ | ...
+SELECT greatest('1', '2');
+ | ---
+ | - metadata:
+ | - name: greatest('1', '2')
+ | type: scalar
+ | rows:
+ | - ['2']
+ | ...
+
+-- Function hex().
+SELECT hex();
+ | ---
+ | - null
+ | - 'Wrong number of arguments is passed to HEX(): expected 1, got 0'
+ | ...
+SELECT hex(X'33');
+ | ---
+ | - metadata:
+ | - name: hex(X'33')
+ | type: string
+ | rows:
+ | - ['33']
+ | ...
+SELECT hex(X'33', X'33');
+ | ---
+ | - null
+ | - 'Wrong number of arguments is passed to HEX(): expected 1, got 2'
+ | ...
+
+-- Function ifnull
+SELECT ifnull();
+ | ---
+ | - null
+ | - 'Wrong number of arguments is passed to IFNULL(): expected 2, got 0'
+ | ...
+SELECT ifnull(1);
+ | ---
+ | - null
+ | - 'Wrong number of arguments is passed to IFNULL(): expected 2, got 1'
+ | ...
+SELECT ifnull(1, 2);
+ | ---
+ | - metadata:
+ | - name: ifnull(1, 2)
+ | type: integer
+ | rows:
+ | - [1]
+ | ...
+SELECT ifnull(1, 2, 3);
+ | ---
+ | - null
+ | - 'Wrong number of arguments is passed to IFNULL(): expected 2, got 3'
+ | ...
+
+-- Function least().
+SELECT least();
+ | ---
+ | - null
+ | - 'Wrong number of arguments is passed to LEAST(): expected from 2 to 127, got 0'
+ | ...
+SELECT least('1');
+ | ---
+ | - null
+ | - 'Wrong number of arguments is passed to LEAST(): expected from 2 to 127, got 1'
+ | ...
+SELECT least('1', '2');
+ | ---
+ | - metadata:
+ | - name: least('1', '2')
+ | type: scalar
+ | rows:
+ | - ['1']
+ | ...
+
+-- Function length().
+SELECT length();
+ | ---
+ | - null
+ | - 'Wrong number of arguments is passed to LENGTH(): expected 1, got 0'
+ | ...
+SELECT length('1');
+ | ---
+ | - metadata:
+ | - name: length('1')
+ | type: integer
+ | rows:
+ | - [1]
+ | ...
+SELECT length('1', '2');
+ | ---
+ | - null
+ | - 'Wrong number of arguments is passed to LENGTH(): expected 1, got 2'
+ | ...
+
+-- Function likelihood
+SELECT likelihood();
+ | ---
+ | - null
+ | - 'Wrong number of arguments is passed to LIKELIHOOD(): expected 2, got 0'
+ | ...
+SELECT likelihood(1);
+ | ---
+ | - null
+ | - 'Wrong number of arguments is passed to LIKELIHOOD(): expected 2, got 1'
+ | ...
+SELECT likelihood(1, 0.5);
+ | ---
+ | - metadata:
+ | - name: likelihood(1, 0.5)
+ | type: integer
+ | rows:
+ | - [1]
+ | ...
+SELECT likelihood(1, 0.5, 3);
+ | ---
+ | - null
+ | - 'Wrong number of arguments is passed to LIKELIHOOD(): expected 2, got 3'
+ | ...
+
+-- Function likely
+SELECT likely();
+ | ---
+ | - null
+ | - 'Wrong number of arguments is passed to LIKELY(): expected 1, got 0'
+ | ...
+SELECT likely(1);
+ | ---
+ | - metadata:
+ | - name: likely(1)
+ | type: integer
+ | rows:
+ | - [1]
+ | ...
+SELECT likely(1, 2);
+ | ---
+ | - null
+ | - 'Wrong number of arguments is passed to LIKELY(): expected 1, got 2'
+ | ...
+
+-- Function lower
+SELECT lower();
+ | ---
+ | - null
+ | - 'Wrong number of arguments is passed to LOWER(): expected 1, got 0'
+ | ...
+SELECT lower('a');
+ | ---
+ | - metadata:
+ | - name: lower('a')
+ | type: string
+ | rows:
+ | - ['a']
+ | ...
+SELECT lower('a', 2);
+ | ---
+ | - null
+ | - 'Wrong number of arguments is passed to LOWER(): expected 1, got 2'
+ | ...
+
+-- Function nullif
+SELECT nullif();
+ | ---
+ | - null
+ | - 'Wrong number of arguments is passed to NULLIF(): expected 2, got 0'
+ | ...
+SELECT nullif(1);
+ | ---
+ | - null
+ | - 'Wrong number of arguments is passed to NULLIF(): expected 2, got 1'
+ | ...
+SELECT nullif(1, 2);
+ | ---
+ | - metadata:
+ | - name: nullif(1, 2)
+ | type: scalar
+ | rows:
+ | - [1]
+ | ...
+SELECT nullif(1, 2, 3);
+ | ---
+ | - null
+ | - 'Wrong number of arguments is passed to NULLIF(): expected 2, got 3'
+ | ...
+
+-- Function position
+SELECT position();
+ | ---
+ | - null
+ | - 'Wrong number of arguments is passed to POSITION(): expected 2, got 0'
+ | ...
+SELECT position('12345');
+ | ---
+ | - null
+ | - 'Wrong number of arguments is passed to POSITION(): expected 2, got 1'
+ | ...
+SELECT position('12345', '2');
+ | ---
+ | - metadata:
+ | - name: position('12345', '2')
+ | type: integer
+ | rows:
+ | - [0]
+ | ...
+SELECT position('12345', '2', 3);
+ | ---
+ | - null
+ | - 'Wrong number of arguments is passed to POSITION(): expected 2, got 3'
+ | ...
+
+-- Function printf
+SELECT printf();
+ | ---
+ | - metadata:
+ | - name: printf()
+ | type: string
+ | rows:
+ | - [null]
+ | ...
+SELECT printf('1');
+ | ---
+ | - metadata:
+ | - name: printf('1')
+ | type: string
+ | rows:
+ | - ['1']
+ | ...
+SELECT printf('1', 2);
+ | ---
+ | - metadata:
+ | - name: printf('1', 2)
+ | type: string
+ | rows:
+ | - ['1']
+ | ...
+SELECT printf('1', 2, 3);
+ | ---
+ | - metadata:
+ | - name: printf('1', 2, 3)
+ | type: string
+ | rows:
+ | - ['1']
+ | ...
+
+-- Function quote
+SELECT quote();
+ | ---
+ | - null
+ | - 'Wrong number of arguments is passed to QUOTE(): expected 1, got 0'
+ | ...
+SELECT quote('1');
+ | ---
+ | - metadata:
+ | - name: quote('1')
+ | type: string
+ | rows:
+ | - ['''1''']
+ | ...
+SELECT quote('1', 2);
+ | ---
+ | - null
+ | - 'Wrong number of arguments is passed to QUOTE(): expected 1, got 2'
+ | ...
+
+-- Function random
+SELECT typeof(random());
+ | ---
+ | - metadata:
+ | - name: typeof(random())
+ | type: string
+ | rows:
+ | - ['integer']
+ | ...
+SELECT typeof(random(1));
+ | ---
+ | - null
+ | - 'Wrong number of arguments is passed to RANDOM(): expected 0, got 1'
+ | ...
+
+-- Function randomblob
+SELECT typeof(randomblob());
+ | ---
+ | - null
+ | - 'Wrong number of arguments is passed to RANDOMBLOB(): expected 1, got 0'
+ | ...
+SELECT typeof(randomblob(1));
+ | ---
+ | - metadata:
+ | - name: typeof(randomblob(1))
+ | type: string
+ | rows:
+ | - ['varbinary']
+ | ...
+SELECT typeof(randomblob(1, 2));
+ | ---
+ | - null
+ | - 'Wrong number of arguments is passed to RANDOMBLOB(): expected 1, got 2'
+ | ...
+
+-- Function replace
+SELECT replace();
+ | ---
+ | - null
+ | - 'Wrong number of arguments is passed to REPLACE(): expected 3, got 0'
+ | ...
+SELECT replace('12345');
+ | ---
+ | - null
+ | - 'Wrong number of arguments is passed to REPLACE(): expected 3, got 1'
+ | ...
+SELECT replace('12345', '2');
+ | ---
+ | - null
+ | - 'Wrong number of arguments is passed to REPLACE(): expected 3, got 2'
+ | ...
+SELECT replace('12345', '2', '3');
+ | ---
+ | - metadata:
+ | - name: replace('12345', '2', '3')
+ | type: string
+ | rows:
+ | - ['13345']
+ | ...
+SELECT replace('12345', '2', '3', 4);
+ | ---
+ | - null
+ | - 'Wrong number of arguments is passed to REPLACE(): expected 3, got 4'
+ | ...
+
+-- Function round
+SELECT round();
+ | ---
+ | - null
+ | - 'Wrong number of arguments is passed to ROUND(): expected from 1 to 2, got 0'
+ | ...
+SELECT round(1.1245);
+ | ---
+ | - metadata:
+ | - name: round(1.1245)
+ | type: integer
+ | rows:
+ | - [1]
+ | ...
+SELECT round(1.1245, 2);
+ | ---
+ | - metadata:
+ | - name: round(1.1245, 2)
+ | type: integer
+ | rows:
+ | - [1.12]
+ | ...
+SELECT round(1.1245, 2, 3);
+ | ---
+ | - null
+ | - 'Wrong number of arguments is passed to ROUND(): expected from 1 to 2, got 3'
+ | ...
+
+-- Function row_count
+SELECT row_count();
+ | ---
+ | - metadata:
+ | - name: row_count()
+ | type: integer
+ | rows:
+ | - [0]
+ | ...
+SELECT row_count(1);
+ | ---
+ | - null
+ | - 'Wrong number of arguments is passed to ROW_COUNT(): expected 0, got 1'
+ | ...
+
+-- Function soundex
+SELECT soundex();
+ | ---
+ | - null
+ | - 'Wrong number of arguments is passed to SOUNDEX(): expected 1, got 0'
+ | ...
+SELECT soundex(1);
+ | ---
+ | - metadata:
+ | - name: soundex(1)
+ | type: string
+ | rows:
+ | - ['?000']
+ | ...
+SELECT soundex(1, 2);
+ | ---
+ | - null
+ | - 'Wrong number of arguments is passed to SOUNDEX(): expected 1, got 2'
+ | ...
+
+-- Function substr
+SELECT substr();
+ | ---
+ | - null
+ | - 'Wrong number of arguments is passed to SUBSTR(): expected from 2 to 3, got 0'
+ | ...
+SELECT substr('12345');
+ | ---
+ | - null
+ | - 'Wrong number of arguments is passed to SUBSTR(): expected from 2 to 3, got 1'
+ | ...
+SELECT substr('12345', 2);
+ | ---
+ | - metadata:
+ | - name: substr('12345', 2)
+ | type: string
+ | rows:
+ | - ['2345']
+ | ...
+SELECT substr('12345', 2, 3);
+ | ---
+ | - metadata:
+ | - name: substr('12345', 2, 3)
+ | type: string
+ | rows:
+ | - ['234']
+ | ...
+SELECT substr('12345', 2, 3, 4);
+ | ---
+ | - null
+ | - 'Wrong number of arguments is passed to SUBSTR(): expected from 2 to 3, got 4'
+ | ...
+
+-- Function typeof
+SELECT typeof();
+ | ---
+ | - null
+ | - 'Wrong number of arguments is passed to TYPEOF(): expected 1, got 0'
+ | ...
+SELECT typeof(1);
+ | ---
+ | - metadata:
+ | - name: typeof(1)
+ | type: string
+ | rows:
+ | - ['integer']
+ | ...
+SELECT typeof(1, 2);
+ | ---
+ | - null
+ | - 'Wrong number of arguments is passed to TYPEOF(): expected 1, got 2'
+ | ...
+
+-- Function unicode
+SELECT unicode();
+ | ---
+ | - null
+ | - 'Wrong number of arguments is passed to UNICODE(): expected 1, got 0'
+ | ...
+SELECT unicode('1');
+ | ---
+ | - metadata:
+ | - name: unicode('1')
+ | type: string
+ | rows:
+ | - [49]
+ | ...
+SELECT unicode('1', '2');
+ | ---
+ | - null
+ | - 'Wrong number of arguments is passed to UNICODE(): expected 1, got 2'
+ | ...
+
+-- Function unlikely
+SELECT unlikely();
+ | ---
+ | - null
+ | - 'Wrong number of arguments is passed to UNLIKELY(): expected 1, got 0'
+ | ...
+SELECT unlikely(1);
+ | ---
+ | - metadata:
+ | - name: unlikely(1)
+ | type: integer
+ | rows:
+ | - [1]
+ | ...
+SELECT unlikely(1, 2);
+ | ---
+ | - null
+ | - 'Wrong number of arguments is passed to UNLIKELY(): expected 1, got 2'
+ | ...
+
+-- Function upper
+SELECT upper();
+ | ---
+ | - null
+ | - 'Wrong number of arguments is passed to UPPER(): expected 1, got 0'
+ | ...
+SELECT upper('a');
+ | ---
+ | - metadata:
+ | - name: upper('a')
+ | type: string
+ | rows:
+ | - ['A']
+ | ...
+SELECT upper('a', 2);
+ | ---
+ | - null
+ | - 'Wrong number of arguments is passed to UPPER(): expected 1, got 2'
+ | ...
+
+-- Function version
+SELECT typeof(version());
+ | ---
+ | - metadata:
+ | - name: typeof(version())
+ | type: string
+ | rows:
+ | - ['string']
+ | ...
+SELECT typeof(version(1));
+ | ---
+ | - null
+ | - 'Wrong number of arguments is passed to VERSION(): expected 0, got 1'
+ | ...
+
+-- Function zeroblob
+SELECT zeroblob();
+ | ---
+ | - null
+ | - 'Wrong number of arguments is passed to ZEROBLOB(): expected 1, got 0'
+ | ...
+SELECT zeroblob(1);
+ | ---
+ | - metadata:
+ | - name: zeroblob(1)
+ | type: varbinary
+ | rows:
+ | - ["\0"]
+ | ...
+SELECT zeroblob(1, 2);
+ | ---
+ | - null
+ | - 'Wrong number of arguments is passed to ZEROBLOB(): expected 1, got 2'
+ | ...
+
+-- Function avg
+SELECT avg() FROM (values(1), (2), (3));
+ | ---
+ | - null
+ | - 'Wrong number of arguments is passed to AVG(): expected 1, got 0'
+ | ...
+SELECT avg("_auto_field_") FROM (values(1), (2), (3));
+ | ---
+ | - metadata:
+ | - name: avg("_auto_field_")
+ | type: number
+ | rows:
+ | - [2]
+ | ...
+SELECT avg("_auto_field_", 2) FROM (values(1), (2), (3));
+ | ---
+ | - null
+ | - 'Wrong number of arguments is passed to AVG(): expected 1, got 2'
+ | ...
+
+-- Function count
+SELECT count() FROM (values(1), (2), (3));
+ | ---
+ | - metadata:
+ | - name: count()
+ | type: integer
+ | rows:
+ | - [3]
+ | ...
+SELECT count("_auto_field_") FROM (values(1), (2), (3));
+ | ---
+ | - metadata:
+ | - name: count("_auto_field_")
+ | type: integer
+ | rows:
+ | - [3]
+ | ...
+SELECT count("_auto_field_", 2) FROM (values(1), (2), (3));
+ | ---
+ | - null
+ | - 'Wrong number of arguments is passed to COUNT(): expected from 0 to 1, got 2'
+ | ...
+
+-- Function group_concat
+SELECT group_concat() FROM (values('1'), ('2'), ('3'));
+ | ---
+ | - null
+ | - 'Wrong number of arguments is passed to GROUP_CONCAT(): expected from 1 to 2, got
+ | 0'
+ | ...
+SELECT group_concat("_auto_field_") FROM (values('1'), ('2'), ('3'));
+ | ---
+ | - metadata:
+ | - name: group_concat("_auto_field_")
+ | type: string
+ | rows:
+ | - ['1,2,3']
+ | ...
+SELECT group_concat("_auto_field_", '2') FROM (values('1'), ('2'), ('3'));
+ | ---
+ | - metadata:
+ | - name: group_concat("_auto_field_", '2')
+ | type: string
+ | rows:
+ | - ['12223']
+ | ...
+SELECT group_concat("_auto_field_", '2', '3') FROM (values('1'), ('2'), ('3'));
+ | ---
+ | - null
+ | - 'Wrong number of arguments is passed to GROUP_CONCAT(): expected from 1 to 2, got
+ | 3'
+ | ...
+
+-- Function max
+SELECT max() FROM (values(1), (2), (3));
+ | ---
+ | - null
+ | - 'Wrong number of arguments is passed to MAX(): expected 1, got 0'
+ | ...
+SELECT max("_auto_field_") FROM (values(1), (2), (3));
+ | ---
+ | - metadata:
+ | - name: max("_auto_field_")
+ | type: scalar
+ | rows:
+ | - [3]
+ | ...
+SELECT max("_auto_field_", 2) FROM (values(1), (2), (3));
+ | ---
+ | - null
+ | - 'Wrong number of arguments is passed to MAX(): expected 1, got 2'
+ | ...
+
+-- Function min
+SELECT min() FROM (values(1), (2), (3));
+ | ---
+ | - null
+ | - 'Wrong number of arguments is passed to MIN(): expected 1, got 0'
+ | ...
+SELECT min("_auto_field_") FROM (values(1), (2), (3));
+ | ---
+ | - metadata:
+ | - name: min("_auto_field_")
+ | type: scalar
+ | rows:
+ | - [1]
+ | ...
+SELECT min("_auto_field_", 2) FROM (values(1), (2), (3));
+ | ---
+ | - null
+ | - 'Wrong number of arguments is passed to MIN(): expected 1, got 2'
+ | ...
+
+-- Function sum
+SELECT sum() FROM (values(1), (2), (3));
+ | ---
+ | - null
+ | - 'Wrong number of arguments is passed to SUM(): expected 1, got 0'
+ | ...
+SELECT sum("_auto_field_") FROM (values(1), (2), (3));
+ | ---
+ | - metadata:
+ | - name: sum("_auto_field_")
+ | type: number
+ | rows:
+ | - [6]
+ | ...
+SELECT sum("_auto_field_", 2) FROM (values(1), (2), (3));
+ | ---
+ | - null
+ | - 'Wrong number of arguments is passed to SUM(): expected 1, got 2'
+ | ...
+
+-- Function total
+SELECT total() FROM (values(1), (2), (3));
+ | ---
+ | - null
+ | - 'Wrong number of arguments is passed to TOTAL(): expected 1, got 0'
+ | ...
+SELECT total("_auto_field_") FROM (values(1), (2), (3));
+ | ---
+ | - metadata:
+ | - name: total("_auto_field_")
+ | type: number
+ | rows:
+ | - [6]
+ | ...
+SELECT total("_auto_field_", 2) FROM (values(1), (2), (3));
+ | ---
+ | - null
+ | - 'Wrong number of arguments is passed to TOTAL(): expected 1, got 2'
+ | ...
diff --git a/test/sql/gh-4159-function-argumens.test.sql b/test/sql/gh-4159-function-argumens.test.sql
new file mode 100644
index 000000000..7578768cb
--- /dev/null
+++ b/test/sql/gh-4159-function-argumens.test.sql
@@ -0,0 +1,193 @@
+-- Function abs().
+SELECT abs();
+SELECT abs(1);
+SELECT abs(1, 2);
+
+-- Function char().
+SELECT char();
+SELECT char(1);
+SELECT char(1, 2);
+
+-- Function character_length().
+SELECT character_length();
+SELECT character_length('1');
+SELECT character_length('1', '2');
+
+-- Function char_length().
+SELECT char_length();
+SELECT char_length('1');
+SELECT char_length('1', '2');
+
+-- Function coalesce().
+SELECT coalesce();
+SELECT coalesce('1');
+SELECT coalesce('1', '2');
+SELECT coalesce('1', '2', '3');
+
+-- Function greatest().
+SELECT greatest();
+SELECT greatest('1');
+SELECT greatest('1', '2');
+
+-- Function hex().
+SELECT hex();
+SELECT hex(X'33');
+SELECT hex(X'33', X'33');
+
+-- Function ifnull
+SELECT ifnull();
+SELECT ifnull(1);
+SELECT ifnull(1, 2);
+SELECT ifnull(1, 2, 3);
+
+-- Function least().
+SELECT least();
+SELECT least('1');
+SELECT least('1', '2');
+
+-- Function length().
+SELECT length();
+SELECT length('1');
+SELECT length('1', '2');
+
+-- Function likelihood
+SELECT likelihood();
+SELECT likelihood(1);
+SELECT likelihood(1, 0.5);
+SELECT likelihood(1, 0.5, 3);
+
+-- Function likely
+SELECT likely();
+SELECT likely(1);
+SELECT likely(1, 2);
+
+-- Function lower
+SELECT lower();
+SELECT lower('a');
+SELECT lower('a', 2);
+
+-- Function nullif
+SELECT nullif();
+SELECT nullif(1);
+SELECT nullif(1, 2);
+SELECT nullif(1, 2, 3);
+
+-- Function position
+SELECT position();
+SELECT position('12345');
+SELECT position('12345', '2');
+SELECT position('12345', '2', 3);
+
+-- Function printf
+SELECT printf();
+SELECT printf('1');
+SELECT printf('1', 2);
+SELECT printf('1', 2, 3);
+
+-- Function quote
+SELECT quote();
+SELECT quote('1');
+SELECT quote('1', 2);
+
+-- Function random
+SELECT typeof(random());
+SELECT typeof(random(1));
+
+-- Function randomblob
+SELECT typeof(randomblob());
+SELECT typeof(randomblob(1));
+SELECT typeof(randomblob(1, 2));
+
+-- Function replace
+SELECT replace();
+SELECT replace('12345');
+SELECT replace('12345', '2');
+SELECT replace('12345', '2', '3');
+SELECT replace('12345', '2', '3', 4);
+
+-- Function round
+SELECT round();
+SELECT round(1.1245);
+SELECT round(1.1245, 2);
+SELECT round(1.1245, 2, 3);
+
+-- Function row_count
+SELECT row_count();
+SELECT row_count(1);
+
+-- Function soundex
+SELECT soundex();
+SELECT soundex(1);
+SELECT soundex(1, 2);
+
+-- Function substr
+SELECT substr();
+SELECT substr('12345');
+SELECT substr('12345', 2);
+SELECT substr('12345', 2, 3);
+SELECT substr('12345', 2, 3, 4);
+
+-- Function typeof
+SELECT typeof();
+SELECT typeof(1);
+SELECT typeof(1, 2);
+
+-- Function unicode
+SELECT unicode();
+SELECT unicode('1');
+SELECT unicode('1', '2');
+
+-- Function unlikely
+SELECT unlikely();
+SELECT unlikely(1);
+SELECT unlikely(1, 2);
+
+-- Function upper
+SELECT upper();
+SELECT upper('a');
+SELECT upper('a', 2);
+
+-- Function version
+SELECT typeof(version());
+SELECT typeof(version(1));
+
+-- Function zeroblob
+SELECT zeroblob();
+SELECT zeroblob(1);
+SELECT zeroblob(1, 2);
+
+-- Function avg
+SELECT avg() FROM (values(1), (2), (3));
+SELECT avg("_auto_field_") FROM (values(1), (2), (3));
+SELECT avg("_auto_field_", 2) FROM (values(1), (2), (3));
+
+-- Function count
+SELECT count() FROM (values(1), (2), (3));
+SELECT count("_auto_field_") FROM (values(1), (2), (3));
+SELECT count("_auto_field_", 2) FROM (values(1), (2), (3));
+
+-- Function group_concat
+SELECT group_concat() FROM (values('1'), ('2'), ('3'));
+SELECT group_concat("_auto_field_") FROM (values('1'), ('2'), ('3'));
+SELECT group_concat("_auto_field_", '2') FROM (values('1'), ('2'), ('3'));
+SELECT group_concat("_auto_field_", '2', '3') FROM (values('1'), ('2'), ('3'));
+
+-- Function max
+SELECT max() FROM (values(1), (2), (3));
+SELECT max("_auto_field_") FROM (values(1), (2), (3));
+SELECT max("_auto_field_", 2) FROM (values(1), (2), (3));
+
+-- Function min
+SELECT min() FROM (values(1), (2), (3));
+SELECT min("_auto_field_") FROM (values(1), (2), (3));
+SELECT min("_auto_field_", 2) FROM (values(1), (2), (3));
+
+-- Function sum
+SELECT sum() FROM (values(1), (2), (3));
+SELECT sum("_auto_field_") FROM (values(1), (2), (3));
+SELECT sum("_auto_field_", 2) FROM (values(1), (2), (3));
+
+-- Function total
+SELECT total() FROM (values(1), (2), (3));
+SELECT total("_auto_field_") FROM (values(1), (2), (3));
+SELECT total("_auto_field_", 2) FROM (values(1), (2), (3));
--
2.25.1
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [Tarantool-patches] [PATCH v4 3/5] sql: check number of arguments in sql_func_by_signature()
2020-07-13 5:33 ` [Tarantool-patches] [PATCH v4 3/5] sql: check number of arguments in sql_func_by_signature() imeevma
@ 2020-07-13 12:21 ` Nikita Pettik
0 siblings, 0 replies; 11+ messages in thread
From: Nikita Pettik @ 2020-07-13 12:21 UTC (permalink / raw)
To: imeevma; +Cc: tarantool-patches
On 13 Jul 08:33, imeevma@tarantool.org wrote:
> After this patch, the number of function arguments will always be
> checked in the sql_func_by_signature() function. This was not the
> case for some of the built-in functions.
At least, I'd split patch into two parts: first introduces new
members and changes data structures, and the second one - adds
new check (if it is necessary at all; see comments below).
> Part of #4159
> ---
> @@ -2195,11 +2164,28 @@ sql_func_by_signature(const char *name, int argc)
> name));
> return NULL;
> }
It looks strange since other 'by' getters (space, index, collation)
dont set diag errors.
> - int param_count = base->def->param_count;
> - if (param_count != -1 && param_count != argc) {
> - const char *err = tt_sprintf("%d", param_count);
> - diag_set(ClientError, ER_FUNC_WRONG_ARG_COUNT,
> - base->def->name, err, argc);
> + if (base->def->language != FUNC_LANGUAGE_SQL_BUILTIN) {
> + int param_count = base->def->param_count;
> + if (param_count != -1 && param_count != argc) {
> + const char *err = tt_sprintf("%d", param_count);
> + diag_set(ClientError, ER_FUNC_WRONG_ARG_COUNT,
> + base->def->name, err, argc);
> + return NULL;
> + }
> + return base;
> + }
> + struct func_sql_builtin *func = (struct func_sql_builtin *)base;
> + uint32_t arg_c = (uint32_t)argc;
> + if (func->args.min_count > arg_c || func->args.max_count < arg_c) {
> + const char *err;
> + uint32_t min = func->args.min_count;
> + uint32_t max = func->args.max_count;
> + if (min != max)
> + err = tt_sprintf("from %d to %d", min, max);
> + else
> + err = tt_sprintf("%d", min);
> + diag_set(ClientError, ER_FUNC_WRONG_ARG_COUNT, base->def->name,
> + err, argc);
> return NULL;
> }
> return base;
> @@ -2242,12 +2228,16 @@ static struct {
> /** Members below are related to struct func_def. */
> bool is_deterministic;
> int param_count;
> + uint32_t min_count;
> + uint32_t max_count;
Why not erase param_count?
min/max_count of what? Also add comments to these members explaining
why do we need them at all.
Why can't we use -1 param_count for variadic args and provide check
in func's implementation?
> enum field_type returns;
> enum func_aggregate aggregate;
> bool export_to_sql;
> } sql_builtins[] = {
> {.name = "ABS",
> .param_count = 1,
> + .min_count = 1,
> + .max_count = 1,
> .returns = FIELD_TYPE_NUMBER,
> .aggregate = FUNC_AGGREGATE_NONE,
> .is_deterministic = true,
> @@ -2318,6 +2320,8 @@ static struct {
> }, {
> .name = "COALESCE",
> .param_count = -1,
> + .min_count = 2,
> + .max_count = SQL_MAX_FUNCTION_ARG,
Why do you need uint32 if max value fits into uint8?
> diff --git a/src/box/sql/sqlInt.h b/src/box/sql/sqlInt.h
> index 58a65acc1..6af9d7473 100644
> --- a/src/box/sql/sqlInt.h
> +++ b/src/box/sql/sqlInt.h
> @@ -4397,11 +4397,24 @@ Expr *sqlExprForVectorField(Parse *, Expr *, int);
> */
> extern int sqlSubProgramsRemaining;
>
> +/**
> + * A structure that contains additional information about
> + * arguments to built-in SQL functions.
> + */
> +struct sql_builtin_func_args {
> + /** Min number of arguments. */
> + uint32_t min_count;
> + /** Max number of arguments. */
> + uint32_t max_count;
> +};
> +
> struct func_sql_builtin {
> /** Function object base class. */
> struct func base;
> /** A bitmask of SQL flags. */
> uint16_t flags;
> + /** Information about arguments to built-in functions. */
> + struct sql_builtin_func_args args;
Why not simply inline two members (min/max)?
> /**
> * A VDBE-memory-compatible call method.
> * SQL built-ins don't use func base class "call"
> diff --git a/test/sql-tap/func.test.lua b/test/sql-tap/func.test.lua
> index 3c088920f..1d3ef9e2a 100755
> --- a/test/sql-tap/func.test.lua
> +++ b/test/sql-tap/func.test.lua
> @@ -428,7 +428,7 @@ test:do_catchsql_test(
> SELECT round(a,b,c) FROM t1
> ]], {
> -- <func-4.5>
> - 1, "Wrong number of arguments is passed to ROUND(): expected 1 or 2, got 3"
> + 1, "Wrong number of arguments is passed to ROUND(): expected from 1 to 2, got 3"
> -- </func-4.5>
> })
>
> @@ -488,7 +488,7 @@ test:do_catchsql_test(
> SELECT round() FROM t1 ORDER BY a
> ]], {
> -- <func-4.11>
> - 1, "Wrong number of arguments is passed to ROUND(): expected 1 or 2, got 0"
> + 1, "Wrong number of arguments is passed to ROUND(): expected from 1 to 2, got 0"
> -- </func-4.11>
> })
>
> @@ -2540,7 +2540,7 @@ test:do_catchsql_test(
> SELECT coalesce()
> ]], {
> -- <func-27.1>
> - 1, "Wrong number of arguments is passed to COALESCE(): expected at least two, got 0"
> + 1, "Wrong number of arguments is passed to COALESCE(): expected from 2 to 127, got 0"
Current error message looks better than new one (127 arguments??).
^ permalink raw reply [flat|nested] 11+ messages in thread
* [Tarantool-patches] [PATCH v4 4/5] sql: change implicit cast for assignment
2020-07-13 5:32 [Tarantool-patches] [PATCH v4 0/5] Change implicit cast for assignment imeevma
` (2 preceding siblings ...)
2020-07-13 5:33 ` [Tarantool-patches] [PATCH v4 3/5] sql: check number of arguments in sql_func_by_signature() imeevma
@ 2020-07-13 5:33 ` imeevma
2020-07-13 14:42 ` Nikita Pettik
2020-07-13 5:33 ` [Tarantool-patches] [PATCH v4 5/5] sql: properly check arguments of functions imeevma
4 siblings, 1 reply; 11+ messages in thread
From: imeevma @ 2020-07-13 5:33 UTC (permalink / raw)
To: korablev, tsafin, tarantool-patches
This patch changes implicit cast for assignment.
Closes #3809
Needed for #4159
Part of #4230
@TarantoolBot document
Title: change implicit cast for comparison
After this patch, these rules will apply during the implicit cast:
| | STRING | BINARY | BOOLEAN | INTEGER | UNSIGNED | DOUBLE |
| :--- | :---: | :---: | :---: | :---: | :---: | :---: |
| STRING | A | N | N | N | N | N |
| BINARY | N | A | N | N | N | N |
| BOOLEAN | N | N | A | N | N | N |
| INTEGER | N | N | N | A | S | Aa |
| UNSIGNED | N | N | N | A | A | Aa |
| DOUBLE | N | N | N | Sa | Sa | A |
In this table, the types of the assigned value are on the left,
and the types that should be after the assignment are at the top.
'A' - the assignment will be completed.
'N' - the assignment won't be completed.
'S' - the appointment may be unsuccessful.
'a' - after assignment, the resulting value may be approximated.
Rules for numeric values are these:
1) Loss of significant digits (overflow) is an error.
2) Loss of insignificant digits is not an error.
Example:
```
tarantool> box.execute([[CREATE TABLE t1(i INT PRIMARY KEY);]])
tarantool> box.execute([[INSERT INTO t1 VALUES ('1');]])
---
- null
- 'Type mismatch: can not convert 1 to integer'
...
tarantool> box.execute('INSERT INTO t1 VALUES (1.2345);')
tarantool> box.execute('SELECT * FROM t1;')
---
- metadata:
- name: I
type: integer
rows:
- [1]
...
tarantool> box.execute([[CREATE TABLE t2(t text PRIMARY KEY);]])
tarantool> box.execute([[INSERT INTO t2 VALUES (1);]])
---
- null
- 'Type mismatch: can not convert 1 to string'
...
```
---
src/box/sql/vdbe.c | 156 ++++-
src/box/sql/vdbeInt.h | 4 +
test/sql-tap/autoinc.test.lua | 2 +-
test/sql-tap/default.test.lua | 6 +-
test/sql-tap/e_select1.test.lua | 27 +-
test/sql-tap/in3.test.lua | 14 +-
test/sql-tap/in4.test.lua | 96 +--
test/sql-tap/index1.test.lua | 24 +-
test/sql-tap/insert3.test.lua | 10 +-
test/sql-tap/intpkey.test.lua | 133 +---
test/sql-tap/limit.test.lua | 2 +-
test/sql-tap/minmax2.test.lua | 6 +-
test/sql-tap/misc1.test.lua | 24 +-
test/sql-tap/numcast.test.lua | 6 +-
test/sql-tap/select1.test.lua | 8 +-
test/sql-tap/select4.test.lua | 12 +-
test/sql-tap/select7.test.lua | 2 +-
test/sql-tap/sort.test.lua | 8 +-
test/sql-tap/subquery.test.lua | 69 +-
test/sql-tap/tkt-3998683a16.test.lua | 26 +-
test/sql-tap/tkt-54844eea3f.test.lua | 8 +-
test/sql-tap/tkt-7bbfb7d442.test.lua | 4 +-
test/sql-tap/tkt-9a8b09f8e6.test.lua | 62 +-
test/sql-tap/tkt-f973c7ac31.test.lua | 18 +-
test/sql-tap/tkt-fc7bd6358f.test.lua | 111 ----
test/sql-tap/tkt1444.test.lua | 4 +-
test/sql-tap/tkt3493.test.lua | 6 +-
test/sql-tap/tkt3841.test.lua | 12 +-
test/sql-tap/transitive1.test.lua | 4 +-
test/sql-tap/triggerA.test.lua | 4 +-
test/sql-tap/unique.test.lua | 8 +-
test/sql-tap/view.test.lua | 2 +-
test/sql-tap/where5.test.lua | 10 +-
test/sql-tap/whereB.test.lua | 908 ---------------------------
test/sql-tap/whereC.test.lua | 8 +-
test/sql/types.result | 620 ++++++++++++++++++
test/sql/types.test.lua | 129 ++++
37 files changed, 1042 insertions(+), 1511 deletions(-)
delete mode 100755 test/sql-tap/tkt-fc7bd6358f.test.lua
delete mode 100755 test/sql-tap/whereB.test.lua
diff --git a/src/box/sql/vdbe.c b/src/box/sql/vdbe.c
index 950f72ddd..863f38f5d 100644
--- a/src/box/sql/vdbe.c
+++ b/src/box/sql/vdbe.c
@@ -417,6 +417,144 @@ sql_value_apply_type(
mem_apply_type((Mem *) pVal, type);
}
+/**
+ * Check that MEM_type of the mem is compatible with given type.
+ *
+ * @param mem The MEM that contains the value to check.
+ * @param type The type to check.
+ * @retval TRUE if the MEM_type of the value and the given type
+ * are compatible, FALSE otherwise.
+ */
+static bool
+mem_is_type_compatible(struct Mem *mem, enum field_type type)
+{
+ 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);
+}
+
+/**
+ * Convert the numeric value contained in MEM to double. If the
+ * is_precise flag is set, the conversion will succeed only if it
+ * is lossless.
+ *
+ * @param mem The MEM that contains the numeric value.
+ * @param is_precise Flag.
+ * @retval 0 if the conversion was successful, -1 otherwise.
+ */
+static int
+mem_convert_to_double(struct Mem *mem, bool is_precise)
+{
+ if ((mem->flags & MEM_Real) != 0)
+ return 0;
+ if ((mem->flags & (MEM_Int | MEM_UInt)) == 0)
+ return -1;
+ if ((mem->flags & MEM_Int) != 0) {
+ int64_t i = mem->u.i;
+ double d = (double)i;
+ if (!is_precise || i == (int64_t)d)
+ mem_set_double(mem, d);
+ else
+ return -1;
+ } else {
+ uint64_t u = mem->u.u;
+ double d = (double)u;
+ if (!is_precise || u == (uint64_t)d)
+ mem_set_double(mem, d);
+ else
+ return -1;
+ }
+ return 0;
+}
+
+/**
+ * Convert the numeric value contained in MEM to unsigned. If the
+ * is_precise flag is set, the conversion will succeed only if it
+ * is lossless.
+ *
+ * @param mem The MEM that contains the numeric value.
+ * @param is_precise Flag.
+ * @retval 0 if the conversion was successful, -1 otherwise.
+ */
+static int
+mem_convert_to_unsigned(struct Mem *mem, bool is_precise)
+{
+ if ((mem->flags & MEM_UInt) != 0)
+ return 0;
+ if ((mem->flags & MEM_Int) != 0)
+ return -1;
+ if ((mem->flags & MEM_Real) == 0)
+ return -1;
+ double d = mem->u.r;
+ if (d < 0.0 || d >= (double)UINT64_MAX)
+ return -1;
+ uint64_t u = (uint64_t)d;
+ if (!is_precise || d == (double)u)
+ mem_set_u64(mem, (uint64_t) d);
+ else
+ return -1;
+ return 0;
+}
+
+/**
+ * Convert the numeric value contained in MEM to integer. If the
+ * is_precise flag is set, the conversion will succeed only if it
+ * is lossless.
+ *
+ * @param mem The MEM that contains the numeric value.
+ * @param is_precise Flag.
+ * @retval 0 if the conversion was successful, -1 otherwise.
+ */
+static int
+mem_convert_to_integer(struct Mem *mem, bool is_precise)
+{
+ if ((mem->flags & (MEM_UInt | MEM_Int)) != 0)
+ return 0;
+ if ((mem->flags & MEM_Real) == 0)
+ return -1;
+ double d = mem->u.r;
+ if (d >= (double)UINT64_MAX || d < (double)INT64_MIN)
+ return -1;
+ if (d < (double)INT64_MAX) {
+ int64_t i = (int64_t)d;
+ if (!is_precise || d == (double)i)
+ mem_set_int(mem, (int64_t) d, d < 0);
+ else
+ return -1;
+ } else {
+ uint64_t u = (uint64_t)d;
+ if (!is_precise || d == (double)u)
+ mem_set_int(mem, (uint64_t) d, false);
+ else
+ return -1;
+ }
+ return 0;
+}
+
+/**
+ * Convert the numeric value contained in MEM to another numeric
+ * type. If the is_precise flag is set, the conversion will
+ * succeed only if it is lossless.
+ *
+ * @param mem The MEM that contains the numeric value.
+ * @param type The type to convert to.
+ * @param is_precise Flag.
+ * @retval 0 if the conversion was successful, -1 otherwise.
+ */
+static int
+mem_convert_to_numeric(struct Mem *mem, enum field_type type, bool is_precise)
+{
+ assert(mp_type_is_numeric(mem_mp_type(mem)) &&
+ sql_type_is_numeric(type));
+ assert(type != FIELD_TYPE_NUMBER);
+ if (type == FIELD_TYPE_DOUBLE)
+ return mem_convert_to_double(mem, is_precise);
+ if (type == FIELD_TYPE_UNSIGNED)
+ return mem_convert_to_unsigned(mem, is_precise);
+ assert(type == FIELD_TYPE_INTEGER);
+ return mem_convert_to_integer(mem, is_precise);
+}
+
/*
* pMem currently only holds a string type (or maybe a BLOB that we can
* interpret as a string if we want to). Compute its corresponding
@@ -2747,11 +2885,11 @@ case OP_Fetch: {
/* Opcode: ApplyType P1 P2 * P4 *
* Synopsis: type(r[P1@P2])
*
- * Apply types to a range of P2 registers starting with P1.
- *
- * P4 is a string that is P2 characters long. The nth character of the
- * string indicates the column type that should be used for the nth
- * memory cell in the range.
+ * Check that types of P2 registers starting from register P1 are
+ * compatible with given field types in P4. If the MEM_type of the
+ * value and the given type are incompatible according to
+ * field_mp_plain_type_is_compatible(), but both are numeric,
+ * this opcode attempts to convert the value to the type.
*/
case OP_ApplyType: {
enum field_type *types = pOp->p4.types;
@@ -2762,7 +2900,13 @@ case OP_ApplyType: {
while((type = *(types++)) != field_type_MAX) {
assert(pIn1 <= &p->aMem[(p->nMem+1 - p->nCursor)]);
assert(memIsValid(pIn1));
- if (mem_apply_type(pIn1, type) != 0) {
+ if (mem_is_type_compatible(pIn1, type)) {
+ pIn1++;
+ continue;
+ }
+ if (!mp_type_is_numeric(mem_mp_type(pIn1)) ||
+ !sql_type_is_numeric(type) ||
+ mem_convert_to_numeric(pIn1, type, false) != 0) {
diag_set(ClientError, ER_SQL_TYPE_MISMATCH,
sql_value_to_diag_str(pIn1),
field_type_strs[type]);
diff --git a/src/box/sql/vdbeInt.h b/src/box/sql/vdbeInt.h
index 44c27bdb7..ad46ab129 100644
--- a/src/box/sql/vdbeInt.h
+++ b/src/box/sql/vdbeInt.h
@@ -566,6 +566,10 @@ mem_mp_type(struct Mem *mem);
*/
#define mp_type_is_bloblike(X) ((X) == MP_BIN || (X) == MP_ARRAY || (X) == MP_MAP)
+/** Return TRUE if MP_type of X is numeric, FALSE otherwise. */
+#define mp_type_is_numeric(X) ((X) == MP_INT || (X) == MP_UINT ||\
+ (X) == MP_DOUBLE)
+
/**
* Memory cell mem contains the context of an aggregate function.
* This routine calls the finalize method for that function. The
diff --git a/test/sql-tap/autoinc.test.lua b/test/sql-tap/autoinc.test.lua
index 37e65e541..07442b60a 100755
--- a/test/sql-tap/autoinc.test.lua
+++ b/test/sql-tap/autoinc.test.lua
@@ -694,7 +694,7 @@ test:do_test(
INSERT INTO t3928(b) VALUES('after-int-' || CAST(new.b AS TEXT));
END;
DELETE FROM t3928 WHERE a!=1;
- UPDATE t3928 SET b=456 WHERE a=1;
+ UPDATE t3928 SET b='456' WHERE a=1;
SELECT * FROM t3928 ORDER BY a;
]])
end, {
diff --git a/test/sql-tap/default.test.lua b/test/sql-tap/default.test.lua
index d3e35c71c..f1def2b10 100755
--- a/test/sql-tap/default.test.lua
+++ b/test/sql-tap/default.test.lua
@@ -109,16 +109,12 @@ test:do_execsql_test(
f VARCHAR(15), --COLLATE RTRIM,
g INTEGER DEFAULT( 3600*12 )
);
- INSERT INTO t3 VALUES(null, 5, 'row1', 5.25, 8.67, 321, 432);
+ INSERT INTO t3 VALUES(null, 5, 'row1', 5.25, 8.67, '321', 432);
SELECT a, typeof(a), b, typeof(b), c, typeof(c),
d, typeof(d), e, typeof(e), f, typeof(f),
g, typeof(g) FROM t3;
]], {
-- <default-3.1>
- -- TODO: In original test "321" is not a string, its a value.
- -- In current situation I don't know what to do, need Kirill's
- -- advice.
- -- Bulat
1, "integer", 5, "integer", "row1", "string", 5.25, "number", 8.67, "number", "321", "string", 432, "integer"
-- </default-3.1>
})
diff --git a/test/sql-tap/e_select1.test.lua b/test/sql-tap/e_select1.test.lua
index 1d3b964b9..578620fca 100755
--- a/test/sql-tap/e_select1.test.lua
+++ b/test/sql-tap/e_select1.test.lua
@@ -1,6 +1,6 @@
#!/usr/bin/env tarantool
test = require("sqltester")
-test:plan(510)
+test:plan(509)
--!./tcltestrunner.lua
-- 2010 July 16
@@ -753,11 +753,11 @@ test:do_execsql_test(
CREATE TABLE z3(id INT primary key, a NUMBER, b NUMBER);
INSERT INTO z1 VALUES(1, 51.65, -59.58, 'belfries');
- INSERT INTO z1 VALUES(2, -5, NULL, 75);
+ INSERT INTO z1 VALUES(2, -5, NULL, '75');
INSERT INTO z1 VALUES(3, -2.2, -23.18, 'suiters');
INSERT INTO z1 VALUES(4, NULL, 67, 'quartets');
INSERT INTO z1 VALUES(5, -1.04, -32.3, 'aspen');
- INSERT INTO z1 VALUES(6, 63, '0', -26);
+ INSERT INTO z1 VALUES(6, 63, 0, '-26');
INSERT INTO z2 VALUES(1, NULL, 21);
INSERT INTO z2 VALUES(2, 36.0, 6.0);
@@ -1457,13 +1457,13 @@ test:do_execsql_test(
CREATE TABLE q2(id INT primary key, d TEXT, e NUMBER);
CREATE TABLE q3(id INT primary key, f TEXT, g INT);
- INSERT INTO q1 VALUES(1, 16, -87.66, NULL);
+ INSERT INTO q1 VALUES(1, '16', -87.66, NULL);
INSERT INTO q1 VALUES(2, 'legible', 94, -42.47);
INSERT INTO q1 VALUES(3, 'beauty', 36, NULL);
INSERT INTO q2 VALUES(1, 'legible', 1);
INSERT INTO q2 VALUES(2, 'beauty', 2);
- INSERT INTO q2 VALUES(3, -65, 4);
+ INSERT INTO q2 VALUES(3, '-65', 4);
INSERT INTO q2 VALUES(4, 'emanating', -16.56);
INSERT INTO q3 VALUES(1, 'beauty', 2);
@@ -1603,7 +1603,7 @@ test:do_execsql_test(
CREATE TABLE w2(a INT PRIMARY KEY, b TEXT);
INSERT INTO w1 VALUES('1', 4.1);
- INSERT INTO w2 VALUES(1, 4.1);
+ INSERT INTO w2 VALUES(1, '4.1');
]], {
-- <e_select-7.10.0>
@@ -2150,7 +2150,6 @@ test:do_select_tests(
{"2", "SELECT b FROM f1 ORDER BY a LIMIT 2+3 ", {"a", "b", "c", "d", "e"}},
{"3", "SELECT b FROM f1 ORDER BY a LIMIT (SELECT a FROM f1 WHERE b = 'e') ", {"a", "b", "c", "d", "e"}},
{"4", "SELECT b FROM f1 ORDER BY a LIMIT 5.0 ", {"a", "b", "c", "d", "e"}},
- {"5", "SELECT b FROM f1 ORDER BY a LIMIT '5' ", {"a", "b", "c", "d", "e"}},
})
-- EVIDENCE-OF: R-46155-47219 If the expression evaluates to a NULL value
@@ -2195,7 +2194,7 @@ test:do_select_tests(
{"1", "SELECT b FROM f1 ORDER BY a LIMIT 0 ", {}},
{"2", "SELECT b FROM f1 ORDER BY a DESC LIMIT 4 ", {"z", "y", "x", "w"}},
{"3", "SELECT b FROM f1 ORDER BY a DESC LIMIT 8 ", {"z", "y", "x", "w", "v", "u", "t", "s"}},
- {"4", "SELECT b FROM f1 ORDER BY a DESC LIMIT '12' ", {"z", y, "x", "w", "v", "u", "t", "s", "r", "q", "p", "o"}},
+ {"4", "SELECT b FROM f1 ORDER BY a DESC LIMIT 12 ", {"z", y, "x", "w", "v", "u", "t", "s", "r", "q", "p", "o"}},
})
-- EVIDENCE-OF: R-54935-19057 Or, if the SELECT statement would return
@@ -2240,10 +2239,10 @@ test:do_select_tests(
{"1", "SELECT b FROM f1 ORDER BY a LIMIT 10 OFFSET 5", {"f", "g", "h", "i", "j", "k", "l", "m", "n", "o"}},
{"2", "SELECT b FROM f1 ORDER BY a LIMIT 2+3 OFFSET 10", {"k", "l", "m", "n", "o"}},
{"3", "SELECT b FROM f1 ORDER BY a LIMIT (SELECT a FROM f1 WHERE b='j') OFFSET (SELECT a FROM f1 WHERE b='b') ", {"c", "d", "e", "f", "g", "h", "i", "j", "k", "l"}},
- {"4", "SELECT b FROM f1 ORDER BY a LIMIT '5' OFFSET 3.0 ", {"d", "e", "f", "g", "h"}},
- {"5", "SELECT b FROM f1 ORDER BY a LIMIT '5' OFFSET 0 ", {"a", "b", "c", "d", "e"}},
+ {"4", "SELECT b FROM f1 ORDER BY a LIMIT 5 OFFSET 3.0 ", {"d", "e", "f", "g", "h"}},
+ {"5", "SELECT b FROM f1 ORDER BY a LIMIT 5 OFFSET 0 ", {"a", "b", "c", "d", "e"}},
{"6", "SELECT b FROM f1 ORDER BY a LIMIT 0 OFFSET 10 ", {}},
- {"7", "SELECT b FROM f1 ORDER BY a LIMIT 3 OFFSET '1'||'5' ", {"p", "q", "r"}},
+ {"7", "SELECT b FROM f1 ORDER BY a LIMIT 3 OFFSET CAST('1'||'5' AS INTEGER) ", {"p", "q", "r"}},
})
-- EVIDENCE-OF: R-34648-44875 Or, if the SELECT would return less than
@@ -2279,10 +2278,10 @@ test:do_select_tests(
{"1", "SELECT b FROM f1 ORDER BY a LIMIT 5, 10 ", {"f", "g", "h", "i", "j", "k", "l", "m", "n", "o"}},
{"2", "SELECT b FROM f1 ORDER BY a LIMIT 10, 2+3 ", {"k", "l", "m", "n", "o"}},
{"3", "SELECT b FROM f1 ORDER BY a LIMIT (SELECT a FROM f1 WHERE b='b'), (SELECT a FROM f1 WHERE b='j')", {"c", "d", "e", "f", "g", "h", "i", "j", "k", "l"}},
- {"4", "SELECT b FROM f1 ORDER BY a LIMIT 3.0, '5' ", {"d", "e", "f", "g", "h"}},
- {"5", "SELECT b FROM f1 ORDER BY a LIMIT 0, '5' ", {"a", "b", "c", "d", "e"}},
+ {"4", "SELECT b FROM f1 ORDER BY a LIMIT 3.0, 5 ", {"d", "e", "f", "g", "h"}},
+ {"5", "SELECT b FROM f1 ORDER BY a LIMIT 0, 5 ", {"a", "b", "c", "d", "e"}},
{"6", "SELECT b FROM f1 ORDER BY a LIMIT 10, 0 ", {}},
- {"7", "SELECT b FROM f1 ORDER BY a LIMIT '1'||'5', 3 ", {"p", "q", "r"}},
+ {"7", "SELECT b FROM f1 ORDER BY a LIMIT CAST('1'||'5' AS INTEGER), 3 ", {"p", "q", "r"}},
{"8", "SELECT b FROM f1 ORDER BY a LIMIT 20, 10 ", {"u", "v", "w", "x", "y", "z"}},
{"9", "SELECT a FROM f1 ORDER BY a DESC LIMIT 18+4, 100 ", {4, 3, 2, 1}},
{"10", "SELECT b FROM f1 ORDER BY a LIMIT 0, 5 ", {"a", "b", "c", "d", "e"}},
diff --git a/test/sql-tap/in3.test.lua b/test/sql-tap/in3.test.lua
index e29db9d93..a6d842962 100755
--- a/test/sql-tap/in3.test.lua
+++ b/test/sql-tap/in3.test.lua
@@ -1,6 +1,6 @@
#!/usr/bin/env tarantool
test = require("sqltester")
-test:plan(29)
+test:plan(28)
--!./tcltestrunner.lua
-- 2007 November 29
@@ -322,18 +322,6 @@ test:do_test(
-- </in3-3.2>
})
-test:do_test(
- "in3-3.3",
- function()
- -- Logically, numeric affinity is applied to both sides before
- -- the comparison, but index can't be used.
- return exec_neph(" SELECT x IN (SELECT b FROM t1) FROM t2 ")
- end, {
- -- <in3-3.3>
- 1, true
- -- </in3-3.3>
- })
-
test:do_test(
"in3-3.4",
function()
diff --git a/test/sql-tap/in4.test.lua b/test/sql-tap/in4.test.lua
index 8c6917379..5c01ccdab 100755
--- a/test/sql-tap/in4.test.lua
+++ b/test/sql-tap/in4.test.lua
@@ -1,6 +1,6 @@
#!/usr/bin/env tarantool
test = require("sqltester")
-test:plan(61)
+test:plan(52)
--!./tcltestrunner.lua
-- 2008 September 1
@@ -140,7 +140,7 @@ test:do_execsql_test(
test:do_execsql_test(
"in4-2.7",
[[
- SELECT b FROM t2 WHERE a IN ('1', '2')
+ SELECT b FROM t2 WHERE a IN (1, 2)
]], {
-- <in4-2.7>
"one", "two"
@@ -585,98 +585,6 @@ test:do_execsql_test(
-- </in4-4.6>
})
-test:do_execsql_test(
- "in4-4.11",
- [[
- CREATE TABLE t4b(a TEXT, b NUMBER, c INT PRIMARY KEY);
- INSERT INTO t4b VALUES('1.0',1,4);
- SELECT c FROM t4b WHERE a=b;
- ]], {
- -- <in4-4.11>
- 4
- -- </in4-4.11>
- })
-
-test:do_execsql_test(
- "in4-4.12",
- [[
- SELECT c FROM t4b WHERE b=a;
- ]], {
- -- <in4-4.12>
- 4
- -- </in4-4.12>
- })
-
-test:do_execsql_test(
- "in4-4.13",
- [[
- SELECT c FROM t4b WHERE +a=b;
- ]], {
- -- <in4-4.13>
- 4
- -- </in4-4.13>
- })
-
-test:do_execsql_test(
- "in4-4.14",
- [[
- SELECT c FROM t4b WHERE a=+b;
- ]], {
- -- <in4-4.14>
- 4
- -- </in4-4.14>
- })
-
-test:do_execsql_test(
- "in4-4.15",
- [[
- SELECT c FROM t4b WHERE +b=a;
- ]], {
- -- <in4-4.15>
- 4
- -- </in4-4.15>
- })
-
-test:do_execsql_test(
- "in4-4.16",
- [[
- SELECT c FROM t4b WHERE b=+a;
- ]], {
- -- <in4-4.16>
- 4
- -- </in4-4.16>
- })
-
-test:do_execsql_test(
- "in4-4.17",
- [[
- SELECT c FROM t4b WHERE a IN (b);
- ]], {
- -- <in4-4.17>
- 4
- -- </in4-4.17>
- })
-
-test:do_execsql_test(
- "in4-4.18",
- [[
- SELECT c FROM t4b WHERE b IN (a);
- ]], {
- -- <in4-4.18>
- 4
- -- </in4-4.18>
- })
-
-test:do_execsql_test(
- "in4-4.19",
- [[
- SELECT c FROM t4b WHERE +b IN (a);
- ]], {
- -- <in4-4.19>
- 4
- -- </in4-4.19>
- })
-
-- MUST_WORK_TEST
-- Tarantool: TBI: Need to support collations. Depends on #2121
-- test:do_execsql_test(
diff --git a/test/sql-tap/index1.test.lua b/test/sql-tap/index1.test.lua
index e173e685c..ce66b7c1e 100755
--- a/test/sql-tap/index1.test.lua
+++ b/test/sql-tap/index1.test.lua
@@ -593,25 +593,17 @@ test:do_test(
-- </index-11.1>
})
end
--- integrity_check index-11.2
--- Numeric strings should compare as if they were numbers. So even if the
--- strings are not character-by-character the same, if they represent the
--- same number they should compare equal to one another. Verify that this
--- is true in indices.
---
--- Updated for sql v3: sql will now store these values as numbers
--- (because the affinity of column a is NUMERIC) so the quirky
--- representations are not retained. i.e. '+1.0' becomes '1'.
+
test:do_execsql_test(
"index-12.1",
[[
CREATE TABLE t4(id INT primary key, a NUMBER,b INT );
- INSERT INTO t4 VALUES(1, '0.0',1);
- INSERT INTO t4 VALUES(2, '0.00',2);
- INSERT INTO t4 VALUES(4, '-1.0',4);
- INSERT INTO t4 VALUES(5, '+1.0',5);
- INSERT INTO t4 VALUES(6, '0',6);
- INSERT INTO t4 VALUES(7, '00000',7);
+ INSERT INTO t4 VALUES(1, 0.0, 1);
+ INSERT INTO t4 VALUES(2, 0.00, 2);
+ INSERT INTO t4 VALUES(4, -1.0, 4);
+ INSERT INTO t4 VALUES(5, +1.0, 5);
+ INSERT INTO t4 VALUES(6, 0, 6);
+ INSERT INTO t4 VALUES(7, 00000, 7);
SELECT a FROM t4 ORDER BY b;
]], {
-- <index-12.1>
@@ -692,7 +684,7 @@ test:do_execsql_test(
c TEXT,
UNIQUE(a,c)
);
- INSERT INTO t5 VALUES(1,2,3);
+ INSERT INTO t5 VALUES(1,2,'3');
SELECT * FROM t5;
]], {
-- <index-13.1>
diff --git a/test/sql-tap/insert3.test.lua b/test/sql-tap/insert3.test.lua
index 43bb06630..b92bc508e 100755
--- a/test/sql-tap/insert3.test.lua
+++ b/test/sql-tap/insert3.test.lua
@@ -60,7 +60,7 @@ test:do_execsql_test(
CREATE TABLE log2(rowid INTEGER PRIMARY KEY AUTOINCREMENT, x TEXT UNIQUE,y INT );
CREATE TRIGGER r2 BEFORE INSERT ON t1 FOR EACH ROW BEGIN
UPDATE log2 SET y=y+1 WHERE x=new.b;
- INSERT OR IGNORE INTO log2(x, y) VALUES(new.b,1);
+ INSERT OR IGNORE INTO log2(x, y) VALUES(CAST(new.b AS STRING),1);
END;
INSERT INTO t1(a, b) VALUES('hi', 453);
SELECT x,y FROM log ORDER BY x;
@@ -129,8 +129,8 @@ test:do_execsql_test(
INSERT INTO t2dup(a,b,c) VALUES(new.a,new.b,new.c);
END;
INSERT INTO t2(a) VALUES(123);
- INSERT INTO t2(b) VALUES(234);
- INSERT INTO t2(c) VALUES(345);
+ INSERT INTO t2(b) VALUES('234');
+ INSERT INTO t2(c) VALUES('345');
SELECT * FROM t2dup;
]], {
-- <insert3-2.1>
@@ -143,8 +143,8 @@ test:do_execsql_test(
[[
DELETE FROM t2dup;
INSERT INTO t2(a) SELECT 1 FROM t1 LIMIT 1;
- INSERT INTO t2(b) SELECT 987 FROM t1 LIMIT 1;
- INSERT INTO t2(c) SELECT 876 FROM t1 LIMIT 1;
+ INSERT INTO t2(b) SELECT '987' FROM t1 LIMIT 1;
+ INSERT INTO t2(c) SELECT '876' FROM t1 LIMIT 1;
SELECT * FROM t2dup;
]], {
-- <insert3-2.2>
diff --git a/test/sql-tap/intpkey.test.lua b/test/sql-tap/intpkey.test.lua
index b6b186632..684a24114 100755
--- a/test/sql-tap/intpkey.test.lua
+++ b/test/sql-tap/intpkey.test.lua
@@ -1,6 +1,6 @@
#!/usr/bin/env tarantool
test = require("sqltester")
-test:plan(40)
+test:plan(31)
--!./tcltestrunner.lua
-- 2001 September 15
@@ -770,142 +770,13 @@ test:do_execsql_test(
-- </intpkey-11.1>
})
--- integrity_check intpkey-12.1
--- Try to use a string that looks like a floating point number as
--- an integer primary key. This should actually work when the floating
--- point value can be rounded to an integer without loss of data.
---
-test:do_execsql_test(
- "intpkey-13.1",
- [[
- SELECT * FROM t1 WHERE a=1;
- ]], {
- -- <intpkey-13.1>
-
- -- </intpkey-13.1>
- })
-
-test:do_execsql_test(
- "intpkey-13.2",
- [[
- INSERT INTO t1 VALUES('1',2,3);
- SELECT * FROM t1 WHERE a=1;
- ]], {
- -- <intpkey-13.2>
- 1, "2", "3"
- -- </intpkey-13.2>
- })
-
--- MUST_WORK_TEST
-if (0 > 0) then
- -- Tarantool: issue submitted #2315
- test:do_catchsql_test(
- "intpkey-13.3",
- [[
- INSERT INTO t1 VALUES('1.5',3,4);
- ]], {
- -- <intpkey-13.3>
- 1, "datatype mismatch"
- -- </intpkey-13.3>
- })
-
- test:do_catchsql_test(
- "intpkey-13.4",
- [[
- INSERT INTO t1 VALUES(x'123456',3,4);
- ]], {
- -- <intpkey-13.4>
- 1, "datatype mismatch"
- -- </intpkey-13.4>
- })
-
-
-
-end
-test:do_catchsql_test(
- "intpkey-13.5",
- [[
- INSERT INTO t1 VALUES('+1234567890',3,4);
- ]], {
- -- <intpkey-13.5>
- 0
- -- </intpkey-13.5>
- })
-
--- Compare an INTEGER PRIMARY KEY against a TEXT expression. The INTEGER
--- affinity should be applied to the text value before the comparison
--- takes place.
---
-test:do_execsql_test(
- "intpkey-14.1",
- [[
- CREATE TABLE t3(a INTEGER PRIMARY KEY, b INTEGER, c TEXT);
- INSERT INTO t3 VALUES(1, 1, 'one');
- INSERT INTO t3 VALUES(2, 2, '2');
- INSERT INTO t3 VALUES(3, 3, 3);
- ]], {
- -- <intpkey-14.1>
-
- -- </intpkey-14.1>
- })
-
-test:do_execsql_test(
- "intpkey-14.2",
- [[
- SELECT * FROM t3 WHERE a>2;
- ]], {
- -- <intpkey-14.2>
- 3, 3, "3"
- -- </intpkey-14.2>
- })
-
-test:do_execsql_test(
- "intpkey-14.3",
- [[
- SELECT * FROM t3 WHERE a>'2';
- ]], {
- -- <intpkey-14.3>
- 3, 3, "3"
- -- </intpkey-14.3>
- })
-
-test:do_execsql_test(
- "intpkey-14.4",
- [[
- SELECT * FROM t3 WHERE a<'2';
- ]], {
- -- <intpkey-14.4>
- 1, 1, "one"
- -- </intpkey-14.4>
- })
-
-test:do_execsql_test(
- "intpkey-14.5",
- [[
- SELECT * FROM t3 WHERE a<c;
- ]], {
- -- <intpkey-14.5>
- 1, 1, "one"
- -- </intpkey-14.5>
- })
-
-test:do_execsql_test(
- "intpkey-14.6",
- [[
- SELECT * FROM t3 WHERE a=c;
- ]], {
- -- <intpkey-14.6>
- 2, 2, "2", 3, 3, "3"
- -- </intpkey-14.6>
- })
-
-- Check for proper handling of primary keys greater than 2^31.
-- Ticket #1188
--
test:do_execsql_test(
"intpkey-15.1",
[[
- INSERT INTO t1 VALUES(2147483647, 'big-1', 123);
+ INSERT INTO t1 VALUES(2147483647, 'big-1', '123');
SELECT * FROM t1 WHERE a>2147483648;
]], {
-- <intpkey-15.1>
diff --git a/test/sql-tap/limit.test.lua b/test/sql-tap/limit.test.lua
index 870233942..a7d1451f7 100755
--- a/test/sql-tap/limit.test.lua
+++ b/test/sql-tap/limit.test.lua
@@ -441,7 +441,7 @@ test:do_catchsql_test(
test:do_execsql_test(
"limit-6.5.2",
[[
- SELECT * FROM t6 LIMIT '12'
+ SELECT * FROM t6 LIMIT 12
]], {
-- <limit-6.5>
1, 2, 3, 4
diff --git a/test/sql-tap/minmax2.test.lua b/test/sql-tap/minmax2.test.lua
index 0e0f0d08e..707d1c4da 100755
--- a/test/sql-tap/minmax2.test.lua
+++ b/test/sql-tap/minmax2.test.lua
@@ -441,9 +441,9 @@ test:do_execsql_test(
"minmax2-8.2",
[[
CREATE TABLE t5(a INTEGER PRIMARY KEY);
- INSERT INTO t5 VALUES('1234');
- INSERT INTO t5 VALUES('234');
- INSERT INTO t5 VALUES('34');
+ INSERT INTO t5 VALUES(1234);
+ INSERT INTO t5 VALUES(234);
+ INSERT INTO t5 VALUES(34);
SELECT min(a), max(a) FROM t5;
]], {
-- <minmax2-8.2>
diff --git a/test/sql-tap/misc1.test.lua b/test/sql-tap/misc1.test.lua
index 32f38cc97..c0136d04c 100755
--- a/test/sql-tap/misc1.test.lua
+++ b/test/sql-tap/misc1.test.lua
@@ -34,9 +34,9 @@ test:do_test(
end
cmd = cmd .. ")"
test:execsql(cmd)
- cmd = "INSERT INTO manycol VALUES(1, 0"
+ cmd = "INSERT INTO manycol VALUES(1, '0'"
for i = 1, 99, 1 do
- cmd = cmd .. ","..i..""
+ cmd = cmd .. ",'"..i.."'"
end
cmd = cmd .. ")"
test:execsql(cmd)
@@ -61,9 +61,9 @@ test:do_test(
"misc1-1.3.1",
function()
for j = 100, 1000, 100 do
- local cmd = string.format("INSERT INTO manycol VALUES(%s, %s", j, j)
+ local cmd = string.format("INSERT INTO manycol VALUES(%s, '%s'", j, j)
for i = 1, 99, 1 do
- cmd = cmd .. ","..(i + j)..""
+ cmd = cmd .. ",'"..(i + j).."'"
end
cmd = cmd .. ")"
test:execsql(cmd)
@@ -176,7 +176,7 @@ test:do_test(
"misc1-2.1",
function()
test:execsql([[
- CREATE TABLE agger(one text primary key, two text, three text, four text);
+ CREATE TABLE agger(one int primary key, two text, three text, four text);
START TRANSACTION;
INSERT INTO agger VALUES(1, 'one', 'hello', 'yes');
INSERT INTO agger VALUES(2, 'two', 'howdy', 'no');
@@ -531,7 +531,7 @@ test:do_test(
"misc1-10.7",
function()
where = string.gsub(where, "x0=0", "x0=100")
- return test:catchsql("UPDATE manycol SET x1=x1+1 "..where.."")
+ return test:catchsql("UPDATE manycol SET x1=CAST(x1+1 AS STRING) "..where.."")
end, {
-- <misc1-10.7>
0
@@ -553,7 +553,7 @@ test:do_execsql_test(
-- } {0 {}}
test:do_execsql_test(
"misc1-10.9",
- "UPDATE manycol SET x1=x1+1 "..where
+ "UPDATE manycol SET x1=CAST(x1+1 AS STRING) "..where
--"UPDATE manycol SET x1=x1+1 $::where AND rowid>0"
, {})
@@ -665,7 +665,7 @@ test:do_execsql_test(
test:do_execsql_test(
"misc1-12.6",
[[
- INSERT OR IGNORE INTO t6 VALUES('y',0);
+ INSERT OR IGNORE INTO t6 VALUES('y','0');
SELECT * FROM t6;
]], {
-- <misc1-12.6>
@@ -679,10 +679,10 @@ test:do_execsql_test(
"misc1-12.7",
[[
CREATE TABLE t7(x INTEGER, y TEXT, z INT primary key);
- INSERT INTO t7 VALUES(0,0,1);
- INSERT INTO t7 VALUES(0.0,0,2);
- INSERT INTO t7 VALUES(0,0.0,3);
- INSERT INTO t7 VALUES(0.0,0.0,4);
+ INSERT INTO t7 VALUES(0,'0',1);
+ INSERT INTO t7 VALUES(0.0,'0',2);
+ INSERT INTO t7 VALUES(0,'0.0',3);
+ INSERT INTO t7 VALUES(0.0,'0.0',4);
SELECT DISTINCT x, y FROM t7 ORDER BY z;
]], {
-- <misc1-12.7>
diff --git a/test/sql-tap/numcast.test.lua b/test/sql-tap/numcast.test.lua
index eeac5353a..3161e48fa 100755
--- a/test/sql-tap/numcast.test.lua
+++ b/test/sql-tap/numcast.test.lua
@@ -135,16 +135,16 @@ test:do_catchsql_test(
INSERT INTO t VALUES(20000000000000000000.01);
SELECT * FROM t;
]], {
- 1,"Tuple field 1 type does not match one required by operation: expected integer"
+ 1,"Type mismatch: can not convert 2.0e+19 to integer"
})
-test:do_catchsql_test(
+test:do_execsql_test(
"cast-2.9",
[[
INSERT INTO t VALUES(2.1);
SELECT * FROM t;
]], {
- 1,"Tuple field 1 type does not match one required by operation: expected integer"
+ 2, 9223372036854775808ULL, 18000000000000000000ULL
})
--
diff --git a/test/sql-tap/select1.test.lua b/test/sql-tap/select1.test.lua
index 6248abb65..0570dc49f 100755
--- a/test/sql-tap/select1.test.lua
+++ b/test/sql-tap/select1.test.lua
@@ -231,7 +231,7 @@ string.format([[
CREATE TABLE t3(id INT, a TEXT, b TEXT, PRIMARY KEY(id));
INSERT INTO t3 VALUES(1, 'abc',NULL);
INSERT INTO t3 VALUES(2, NULL,'xyz');
- INSERT INTO t3 SELECT f1, * FROM test1;
+ INSERT INTO t3 SELECT f1, CAST(f1 AS STRING), CAST(f2 AS STRING) FROM test1;
DROP TABLE IF EXISTS t4;
CREATE TABLE t4(id INT, a INT , b TEXT , PRIMARY KEY(id));
INSERT INTO t4 VALUES(1, NULL,'%s');
@@ -1671,8 +1671,8 @@ test:do_execsql_test(
[[
DELETE FROM t3;
DELETE FROM t4;
- INSERT INTO t3 VALUES(0,1,2);
- INSERT INTO t4 VALUES(0,3,4);
+ INSERT INTO t3 VALUES(0,'1','2');
+ INSERT INTO t4 VALUES(0,3,'4');
SELECT * FROM t3, t4;
]], {
-- <select1-11.1>
@@ -1878,7 +1878,7 @@ test:do_execsql_test(
"select1-12.4",
[[
DELETE FROM t3;
- INSERT INTO t3 VALUES(0,1,2);
+ INSERT INTO t3 VALUES(0,'1','2');
]], {
-- <select1-12.4>
diff --git a/test/sql-tap/select4.test.lua b/test/sql-tap/select4.test.lua
index 23cf1bf1b..f7a320438 100755
--- a/test/sql-tap/select4.test.lua
+++ b/test/sql-tap/select4.test.lua
@@ -761,12 +761,12 @@ test:do_test(
test:execsql [[
CREATE TABLE t3(a text primary key, b NUMBER, c text);
START TRANSACTION;
- INSERT INTO t3 VALUES(1, 1.1, '1.1');
- INSERT INTO t3 VALUES(2, 1.10, '1.10');
- INSERT INTO t3 VALUES(3, 1.10, '1.1');
- INSERT INTO t3 VALUES(4, 1.1, '1.10');
- INSERT INTO t3 VALUES(5, 1.2, '1.2');
- INSERT INTO t3 VALUES(6, 1.3, '1.3');
+ INSERT INTO t3 VALUES('1', 1.1, '1.1');
+ INSERT INTO t3 VALUES('2', 1.10, '1.10');
+ INSERT INTO t3 VALUES('3', 1.10, '1.1');
+ INSERT INTO t3 VALUES('4', 1.1, '1.10');
+ INSERT INTO t3 VALUES('5', 1.2, '1.2');
+ INSERT INTO t3 VALUES('6', 1.3, '1.3');
COMMIT;
]]
return test:execsql [[
diff --git a/test/sql-tap/select7.test.lua b/test/sql-tap/select7.test.lua
index fec5d7a41..e1e43c557 100755
--- a/test/sql-tap/select7.test.lua
+++ b/test/sql-tap/select7.test.lua
@@ -255,7 +255,7 @@ test:do_execsql_test(
[[
DROP TABLE IF EXISTS t5;
CREATE TABLE t5(a TEXT primary key, b INT);
- INSERT INTO t5 VALUES(123, 456);
+ INSERT INTO t5 VALUES('123', 456);
SELECT typeof(a), a FROM t5 GROUP BY a HAVING a<b;
]], {
-- <select7-7.7>
diff --git a/test/sql-tap/sort.test.lua b/test/sql-tap/sort.test.lua
index 36074d6ef..18bfd443d 100755
--- a/test/sql-tap/sort.test.lua
+++ b/test/sql-tap/sort.test.lua
@@ -505,10 +505,10 @@ test:do_execsql_test(
a INTEGER PRIMARY KEY,
b VARCHAR(30)
);
- INSERT INTO t4 VALUES(1,1);
- INSERT INTO t4 VALUES(2,2);
- INSERT INTO t4 VALUES(11,11);
- INSERT INTO t4 VALUES(12,12);
+ INSERT INTO t4 VALUES(1,'1');
+ INSERT INTO t4 VALUES(2,'2');
+ INSERT INTO t4 VALUES(11,'11');
+ INSERT INTO t4 VALUES(12,'12');
SELECT a FROM t4 ORDER BY 1;
]], {
-- <sort-7.1>
diff --git a/test/sql-tap/subquery.test.lua b/test/sql-tap/subquery.test.lua
index 15c4c8276..e0771825e 100755
--- a/test/sql-tap/subquery.test.lua
+++ b/test/sql-tap/subquery.test.lua
@@ -1,6 +1,6 @@
#!/usr/bin/env tarantool
test = require("sqltester")
-test:plan(73)
+test:plan(69)
--!./tcltestrunner.lua
-- 2005 January 19
@@ -335,73 +335,6 @@ test:do_execsql_test(
-- </subquery-2.4.3>
})
-test:do_execsql_test(
- "subquery-2.5.1",
- [[
- CREATE TABLE t3(a INTEGER PRIMARY KEY);
- INSERT INTO t3 VALUES(10);
-
- CREATE TABLE t4(x TEXT PRIMARY KEY);
- INSERT INTO t4 VALUES('10');
- ]], {
- -- <subquery-2.5.1>
-
- -- </subquery-2.5.1>
- })
-
-test:do_test(
- "subquery-2.5.2",
- function()
- -- In the expr "x IN (SELECT a FROM t3)" the RHS of the IN operator
- -- has text affinity and the LHS has integer affinity. The rule is
- -- that we try to convert both sides to an integer before doing the
- -- comparision. Hence, the integer value 10 in t3 will compare equal
- -- to the string value '10.0' in t4 because the t4 value will be
- -- converted into an integer.
- return test:execsql [[
- SELECT * FROM t4 WHERE x IN (SELECT a FROM t3);
- ]]
- end, {
- -- <subquery-2.5.2>
- "10"
- -- </subquery-2.5.2>
- })
-
-test:do_test(
- "subquery-2.5.3.1",
- function()
- -- The t4i index cannot be used to resolve the "x IN (...)" constraint
- -- because the constraint has integer affinity but t4i has text affinity.
- return test:execsql [[
- CREATE INDEX t4i ON t4(x);
- SELECT * FROM t4 WHERE x IN (SELECT a FROM t3);
- ]]
- end, {
- -- <subquery-2.5.3.1>
- "10"
- -- </subquery-2.5.3.1>
- })
-
--- Tarantool: no-rowid is implied for the table, so query plan contains
--- scan over t4i. Verified w/ vanilla sql. Comment this case
---do_test subquery-2.5.3.2 {
--- Verify that the t4i index was not used in the previous query
--- execsql {
--- EXPLAIN QUERY PLAN
--- SELECT * FROM t4 WHERE x IN (SELECT a FROM t3);
--- }
---} {~/t4i/}
-test:do_execsql_test(
- "subquery-2.5.4",
- [[
- DROP TABLE t3;
- DROP TABLE t4;
- ]], {
- -- <subquery-2.5.4>
-
- -- </subquery-2.5.4>
- })
-
--------------------------------------------------------------------
-- The following test cases - subquery-3.* - test tickets that
-- were raised during development of correlated subqueries.
diff --git a/test/sql-tap/tkt-3998683a16.test.lua b/test/sql-tap/tkt-3998683a16.test.lua
index 885dcf5cd..9bc310358 100755
--- a/test/sql-tap/tkt-3998683a16.test.lua
+++ b/test/sql-tap/tkt-3998683a16.test.lua
@@ -26,29 +26,17 @@ test:do_test(
function()
return test:execsql [[
CREATE TABLE t1(x INT primary key, y NUMBER);
- INSERT INTO t1 VALUES(1, '1.0');
- INSERT INTO t1 VALUES(2, '.125');
- INSERT INTO t1 VALUES(3, '123.');
- INSERT INTO t1 VALUES(4, '123.e+2');
- INSERT INTO t1 VALUES(5, '.125e+3');
- INSERT INTO t1 VALUES(6, '123e4');
- INSERT INTO t1 VALUES(11, ' 1.0');
- INSERT INTO t1 VALUES(12, ' .125');
- INSERT INTO t1 VALUES(13, ' 123.');
- INSERT INTO t1 VALUES(14, ' 123.e+2');
- INSERT INTO t1 VALUES(15, ' .125e+3');
- INSERT INTO t1 VALUES(16, ' 123e4');
- INSERT INTO t1 VALUES(21, '1.0 ');
- INSERT INTO t1 VALUES(22, '.125 ');
- INSERT INTO t1 VALUES(23, '123. ');
- INSERT INTO t1 VALUES(24, '123.e+2 ');
- INSERT INTO t1 VALUES(25, '.125e+3 ');
- INSERT INTO t1 VALUES(26, '123e4 ');
+ INSERT INTO t1 VALUES(1, 1.0);
+ INSERT INTO t1 VALUES(2, .125);
+ INSERT INTO t1 VALUES(3, 123.);
+ INSERT INTO t1 VALUES(4, 123.e+2);
+ INSERT INTO t1 VALUES(5, .125e+3);
+ INSERT INTO t1 VALUES(6, 123e4);
SELECT x FROM t1 WHERE typeof(y)=='number' ORDER BY x;
]]
end, {
-- <tkt-3998683a16.1>
- 1, 2, 3, 4, 5, 6, 11, 12, 13, 14, 15, 16, 21, 22, 23, 24, 25, 26
+ 1, 2, 3, 4, 5, 6
-- </tkt-3998683a16.1>
})
diff --git a/test/sql-tap/tkt-54844eea3f.test.lua b/test/sql-tap/tkt-54844eea3f.test.lua
index d6cd56e52..89d0d1218 100755
--- a/test/sql-tap/tkt-54844eea3f.test.lua
+++ b/test/sql-tap/tkt-54844eea3f.test.lua
@@ -62,10 +62,10 @@ test:do_execsql_test(
"1.2",
[[
CREATE TABLE t4(id INT primary key, a TEXT, b TEXT, c TEXT);
- INSERT INTO t4 VALUES(1, 'a', 1, 'one');
- INSERT INTO t4 VALUES(2, 'a', 2, 'two');
- INSERT INTO t4 VALUES(3, 'b', 1, 'three');
- INSERT INTO t4 VALUES(4, 'b', 2, 'four');
+ INSERT INTO t4 VALUES(1, 'a', '1', 'one');
+ INSERT INTO t4 VALUES(2, 'a', '2', 'two');
+ INSERT INTO t4 VALUES(3, 'b', '1', 'three');
+ INSERT INTO t4 VALUES(4, 'b', '2', 'four');
SELECT (
SELECT c FROM (
SELECT a,b,c FROM t4 WHERE a=output.a ORDER BY b LIMIT 10 OFFSET 1
diff --git a/test/sql-tap/tkt-7bbfb7d442.test.lua b/test/sql-tap/tkt-7bbfb7d442.test.lua
index 535303771..bfddcd920 100755
--- a/test/sql-tap/tkt-7bbfb7d442.test.lua
+++ b/test/sql-tap/tkt-7bbfb7d442.test.lua
@@ -109,13 +109,13 @@ if (1 > 0)
T1.Variant AS Variant,
T1.ControlDate AS ControlDate,
1 AS ControlState,
- COALESCE(T2.DeliveredQty,0) AS DeliveredQty
+ CAST(COALESCE(T2.DeliveredQty,0) AS STRING) AS DeliveredQty
FROM (
SELECT
NEW.InventoryControlId AS InventoryControlId,
II.SKU AS SKU,
II.Variant AS Variant,
- COALESCE(LastClosedIC.ControlDate,NEW.ControlDate) AS ControlDate
+ CAST(COALESCE(LastClosedIC.ControlDate,NEW.ControlDate) AS STRING) AS ControlDate
FROM
InventoryItem II
LEFT JOIN
diff --git a/test/sql-tap/tkt-9a8b09f8e6.test.lua b/test/sql-tap/tkt-9a8b09f8e6.test.lua
index cb5348ab4..ca3a5427a 100755
--- a/test/sql-tap/tkt-9a8b09f8e6.test.lua
+++ b/test/sql-tap/tkt-9a8b09f8e6.test.lua
@@ -1,6 +1,6 @@
#!/usr/bin/env tarantool
test = require("sqltester")
-test:plan(49)
+test:plan(47)
--!./tcltestrunner.lua
-- 2014 June 26
@@ -193,16 +193,6 @@ test:do_execsql_test(
-- </3.3>
})
-test:do_execsql_test(
- 3.4,
- [[
- SELECT x FROM t2 WHERE x IN ('1');
- ]], {
- -- <3.4>
- 1
- -- </3.4>
- })
-
test:do_execsql_test(
3.5,
[[
@@ -233,16 +223,6 @@ test:do_execsql_test(
-- </3.7>
})
-test:do_execsql_test(
- 3.8,
- [[
- SELECT x FROM t2 WHERE '1' IN (x);
- ]], {
- -- <3.8>
- 1
- -- </3.8>
- })
-
test:do_execsql_test(
4.1,
[[
@@ -263,23 +243,23 @@ test:do_execsql_test(
-- </4.2>
})
-test:do_execsql_test(
+test:do_catchsql_test(
4.3,
[[
SELECT x FROM t3 WHERE x IN ('1');
]], {
-- <4.3>
- 1.0
+ 1, "Type mismatch: can not convert 1 to number"
-- </4.3>
})
-test:do_execsql_test(
+test:do_catchsql_test(
4.4,
[[
SELECT x FROM t3 WHERE x IN ('1.0');
]], {
-- <4.4>
- 1.0
+ 1, "Type mismatch: can not convert 1.0 to number"
-- </4.4>
})
@@ -303,23 +283,23 @@ test:do_execsql_test(
-- </4.6>
})
-test:do_execsql_test(
+test:do_catchsql_test(
4.7,
[[
SELECT x FROM t3 WHERE '1' IN (x);
]], {
-- <4.7>
- 1
+ 1, "Type mismatch: can not convert 1 to number"
-- </4.7>
})
-test:do_execsql_test(
+test:do_catchsql_test(
4.8,
[[
SELECT x FROM t3 WHERE '1.0' IN (x);
]], {
-- <4.8>
- 1
+ 1, "Type mismatch: can not convert 1.0 to number"
-- </4.8>
})
@@ -343,23 +323,23 @@ test:do_execsql_test(
-- </5.2>
})
-test:do_execsql_test(
+test:do_catchsql_test(
5.3,
[[
SELECT x FROM t4 WHERE x IN ('1');
]], {
-- <5.3>
-
+ 1, "Type mismatch: can not convert 1 to number"
-- </5.3>
})
-test:do_execsql_test(
+test:do_catchsql_test(
5.4,
[[
SELECT x FROM t4 WHERE x IN ('1.0');
]], {
-- <5.4>
-
+ 1, "Type mismatch: can not convert 1.0 to number"
-- </5.4>
})
@@ -373,13 +353,13 @@ test:do_execsql_test(
-- </5.5>
})
-test:do_execsql_test(
+test:do_catchsql_test(
5.6,
[[
SELECT x FROM t4 WHERE x IN ('1.11');
]], {
-- <5.6>
- 1.11
+ 1, "Type mismatch: can not convert 1.11 to number"
-- </5.6>
})
@@ -403,23 +383,23 @@ test:do_execsql_test(
-- </5.8>
})
-test:do_execsql_test(
+test:do_catchsql_test(
5.9,
[[
SELECT x FROM t4 WHERE '1' IN (x);
]], {
-- <5.9>
-
+ 1, "Type mismatch: can not convert 1 to number"
-- </5.9>
})
-test:do_execsql_test(
+test:do_catchsql_test(
5.10,
[[
SELECT x FROM t4 WHERE '1.0' IN (x);
]], {
-- <5.10>
-
+ 1, "Type mismatch: can not convert 1.0 to number"
-- </5.10>
})
@@ -433,13 +413,13 @@ test:do_execsql_test(
-- </5.11>
})
-test:do_execsql_test(
+test:do_catchsql_test(
5.12,
[[
SELECT x FROM t4 WHERE '1.11' IN (x);
]], {
-- <5.12>
- 1.11
+ 1, "Type mismatch: can not convert 1.11 to number"
-- </5.12>
})
diff --git a/test/sql-tap/tkt-f973c7ac31.test.lua b/test/sql-tap/tkt-f973c7ac31.test.lua
index 82bdb52f8..381f29c65 100755
--- a/test/sql-tap/tkt-f973c7ac31.test.lua
+++ b/test/sql-tap/tkt-f973c7ac31.test.lua
@@ -39,9 +39,8 @@ for tn, sql in ipairs(sqls) do
test:do_execsql_test(
"tkt-f973c7ac3-1."..tn..".1",
[[
- SELECT c1,c2 FROM t WHERE c1 = 5 AND c2>0 AND c2<='2' ORDER BY c2 DESC
+ SELECT c1,c2 FROM t WHERE c1 = 5 AND c2>0 AND CAST(c2 AS STRING)<='2' ORDER BY c2 DESC
]], {
-
})
test:do_execsql_test(
@@ -55,7 +54,7 @@ for tn, sql in ipairs(sqls) do
test:do_execsql_test(
"tkt-f973c7ac3-1."..tn..".3",
[[
- SELECT c1,c2 FROM t WHERE c1 = 5 AND c2>0 AND c2<='5' ORDER BY c2 DESC
+ SELECT c1,c2 FROM t WHERE c1 = 5 AND c2>0 AND CAST(c2 AS STRING)<='5' ORDER BY c2 DESC
]], {
5, 5, 5, 4
})
@@ -63,7 +62,7 @@ for tn, sql in ipairs(sqls) do
test:do_execsql_test(
"tkt-f973c7ac3-1."..tn..".4",
[[
- SELECT c1,c2 FROM t WHERE c1 = 5 AND c2>'0' AND c2<=5 ORDER BY c2 DESC
+ SELECT c1,c2 FROM t WHERE c1 = 5 AND CAST(c2 AS STRING)>'0' AND c2<=5 ORDER BY c2 DESC
]], {
5, 5, 5, 4
})
@@ -71,7 +70,7 @@ for tn, sql in ipairs(sqls) do
test:do_execsql_test(
"tkt-f973c7ac3-1."..tn..".5",
[[
- SELECT c1,c2 FROM t WHERE c1 = 5 AND c2>'0' AND c2<='5' ORDER BY c2 DESC
+ SELECT c1,c2 FROM t WHERE c1 = 5 AND CAST(c2 AS STRING)>'0' AND CAST(c2 AS STRING)<='5' ORDER BY c2 DESC
]], {
5, 5, 5, 4
})
@@ -79,9 +78,8 @@ for tn, sql in ipairs(sqls) do
test:do_execsql_test(
"tkt-f973c7ac3-1."..tn..".6",
[[
- SELECT c1,c2 FROM t WHERE c1 = 5 AND c2>0 AND c2<='2' ORDER BY c2 ASC
+ SELECT c1,c2 FROM t WHERE c1 = 5 AND c2>0 AND CAST(c2 AS STRING)<='2' ORDER BY c2 ASC
]], {
-
})
test:do_execsql_test(
@@ -95,7 +93,7 @@ for tn, sql in ipairs(sqls) do
test:do_execsql_test(
"tkt-f973c7ac3-1."..tn..".8",
[[
- SELECT c1,c2 FROM t WHERE c1 = 5 AND c2>0 AND c2<='5' ORDER BY c2 ASC
+ SELECT c1,c2 FROM t WHERE c1 = 5 AND c2>0 AND CAST(c2 AS STRING)<='5' ORDER BY c2 ASC
]], {
5, 4, 5, 5
})
@@ -103,7 +101,7 @@ for tn, sql in ipairs(sqls) do
test:do_execsql_test(
"tkt-f973c7ac3-1."..tn..".9",
[[
- SELECT c1,c2 FROM t WHERE c1 = 5 AND c2>'0' AND c2<=5 ORDER BY c2 ASC
+ SELECT c1,c2 FROM t WHERE c1 = 5 AND CAST(c2 AS STRING)>'0' AND c2<=5 ORDER BY c2 ASC
]], {
5, 4, 5, 5
})
@@ -111,7 +109,7 @@ for tn, sql in ipairs(sqls) do
test:do_execsql_test(
"tkt-f973c7ac3-1."..tn..".10",
[[
- SELECT c1,c2 FROM t WHERE c1 = 5 AND c2>'0' AND c2<='5' ORDER BY c2 ASC
+ SELECT c1,c2 FROM t WHERE c1 = 5 AND CAST(c2 AS STRING)>'0' AND CAST(c2 AS STRING)<='5' ORDER BY c2 ASC
]], {
5, 4, 5, 5
})
diff --git a/test/sql-tap/tkt-fc7bd6358f.test.lua b/test/sql-tap/tkt-fc7bd6358f.test.lua
deleted file mode 100755
index fe5d6200f..000000000
--- a/test/sql-tap/tkt-fc7bd6358f.test.lua
+++ /dev/null
@@ -1,111 +0,0 @@
-#!/usr/bin/env tarantool
-test = require("sqltester")
-test:plan(50)
-
---!./tcltestrunner.lua
--- 2013 March 05
---
--- The author disclaims copyright to this source code. In place of
--- a legal notice, here is a blessing:
---
--- May you do good and not evil.
--- May you find forgiveness for yourself and forgive others.
--- May you share freely, never taking more than you give.
---
--------------------------------------------------------------------------
--- This file implements regression tests for sql library. Specifically,
--- it tests that ticket [fc7bd6358f]:
---
--- The following SQL yields an incorrect result (zero rows) in all
--- versions of sql between 3.6.14 and 3.7.15.2:
---
--- CREATE TABLE t(textid TEXT);
--- INSERT INTO t VALUES('12');
--- INSERT INTO t VALUES('34');
--- CREATE TABLE i(intid INTEGER PRIMARY KEY);
--- INSERT INTO i VALUES(12);
--- INSERT INTO i VALUES(34);
---
--- SELECT t1.textid AS a, i.intid AS b, t2.textid AS c
--- FROM t t1, i, t t2
--- WHERE t1.textid = i.intid
--- AND t1.textid = t2.textid;
---
--- The correct result should be two rows, one with 12|12|12 and the other
--- with 34|34|34. With this bug, no rows are returned. Bisecting shows that
--- this bug was introduced with check-in [dd4d67a67454] on 2009-04-23.
---
--- ["set","testdir",[["file","dirname",["argv0"]]]]
--- ["source",[["testdir"],"\/tester.tcl"]]
-test:do_test(
- "tkt-fc7bd6358f.100",
- function()
- return test:execsql [[
- CREATE TABLE t(textid TEXT PRIMARY KEY);
- INSERT INTO t VALUES('12');
- INSERT INTO t VALUES('34');
- CREATE TABLE i(intid INTEGER PRIMARY KEY);
- INSERT INTO i VALUES(12);
- INSERT INTO i VALUES(34);
- ]]
- end, {
- -- <tkt-fc7bd6358f.100>
-
- -- </tkt-fc7bd6358f.100>
- })
-
--- ["unset","-nocomplain","from"]
--- ["unset","-nocomplain","where"]
--- ["unset","-nocomplain","a"]
--- ["unset","-nocomplain","b"]
-local froms = {
- "FROM t t1, i, t t2",
- "FROM i, t t1, t t2",
- "FROM t t1, t t2, i",
-}
-local wheres = {
- "WHERE t1.textid=i.intid AND t1.textid=t2.textid",
- "WHERE i.intid=t1.textid AND t1.textid=t2.textid",
- "WHERE t1.textid=i.intid AND i.intid=t2.textid",
- "WHERE t1.textid=i.intid AND t2.textid=i.intid",
- "WHERE i.intid=t1.textid AND i.intid=t2.textid",
- "WHERE i.intid=t1.textid AND t2.textid=i.intid",
- "WHERE t1.textid=t2.textid AND i.intid=t2.textid",
- "WHERE t1.textid=t2.textid AND t2.textid=i.intid",
-}
-for a, from in ipairs(froms) do
- for b, where in ipairs(wheres) do
- test:do_test(
- string.format("tkt-fc7bd6358f.110.%s.%s.1", a, b),
- function()
- return test:execsql(string.format("SELECT t1.textid, i.intid, t2.textid %s %s", from, where))
- end, {
- "12", 12, "12", "34", 34, "34"
- })
-
- test:do_test(
- string.format("tkt-fc7bd6358f.110.%s.%s.2", a, b),
- function()
- return test:execsql(string.format("SELECT t1.textid, i.intid, t2.textid %s %s", from, where))
- end, {
- "12", 12, "12", "34", 34, "34"
- })
-
- end
-end
-
-test:do_test(
- "tkt-fc7bd6358f.200",
- function()
- return test:execsql [[
- DROP TABLE t;
- DROP TABLE i;
- ]]
- end, {
- -- <tkt-fc7bd6358f.100>
-
- -- </tkt-fc7bd6358f.100>
- })
-
-test:finish_test()
-
diff --git a/test/sql-tap/tkt1444.test.lua b/test/sql-tap/tkt1444.test.lua
index 82a5ded25..fb148bc5f 100755
--- a/test/sql-tap/tkt1444.test.lua
+++ b/test/sql-tap/tkt1444.test.lua
@@ -30,8 +30,8 @@ test:do_execsql_test(
[[
CREATE TABLE DemoTable (id INT primary key, x INTEGER, TextKey TEXT, DKey NUMBER);
CREATE INDEX DemoTableIdx ON DemoTable (TextKey);
- INSERT INTO DemoTable VALUES(1, 9,8,7);
- INSERT INTO DemoTable VALUES(2, 1,2,3);
+ INSERT INTO DemoTable VALUES(1, 9,'8',7);
+ INSERT INTO DemoTable VALUES(2, 1,'2',3);
CREATE VIEW DemoView AS SELECT x, TextKey, DKey FROM DemoTable ORDER BY TextKey;
SELECT x,TextKey,DKey FROM DemoTable UNION ALL SELECT * FROM DemoView ORDER BY 1;
]], {
diff --git a/test/sql-tap/tkt3493.test.lua b/test/sql-tap/tkt3493.test.lua
index 7ceec4702..de77e61e9 100755
--- a/test/sql-tap/tkt3493.test.lua
+++ b/test/sql-tap/tkt3493.test.lua
@@ -29,8 +29,8 @@ test:do_execsql_test(
START TRANSACTION;
INSERT INTO A VALUES(1,'123');
INSERT INTO A VALUES(2,'456');
- INSERT INTO B VALUES(1,1);
- INSERT INTO B VALUES(2,2);
+ INSERT INTO B VALUES(1,'1');
+ INSERT INTO B VALUES(2,'2');
INSERT INTO A_B VALUES(1,1);
INSERT INTO A_B VALUES(2,2);
COMMIT;
@@ -116,7 +116,7 @@ test:do_execsql_test(
"tkt3493-2.1",
[[
CREATE TABLE t1(a TEXT PRIMARY KEY, b INT);
- INSERT INTO t1 VALUES(123, 456);
+ INSERT INTO t1 VALUES('123', 456);
]], {
-- <tkt3493-2.1>
diff --git a/test/sql-tap/tkt3841.test.lua b/test/sql-tap/tkt3841.test.lua
index 5203d0cd4..56668f6a3 100755
--- a/test/sql-tap/tkt3841.test.lua
+++ b/test/sql-tap/tkt3841.test.lua
@@ -31,12 +31,12 @@ test:do_execsql_test(
INSERT INTO table2 VALUES ('a', 'alist');
INSERT INTO table2 VALUES ('b', 'blist');
- INSERT INTO list VALUES ('a', 1);
- INSERT INTO list VALUES ('a', 2);
- INSERT INTO list VALUES ('a', 3);
- INSERT INTO list VALUES ('b', 4);
- INSERT INTO list VALUES ('b', 5);
- INSERT INTO list VALUES ('b', 6);
+ INSERT INTO list VALUES ('a', '1');
+ INSERT INTO list VALUES ('a', '2');
+ INSERT INTO list VALUES ('a', '3');
+ INSERT INTO list VALUES ('b', '4');
+ INSERT INTO list VALUES ('b', '5');
+ INSERT INTO list VALUES ('b', '6');
SELECT
table2.x,
diff --git a/test/sql-tap/transitive1.test.lua b/test/sql-tap/transitive1.test.lua
index e96056580..96895b4a7 100755
--- a/test/sql-tap/transitive1.test.lua
+++ b/test/sql-tap/transitive1.test.lua
@@ -338,7 +338,7 @@ test:do_execsql_test(
ON tvshow.idshow = episode.idshow
LEFT JOIN seasons
ON seasons.idshow = episode.idshow
- AND seasons.season = episode.c12
+ AND seasons.season = CAST(episode.c12 AS INTEGER)
JOIN path
ON files.idpath = path.idpath
LEFT JOIN bookmark
@@ -378,7 +378,7 @@ test:do_execsql_test(
FROM episodeview
JOIN tvshowview ON tvshowview.idShow = episodeview.idShow
JOIN seasons ON (seasons.idShow = tvshowview.idShow
- AND seasons.season = episodeview.c12)
+ AND seasons.season = CAST(episodeview.c12 AS INTEGER))
JOIN files ON files.idFile = episodeview.idFile
JOIN tvshowlinkpath ON tvshowlinkpath.idShow = tvshowview.idShow
JOIN path ON path.idPath = tvshowlinkpath.idPath
diff --git a/test/sql-tap/triggerA.test.lua b/test/sql-tap/triggerA.test.lua
index fac51ca14..fc8ecfe17 100755
--- a/test/sql-tap/triggerA.test.lua
+++ b/test/sql-tap/triggerA.test.lua
@@ -283,7 +283,7 @@ test:do_test(
CREATE TABLE result2(id INTEGER PRIMARY KEY, a TEXT,b INT);
CREATE TRIGGER r5d INSTEAD OF DELETE ON v5 FOR EACH ROW BEGIN
INSERT INTO result2(id, a,b) VALUES((SELECT coalesce(max(id),0) + 1 FROM result2),
- old.x, old.b);
+ CAST(old.x AS STRING), old.b);
END;
DELETE FROM v5 WHERE x=5;
SELECT a, b FROM result2;
@@ -301,7 +301,7 @@ test:do_test(
DELETE FROM result4;
CREATE TRIGGER r5u INSTEAD OF UPDATE ON v5 FOR EACH ROW BEGIN
INSERT INTO result4(id, a,b,c,d) VALUES((SELECT coalesce(max(id),0) + 1 FROM result4),
- old.x, old.b, new.x, new.b);
+ CAST(old.x AS STRING), old.b, CAST(new.x AS STRING), new.b);
END;
UPDATE v5 SET b = b+9900000 WHERE x BETWEEN 3 AND 5;
SELECT a,b,c,d FROM result4 ORDER BY a;
diff --git a/test/sql-tap/unique.test.lua b/test/sql-tap/unique.test.lua
index 9818f90a8..6b0a7e20d 100755
--- a/test/sql-tap/unique.test.lua
+++ b/test/sql-tap/unique.test.lua
@@ -52,7 +52,7 @@ test:do_catchsql_test(
test:do_catchsql_test(
"unique-1.2",
[[
- INSERT INTO t1(a,b,c) VALUES(1,2,3)
+ INSERT INTO t1(a,b,c) VALUES(1,2,'3')
]], {
-- <unique-1.2>
0
@@ -62,7 +62,7 @@ test:do_catchsql_test(
test:do_catchsql_test(
"unique-1.3",
[[
- INSERT INTO t1(a,b,c) VALUES(1,3,4)
+ INSERT INTO t1(a,b,c) VALUES(1,3,'4')
]], {
-- <unique-1.3>
1, "Duplicate key exists in unique index 'pk_unnamed_T1_1' in space 'T1'"
@@ -83,7 +83,7 @@ test:do_execsql_test(
test:do_catchsql_test(
"unique-1.5",
[[
- INSERT INTO t1(a,b,c) VALUES(3,2,4)
+ INSERT INTO t1(a,b,c) VALUES(3,2,'4')
]], {
-- <unique-1.5>
1, "Duplicate key exists in unique index 'unique_unnamed_T1_2' in space 'T1'"
@@ -104,7 +104,7 @@ test:do_execsql_test(
test:do_catchsql_test(
"unique-1.7",
[[
- INSERT INTO t1(a,b,c) VALUES(3,4,5)
+ INSERT INTO t1(a,b,c) VALUES(3,4,'5')
]], {
-- <unique-1.7>
0
diff --git a/test/sql-tap/view.test.lua b/test/sql-tap/view.test.lua
index e553b91c7..ab14c5edb 100755
--- a/test/sql-tap/view.test.lua
+++ b/test/sql-tap/view.test.lua
@@ -757,7 +757,7 @@ test:do_execsql_test(
"view-10.1",
[=[
CREATE TABLE t3("9" integer primary key, "4" text);
- INSERT INTO t3 VALUES(1,2);
+ INSERT INTO t3 VALUES(1,'2');
CREATE VIEW v_t3_a AS SELECT a."9" FROM t3 AS a;
CREATE VIEW v_t3_b AS SELECT "4" FROM t3;
SELECT * FROM v_t3_a;
diff --git a/test/sql-tap/where5.test.lua b/test/sql-tap/where5.test.lua
index 749201564..3aefcaca5 100755
--- a/test/sql-tap/where5.test.lua
+++ b/test/sql-tap/where5.test.lua
@@ -27,11 +27,11 @@ test:do_test("where5-1.0", function()
CREATE TABLE t1(x TEXT primary key);
CREATE TABLE t2(x integer primary key);
CREATE TABLE t3(x integer PRIMARY KEY);
- INSERT INTO t1 VALUES(-1);
- INSERT INTO t1 VALUES(0);
- INSERT INTO t1 VALUES(1);
- INSERT INTO t2 SELECT * FROM t1;
- INSERT INTO t3 SELECT * FROM t1;
+ INSERT INTO t1 VALUES('-1');
+ INSERT INTO t1 VALUES('0');
+ INSERT INTO t1 VALUES('1');
+ INSERT INTO t2 SELECT CAST(x AS INTEGER) FROM t1;
+ INSERT INTO t3 SELECT CAST(x AS INTEGER) FROM t1;
]]
return test:execsql [[
SELECT * FROM t1 WHERE x<0
diff --git a/test/sql-tap/whereB.test.lua b/test/sql-tap/whereB.test.lua
deleted file mode 100755
index d98645fdc..000000000
--- a/test/sql-tap/whereB.test.lua
+++ /dev/null
@@ -1,908 +0,0 @@
-#!/usr/bin/env tarantool
-test = require("sqltester")
-test:plan(63)
-
---!./tcltestrunner.lua
--- 2009 August 13
---
--- The author disclaims copyright to this source code. In place of
--- a legal notice, here is a blessing:
---
--- May you do good and not evil.
--- May you find forgiveness for yourself and forgive others.
--- May you share freely, never taking more than you give.
---
--------------------------------------------------------------------------
--- This file implements regression tests for sql library. The
--- focus of this file is testing WHERE clause conditions with
--- subtle affinity issues.
---
--- ["set","testdir",[["file","dirname",["argv0"]]]]
--- ["source",[["testdir"],"\/tester.tcl"]]
--- For this set of tests:
---
--- * t1.y holds an integer value with affinity NONE
--- * t2.b holds a text value with affinity TEXT
---
--- These values are not equal and because neither affinity is NUMERIC
--- no type conversion occurs.
---
-test:do_execsql_test(
- "whereB-1.1",
- [[
- CREATE TABLE t1(x INT primary key,y INT ); -- affinity of t1.y is NONE
- INSERT INTO t1 VALUES(1,99);
-
- CREATE TABLE t2(a INT primary key, b TEXT); -- affinity of t2.b is TEXT
- CREATE INDEX t2b ON t2(b);
- INSERT INTO t2 VALUES(2,'99');
-
- SELECT x, a, y=b FROM t1, t2 ORDER BY +x, +a;
- ]],
- {
- -- <whereB-1.1>
- 1, 2, true
- -- </whereB-1.1>
- })
-
-test:do_execsql_test(
- "whereB-1.2",
- [[
- SELECT x, a, y=b FROM t1, t2 WHERE y=b;
- ]],
- {
- -- <whereB-1.2>
- 1, 2, true
- -- </whereB-1.2>
- })
-
-test:do_execsql_test(
- "whereB-1.3",
- [[
- SELECT x, a, y=b FROM t1, t2 WHERE b=y;
- ]],
- {
- -- <whereB-1.3>
- 1, 2, true
- -- </whereB-1.3>
- })
-
-test:do_execsql_test(
- "whereB-1.4",
- [[
- SELECT x, a, y=b FROM t1, t2 WHERE +y=+b;
- ]],
- {
- -- <whereB-1.4>
- 1, 2, true
- -- </whereB-1.4>
- })
-
-test:do_execsql_test(
- "whereB-1.100",
- [[
- DROP INDEX t2b ON t2;
- SELECT x, a, y=b FROM t1, t2 WHERE y=b;
- ]],
- {
- -- <whereB-1.100>
- 1, 2, true
- -- </whereB-1.100>
- })
-
-test:do_execsql_test(
- "whereB-1.101",
- [[
- SELECT x, a, y=b FROM t1, t2 WHERE b=y;
- ]],
- {
- -- <whereB-1.101>
- 1, 2, true
- -- </whereB-1.101>
- })
-
-test:do_execsql_test(
- "whereB-1.102",
- [[
- SELECT x, a, y=b FROM t1, t2 WHERE +y=+b;
- ]],
- {
- -- <whereB-1.102>
- 1, 2, true
- -- </whereB-1.102>
- })
-
--- For this set of tests:
---
--- * t1.y holds a text value with affinity TEXT
--- * t2.b holds an integer value with affinity NONE
---
--- These values are not equal and because neither affinity is NUMERIC
--- no type conversion occurs.
---
-test:do_execsql_test(
- "whereB-2.1",
- [[
- DROP TABLE t1;
- DROP TABLE t2;
-
- CREATE TABLE t1(x INT primary key, y TEXT); -- affinity of t1.y is TEXT
- INSERT INTO t1 VALUES(1,99);
-
- CREATE TABLE t2(a INT primary key, b SCALAR); -- affinity of t2.b is NONE
- CREATE INDEX t2b ON t2(b);
- INSERT INTO t2 VALUES(2, 99);
-
- SELECT x, a, y=b FROM t1, t2 ORDER BY +x, +a;
- ]],
- {
- -- <whereB-2.1>
- 1, 2, false
- -- </whereB-2.1>
- })
-
-test:do_execsql_test(
- "whereB-2.2",
- [[
- SELECT x, a, y=b FROM t1, t2 WHERE y=b;
- ]],
- {
- -- <whereB-2.2>
-
- -- </whereB-2.2>
- })
-
-test:do_execsql_test(
- "whereB-2.3",
- [[
- SELECT x, a, y=b FROM t1, t2 WHERE b=y;
- ]],
- {
- -- <whereB-2.3>
-
- -- </whereB-2.3>
- })
-
-test:do_execsql_test(
- "whereB-2.4",
- [[
- SELECT x, a, y=b FROM t1, t2 WHERE +y=+b;
- ]],
- {
- -- <whereB-2.4>
-
- -- </whereB-2.4>
- })
-
-test:do_execsql_test(
- "whereB-2.100",
- [[
- DROP INDEX t2b ON t2;
- SELECT x, a, y=b FROM t1, t2 WHERE y=b;
- ]],
- {
- -- <whereB-2.100>
-
- -- </whereB-2.100>
- })
-
-test:do_execsql_test(
- "whereB-2.101",
- [[
- SELECT x, a, y=b FROM t1, t2 WHERE b=y;
- ]],
- {
- -- <whereB-2.101>
-
- -- </whereB-2.101>
- })
-
-test:do_execsql_test(
- "whereB-2.102",
- [[
- SELECT x, a, y=b FROM t1, t2 WHERE +y=+b;
- ]],
- {
- -- <whereB-2.102>
-
- -- </whereB-2.102>
- })
-
--- For this set of tests:
---
--- * t1.y holds a text value with affinity NONE
--- * t2.b holds an integer value with affinity NONE
---
--- These values are not equal and because neither affinity is NUMERIC
--- no type conversion occurs.
---
-test:do_execsql_test(
- "whereB-3.1",
- [[
- DROP TABLE t1;
- DROP TABLE t2;
-
- CREATE TABLE t1(x INT primary key, y SCALAR); -- affinity of t1.y is NONE
- INSERT INTO t1 VALUES(1,99);
-
- CREATE TABLE t2(a INT primary key, b SCALAR); -- affinity of t2.b is NONE
- CREATE INDEX t2b ON t2(b);
- INSERT INTO t2 VALUES(2,'99');
-
- SELECT x, a, y=b FROM t1, t2;
- ]],
- {
- -- <whereB-3.1>
- 1, 2, false
- -- </whereB-3.1>
- })
-
-test:do_execsql_test(
- "whereB-3.2",
- [[
- SELECT x, a, y=b FROM t1, t2 WHERE y=b;
- ]],
- {
- -- <whereB-3.2>
-
- -- </whereB-3.2>
- })
-
-test:do_execsql_test(
- "whereB-3.3",
- [[
- SELECT x, a, y=b FROM t1, t2 WHERE b=y;
- ]],
- {
- -- <whereB-3.3>
-
- -- </whereB-3.3>
- })
-
-test:do_execsql_test(
- "whereB-3.4",
- [[
- SELECT x, a, y=b FROM t1, t2 WHERE +y=+b;
- ]],
- {
- -- <whereB-3.4>
-
- -- </whereB-3.4>
- })
-
-test:do_execsql_test(
- "whereB-3.100",
- [[
- DROP INDEX t2b ON t2;
- SELECT x, a, y=b FROM t1, t2 WHERE y=b;
- ]],
- {
- -- <whereB-3.100>
-
- -- </whereB-3.100>
- })
-
-test:do_execsql_test(
- "whereB-3.101",
- [[
- SELECT x, a, y=b FROM t1, t2 WHERE b=y;
- ]],
- {
- -- <whereB-3.101>
-
- -- </whereB-3.101>
- })
-
-test:do_execsql_test(
- "whereB-3.102",
- [[
- SELECT x, a, y=b FROM t1, t2 WHERE +y=+b;
- ]],
- {
- -- <whereB-3.102>
-
- -- </whereB-3.102>
- })
-
--- For this set of tests:
---
--- * t1.y holds a text value with affinity NONE
--- * t2.b holds an integer value with affinity NUMERIC
---
--- Because t2.b has a numeric affinity, type conversion should occur
--- and the two fields should be equal.
---
-test:do_execsql_test(
- "whereB-4.1",
- [[
- DROP TABLE IF EXISTS t1;
- DROP TABLE IF EXISTS t2;
-
- CREATE TABLE t1(x INT primary key, y SCALAR); -- affinity of t1.y is NONE
- INSERT INTO t1 VALUES(1,'99');
-
- CREATE TABLE t2(a INT primary key, b NUMBER); -- affinity of t2.b is NUMERIC
- CREATE INDEX t2b ON t2(b);
- INSERT INTO t2 VALUES(2,99);
-
- SELECT x, a, y=b FROM t1, t2;
- ]],
- {
- -- <whereB-4.1>
- 1, 2, true
- -- </whereB-4.1>
- })
-
-test:do_execsql_test(
- "whereB-4.2",
- [[
- SELECT x, a, y=b FROM t1, t2 WHERE y=b;
- ]],
- {
- -- <whereB-4.2>
- 1, 2, true
- -- </whereB-4.2>
- })
-
-test:do_execsql_test(
- "whereB-4.3",
- [[
- SELECT x, a, y=b FROM t1, t2 WHERE b=y;
- ]],
- {
- -- <whereB-4.3>
- 1, 2, true
- -- </whereB-4.3>
- })
-
-test:do_execsql_test(
- "whereB-4.4",
- -- In this case the unary "+" operator shouldn't
- -- affect result set of query.
- [[
- SELECT x, a, y=b FROM t1, t2 WHERE +y=+b;
- ]],
- {
- -- <whereB-4.4>
- 1, 2, true
- -- </whereB-4.4>
- })
-
-test:do_execsql_test(
- "whereB-4.100",
- [[
- DROP INDEX t2b ON t2;
- SELECT x, a, y=b FROM t1, t2 WHERE y=b;
- ]],
- {
- -- <whereB-4.100>
- 1, 2, true
- -- </whereB-4.100>
- })
-
-test:do_execsql_test(
- "whereB-4.101",
- [[
- SELECT x, a, y=b FROM t1, t2 WHERE b=y;
- ]],
- {
- -- <whereB-4.101>
- 1, 2, true
- -- </whereB-4.101>
- })
-
-test:do_execsql_test(
- "whereB-4.102",
- -- In this case the unary "+" operator shouldn't
- -- affect result set of query.
- [[
- SELECT x, a, y=b FROM t1, t2 WHERE +y=+b;
- ]],
- {
- -- <whereB-4.102>
- 1, 2, true
- -- </whereB-4.102>
- })
-
--- For this set of tests:
---
--- * t1.y holds a text value with affinity NONE
--- * t2.b holds an integer value with affinity INTEGER
---
--- Because t2.b has a numeric affinity, type conversion should occur
--- and the two fields should be equal.
---
-test:do_execsql_test(
- "whereB-5.1",
- [[
- DROP TABLE t1;
- DROP TABLE t2;
-
- CREATE TABLE t1(x INT primary key, y SCALAR); -- affinity of t1.y is NONE
- INSERT INTO t1 VALUES(1,'99');
-
- CREATE TABLE t2(a INT primary key, b INT); -- affinity of t2.b is INTEGER
- CREATE INDEX t2b ON t2(b);
- INSERT INTO t2 VALUES(2,99);
-
- SELECT x, a, y=b FROM t1, t2;
- ]],
- {
- -- <whereB-5.1>
- 1, 2, true
- -- </whereB-5.1>
- })
-
-test:do_execsql_test(
- "whereB-5.2",
- [[
- SELECT x, a, y=b FROM t1, t2 WHERE y=b;
- ]],
- {
- -- <whereB-5.2>
- 1, 2, true
- -- </whereB-5.2>
- })
-
-test:do_execsql_test(
- "whereB-5.3",
- [[
- SELECT x, a, y=b FROM t1, t2 WHERE b=y;
- ]],
- {
- -- <whereB-5.3>
- 1, 2, true
- -- </whereB-5.3>
- })
-
-test:do_execsql_test(
- "whereB-5.4",
- -- In this case the unary "+" operator shouldn't
- -- affect result set of query.
- [[
- SELECT x, a, y=b FROM t1, t2 WHERE +y=+b;
- ]],
- {
- -- <whereB-5.4>
- 1, 2, true
- -- </whereB-5.4>
- })
-
-test:do_execsql_test(
- "whereB-5.100",
- [[
- DROP INDEX t2b ON t2;
- SELECT x, a, y=b FROM t1, t2 WHERE y=b;
- ]],
- {
- -- <whereB-5.100>
- 1, 2, true
- -- </whereB-5.100>
- })
-
-test:do_execsql_test(
- "whereB-5.101",
- [[
- SELECT x, a, y=b FROM t1, t2 WHERE b=y;
- ]],
- {
- -- <whereB-5.101>
- 1, 2, true
- -- </whereB-5.101>
- })
-
-test:do_execsql_test(
- "whereB-5.102",
- -- In this case the unary "+" operator shouldn't
- -- affect result set of query.
- [[
- SELECT x, a, y=b FROM t1, t2 WHERE +y=+b;
- ]],
- {
- -- <whereB-5.102>
- 1, 2, true
- -- </whereB-5.102>
- })
-
--- For this set of tests:
---
--- * t1.y holds a text value with affinity NONE
--- * t2.b holds an integer value with affinity REAL
---
--- Because t2.b has a numeric affinity, type conversion should occur
--- and the two fields should be equal.
---
-test:do_execsql_test(
- "whereB-6.1",
- [[
- DROP TABLE t1;
- DROP TABLE t2;
-
- CREATE TABLE t1(x INT primary key, y SCALAR); -- affinity of t1.y is NONE
- INSERT INTO t1 VALUES(1,'99');
-
- CREATE TABLE t2(a INT primary key, b NUMBER); -- affinity of t2.b is REAL
- CREATE INDEX t2b ON t2(b);
- INSERT INTO t2 VALUES(2,99.0);
-
- SELECT x, a, y=b FROM t1, t2;
- ]],
- {
- -- <whereB-6.1>
- 1, 2, true
- -- </whereB-6.1>
- })
-
-test:do_execsql_test(
- "whereB-6.2",
- [[
- SELECT x, a, y=b FROM t1, t2 WHERE y=b;
- ]],
- {
- -- <whereB-6.2>
- 1, 2, true
- -- </whereB-6.2>
- })
-
-test:do_execsql_test(
- "whereB-6.3",
- [[
- SELECT x, a, y=b FROM t1, t2 WHERE b=y;
- ]],
- {
- -- <whereB-6.3>
- 1, 2, true
- -- </whereB-6.3>
- })
-
-test:do_execsql_test(
- "whereB-6.4",
- -- In this case the unary "+" operator shouldn't
- -- affect result set of query.
- [[
- SELECT x, a, y=b FROM t1, t2 WHERE +y=+b;
- ]],
- {
- -- <whereB-6.4>
- 1, 2, true
- -- </whereB-6.4>
- })
-
-test:do_execsql_test(
- "whereB-6.100",
- [[
- DROP INDEX t2b ON t2;
- SELECT x, a, y=b FROM t1, t2 WHERE y=b;
- ]],
- {
- -- <whereB-6.100>
- 1, 2, true
- -- </whereB-6.100>
- })
-
-test:do_execsql_test(
- "whereB-6.101",
- [[
- SELECT x, a, y=b FROM t1, t2 WHERE b=y;
- ]],
- {
- -- <whereB-6.101>
- 1, 2, true
- -- </whereB-6.101>
- })
-
-test:do_execsql_test(
- "whereB-6.102",
- -- In this case the unary "+" operator shouldn't
- -- affect result set of query.
- [[
- SELECT x, a, y=b FROM t1, t2 WHERE +y=+b;
- ]],
- {
- -- <whereB-6.102>
- 1, 2, true
- -- </whereB-6.102>
- })
-
--- For this set of tests:
---
--- * t1.y holds an integer value with affinity NUMERIC
--- * t2.b holds a text value with affinity NONE
---
--- Because t1.y has a numeric affinity, type conversion should occur
--- and the two fields should be equal.
---
-test:do_execsql_test(
- "whereB-7.1",
- [[
- DROP TABLE t1;
- DROP TABLE t2;
-
- CREATE TABLE t1(x INT primary key, y NUMBER); -- affinity of t1.y is NUMERIC
- INSERT INTO t1 VALUES(1,99);
-
- CREATE TABLE t2(a INT primary key, b SCALAR); -- affinity of t2.b is NONE
- CREATE INDEX t2b ON t2(b);
- INSERT INTO t2 VALUES(2,'99');
-
- SELECT x, a, y=b FROM t1, t2;
- ]],
- {
- -- <whereB-7.1>
- 1, 2, true
- -- </whereB-7.1>
- })
-
-test:do_execsql_test(
- "whereB-7.2",
- [[
- SELECT x, a, y=b FROM t1, t2 WHERE y=b;
- ]],
- {
- -- <whereB-7.2>
- 1, 2, true
- -- </whereB-7.2>
- })
-
-test:do_execsql_test(
- "whereB-7.3",
- [[
- SELECT x, a, y=b FROM t1, t2 WHERE b=y;
- ]],
- {
- -- <whereB-7.3>
- 1, 2, true
- -- </whereB-7.3>
- })
-
-test:do_execsql_test(
- "whereB-7.4",
- -- In this case the unary "+" operator shouldn't
- -- affect result set of query.
- [[
- SELECT x, a, y=b FROM t1, t2 WHERE +y=+b;
- ]],
- {
- -- <whereB-7.4>
- 1, 2, true
- -- </whereB-7.4>
- })
-
-test:do_execsql_test(
- "whereB-7.100",
- [[
- DROP INDEX t2b ON t2;
- SELECT x, a, y=b FROM t1, t2 WHERE y=b;
- ]],
- {
- -- <whereB-7.100>
- 1, 2, true
- -- </whereB-7.100>
- })
-
-test:do_execsql_test(
- "whereB-7.101",
- [[
- SELECT x, a, y=b FROM t1, t2 WHERE b=y;
- ]],
- {
- -- <whereB-7.101>
- 1, 2, true
- -- </whereB-7.101>
- })
-
-test:do_execsql_test(
- "whereB-7.102",
- -- In this case the unary "+" operator shouldn't
- -- affect result set of query.
- [[
- SELECT x, a, y=b FROM t1, t2 WHERE +y=+b;
- ]],
- {
- -- <whereB-7.102>
- 1, 2, true
- -- </whereB-7.102>
- })
-
--- For this set of tests:
---
--- * t1.y holds an integer value with affinity INTEGER
--- * t2.b holds a text value with affinity NONE
---
--- Because t1.y has a numeric affinity, type conversion should occur
--- and the two fields should be equal.
---
-test:do_execsql_test(
- "whereB-8.1",
- [[
- DROP TABLE t1;
- DROP TABLE t2;
-
- CREATE TABLE t1(x INT primary key, y INT); -- affinity of t1.y is INTEGER
- INSERT INTO t1 VALUES(1,99);
-
- CREATE TABLE t2(a INT primary key, b SCALAR); -- affinity of t2.b is NONE
- CREATE INDEX t2b ON t2(b);
- INSERT INTO t2 VALUES(2,'99');
-
- SELECT x, a, y=b FROM t1, t2;
- ]],
- {
- -- <whereB-8.1>
- 1, 2, true
- -- </whereB-8.1>
- })
-
-test:do_execsql_test(
- "whereB-8.2",
- [[
- SELECT x, a, y=b FROM t1, t2 WHERE y=b;
- ]],
- {
- -- <whereB-8.2>
- 1, 2, true
- -- </whereB-8.2>
- })
-
-test:do_execsql_test(
- "whereB-8.3",
- [[
- SELECT x, a, y=b FROM t1, t2 WHERE b=y;
- ]],
- {
- -- <whereB-8.3>
- 1, 2, true
- -- </whereB-8.3>
- })
-
-test:do_execsql_test(
- "whereB-8.4",
- -- In this case the unary "+" operator shouldn't
- -- affect result set of query.
- [[
- SELECT x, a, y=b FROM t1, t2 WHERE +y=+b;
- ]],
- {
- -- <whereB-8.4>
- 1, 2, true
- -- </whereB-8.4>
- })
-
-test:do_execsql_test(
- "whereB-8.100",
- [[
- DROP INDEX t2b ON t2;
- SELECT x, a, y=b FROM t1, t2 WHERE y=b;
- ]],
- {
- -- <whereB-8.100>
- 1, 2, true
- -- </whereB-8.100>
- })
-
-test:do_execsql_test(
- "whereB-8.101",
- [[
- SELECT x, a, y=b FROM t1, t2 WHERE b=y;
- ]],
- {
- -- <whereB-8.101>
- 1, 2, true
- -- </whereB-8.101>
- })
-
-test:do_execsql_test(
- "whereB-8.102",
- -- In this case the unary "+" operator shouldn't
- -- affect result set of query.
- [[
- SELECT x, a, y=b FROM t1, t2 WHERE +y=+b;
- ]],
- {
- -- <whereB-8.102>
- 1, 2, true
- -- </whereB-8.102>
- })
-
--- For this set of tests:
---
--- * t1.y holds an integer value with affinity REAL
--- * t2.b holds a text value with affinity NONE
---
--- Because t1.y has a numeric affinity, type conversion should occur
--- and the two fields should be equal.
---
-test:do_execsql_test(
- "whereB-9.1",
- [[
- DROP TABLE t1;
- DROP TABLE t2;
-
- CREATE TABLE t1(x INT primary key, y NUMBER); -- affinity of t1.y is REAL
- INSERT INTO t1 VALUES(1,99.0);
-
- CREATE TABLE t2(a INT primary key, b SCALAR); -- affinity of t2.b is NONE
- CREATE INDEX t2b ON t2(b);
- INSERT INTO t2 VALUES(2,'99');
-
- SELECT x, a, y=b FROM t1, t2;
- ]],
- {
- -- <whereB-9.1>
- 1, 2, true
- -- </whereB-9.1>
- })
-
-test:do_execsql_test(
- "whereB-9.2",
- [[
- SELECT x, a, y=b FROM t1, t2 WHERE y=b;
- ]],
- {
- -- <whereB-9.2>
- 1, 2, true
- -- </whereB-9.2>
- })
-
-test:do_execsql_test(
- "whereB-9.3",
- [[
- SELECT x, a, y=b FROM t1, t2 WHERE b=y;
- ]],
- {
- -- <whereB-9.3>
- 1, 2, true
- -- </whereB-9.3>
- })
-
-test:do_execsql_test(
- "whereB-9.4",
- -- In this case the unary "+" operator shouldn't
- -- affect result set of query.
- [[
- SELECT x, a, y=b FROM t1, t2 WHERE +y=+b;
- ]],
- {
- -- <whereB-9.4>
- 1, 2, true
- -- </whereB-9.4>
- })
-
-test:do_execsql_test(
- "whereB-9.100",
- [[
- DROP INDEX t2b ON t2;
- SELECT x, a, y=b FROM t1, t2 WHERE y=b;
- ]],
- {
- -- <whereB-9.100>
- 1, 2, true
- -- </whereB-9.100>
- })
-
-test:do_execsql_test(
- "whereB-9.101",
- [[
- SELECT x, a, y=b FROM t1, t2 WHERE b=y;
- ]],
- {
- -- <whereB-9.101>
- 1, 2, true
- -- </whereB-9.101>
- })
-
-test:do_execsql_test(
- "whereB-9.102",
- -- In this case the unary "+" operator shouldn't
- -- affect result set of query.
- [[
- SELECT x, a, y=b FROM t1, t2 WHERE +y=+b;
- ]],
- {
- -- <whereB-9.102>
- 1, 2, true
- -- </whereB-9.102>
- })
-
-test:finish_test()
-
diff --git a/test/sql-tap/whereC.test.lua b/test/sql-tap/whereC.test.lua
index 89459dee3..58c049553 100755
--- a/test/sql-tap/whereC.test.lua
+++ b/test/sql-tap/whereC.test.lua
@@ -55,9 +55,9 @@ test:do_execsql_test(
test:test("main", function()
local data = {{"SELECT i FROM t1 WHERE a=1 AND b=2 AND i>3", {4, 5}},
-- {"SELECT i FROM t1 WHERE rowid='12'", {12}},
- {"SELECT i FROM t1 WHERE a=1 AND b='2'", {3, 4, 5}},
- {"SELECT i FROM t1 WHERE a=1 AND b='2' AND i>'3'", {4, 5}},
- {"SELECT i FROM t1 WHERE a=1 AND b='2' AND i<5", {3, 4}},
+ {"SELECT i FROM t1 WHERE a=1 AND b=2", {3, 4, 5}},
+ {"SELECT i FROM t1 WHERE a=1 AND b=2 AND i>3", {4, 5}},
+ {"SELECT i FROM t1 WHERE a=1 AND b=2 AND i<5", {3, 4}},
{"SELECT i FROM t1 WHERE a=2 AND b=2 AND i<12", {10, 11}},
{"SELECT i FROM t1 WHERE a IN(1, 2) AND b=2 AND i<11", {3, 4, 5, 10}},
{"SELECT i FROM t1 WHERE a=2 AND b=2 AND i BETWEEN 10 AND 12", {10, 11, 12}},
@@ -66,7 +66,7 @@ test:test("main", function()
{"SELECT i FROM t1 WHERE a=2 AND b=2 AND i BETWEEN 12 AND 10", {}},
{"SELECT i FROM t1 WHERE a=2 AND b=2 AND i<NULL", {}},
{"SELECT i FROM t1 WHERE a=2 AND b=2 AND i>=NULL", {}},
- {"SELECT i FROM t1 WHERE a=1 AND b='2' AND i<4.5", {3, 4}}}
+ {"SELECT i FROM t1 WHERE a=1 AND b=2 AND i<4.5", {3, 4}}}
-- {"SELECT i FROM t1 WHERE rowid IS '12'", {12}}}
for tn, t in ipairs(data) do
diff --git a/test/sql/types.result b/test/sql/types.result
index 54aff460e..70fbbc5a2 100644
--- a/test/sql/types.result
+++ b/test/sql/types.result
@@ -2155,3 +2155,623 @@ box.execute([[SELECT * FROM "s" WHERE "id" = ?;]])
s:drop()
---
...
+--
+-- gh-3809: Make sure there are no implicit casts during
+-- assignment, except for the implicit cast between numeric
+-- values.
+--
+-- Check INSERT.
+box.execute([[CREATE TABLE ti (a INT PRIMARY KEY AUTOINCREMENT, i INTEGER);]])
+---
+- row_count: 1
+...
+box.execute([[CREATE TABLE td (a INT PRIMARY KEY AUTOINCREMENT, d DOUBLE);]])
+---
+- row_count: 1
+...
+box.execute([[CREATE TABLE tb (a INT PRIMARY KEY AUTOINCREMENT, b BOOLEAN);]])
+---
+- row_count: 1
+...
+box.execute([[CREATE TABLE tt (a INT PRIMARY KEY AUTOINCREMENT, t TEXT);]])
+---
+- row_count: 1
+...
+box.execute([[CREATE TABLE tv (a INT PRIMARY KEY AUTOINCREMENT, v VARBINARY);]])
+---
+- row_count: 1
+...
+box.execute([[CREATE TABLE ts (a INT PRIMARY KEY AUTOINCREMENT, s SCALAR);]])
+---
+- row_count: 1
+...
+box.execute([[INSERT INTO ti(i) VALUES (NULL);]])
+---
+- autoincrement_ids:
+ - 1
+ row_count: 1
+...
+box.execute([[INSERT INTO ti(i) VALUES (11);]])
+---
+- autoincrement_ids:
+ - 2
+ row_count: 1
+...
+box.execute([[INSERT INTO ti(i) VALUES (100000000000000000000000000000000.1);]])
+---
+- null
+- 'Type mismatch: can not convert 1.0e+32 to integer'
+...
+box.execute([[INSERT INTO ti(i) VALUES (33.0);]])
+---
+- autoincrement_ids:
+ - 3
+ row_count: 1
+...
+box.execute([[INSERT INTO ti(i) VALUES (true);]])
+---
+- null
+- 'Type mismatch: can not convert TRUE to integer'
+...
+box.execute([[INSERT INTO ti(i) VALUES ('33');]])
+---
+- null
+- 'Type mismatch: can not convert 33 to integer'
+...
+box.execute([[INSERT INTO ti(i) VALUES (X'3434');]])
+---
+- null
+- 'Type mismatch: can not convert varbinary to integer'
+...
+box.execute([[SELECT * FROM ti;]])
+---
+- metadata:
+ - name: A
+ type: integer
+ - name: I
+ type: integer
+ rows:
+ - [1, null]
+ - [2, 11]
+ - [3, 33]
+...
+box.execute([[INSERT INTO td(d) VALUES (NULL);]])
+---
+- autoincrement_ids:
+ - 1
+ row_count: 1
+...
+box.execute([[INSERT INTO td(d) VALUES (11);]])
+---
+- autoincrement_ids:
+ - 2
+ row_count: 1
+...
+box.execute([[INSERT INTO td(d) VALUES (100000000000000001);;]])
+---
+- null
+- Syntax error at line 1 near ';'
+...
+box.execute([[INSERT INTO td(d) VALUES (22.2);]])
+---
+- autoincrement_ids:
+ - 3
+ row_count: 1
+...
+box.execute([[INSERT INTO td(d) VALUES (true);]])
+---
+- null
+- 'Type mismatch: can not convert TRUE to double'
+...
+box.execute([[INSERT INTO td(d) VALUES ('33');]])
+---
+- null
+- 'Type mismatch: can not convert 33 to double'
+...
+box.execute([[INSERT INTO td(d) VALUES (X'3434');]])
+---
+- null
+- 'Type mismatch: can not convert varbinary to double'
+...
+box.execute([[SELECT * FROM td;]])
+---
+- metadata:
+ - name: A
+ type: integer
+ - name: D
+ type: double
+ rows:
+ - [1, null]
+ - [2, 11]
+ - [3, 22.2]
+...
+box.execute([[INSERT INTO tb(b) VALUES (NULL);]])
+---
+- autoincrement_ids:
+ - 1
+ row_count: 1
+...
+box.execute([[INSERT INTO tb(b) VALUES (11);]])
+---
+- null
+- 'Type mismatch: can not convert 11 to boolean'
+...
+box.execute([[INSERT INTO tb(b) VALUES (22.2);]])
+---
+- null
+- 'Type mismatch: can not convert 22.2 to boolean'
+...
+box.execute([[INSERT INTO tb(b) VALUES (true);]])
+---
+- autoincrement_ids:
+ - 2
+ row_count: 1
+...
+box.execute([[INSERT INTO tb(b) VALUES ('33');]])
+---
+- null
+- 'Type mismatch: can not convert 33 to boolean'
+...
+box.execute([[INSERT INTO tb(b) VALUES (X'3434');]])
+---
+- null
+- 'Type mismatch: can not convert varbinary to boolean'
+...
+box.execute([[SELECT * FROM tb;]])
+---
+- metadata:
+ - name: A
+ type: integer
+ - name: B
+ type: boolean
+ rows:
+ - [1, null]
+ - [2, true]
+...
+box.execute([[INSERT INTO tt(t) VALUES (NULL);]])
+---
+- autoincrement_ids:
+ - 1
+ row_count: 1
+...
+box.execute([[INSERT INTO tt(t) VALUES (11);]])
+---
+- null
+- 'Type mismatch: can not convert 11 to string'
+...
+box.execute([[INSERT INTO tt(t) VALUES (22.2);]])
+---
+- null
+- 'Type mismatch: can not convert 22.2 to string'
+...
+box.execute([[INSERT INTO tt(t) VALUES (true);]])
+---
+- null
+- 'Type mismatch: can not convert TRUE to string'
+...
+box.execute([[INSERT INTO tt(t) VALUES ('33');]])
+---
+- autoincrement_ids:
+ - 2
+ row_count: 1
+...
+box.execute([[INSERT INTO tt(t) VALUES (X'3434');]])
+---
+- null
+- 'Type mismatch: can not convert varbinary to string'
+...
+box.execute([[SELECT * FROM tt;]])
+---
+- metadata:
+ - name: A
+ type: integer
+ - name: T
+ type: string
+ rows:
+ - [1, null]
+ - [2, '33']
+...
+box.execute([[INSERT INTO tv(v) VALUES (NULL);]])
+---
+- autoincrement_ids:
+ - 1
+ row_count: 1
+...
+box.execute([[INSERT INTO tv(v) VALUES (11);]])
+---
+- null
+- 'Type mismatch: can not convert 11 to varbinary'
+...
+box.execute([[INSERT INTO tv(v) VALUES (22.2);]])
+---
+- null
+- 'Type mismatch: can not convert 22.2 to varbinary'
+...
+box.execute([[INSERT INTO tv(v) VALUES (true);]])
+---
+- null
+- 'Type mismatch: can not convert TRUE to varbinary'
+...
+box.execute([[INSERT INTO tv(v) VALUES ('33');]])
+---
+- null
+- 'Type mismatch: can not convert 33 to varbinary'
+...
+box.execute([[INSERT INTO tv(v) VALUES (X'3434');]])
+---
+- autoincrement_ids:
+ - 2
+ row_count: 1
+...
+box.execute([[SELECT * FROM tv;]])
+---
+- metadata:
+ - name: A
+ type: integer
+ - name: V
+ type: varbinary
+ rows:
+ - [1, null]
+ - [2, '44']
+...
+box.execute([[INSERT INTO ts(s) VALUES (NULL);]])
+---
+- autoincrement_ids:
+ - 1
+ row_count: 1
+...
+box.execute([[INSERT INTO ts(s) VALUES (11);]])
+---
+- autoincrement_ids:
+ - 2
+ row_count: 1
+...
+box.execute([[INSERT INTO ts(s) VALUES (22.2);]])
+---
+- autoincrement_ids:
+ - 3
+ row_count: 1
+...
+box.execute([[INSERT INTO ts(s) VALUES (true);]])
+---
+- autoincrement_ids:
+ - 4
+ row_count: 1
+...
+box.execute([[INSERT INTO ts(s) VALUES ('33');]])
+---
+- autoincrement_ids:
+ - 5
+ row_count: 1
+...
+box.execute([[INSERT INTO ts(s) VALUES (X'3434');]])
+---
+- autoincrement_ids:
+ - 6
+ row_count: 1
+...
+box.execute([[SELECT * FROM ts;]])
+---
+- metadata:
+ - name: A
+ type: integer
+ - name: S
+ type: scalar
+ rows:
+ - [1, null]
+ - [2, 11]
+ - [3, 22.2]
+ - [4, true]
+ - [5, '33']
+ - [6, '44']
+...
+-- Check for UPDATE.
+box.execute([[DELETE FROM ti;]])
+---
+- row_count: 3
+...
+box.execute([[DELETE FROM td;]])
+---
+- row_count: 3
+...
+box.execute([[DELETE FROM tb;]])
+---
+- row_count: 2
+...
+box.execute([[DELETE FROM tt;]])
+---
+- row_count: 2
+...
+box.execute([[DELETE FROM tv;]])
+---
+- row_count: 2
+...
+box.execute([[DELETE FROM ts;]])
+---
+- row_count: 6
+...
+box.execute([[INSERT INTO ti VALUES(1, NULL);]])
+---
+- row_count: 1
+...
+box.execute([[INSERT INTO td VALUES(1, NULL);]])
+---
+- row_count: 1
+...
+box.execute([[INSERT INTO tb VALUES(1, NULL);]])
+---
+- row_count: 1
+...
+box.execute([[INSERT INTO tt VALUES(1, NULL);]])
+---
+- row_count: 1
+...
+box.execute([[INSERT INTO tv VALUES(1, NULL);]])
+---
+- row_count: 1
+...
+box.execute([[INSERT INTO ts VALUES(1, NULL);]])
+---
+- row_count: 1
+...
+box.execute([[SELECT * FROM ti, td, tb, tt, tv, ts;]])
+---
+- metadata:
+ - name: A
+ type: integer
+ - name: I
+ type: integer
+ - name: A
+ type: integer
+ - name: D
+ type: double
+ - name: A
+ type: integer
+ - name: B
+ type: boolean
+ - name: A
+ type: integer
+ - name: T
+ type: string
+ - name: A
+ type: integer
+ - name: V
+ type: varbinary
+ - name: A
+ type: integer
+ - name: S
+ type: scalar
+ rows:
+ - [1, null, 1, null, 1, null, 1, null, 1, null, 1, null]
+...
+box.execute([[UPDATE ti SET i = NULL WHERE a = 1;]])
+---
+- row_count: 1
+...
+box.execute([[UPDATE ti SET i = 11 WHERE a = 1;]])
+---
+- row_count: 1
+...
+box.execute([[UPDATE ti SET i = 100000000000000000000000000000000.1 WHERE a = 1;]])
+---
+- null
+- 'Type mismatch: can not convert 1.0e+32 to integer'
+...
+box.execute([[UPDATE ti SET i = 33.0 WHERE a = 1;]])
+---
+- row_count: 1
+...
+box.execute([[UPDATE ti SET i = true WHERE a = 1;]])
+---
+- null
+- 'Type mismatch: can not convert TRUE to integer'
+...
+box.execute([[UPDATE ti SET i = '33' WHERE a = 1;]])
+---
+- null
+- 'Type mismatch: can not convert 33 to integer'
+...
+box.execute([[UPDATE ti SET i = X'3434' WHERE a = 1;]])
+---
+- null
+- 'Type mismatch: can not convert varbinary to integer'
+...
+box.execute([[SELECT * FROM ti;]])
+---
+- metadata:
+ - name: A
+ type: integer
+ - name: I
+ type: integer
+ rows:
+ - [1, 33]
+...
+box.execute([[UPDATE td SET d = NULL WHERE a = 1;]])
+---
+- row_count: 1
+...
+box.execute([[UPDATE td SET d = 11 WHERE a = 1;]])
+---
+- row_count: 1
+...
+box.execute([[UPDATE td SET d = 100000000000000001 WHERE a = 1;]])
+---
+- row_count: 1
+...
+box.execute([[UPDATE td SET d = 22.2 WHERE a = 1;]])
+---
+- row_count: 1
+...
+box.execute([[UPDATE td SET d = true WHERE a = 1;]])
+---
+- null
+- 'Type mismatch: can not convert TRUE to double'
+...
+box.execute([[UPDATE td SET d = '33' WHERE a = 1;]])
+---
+- null
+- 'Type mismatch: can not convert 33 to double'
+...
+box.execute([[UPDATE td SET d = X'3434' WHERE a = 1;]])
+---
+- null
+- 'Type mismatch: can not convert varbinary to double'
+...
+box.execute([[SELECT * FROM td;]])
+---
+- metadata:
+ - name: A
+ type: integer
+ - name: D
+ type: double
+ rows:
+ - [1, 22.2]
+...
+box.execute([[UPDATE tb SET b = NULL WHERE a = 1;]])
+---
+- row_count: 1
+...
+box.execute([[UPDATE tb SET b = 11 WHERE a = 1;]])
+---
+- null
+- 'Type mismatch: can not convert 11 to boolean'
+...
+box.execute([[UPDATE tb SET b = 22.2 WHERE a = 1;]])
+---
+- null
+- 'Type mismatch: can not convert 22.2 to boolean'
+...
+box.execute([[UPDATE tb SET b = true WHERE a = 1;]])
+---
+- row_count: 1
+...
+box.execute([[UPDATE tb SET b = '33' WHERE a = 1;]])
+---
+- null
+- 'Type mismatch: can not convert 33 to boolean'
+...
+box.execute([[UPDATE tb SET b = X'3434' WHERE a = 1;]])
+---
+- null
+- 'Type mismatch: can not convert varbinary to boolean'
+...
+box.execute([[SELECT * FROM tb;]])
+---
+- metadata:
+ - name: A
+ type: integer
+ - name: B
+ type: boolean
+ rows:
+ - [1, true]
+...
+box.execute([[UPDATE tt SET t = NULL WHERE a = 1;]])
+---
+- row_count: 1
+...
+box.execute([[UPDATE tt SET t = 11 WHERE a = 1;]])
+---
+- null
+- 'Type mismatch: can not convert 11 to string'
+...
+box.execute([[UPDATE tt SET t = 22.2 WHERE a = 1;]])
+---
+- null
+- 'Type mismatch: can not convert 22.2 to string'
+...
+box.execute([[UPDATE tt SET t = true WHERE a = 1;]])
+---
+- null
+- 'Type mismatch: can not convert TRUE to string'
+...
+box.execute([[UPDATE tt SET t = '33' WHERE a = 1;]])
+---
+- row_count: 1
+...
+box.execute([[UPDATE tt SET t = X'3434' WHERE a = 1;]])
+---
+- null
+- 'Type mismatch: can not convert varbinary to string'
+...
+box.execute([[SELECT * FROM tt;]])
+---
+- metadata:
+ - name: A
+ type: integer
+ - name: T
+ type: string
+ rows:
+ - [1, '33']
+...
+box.execute([[UPDATE tv SET v = NULL WHERE a = 1;]])
+---
+- row_count: 1
+...
+box.execute([[UPDATE tv SET v = 11 WHERE a = 1;]])
+---
+- null
+- 'Type mismatch: can not convert 11 to varbinary'
+...
+box.execute([[UPDATE tv SET v = 22.2 WHERE a = 1;]])
+---
+- null
+- 'Type mismatch: can not convert 22.2 to varbinary'
+...
+box.execute([[UPDATE tv SET v = true WHERE a = 1;]])
+---
+- null
+- 'Type mismatch: can not convert TRUE to varbinary'
+...
+box.execute([[UPDATE tv SET v = '33' WHERE a = 1;]])
+---
+- null
+- 'Type mismatch: can not convert 33 to varbinary'
+...
+box.execute([[UPDATE tv SET v = X'3434' WHERE a = 1;]])
+---
+- row_count: 1
+...
+box.execute([[SELECT * FROM tv;]])
+---
+- metadata:
+ - name: A
+ type: integer
+ - name: V
+ type: varbinary
+ rows:
+ - [1, '44']
+...
+box.execute([[UPDATE ts SET s = NULL WHERE a = 1;]])
+---
+- row_count: 1
+...
+box.execute([[UPDATE ts SET s = 11 WHERE a = 1;]])
+---
+- row_count: 1
+...
+box.execute([[UPDATE ts SET s = 22.2 WHERE a = 1;]])
+---
+- row_count: 1
+...
+box.execute([[UPDATE ts SET s = true WHERE a = 1;]])
+---
+- row_count: 1
+...
+box.execute([[UPDATE ts SET s = '33' WHERE a = 1;]])
+---
+- row_count: 1
+...
+box.execute([[UPDATE ts SET s = X'3434' WHERE a = 1;]])
+---
+- row_count: 1
+...
+box.execute([[SELECT * FROM ts;]])
+---
+- metadata:
+ - name: A
+ type: integer
+ - name: S
+ type: scalar
+ rows:
+ - [1, '44']
+...
diff --git a/test/sql/types.test.lua b/test/sql/types.test.lua
index bd14b342d..2dc70f3c5 100644
--- a/test/sql/types.test.lua
+++ b/test/sql/types.test.lua
@@ -487,3 +487,132 @@ s:format({ \
box.execute([[SELECT * FROM "s" WHERE "id" = ?;]])
s:drop()
+
+--
+-- gh-3809: Make sure there are no implicit casts during
+-- assignment, except for the implicit cast between numeric
+-- values.
+--
+
+-- Check INSERT.
+box.execute([[CREATE TABLE ti (a INT PRIMARY KEY AUTOINCREMENT, i INTEGER);]])
+box.execute([[CREATE TABLE td (a INT PRIMARY KEY AUTOINCREMENT, d DOUBLE);]])
+box.execute([[CREATE TABLE tb (a INT PRIMARY KEY AUTOINCREMENT, b BOOLEAN);]])
+box.execute([[CREATE TABLE tt (a INT PRIMARY KEY AUTOINCREMENT, t TEXT);]])
+box.execute([[CREATE TABLE tv (a INT PRIMARY KEY AUTOINCREMENT, v VARBINARY);]])
+box.execute([[CREATE TABLE ts (a INT PRIMARY KEY AUTOINCREMENT, s SCALAR);]])
+
+box.execute([[INSERT INTO ti(i) VALUES (NULL);]])
+box.execute([[INSERT INTO ti(i) VALUES (11);]])
+box.execute([[INSERT INTO ti(i) VALUES (100000000000000000000000000000000.1);]])
+box.execute([[INSERT INTO ti(i) VALUES (33.0);]])
+box.execute([[INSERT INTO ti(i) VALUES (true);]])
+box.execute([[INSERT INTO ti(i) VALUES ('33');]])
+box.execute([[INSERT INTO ti(i) VALUES (X'3434');]])
+box.execute([[SELECT * FROM ti;]])
+
+box.execute([[INSERT INTO td(d) VALUES (NULL);]])
+box.execute([[INSERT INTO td(d) VALUES (11);]])
+box.execute([[INSERT INTO td(d) VALUES (100000000000000001);;]])
+box.execute([[INSERT INTO td(d) VALUES (22.2);]])
+box.execute([[INSERT INTO td(d) VALUES (true);]])
+box.execute([[INSERT INTO td(d) VALUES ('33');]])
+box.execute([[INSERT INTO td(d) VALUES (X'3434');]])
+box.execute([[SELECT * FROM td;]])
+
+box.execute([[INSERT INTO tb(b) VALUES (NULL);]])
+box.execute([[INSERT INTO tb(b) VALUES (11);]])
+box.execute([[INSERT INTO tb(b) VALUES (22.2);]])
+box.execute([[INSERT INTO tb(b) VALUES (true);]])
+box.execute([[INSERT INTO tb(b) VALUES ('33');]])
+box.execute([[INSERT INTO tb(b) VALUES (X'3434');]])
+box.execute([[SELECT * FROM tb;]])
+
+box.execute([[INSERT INTO tt(t) VALUES (NULL);]])
+box.execute([[INSERT INTO tt(t) VALUES (11);]])
+box.execute([[INSERT INTO tt(t) VALUES (22.2);]])
+box.execute([[INSERT INTO tt(t) VALUES (true);]])
+box.execute([[INSERT INTO tt(t) VALUES ('33');]])
+box.execute([[INSERT INTO tt(t) VALUES (X'3434');]])
+box.execute([[SELECT * FROM tt;]])
+
+box.execute([[INSERT INTO tv(v) VALUES (NULL);]])
+box.execute([[INSERT INTO tv(v) VALUES (11);]])
+box.execute([[INSERT INTO tv(v) VALUES (22.2);]])
+box.execute([[INSERT INTO tv(v) VALUES (true);]])
+box.execute([[INSERT INTO tv(v) VALUES ('33');]])
+box.execute([[INSERT INTO tv(v) VALUES (X'3434');]])
+box.execute([[SELECT * FROM tv;]])
+
+box.execute([[INSERT INTO ts(s) VALUES (NULL);]])
+box.execute([[INSERT INTO ts(s) VALUES (11);]])
+box.execute([[INSERT INTO ts(s) VALUES (22.2);]])
+box.execute([[INSERT INTO ts(s) VALUES (true);]])
+box.execute([[INSERT INTO ts(s) VALUES ('33');]])
+box.execute([[INSERT INTO ts(s) VALUES (X'3434');]])
+box.execute([[SELECT * FROM ts;]])
+
+-- Check for UPDATE.
+box.execute([[DELETE FROM ti;]])
+box.execute([[DELETE FROM td;]])
+box.execute([[DELETE FROM tb;]])
+box.execute([[DELETE FROM tt;]])
+box.execute([[DELETE FROM tv;]])
+box.execute([[DELETE FROM ts;]])
+box.execute([[INSERT INTO ti VALUES(1, NULL);]])
+box.execute([[INSERT INTO td VALUES(1, NULL);]])
+box.execute([[INSERT INTO tb VALUES(1, NULL);]])
+box.execute([[INSERT INTO tt VALUES(1, NULL);]])
+box.execute([[INSERT INTO tv VALUES(1, NULL);]])
+box.execute([[INSERT INTO ts VALUES(1, NULL);]])
+box.execute([[SELECT * FROM ti, td, tb, tt, tv, ts;]])
+
+box.execute([[UPDATE ti SET i = NULL WHERE a = 1;]])
+box.execute([[UPDATE ti SET i = 11 WHERE a = 1;]])
+box.execute([[UPDATE ti SET i = 100000000000000000000000000000000.1 WHERE a = 1;]])
+box.execute([[UPDATE ti SET i = 33.0 WHERE a = 1;]])
+box.execute([[UPDATE ti SET i = true WHERE a = 1;]])
+box.execute([[UPDATE ti SET i = '33' WHERE a = 1;]])
+box.execute([[UPDATE ti SET i = X'3434' WHERE a = 1;]])
+box.execute([[SELECT * FROM ti;]])
+
+box.execute([[UPDATE td SET d = NULL WHERE a = 1;]])
+box.execute([[UPDATE td SET d = 11 WHERE a = 1;]])
+box.execute([[UPDATE td SET d = 100000000000000001 WHERE a = 1;]])
+box.execute([[UPDATE td SET d = 22.2 WHERE a = 1;]])
+box.execute([[UPDATE td SET d = true WHERE a = 1;]])
+box.execute([[UPDATE td SET d = '33' WHERE a = 1;]])
+box.execute([[UPDATE td SET d = X'3434' WHERE a = 1;]])
+box.execute([[SELECT * FROM td;]])
+
+box.execute([[UPDATE tb SET b = NULL WHERE a = 1;]])
+box.execute([[UPDATE tb SET b = 11 WHERE a = 1;]])
+box.execute([[UPDATE tb SET b = 22.2 WHERE a = 1;]])
+box.execute([[UPDATE tb SET b = true WHERE a = 1;]])
+box.execute([[UPDATE tb SET b = '33' WHERE a = 1;]])
+box.execute([[UPDATE tb SET b = X'3434' WHERE a = 1;]])
+box.execute([[SELECT * FROM tb;]])
+
+box.execute([[UPDATE tt SET t = NULL WHERE a = 1;]])
+box.execute([[UPDATE tt SET t = 11 WHERE a = 1;]])
+box.execute([[UPDATE tt SET t = 22.2 WHERE a = 1;]])
+box.execute([[UPDATE tt SET t = true WHERE a = 1;]])
+box.execute([[UPDATE tt SET t = '33' WHERE a = 1;]])
+box.execute([[UPDATE tt SET t = X'3434' WHERE a = 1;]])
+box.execute([[SELECT * FROM tt;]])
+
+box.execute([[UPDATE tv SET v = NULL WHERE a = 1;]])
+box.execute([[UPDATE tv SET v = 11 WHERE a = 1;]])
+box.execute([[UPDATE tv SET v = 22.2 WHERE a = 1;]])
+box.execute([[UPDATE tv SET v = true WHERE a = 1;]])
+box.execute([[UPDATE tv SET v = '33' WHERE a = 1;]])
+box.execute([[UPDATE tv SET v = X'3434' WHERE a = 1;]])
+box.execute([[SELECT * FROM tv;]])
+
+box.execute([[UPDATE ts SET s = NULL WHERE a = 1;]])
+box.execute([[UPDATE ts SET s = 11 WHERE a = 1;]])
+box.execute([[UPDATE ts SET s = 22.2 WHERE a = 1;]])
+box.execute([[UPDATE ts SET s = true WHERE a = 1;]])
+box.execute([[UPDATE ts SET s = '33' WHERE a = 1;]])
+box.execute([[UPDATE ts SET s = X'3434' WHERE a = 1;]])
+box.execute([[SELECT * FROM ts;]])
--
2.25.1
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [Tarantool-patches] [PATCH v4 4/5] sql: change implicit cast for assignment
2020-07-13 5:33 ` [Tarantool-patches] [PATCH v4 4/5] sql: change implicit cast for assignment imeevma
@ 2020-07-13 14:42 ` Nikita Pettik
0 siblings, 0 replies; 11+ messages in thread
From: Nikita Pettik @ 2020-07-13 14:42 UTC (permalink / raw)
To: imeevma; +Cc: tarantool-patches
On 13 Jul 08:33, imeevma@tarantool.org wrote:
> +
> * Synopsis: type(r[P1@P2])
> *
> - * Apply types to a range of P2 registers starting with P1.
> - *
> - * P4 is a string that is P2 characters long. The nth character of the
> - * string indicates the column type that should be used for the nth
> - * memory cell in the range.
> + * Check that types of P2 registers starting from register P1 are
> + * compatible with given field types in P4. If the MEM_type of the
> + * value and the given type are incompatible according to
> + * field_mp_plain_type_is_compatible(), but both are numeric,
> + * this opcode attempts to convert the value to the type.
> */
> case OP_ApplyType: {
> enum field_type *types = pOp->p4.types;
> @@ -2762,7 +2900,13 @@ case OP_ApplyType: {
> while((type = *(types++)) != field_type_MAX) {
> assert(pIn1 <= &p->aMem[(p->nMem+1 - p->nCursor)]);
> assert(memIsValid(pIn1));
> - if (mem_apply_type(pIn1, type) != 0) {
> + if (mem_is_type_compatible(pIn1, type)) {
> + pIn1++;
> + continue;
> + }
> + if (!mp_type_is_numeric(mem_mp_type(pIn1)) ||
> + !sql_type_is_numeric(type) ||
> + mem_convert_to_numeric(pIn1, type, false) != 0) {
Consider refactoring:
diff --git a/src/box/sql/vdbe.c b/src/box/sql/vdbe.c
index 863f38f5d..41a4750da 100644
--- a/src/box/sql/vdbe.c
+++ b/src/box/sql/vdbe.c
@@ -2900,19 +2900,23 @@ case OP_ApplyType: {
while((type = *(types++)) != field_type_MAX) {
assert(pIn1 <= &p->aMem[(p->nMem+1 - p->nCursor)]);
assert(memIsValid(pIn1));
- if (mem_is_type_compatible(pIn1, type)) {
- pIn1++;
- continue;
- }
- if (!mp_type_is_numeric(mem_mp_type(pIn1)) ||
- !sql_type_is_numeric(type) ||
- mem_convert_to_numeric(pIn1, type, false) != 0) {
- diag_set(ClientError, ER_SQL_TYPE_MISMATCH,
- sql_value_to_diag_str(pIn1),
- field_type_strs[type]);
- goto abort_due_to_error;
+ if (!mem_is_type_compatible(pIn1, type)) {
+ /* Implicit cast is allowed only to numeric type. */
+ if (!sql_type_is_numeric(type))
+ goto type_mismatch;
+ /* Implicit cast is allowed only from numeric type. */
+ if (!mp_type_is_numeric(mem_mp_type(pIn1)))
+ goto type_mismatch;
+ /* Try to convert numeric-to-numeric. */
+ if (mem_convert_to_numeric(pIn1, type, false) != 0)
+ goto type_mismatch;
}
pIn1++;
+ continue;
+type_mismatch:
+ diag_set(ClientError, ER_SQL_TYPE_MISMATCH,
+ sql_value_to_diag_str(pIn1), field_type_strs[type]);
+ goto abort_due_to_error;
}
break;
}
Otherwise LGTM
> diag_set(ClientError, ER_SQL_TYPE_MISMATCH,
> sql_value_to_diag_str(pIn1),
> field_type_strs[type]);
> diff --git a/src/box/sql/vdbeInt.h b/src/box/sql/vdbeInt.h
> index 44c27bdb7..ad46ab129 100644
> --- a/src/box/sql/vdbeInt.h
> +++ b/src/box/sql/vdbeInt.h
> @@ -566,6 +566,10 @@ mem_mp_type(struct Mem *mem);
> */
> #define mp_type_is_bloblike(X) ((X) == MP_BIN || (X) == MP_ARRAY || (X) == MP_MAP)
>
> +/** Return TRUE if MP_type of X is numeric, FALSE otherwise. */
> +#define mp_type_is_numeric(X) ((X) == MP_INT || (X) == MP_UINT ||\
> + (X) == MP_DOUBLE)
^ permalink raw reply [flat|nested] 11+ messages in thread
* [Tarantool-patches] [PATCH v4 5/5] sql: properly check arguments of functions
2020-07-13 5:32 [Tarantool-patches] [PATCH v4 0/5] Change implicit cast for assignment imeevma
` (3 preceding siblings ...)
2020-07-13 5:33 ` [Tarantool-patches] [PATCH v4 4/5] sql: change implicit cast for assignment imeevma
@ 2020-07-13 5:33 ` imeevma
2020-07-13 14:56 ` Nikita Pettik
4 siblings, 1 reply; 11+ messages in thread
From: imeevma @ 2020-07-13 5:33 UTC (permalink / raw)
To: korablev, tsafin, tarantool-patches
After this patch, the function arguments will be checked using ApplyType
opcode before they are passed to the function. This means that the rules
for implicitly casting values that were specified as arguments are
defined by this opcode.
Closes #4159
---
src/box/sql/expr.c | 5 +
src/box/sql/func.c | 360 +++-
src/box/sql/select.c | 31 +
src/box/sql/sqlInt.h | 32 +
src/box/sql/vdbe.c | 12 +-
test/sql-tap/cse.test.lua | 8 +-
test/sql-tap/func.test.lua | 48 +-
test/sql-tap/orderby1.test.lua | 2 +-
test/sql-tap/position.test.lua | 6 +-
test/sql/boolean.result | 32 +-
test/sql/gh-4159-function-argumens.result | 2131 +++++++++++++++++--
test/sql/gh-4159-function-argumens.test.sql | 272 +++
test/sql/types.result | 76 +-
test/sql/types.test.lua | 2 +-
14 files changed, 2584 insertions(+), 433 deletions(-)
diff --git a/src/box/sql/expr.c b/src/box/sql/expr.c
index 7aee240a3..aa5477c6a 100644
--- a/src/box/sql/expr.c
+++ b/src/box/sql/expr.c
@@ -4104,6 +4104,11 @@ sqlExprCodeTarget(Parse * pParse, Expr * pExpr, int target)
} else {
r1 = 0;
}
+ if (func->def->language == FUNC_LANGUAGE_SQL_BUILTIN) {
+ struct func_sql_builtin *f =
+ (struct func_sql_builtin *)func;
+ sql_emit_func_types(v, &f->args, r1, nFarg);
+ }
if (sql_func_flag_is_set(func, SQL_FUNC_NEEDCOLL)) {
sqlVdbeAddOp4(v, OP_CollSeq, 0, 0, 0,
(char *)coll, P4_COLLSEQ);
diff --git a/src/box/sql/func.c b/src/box/sql/func.c
index 9d4c26081..66edc3792 100644
--- a/src/box/sql/func.c
+++ b/src/box/sql/func.c
@@ -459,17 +459,10 @@ static void
lengthFunc(sql_context * context, int argc, sql_value ** argv)
{
int len;
-
assert(argc == 1);
UNUSED_PARAMETER(argc);
switch (sql_value_type(argv[0])) {
- case MP_BIN:
- case MP_ARRAY:
- case MP_MAP:
- case MP_INT:
- case MP_UINT:
- case MP_BOOL:
- case MP_DOUBLE:{
+ case MP_BIN: {
sql_result_uint(context, sql_value_bytes(argv[0]));
break;
}
@@ -482,6 +475,7 @@ lengthFunc(sql_context * context, int argc, sql_value ** argv)
break;
}
default:{
+ assert(sql_value_type(argv[0]) == MP_NIL);
sql_result_null(context);
break;
}
@@ -510,32 +504,16 @@ absFunc(sql_context * context, int argc, sql_value ** argv)
sql_result_uint(context, -value);
break;
}
- case MP_NIL:{
- /* IMP: R-37434-19929 Abs(X) returns NULL if X is NULL. */
- sql_result_null(context);
- break;
- }
- case MP_BOOL:
- case MP_BIN:
- case MP_ARRAY:
- case MP_MAP: {
- diag_set(ClientError, ER_INCONSISTENT_TYPES, "number",
- mem_type_to_str(argv[0]));
- context->is_aborted = true;
- return;
+ case MP_DOUBLE: {
+ double rVal = sql_value_double(argv[0]);
+ if (rVal < 0)
+ rVal = -rVal;
+ sql_result_double(context, rVal);
+ break;
}
- default:{
- /* Because sql_value_double() returns 0.0 if the argument is not
- * something that can be converted into a number, we have:
- * IMP: R-01992-00519 Abs(X) returns 0.0 if X is a string or blob
- * that cannot be converted to a numeric value.
- */
- double rVal = sql_value_double(argv[0]);
- if (rVal < 0)
- rVal = -rVal;
- sql_result_double(context, rVal);
- break;
- }
+ default:
+ assert(sql_value_type(argv[0]) == MP_NIL);
+ sql_result_null(context);
}
}
@@ -561,22 +539,8 @@ position_func(struct sql_context *context, int argc, struct Mem **argv)
if (haystack_type == MP_NIL || needle_type == MP_NIL)
return;
- /*
- * Position function can be called only with string
- * or blob params.
- */
- struct Mem *inconsistent_type_arg = NULL;
- if (needle_type != MP_STR && needle_type != MP_BIN)
- inconsistent_type_arg = needle;
- if (haystack_type != MP_STR && haystack_type != MP_BIN)
- inconsistent_type_arg = haystack;
- if (inconsistent_type_arg != NULL) {
- diag_set(ClientError, ER_INCONSISTENT_TYPES,
- "text or varbinary",
- mem_type_to_str(inconsistent_type_arg));
- context->is_aborted = true;
- return;
- }
+ assert(needle_type == MP_STR || needle_type == MP_BIN);
+ assert(haystack_type == MP_STR || haystack_type == MP_BIN);
/*
* Both params of Position function must be of the same
* type.
@@ -731,6 +695,9 @@ substrFunc(sql_context * context, int argc, sql_value ** argv)
return;
}
p0type = sql_value_type(argv[0]);
+ assert(p0type == MP_NIL || p0type == MP_STR || p0type == MP_BIN);
+ assert(sql_value_type(argv[1]) == MP_INT ||
+ sql_value_type(argv[1]) == MP_UINT);
p1 = sql_value_int(argv[1]);
if (p0type == MP_BIN) {
len = sql_value_bytes(argv[0]);
@@ -747,6 +714,8 @@ substrFunc(sql_context * context, int argc, sql_value ** argv)
len = sql_utf8_char_count(z, sql_value_bytes(argv[0]));
}
if (argc == 3) {
+ assert(sql_value_type(argv[2]) == MP_INT ||
+ sql_value_type(argv[2]) == MP_UINT);
p2 = sql_value_int(argv[2]);
if (p2 < 0) {
p2 = -p2;
@@ -811,6 +780,8 @@ substrFunc(sql_context * context, int argc, sql_value ** argv)
SQL_TRANSIENT);
}
}
+enum field_type func_substr_types[] = {FIELD_TYPE_STRING, FIELD_TYPE_INTEGER,
+ FIELD_TYPE_INTEGER};
/*
* Implementation of the round() function
@@ -824,19 +795,14 @@ roundFunc(sql_context * context, int argc, sql_value ** argv)
if (argc == 2) {
if (sql_value_is_null(argv[1]))
return;
+ assert(sql_value_type(argv[1]) == MP_UINT);
n = sql_value_int(argv[1]);
if (n < 0)
n = 0;
}
if (sql_value_is_null(argv[0]))
return;
- enum mp_type mp_type = sql_value_type(argv[0]);
- if (mp_type_is_bloblike(mp_type)) {
- diag_set(ClientError, ER_SQL_TYPE_MISMATCH,
- sql_value_to_diag_str(argv[0]), "numeric");
- context->is_aborted = true;
- return;
- }
+ assert(sql_value_type(argv[0]) == MP_DOUBLE);
r = sql_value_double(argv[0]);
/* If Y==0 and X will fit in a 64-bit int,
* handle the rounding directly,
@@ -852,6 +818,7 @@ roundFunc(sql_context * context, int argc, sql_value ** argv)
}
sql_result_double(context, r);
}
+enum field_type func_round_types[] = {FIELD_TYPE_DOUBLE, FIELD_TYPE_UNSIGNED};
/*
* Allocate nByte bytes of space using sqlMalloc(). If the
@@ -891,13 +858,9 @@ case_type##ICUFunc(sql_context *context, int argc, sql_value **argv) \
const char *z2; \
int n; \
UNUSED_PARAMETER(argc); \
- int arg_type = sql_value_type(argv[0]); \
- if (mp_type_is_bloblike(arg_type)) { \
- diag_set(ClientError, ER_INCONSISTENT_TYPES, "text", \
- "varbinary"); \
- context->is_aborted = true; \
- return; \
- } \
+ assert(sql_value_type(argv[0]) == MP_NIL || \
+ sql_value_type(argv[0]) == MP_STR || \
+ sql_value_type(argv[0]) == MP_BIN); \
z2 = (char *)sql_value_text(argv[0]); \
n = sql_value_bytes(argv[0]); \
/* \
@@ -949,6 +912,7 @@ ICU_CASE_CONVERT(Upper);
* is. We might as well use the "version()" function as a substitute.
*/
#define noopFunc sql_func_version /* Substitute function - never called */
+enum field_type func_likelihood_types[] = {FIELD_TYPE_ANY, FIELD_TYPE_DOUBLE};
/*
* Implementation of random(). Return a random integer.
@@ -973,12 +937,8 @@ randomBlob(sql_context * context, int argc, sql_value ** argv)
unsigned char *p;
assert(argc == 1);
UNUSED_PARAMETER(argc);
- if (mp_type_is_bloblike(sql_value_type(argv[0]))) {
- diag_set(ClientError, ER_SQL_TYPE_MISMATCH,
- sql_value_to_diag_str(argv[0]), "numeric");
- context->is_aborted = true;
- return;
- }
+ assert(sql_value_type(argv[0]) == MP_NIL ||
+ sql_value_type(argv[0]) == MP_UINT);
n = sql_value_int(argv[0]);
if (n < 1)
return;
@@ -1218,17 +1178,9 @@ likeFunc(sql_context *context, int argc, sql_value **argv)
int rhs_type = sql_value_type(argv[0]);
int lhs_type = sql_value_type(argv[1]);
- if (lhs_type != MP_STR || rhs_type != MP_STR) {
- if (lhs_type == MP_NIL || rhs_type == MP_NIL)
- return;
- char *inconsistent_type = rhs_type != MP_STR ?
- mem_type_to_str(argv[0]) :
- mem_type_to_str(argv[1]);
- diag_set(ClientError, ER_INCONSISTENT_TYPES, "text",
- inconsistent_type);
- context->is_aborted = true;
+ if (lhs_type == MP_NIL || rhs_type == MP_NIL)
return;
- }
+ assert(lhs_type == MP_STR && rhs_type == MP_STR);
const char *zB = (const char *) sql_value_text(argv[0]);
const char *zA = (const char *) sql_value_text(argv[1]);
const char *zB_end = zB + sql_value_bytes(argv[0]);
@@ -1252,6 +1204,8 @@ likeFunc(sql_context *context, int argc, sql_value **argv)
assert(zB == (const char *) sql_value_text(argv[0]));
if (argc == 3) {
+ assert(sql_value_type(argv[2]) == MP_NIL ||
+ sql_value_type(argv[2]) == MP_STR);
/*
* The escape character string must consist of a
* single UTF-8 character. Otherwise, return an
@@ -1432,6 +1386,10 @@ quoteFunc(sql_context * context, int argc, sql_value ** argv)
static void
unicodeFunc(sql_context * context, int argc, sql_value ** argv)
{
+ assert(argc == 1);
+ assert(sql_value_type(argv[0]) == MP_NIL ||
+ sql_value_type(argv[0]) == MP_STR ||
+ sql_value_type(argv[0]) == MP_BIN);
const unsigned char *z = sql_value_text(argv[0]);
(void)argc;
if (z && z[0])
@@ -1456,10 +1414,11 @@ charFunc(sql_context * context, int argc, sql_value ** argv)
for (i = 0; i < argc; i++) {
uint64_t x;
unsigned c;
- if (sql_value_type(argv[i]) == MP_INT)
- x = 0xfffd;
- else
- x = sql_value_uint64(argv[i]);
+ enum mp_type type = sql_value_type(argv[i]);
+ if (type == MP_NIL)
+ return;
+ assert(sql_value_type(argv[i]) == MP_UINT);
+ x = sql_value_uint64(argv[i]);
if (x > 0x10ffff)
x = 0xfffd;
c = (unsigned)(x & 0x1fffff);
@@ -1493,6 +1452,9 @@ hexFunc(sql_context * context, int argc, sql_value ** argv)
const unsigned char *pBlob;
char *zHex, *z;
assert(argc == 1);
+ assert(sql_value_type(argv[0]) == MP_NIL ||
+ sql_value_type(argv[0]) == MP_STR ||
+ sql_value_type(argv[0]) == MP_BIN);
UNUSED_PARAMETER(argc);
pBlob = sql_value_blob(argv[0]);
n = sql_value_bytes(argv[0]);
@@ -1518,6 +1480,8 @@ zeroblobFunc(sql_context * context, int argc, sql_value ** argv)
i64 n;
assert(argc == 1);
UNUSED_PARAMETER(argc);
+ assert(sql_value_type(argv[0]) == MP_NIL ||
+ sql_value_type(argv[0]) == MP_UINT);
n = sql_value_int64(argv[0]);
if (n < 0)
n = 0;
@@ -1550,6 +1514,15 @@ replaceFunc(sql_context * context, int argc, sql_value ** argv)
assert(argc == 3);
UNUSED_PARAMETER(argc);
+ assert(sql_value_type(argv[0]) == MP_NIL ||
+ sql_value_type(argv[0]) == MP_STR ||
+ sql_value_type(argv[0]) == MP_BIN);
+ assert(sql_value_type(argv[1]) == MP_NIL ||
+ sql_value_type(argv[1]) == MP_STR ||
+ sql_value_type(argv[1]) == MP_BIN);
+ assert(sql_value_type(argv[2]) == MP_NIL ||
+ sql_value_type(argv[2]) == MP_STR ||
+ sql_value_type(argv[2]) == MP_BIN);
zStr = sql_value_text(argv[0]);
if (zStr == 0)
return;
@@ -1858,13 +1831,9 @@ soundexFunc(sql_context * context, int argc, sql_value ** argv)
1, 2, 6, 2, 3, 0, 1, 0, 2, 0, 2, 0, 0, 0, 0, 0,
};
assert(argc == 1);
- enum mp_type mp_type = sql_value_type(argv[0]);
- if (mp_type_is_bloblike(mp_type)) {
- diag_set(ClientError, ER_SQL_TYPE_MISMATCH,
- sql_value_to_diag_str(argv[0]), "text");
- context->is_aborted = true;
- return;
- }
+ assert(sql_value_type(argv[0]) == MP_NIL ||
+ sql_value_type(argv[0]) == MP_STR ||
+ sql_value_type(argv[0]) == MP_BIN);
zIn = (u8 *) sql_value_text(argv[0]);
if (zIn == 0)
zIn = (u8 *) "";
@@ -1931,15 +1900,7 @@ sum_step(struct sql_context *context, int argc, sql_value **argv)
int type = sql_value_type(argv[0]);
if (type == MP_NIL || p == NULL)
return;
- if (type != MP_DOUBLE && type != MP_INT && type != MP_UINT) {
- if (mem_apply_numeric_type(argv[0]) != 0) {
- diag_set(ClientError, ER_SQL_TYPE_MISMATCH,
- sql_value_to_diag_str(argv[0]), "number");
- context->is_aborted = true;
- return;
- }
- type = sql_value_type(argv[0]);
- }
+ assert(mp_type_is_numeric(type));
p->cnt++;
if (type == MP_INT || type == MP_UINT) {
int64_t v = sql_value_int64(argv[0]);
@@ -2230,6 +2191,9 @@ static struct {
int param_count;
uint32_t min_count;
uint32_t max_count;
+ enum field_type *types;
+ enum field_type recurrent_type;
+ bool is_blob_like_str;
enum field_type returns;
enum func_aggregate aggregate;
bool export_to_sql;
@@ -2238,6 +2202,9 @@ static struct {
.param_count = 1,
.min_count = 1,
.max_count = 1,
+ .types = NULL,
+ .recurrent_type = FIELD_TYPE_NUMBER,
+ .is_blob_like_str = false,
.returns = FIELD_TYPE_NUMBER,
.aggregate = FUNC_AGGREGATE_NONE,
.is_deterministic = true,
@@ -2250,6 +2217,9 @@ static struct {
.param_count = 1,
.min_count = 1,
.max_count = 1,
+ .types = NULL,
+ .recurrent_type = FIELD_TYPE_NUMBER,
+ .is_blob_like_str = false,
.returns = FIELD_TYPE_NUMBER,
.is_deterministic = false,
.aggregate = FUNC_AGGREGATE_GROUP,
@@ -2264,6 +2234,9 @@ static struct {
.param_count = -1,
.min_count = 0,
.max_count = 0,
+ .types = NULL,
+ .recurrent_type = FIELD_TYPE_ANY,
+ .is_blob_like_str = false,
.returns = FIELD_TYPE_ANY,
.aggregate = FUNC_AGGREGATE_NONE,
.is_deterministic = false,
@@ -2276,6 +2249,9 @@ static struct {
.param_count = -1,
.min_count = 0,
.max_count = 0,
+ .types = NULL,
+ .recurrent_type = FIELD_TYPE_ANY,
+ .is_blob_like_str = false,
.returns = FIELD_TYPE_ANY,
.aggregate = FUNC_AGGREGATE_NONE,
.is_deterministic = false,
@@ -2286,6 +2262,9 @@ static struct {
.param_count = -1,
.min_count = 0,
.max_count = SQL_MAX_FUNCTION_ARG,
+ .types = NULL,
+ .recurrent_type = FIELD_TYPE_UNSIGNED,
+ .is_blob_like_str = false,
.returns = FIELD_TYPE_STRING,
.is_deterministic = true,
.aggregate = FUNC_AGGREGATE_NONE,
@@ -2298,6 +2277,9 @@ static struct {
.param_count = 1,
.min_count = 1,
.max_count = 1,
+ .types = NULL,
+ .recurrent_type = FIELD_TYPE_STRING,
+ .is_blob_like_str = true,
.returns = FIELD_TYPE_INTEGER,
.aggregate = FUNC_AGGREGATE_NONE,
.is_deterministic = true,
@@ -2310,6 +2292,9 @@ static struct {
.param_count = 1,
.min_count = 1,
.max_count = 1,
+ .types = NULL,
+ .recurrent_type = FIELD_TYPE_STRING,
+ .is_blob_like_str = true,
.returns = FIELD_TYPE_INTEGER,
.aggregate = FUNC_AGGREGATE_NONE,
.is_deterministic = true,
@@ -2322,6 +2307,9 @@ static struct {
.param_count = -1,
.min_count = 2,
.max_count = SQL_MAX_FUNCTION_ARG,
+ .types = NULL,
+ .recurrent_type = FIELD_TYPE_ANY,
+ .is_blob_like_str = false,
.returns = FIELD_TYPE_SCALAR,
.aggregate = FUNC_AGGREGATE_NONE,
.is_deterministic = true,
@@ -2334,6 +2322,9 @@ static struct {
.param_count = -1,
.min_count = 0,
.max_count = 1,
+ .types = NULL,
+ .recurrent_type = FIELD_TYPE_ANY,
+ .is_blob_like_str = false,
.returns = FIELD_TYPE_INTEGER,
.aggregate = FUNC_AGGREGATE_GROUP,
.is_deterministic = false,
@@ -2348,6 +2339,9 @@ static struct {
.param_count = -1,
.min_count = 0,
.max_count = 0,
+ .types = NULL,
+ .recurrent_type = FIELD_TYPE_ANY,
+ .is_blob_like_str = false,
.returns = FIELD_TYPE_ANY,
.aggregate = FUNC_AGGREGATE_NONE,
.is_deterministic = false,
@@ -2360,6 +2354,9 @@ static struct {
.param_count = -1,
.min_count = 0,
.max_count = 0,
+ .types = NULL,
+ .recurrent_type = FIELD_TYPE_ANY,
+ .is_blob_like_str = false,
.returns = FIELD_TYPE_ANY,
.aggregate = FUNC_AGGREGATE_NONE,
.is_deterministic = false,
@@ -2372,6 +2369,9 @@ static struct {
.param_count = -1,
.min_count = 0,
.max_count = 0,
+ .types = NULL,
+ .recurrent_type = FIELD_TYPE_ANY,
+ .is_blob_like_str = false,
.returns = FIELD_TYPE_ANY,
.aggregate = FUNC_AGGREGATE_NONE,
.is_deterministic = false,
@@ -2384,6 +2384,9 @@ static struct {
.param_count = -1,
.min_count = 0,
.max_count = 0,
+ .types = NULL,
+ .recurrent_type = FIELD_TYPE_ANY,
+ .is_blob_like_str = false,
.returns = FIELD_TYPE_ANY,
.aggregate = FUNC_AGGREGATE_NONE,
.is_deterministic = false,
@@ -2396,6 +2399,9 @@ static struct {
.param_count = -1,
.min_count = 0,
.max_count = 0,
+ .types = NULL,
+ .recurrent_type = FIELD_TYPE_ANY,
+ .is_blob_like_str = false,
.returns = FIELD_TYPE_ANY,
.aggregate = FUNC_AGGREGATE_NONE,
.is_deterministic = false,
@@ -2408,6 +2414,9 @@ static struct {
.param_count = -1,
.min_count = 0,
.max_count = 0,
+ .types = NULL,
+ .recurrent_type = FIELD_TYPE_ANY,
+ .is_blob_like_str = false,
.returns = FIELD_TYPE_ANY,
.aggregate = FUNC_AGGREGATE_NONE,
.is_deterministic = false,
@@ -2420,6 +2429,9 @@ static struct {
.param_count = -1,
.min_count = 0,
.max_count = 0,
+ .types = NULL,
+ .recurrent_type = FIELD_TYPE_ANY,
+ .is_blob_like_str = false,
.returns = FIELD_TYPE_ANY,
.aggregate = FUNC_AGGREGATE_NONE,
.is_deterministic = false,
@@ -2432,6 +2444,9 @@ static struct {
.param_count = -1,
.min_count = 0,
.max_count = 0,
+ .types = NULL,
+ .recurrent_type = FIELD_TYPE_ANY,
+ .is_blob_like_str = false,
.returns = FIELD_TYPE_ANY,
.aggregate = FUNC_AGGREGATE_NONE,
.is_deterministic = false,
@@ -2444,6 +2459,9 @@ static struct {
.param_count = -1,
.min_count = 0,
.max_count = 0,
+ .types = NULL,
+ .recurrent_type = FIELD_TYPE_ANY,
+ .is_blob_like_str = false,
.returns = FIELD_TYPE_ANY,
.aggregate = FUNC_AGGREGATE_NONE,
.is_deterministic = false,
@@ -2456,6 +2474,9 @@ static struct {
.param_count = -1,
.min_count = 0,
.max_count = 0,
+ .types = NULL,
+ .recurrent_type = FIELD_TYPE_ANY,
+ .is_blob_like_str = false,
.returns = FIELD_TYPE_ANY,
.aggregate = FUNC_AGGREGATE_NONE,
.is_deterministic = false,
@@ -2468,6 +2489,9 @@ static struct {
.param_count = -1,
.min_count = 0,
.max_count = 0,
+ .types = NULL,
+ .recurrent_type = FIELD_TYPE_ANY,
+ .is_blob_like_str = false,
.returns = FIELD_TYPE_ANY,
.aggregate = FUNC_AGGREGATE_NONE,
.is_deterministic = false,
@@ -2478,6 +2502,9 @@ static struct {
.param_count = -1,
.min_count = 2,
.max_count = SQL_MAX_FUNCTION_ARG,
+ .types = NULL,
+ .recurrent_type = FIELD_TYPE_ANY,
+ .is_blob_like_str = false,
.returns = FIELD_TYPE_SCALAR,
.aggregate = FUNC_AGGREGATE_NONE,
.is_deterministic = true,
@@ -2490,6 +2517,9 @@ static struct {
.param_count = -1,
.min_count = 1,
.max_count = 2,
+ .types = NULL,
+ .recurrent_type = FIELD_TYPE_SCALAR,
+ .is_blob_like_str = false,
.returns = FIELD_TYPE_STRING,
.aggregate = FUNC_AGGREGATE_GROUP,
.is_deterministic = false,
@@ -2502,6 +2532,9 @@ static struct {
.param_count = 1,
.min_count = 1,
.max_count = 1,
+ .types = NULL,
+ .recurrent_type = FIELD_TYPE_STRING,
+ .is_blob_like_str = true,
.returns = FIELD_TYPE_STRING,
.aggregate = FUNC_AGGREGATE_NONE,
.is_deterministic = true,
@@ -2514,6 +2547,9 @@ static struct {
.param_count = 2,
.min_count = 2,
.max_count = 2,
+ .types = NULL,
+ .recurrent_type = FIELD_TYPE_ANY,
+ .is_blob_like_str = false,
.returns = FIELD_TYPE_INTEGER,
.aggregate = FUNC_AGGREGATE_NONE,
.is_deterministic = true,
@@ -2528,6 +2564,9 @@ static struct {
.param_count = -1,
.min_count = 0,
.max_count = 0,
+ .types = NULL,
+ .recurrent_type = FIELD_TYPE_ANY,
+ .is_blob_like_str = false,
.returns = FIELD_TYPE_ANY,
.aggregate = FUNC_AGGREGATE_NONE,
.is_deterministic = false,
@@ -2538,6 +2577,9 @@ static struct {
.param_count = -1,
.min_count = 2,
.max_count = SQL_MAX_FUNCTION_ARG,
+ .types = NULL,
+ .recurrent_type = FIELD_TYPE_ANY,
+ .is_blob_like_str = false,
.returns = FIELD_TYPE_SCALAR,
.aggregate = FUNC_AGGREGATE_NONE,
.is_deterministic = true,
@@ -2550,6 +2592,9 @@ static struct {
.param_count = 1,
.min_count = 1,
.max_count = 1,
+ .types = NULL,
+ .recurrent_type = FIELD_TYPE_STRING,
+ .is_blob_like_str = true,
.returns = FIELD_TYPE_INTEGER,
.aggregate = FUNC_AGGREGATE_NONE,
.is_deterministic = true,
@@ -2564,6 +2609,9 @@ static struct {
.param_count = -1,
.min_count = 0,
.max_count = 0,
+ .types = NULL,
+ .recurrent_type = FIELD_TYPE_ANY,
+ .is_blob_like_str = false,
.returns = FIELD_TYPE_ANY,
.aggregate = FUNC_AGGREGATE_NONE,
.is_deterministic = false,
@@ -2574,6 +2622,9 @@ static struct {
.param_count = -1,
.min_count = 2,
.max_count = 3,
+ .types = NULL,
+ .recurrent_type = FIELD_TYPE_STRING,
+ .is_blob_like_str = false,
.returns = FIELD_TYPE_INTEGER,
.aggregate = FUNC_AGGREGATE_NONE,
.is_deterministic = true,
@@ -2586,6 +2637,9 @@ static struct {
.param_count = 2,
.min_count = 2,
.max_count = 2,
+ .types = func_likelihood_types,
+ .recurrent_type = field_type_MAX,
+ .is_blob_like_str = false,
.returns = FIELD_TYPE_BOOLEAN,
.aggregate = FUNC_AGGREGATE_NONE,
.is_deterministic = true,
@@ -2598,6 +2652,9 @@ static struct {
.param_count = 1,
.min_count = 1,
.max_count = 1,
+ .types = NULL,
+ .recurrent_type = FIELD_TYPE_ANY,
+ .is_blob_like_str = false,
.returns = FIELD_TYPE_BOOLEAN,
.aggregate = FUNC_AGGREGATE_NONE,
.is_deterministic = true,
@@ -2612,6 +2669,9 @@ static struct {
.param_count = -1,
.min_count = 0,
.max_count = 0,
+ .types = NULL,
+ .recurrent_type = FIELD_TYPE_ANY,
+ .is_blob_like_str = false,
.returns = FIELD_TYPE_ANY,
.aggregate = FUNC_AGGREGATE_NONE,
.is_deterministic = false,
@@ -2622,6 +2682,9 @@ static struct {
.param_count = 1,
.min_count = 1,
.max_count = 1,
+ .types = NULL,
+ .recurrent_type = FIELD_TYPE_STRING,
+ .is_blob_like_str = true,
.returns = FIELD_TYPE_STRING,
.aggregate = FUNC_AGGREGATE_NONE,
.is_deterministic = true,
@@ -2634,6 +2697,9 @@ static struct {
.param_count = 1,
.min_count = 1,
.max_count = 1,
+ .types = NULL,
+ .recurrent_type = FIELD_TYPE_ANY,
+ .is_blob_like_str = false,
.returns = FIELD_TYPE_SCALAR,
.aggregate = FUNC_AGGREGATE_GROUP,
.is_deterministic = false,
@@ -2646,6 +2712,9 @@ static struct {
.param_count = 1,
.min_count = 1,
.max_count = 1,
+ .types = NULL,
+ .recurrent_type = FIELD_TYPE_ANY,
+ .is_blob_like_str = false,
.returns = FIELD_TYPE_SCALAR,
.aggregate = FUNC_AGGREGATE_GROUP,
.is_deterministic = false,
@@ -2660,6 +2729,9 @@ static struct {
.param_count = -1,
.min_count = 0,
.max_count = 0,
+ .types = NULL,
+ .recurrent_type = FIELD_TYPE_ANY,
+ .is_blob_like_str = false,
.returns = FIELD_TYPE_ANY,
.aggregate = FUNC_AGGREGATE_NONE,
.is_deterministic = false,
@@ -2670,6 +2742,9 @@ static struct {
.param_count = 2,
.min_count = 2,
.max_count = 2,
+ .types = NULL,
+ .recurrent_type = FIELD_TYPE_ANY,
+ .is_blob_like_str = false,
.returns = FIELD_TYPE_SCALAR,
.aggregate = FUNC_AGGREGATE_NONE,
.is_deterministic = true,
@@ -2684,6 +2759,9 @@ static struct {
.param_count = -1,
.min_count = 0,
.max_count = 0,
+ .types = NULL,
+ .recurrent_type = FIELD_TYPE_ANY,
+ .is_blob_like_str = false,
.returns = FIELD_TYPE_ANY,
.aggregate = FUNC_AGGREGATE_NONE,
.is_deterministic = false,
@@ -2694,6 +2772,9 @@ static struct {
.param_count = 2,
.min_count = 2,
.max_count = 2,
+ .types = NULL,
+ .recurrent_type = FIELD_TYPE_STRING,
+ .is_blob_like_str = true,
.returns = FIELD_TYPE_INTEGER,
.aggregate = FUNC_AGGREGATE_NONE,
.is_deterministic = true,
@@ -2708,6 +2789,9 @@ static struct {
.param_count = -1,
.min_count = 0,
.max_count = 0,
+ .types = NULL,
+ .recurrent_type = FIELD_TYPE_ANY,
+ .is_blob_like_str = false,
.returns = FIELD_TYPE_ANY,
.aggregate = FUNC_AGGREGATE_NONE,
.is_deterministic = false,
@@ -2718,6 +2802,9 @@ static struct {
.param_count = -1,
.min_count = 0,
.max_count = SQL_MAX_FUNCTION_ARG,
+ .types = NULL,
+ .recurrent_type = FIELD_TYPE_ANY,
+ .is_blob_like_str = false,
.returns = FIELD_TYPE_STRING,
.aggregate = FUNC_AGGREGATE_NONE,
.is_deterministic = true,
@@ -2730,6 +2817,9 @@ static struct {
.param_count = 1,
.min_count = 1,
.max_count = 1,
+ .types = NULL,
+ .recurrent_type = FIELD_TYPE_ANY,
+ .is_blob_like_str = false,
.returns = FIELD_TYPE_STRING,
.aggregate = FUNC_AGGREGATE_NONE,
.is_deterministic = true,
@@ -2742,6 +2832,9 @@ static struct {
.param_count = 0,
.min_count = 0,
.max_count = 0,
+ .types = NULL,
+ .recurrent_type = FIELD_TYPE_ANY,
+ .is_blob_like_str = false,
.returns = FIELD_TYPE_INTEGER,
.aggregate = FUNC_AGGREGATE_NONE,
.is_deterministic = false,
@@ -2754,6 +2847,9 @@ static struct {
.param_count = 1,
.min_count = 1,
.max_count = 1,
+ .types = NULL,
+ .recurrent_type = FIELD_TYPE_UNSIGNED,
+ .is_blob_like_str = false,
.returns = FIELD_TYPE_VARBINARY,
.aggregate = FUNC_AGGREGATE_NONE,
.is_deterministic = false,
@@ -2766,6 +2862,9 @@ static struct {
.param_count = 3,
.min_count = 3,
.max_count = 3,
+ .types = NULL,
+ .recurrent_type = FIELD_TYPE_STRING,
+ .is_blob_like_str = true,
.returns = FIELD_TYPE_STRING,
.aggregate = FUNC_AGGREGATE_NONE,
.is_deterministic = true,
@@ -2778,6 +2877,9 @@ static struct {
.param_count = -1,
.min_count = 1,
.max_count = 2,
+ .types = func_round_types,
+ .recurrent_type = field_type_MAX,
+ .is_blob_like_str = false,
.returns = FIELD_TYPE_INTEGER,
.aggregate = FUNC_AGGREGATE_NONE,
.is_deterministic = true,
@@ -2790,6 +2892,9 @@ static struct {
.param_count = 0,
.min_count = 0,
.max_count = 0,
+ .types = NULL,
+ .recurrent_type = FIELD_TYPE_ANY,
+ .is_blob_like_str = false,
.returns = FIELD_TYPE_INTEGER,
.aggregate = FUNC_AGGREGATE_NONE,
.is_deterministic = true,
@@ -2804,6 +2909,9 @@ static struct {
.param_count = -1,
.min_count = 0,
.max_count = 0,
+ .types = NULL,
+ .recurrent_type = FIELD_TYPE_ANY,
+ .is_blob_like_str = false,
.returns = FIELD_TYPE_ANY,
.aggregate = FUNC_AGGREGATE_NONE,
.is_deterministic = false,
@@ -2814,6 +2922,9 @@ static struct {
.param_count = 1,
.min_count = 1,
.max_count = 1,
+ .types = NULL,
+ .recurrent_type = FIELD_TYPE_STRING,
+ .is_blob_like_str = true,
.returns = FIELD_TYPE_STRING,
.aggregate = FUNC_AGGREGATE_NONE,
.is_deterministic = true,
@@ -2828,6 +2939,9 @@ static struct {
.param_count = -1,
.min_count = 0,
.max_count = 0,
+ .types = NULL,
+ .recurrent_type = FIELD_TYPE_ANY,
+ .is_blob_like_str = false,
.returns = FIELD_TYPE_ANY,
.aggregate = FUNC_AGGREGATE_NONE,
.is_deterministic = false,
@@ -2840,6 +2954,9 @@ static struct {
.param_count = -1,
.min_count = 0,
.max_count = 0,
+ .types = NULL,
+ .recurrent_type = FIELD_TYPE_ANY,
+ .is_blob_like_str = false,
.returns = FIELD_TYPE_ANY,
.aggregate = FUNC_AGGREGATE_NONE,
.is_deterministic = false,
@@ -2850,6 +2967,9 @@ static struct {
.param_count = -1,
.min_count = 2,
.max_count = 3,
+ .types = func_substr_types,
+ .recurrent_type = field_type_MAX,
+ .is_blob_like_str = true,
.returns = FIELD_TYPE_STRING,
.aggregate = FUNC_AGGREGATE_NONE,
.is_deterministic = true,
@@ -2862,6 +2982,9 @@ static struct {
.param_count = 1,
.min_count = 1,
.max_count = 1,
+ .types = NULL,
+ .recurrent_type = FIELD_TYPE_NUMBER,
+ .is_blob_like_str = false,
.returns = FIELD_TYPE_NUMBER,
.aggregate = FUNC_AGGREGATE_GROUP,
.is_deterministic = false,
@@ -2876,6 +2999,9 @@ static struct {
.param_count = -1,
.min_count = 0,
.max_count = 0,
+ .types = NULL,
+ .recurrent_type = FIELD_TYPE_ANY,
+ .is_blob_like_str = false,
.returns = FIELD_TYPE_ANY,
.aggregate = FUNC_AGGREGATE_NONE,
.is_deterministic = false,
@@ -2886,6 +3012,9 @@ static struct {
.param_count = 1,
.min_count = 1,
.max_count = 1,
+ .types = NULL,
+ .recurrent_type = FIELD_TYPE_NUMBER,
+ .is_blob_like_str = false,
.returns = FIELD_TYPE_NUMBER,
.aggregate = FUNC_AGGREGATE_GROUP,
.is_deterministic = false,
@@ -2898,6 +3027,9 @@ static struct {
.param_count = -1,
.min_count = 1,
.max_count = 3,
+ .types = NULL,
+ .recurrent_type = FIELD_TYPE_ANY,
+ .is_blob_like_str = false,
.returns = FIELD_TYPE_STRING,
.aggregate = FUNC_AGGREGATE_NONE,
.is_deterministic = true,
@@ -2910,6 +3042,9 @@ static struct {
.param_count = 1,
.min_count = 1,
.max_count = 1,
+ .types = NULL,
+ .recurrent_type = FIELD_TYPE_ANY,
+ .is_blob_like_str = false,
.returns = FIELD_TYPE_STRING,
.aggregate = FUNC_AGGREGATE_NONE,
.is_deterministic = true,
@@ -2922,6 +3057,9 @@ static struct {
.param_count = 1,
.min_count = 1,
.max_count = 1,
+ .types = NULL,
+ .recurrent_type = FIELD_TYPE_STRING,
+ .is_blob_like_str = true,
.returns = FIELD_TYPE_STRING,
.aggregate = FUNC_AGGREGATE_NONE,
.is_deterministic = true,
@@ -2934,6 +3072,9 @@ static struct {
.param_count = 1,
.min_count = 1,
.max_count = 1,
+ .types = NULL,
+ .recurrent_type = FIELD_TYPE_ANY,
+ .is_blob_like_str = false,
.returns = FIELD_TYPE_BOOLEAN,
.aggregate = FUNC_AGGREGATE_NONE,
.is_deterministic = true,
@@ -2946,6 +3087,9 @@ static struct {
.param_count = 1,
.min_count = 1,
.max_count = 1,
+ .types = NULL,
+ .recurrent_type = FIELD_TYPE_STRING,
+ .is_blob_like_str = true,
.returns = FIELD_TYPE_STRING,
.aggregate = FUNC_AGGREGATE_NONE,
.is_deterministic = true,
@@ -2958,6 +3102,9 @@ static struct {
.param_count = 0,
.min_count = 0,
.max_count = 0,
+ .types = NULL,
+ .recurrent_type = FIELD_TYPE_ANY,
+ .is_blob_like_str = false,
.returns = FIELD_TYPE_STRING,
.aggregate = FUNC_AGGREGATE_NONE,
.is_deterministic = true,
@@ -2970,6 +3117,9 @@ static struct {
.param_count = 1,
.min_count = 1,
.max_count = 1,
+ .types = NULL,
+ .recurrent_type = FIELD_TYPE_UNSIGNED,
+ .is_blob_like_str = false,
.returns = FIELD_TYPE_VARBINARY,
.aggregate = FUNC_AGGREGATE_NONE,
.is_deterministic = true,
@@ -2984,6 +3134,9 @@ static struct {
.param_count = -1,
.min_count = 0,
.max_count = 0,
+ .types = NULL,
+ .recurrent_type = FIELD_TYPE_ANY,
+ .is_blob_like_str = false,
.returns = FIELD_TYPE_ANY,
.aggregate = FUNC_AGGREGATE_NONE,
.is_deterministic = false,
@@ -2996,6 +3149,9 @@ static struct {
.param_count = -1,
.min_count = 0,
.max_count = 0,
+ .types = NULL,
+ .recurrent_type = FIELD_TYPE_ANY,
+ .is_blob_like_str = false,
.returns = FIELD_TYPE_ANY,
.aggregate = FUNC_AGGREGATE_NONE,
.is_deterministic = false,
@@ -3008,6 +3164,9 @@ static struct {
.param_count = -1,
.min_count = 0,
.max_count = 0,
+ .types = false,
+ .recurrent_type = FIELD_TYPE_ANY,
+ .is_blob_like_str = false,
.returns = FIELD_TYPE_ANY,
.aggregate = FUNC_AGGREGATE_NONE,
.is_deterministic = false,
@@ -3061,6 +3220,9 @@ func_sql_builtin_new(struct func_def *def)
func->finalize = sql_builtins[idx].finalize;
func->args.min_count = sql_builtins[idx].min_count;
func->args.max_count = sql_builtins[idx].max_count;
+ func->args.types = sql_builtins[idx].types;
+ func->args.recurrent_type = sql_builtins[idx].recurrent_type;
+ func->args.is_blob_like_str = sql_builtins[idx].is_blob_like_str;
def->param_count = sql_builtins[idx].param_count;
def->is_deterministic = sql_builtins[idx].is_deterministic;
def->returns = sql_builtins[idx].returns;
diff --git a/src/box/sql/select.c b/src/box/sql/select.c
index 4b069addb..fe56ede1b 100644
--- a/src/box/sql/select.c
+++ b/src/box/sql/select.c
@@ -124,6 +124,34 @@ clearSelect(sql * db, Select * p, int bFree)
}
}
+void
+sql_emit_func_types(struct Vdbe *vdbe, struct sql_builtin_func_args *args,
+ int reg, uint32_t argc)
+{
+ assert(argc <= args->max_count);
+ enum field_type recurrent_type = args->recurrent_type;
+ assert(args->max_count > 0 || recurrent_type == FIELD_TYPE_ANY);
+ /*
+ * It makes no sense to check types of the MEMs if all
+ * arguments should be of type ANY.
+ */
+ if (recurrent_type == FIELD_TYPE_ANY)
+ return;
+ size_t size = (argc + 1) * sizeof(enum field_type);
+ enum field_type *types = sqlDbMallocZero(sql_get(), size);
+ for (uint32_t i = 0; i < argc; ++i) {
+ if (args->types == NULL)
+ types[i] = args->recurrent_type;
+ else
+ types[i] = args->types[i];
+ }
+ types[argc] = field_type_MAX;
+ sqlVdbeAddOp4(vdbe, OP_ApplyType, reg, argc, 0, (char *)types,
+ P4_DYNAMIC);
+ if (args->is_blob_like_str)
+ sqlVdbeChangeP5(vdbe, OPFLAG_BLOB_LIKE_STRING);
+}
+
/*
* Initialize a SelectDest structure.
*/
@@ -5414,6 +5442,9 @@ updateAccumulator(Parse * pParse, AggInfo * pAggInfo)
sqlVdbeAddOp4(v, OP_CollSeq, regHit, 0, 0,
(char *)coll, P4_COLLSEQ);
}
+ struct func_sql_builtin *f =
+ (struct func_sql_builtin *)pF->func;
+ sql_emit_func_types(v, &f->args, regAgg, nArg);
sqlVdbeAddOp3(v, OP_AggStep0, 0, regAgg, pF->iMem);
sqlVdbeAppendP4(v, pF->func, P4_FUNC);
sqlVdbeChangeP5(v, (u8) nArg);
diff --git a/src/box/sql/sqlInt.h b/src/box/sql/sqlInt.h
index 6af9d7473..f82ae4eeb 100644
--- a/src/box/sql/sqlInt.h
+++ b/src/box/sql/sqlInt.h
@@ -265,6 +265,8 @@
#include <assert.h>
#include <stddef.h>
+struct sql_builtin_func_args;
+
typedef long long int sql_int64;
typedef unsigned long long int sql_uint64;
typedef sql_int64 sql_int64;
@@ -2307,6 +2309,8 @@ struct Parse {
#define OPFLAG_SYSTEMSP 0x20 /* OP_Open**: set if space pointer
* points to system space.
*/
+/** OP_ApplyType: Treat BLOB as STRING. */
+#define OPFLAG_BLOB_LIKE_STRING 0x01
/**
* Prepare vdbe P5 flags for OP_{IdxInsert, IdxReplace, Update}
@@ -3881,6 +3885,20 @@ sql_index_type_str(struct sql *db, const struct index_def *idx_def);
void
sql_emit_table_types(struct Vdbe *v, struct space_def *def, int reg);
+/**
+ * Code an OP_ApplyType opcode that will force types for given
+ * range of register starting from @a reg. These values then will
+ * be used as arguments of a function.
+ *
+ * @param vdbe VDBE.
+ * @param args Information about arguments of the function.
+ * @param reg Register where types will be placed.
+ * @param argc Number of arguments.
+ */
+void
+sql_emit_func_types(struct Vdbe *vdbe, struct sql_builtin_func_args *args,
+ int reg, uint32_t argc);
+
enum field_type
sql_type_result(enum field_type lhs, enum field_type rhs);
@@ -4406,6 +4424,20 @@ struct sql_builtin_func_args {
uint32_t min_count;
/** Max number of arguments. */
uint32_t max_count;
+ /**
+ * If function arguments may not be of the same type, all
+ * argument types are described here.
+ */
+ enum field_type *types;
+ /**
+ * Contains the type of arguments if all arguments to the
+ * function are of the same type.
+ */
+ enum field_type recurrent_type;
+ /**
+ * TRUE if the function should treat the BLOB as STRING.
+ */
+ bool is_blob_like_str;
};
struct func_sql_builtin {
diff --git a/src/box/sql/vdbe.c b/src/box/sql/vdbe.c
index 863f38f5d..a4bf84bcc 100644
--- a/src/box/sql/vdbe.c
+++ b/src/box/sql/vdbe.c
@@ -2882,7 +2882,7 @@ case OP_Fetch: {
break;
}
-/* Opcode: ApplyType P1 P2 * P4 *
+/* Opcode: ApplyType P1 P2 * P4 P5
* Synopsis: type(r[P1@P2])
*
* Check that types of P2 registers starting from register P1 are
@@ -2890,6 +2890,9 @@ case OP_Fetch: {
* value and the given type are incompatible according to
* field_mp_plain_type_is_compatible(), but both are numeric,
* this opcode attempts to convert the value to the type.
+ *
+ * If P5 contains the OPFLAG_BLOB_LIKE_STRING flag, the BLOB
+ * values are processed as if they had the field type STRING.
*/
case OP_ApplyType: {
enum field_type *types = pOp->p4.types;
@@ -2904,6 +2907,13 @@ case OP_ApplyType: {
pIn1++;
continue;
}
+ if ((pOp->p5 & OPFLAG_BLOB_LIKE_STRING) != 0) {
+ if (type == FIELD_TYPE_STRING &&
+ mem_mp_type(pIn1) == MP_BIN) {
+ pIn1++;
+ continue;
+ }
+ }
if (!mp_type_is_numeric(mem_mp_type(pIn1)) ||
!sql_type_is_numeric(type) ||
mem_convert_to_numeric(pIn1, type, false) != 0) {
diff --git a/test/sql-tap/cse.test.lua b/test/sql-tap/cse.test.lua
index 341b6de01..3c2076a1d 100755
--- a/test/sql-tap/cse.test.lua
+++ b/test/sql-tap/cse.test.lua
@@ -195,23 +195,23 @@ test:do_execsql_test(
-test:do_execsql_test(
+test:do_catchsql_test(
"cse-1.13",
[[
SELECT upper(b), typeof(b), b FROM t1
]], {
-- <cse-1.13>
- "11", "integer", 11, "21", "integer", 21
+ 1, "Type mismatch: can not convert 11 to string"
-- </cse-1.13>
})
-test:do_execsql_test(
+test:do_catchsql_test(
"cse-1.14",
[[
SELECT b, typeof(b), upper(b), typeof(b), b FROM t1
]], {
-- <cse-1.14>
- 11, "integer", "11", "integer", 11, 21, "integer", "21", "integer", 21
+ 1, "Type mismatch: can not convert 11 to string"
-- </cse-1.14>
})
diff --git a/test/sql-tap/func.test.lua b/test/sql-tap/func.test.lua
index 1d3ef9e2a..ae97994d7 100755
--- a/test/sql-tap/func.test.lua
+++ b/test/sql-tap/func.test.lua
@@ -92,13 +92,13 @@ test:do_execsql_test(
-- </func-1.3>
})
-test:do_execsql_test(
+test:do_catchsql_test(
"func-1.4",
[[
SELECT coalesce(length(a),-1) FROM t2
]], {
-- <func-1.4>
- 1, -1, 3, -1, 5
+ 1, "Type mismatch: can not convert 1 to string"
-- </func-1.4>
})
@@ -194,23 +194,23 @@ test:do_execsql_test(
-- </func-2.8>
})
-test:do_execsql_test(
+test:do_catchsql_test(
"func-2.9",
[[
SELECT substr(a,1,1) FROM t2
]], {
-- <func-2.9>
- "1", "", "3", "", "6"
+ 1, "Type mismatch: can not convert 1 to string"
-- </func-2.9>
})
-test:do_execsql_test(
+test:do_catchsql_test(
"func-2.10",
[[
SELECT substr(a,2,2) FROM t2
]], {
-- <func-2.10>
- "", "", "45", "", "78"
+ 1, "Type mismatch: can not convert 1 to string"
-- </func-2.10>
})
@@ -412,13 +412,13 @@ test:do_execsql_test(
-- </func-4.4.1>
})
-test:do_execsql_test(
+test:do_catchsql_test(
"func-4.4.2",
[[
SELECT abs(t1) FROM tbl1
]], {
-- <func-4.4.2>
- 0.0, 0.0, 0.0, 0.0, 0.0
+ 1, "Type mismatch: can not convert this to number"
-- </func-4.4.2>
})
@@ -502,13 +502,13 @@ test:do_execsql_test(
-- </func-4.12>
})
-test:do_execsql_test(
+test:do_catchsql_test(
"func-4.13",
[[
SELECT round(t1,2) FROM tbl1
]], {
-- <func-4.13>
- 0.0, 0.0, 0.0, 0.0, 0.0
+ 1, "Type mismatch: can not convert this to double"
-- </func-4.13>
})
@@ -760,13 +760,13 @@ test:do_execsql_test(
-- </func-5.2>
})
-test:do_execsql_test(
+test:do_catchsql_test(
"func-5.3",
[[
SELECT upper(a), lower(a) FROM t2
]], {
-- <func-5.3>
- "1","1","","","345","345","","","67890","67890"
+ 1, "Type mismatch: can not convert 1 to string"
-- </func-5.3>
})
@@ -794,13 +794,13 @@ test:do_execsql_test(
-- </func-6.1>
})
-test:do_execsql_test(
+test:do_catchsql_test(
"func-6.2",
[[
SELECT coalesce(upper(a),'nil') FROM t2
]], {
-- <func-6.2>
- "1","nil","345","nil","67890"
+ 1, "Type mismatch: can not convert 1 to string"
-- </func-6.2>
})
@@ -893,7 +893,7 @@ test:do_execsql_test(
test:do_execsql_test(
"func-8.5",
[[
- SELECT sum(x) FROM (SELECT '9223372036' || '854775807' AS x
+ SELECT sum(x) FROM (SELECT CAST('9223372036' || '854775807' AS INTEGER) AS x
UNION ALL SELECT -9223372036854775807)
]], {
-- <func-8.5>
@@ -904,7 +904,7 @@ test:do_execsql_test(
test:do_execsql_test(
"func-8.6",
[[
- SELECT typeof(sum(x)) FROM (SELECT '9223372036' || '854775807' AS x
+ SELECT typeof(sum(x)) FROM (SELECT CAST('9223372036' || '854775807' AS INTEGER) AS x
UNION ALL SELECT -9223372036854775807)
]], {
-- <func-8.6>
@@ -915,7 +915,7 @@ test:do_execsql_test(
test:do_execsql_test(
"func-8.7",
[[
- SELECT typeof(sum(x)) FROM (SELECT '9223372036' || '854775808' AS x
+ SELECT typeof(sum(x)) FROM (SELECT CAST('9223372036' || '854775808' AS INTEGER) AS x
UNION ALL SELECT -9223372036854775807)
]], {
-- <func-8.7>
@@ -926,7 +926,7 @@ test:do_execsql_test(
test:do_execsql_test(
"func-8.8",
[[
- SELECT sum(x)>0.0 FROM (SELECT '9223372036' || '854775808' AS x
+ SELECT sum(x)>0.0 FROM (SELECT CAST('9223372036' || '854775808' AS INTEGER) AS x
UNION ALL SELECT -9223372036850000000)
]], {
-- <func-8.8>
@@ -982,14 +982,14 @@ test:do_execsql_test(
-- </func-9.4>
})
-test:do_execsql_test(
+test:do_catchsql_test(
"func-9.5",
[[
SELECT length(randomblob(32)), length(randomblob(-5)),
length(randomblob(2000))
]], {
-- <func-9.5>
- 32, "", 2000
+ 1, "Type mismatch: can not convert -5 to unsigned"
-- </func-9.5>
})
@@ -2918,7 +2918,7 @@ test:do_catchsql_test(
SELECT ROUND(X'FF')
]], {
-- <func-76.1>
- 1, "Type mismatch: can not convert varbinary to numeric"
+ 1, "Type mismatch: can not convert varbinary to double"
-- </func-76.1>
})
@@ -2928,17 +2928,17 @@ test:do_catchsql_test(
SELECT RANDOMBLOB(X'FF')
]], {
-- <func-76.2>
- 1, "Type mismatch: can not convert varbinary to numeric"
+ 1, "Type mismatch: can not convert varbinary to unsigned"
-- </func-76.2>
})
-test:do_catchsql_test(
+test:do_execsql_test(
"func-76.3",
[[
SELECT SOUNDEX(X'FF')
]], {
-- <func-76.3>
- 1, "Type mismatch: can not convert varbinary to text"
+ "?000"
-- </func-76.3>
})
diff --git a/test/sql-tap/orderby1.test.lua b/test/sql-tap/orderby1.test.lua
index 51e8d301f..95a8de487 100755
--- a/test/sql-tap/orderby1.test.lua
+++ b/test/sql-tap/orderby1.test.lua
@@ -735,7 +735,7 @@ test:do_execsql_test(
SELECT (
SELECT 'hardware' FROM (
SELECT 'software' ORDER BY 'firmware' ASC, 'sportswear' DESC
- ) GROUP BY 1 HAVING length(b) <> 0
+ ) GROUP BY 1 HAVING length(CAST(b AS STRING)) <> 0
)
FROM abc;
]], {
diff --git a/test/sql-tap/position.test.lua b/test/sql-tap/position.test.lua
index e0455abc9..0d4f6f371 100755
--- a/test/sql-tap/position.test.lua
+++ b/test/sql-tap/position.test.lua
@@ -228,7 +228,7 @@ test:do_test(
return test:catchsql "SELECT position(34, 12345);"
end, {
-- <position-1.23>
- 1, "Inconsistent types: expected text or varbinary got unsigned"
+ 1, "Type mismatch: can not convert 34 to string"
-- </position-1.23>
})
@@ -238,7 +238,7 @@ test:do_test(
return test:catchsql "SELECT position(34, 123456.78);"
end, {
-- <position-1.24>
- 1, "Inconsistent types: expected text or varbinary got real"
+ 1, "Type mismatch: can not convert 34 to string"
-- </position-1.24>
})
@@ -248,7 +248,7 @@ test:do_test(
return test:catchsql "SELECT position(x'3334', 123456.78);"
end, {
-- <position-1.25>
- 1, "Inconsistent types: expected text or varbinary got real"
+ 1, "Type mismatch: can not convert 123456.78 to string"
-- </position-1.25>
})
diff --git a/test/sql/boolean.result b/test/sql/boolean.result
index 112e41a12..7e7f97284 100644
--- a/test/sql/boolean.result
+++ b/test/sql/boolean.result
@@ -276,29 +276,17 @@ SELECT is_boolean('true');
SELECT abs(a) FROM t0;
| ---
| - null
- | - 'Inconsistent types: expected number got boolean'
+ | - 'Type mismatch: can not convert FALSE to number'
| ...
SELECT lower(a) FROM t0;
| ---
- | - metadata:
- | - name: lower(a)
- | type: string
- | rows:
- | - ['false']
- | - ['true']
- | - [null]
- | - [null]
+ | - null
+ | - 'Type mismatch: can not convert FALSE to string'
| ...
SELECT upper(a) FROM t0;
| ---
- | - metadata:
- | - name: upper(a)
- | type: string
- | rows:
- | - ['FALSE']
- | - ['TRUE']
- | - [null]
- | - [null]
+ | - null
+ | - 'Type mismatch: can not convert FALSE to string'
| ...
SELECT quote(a) FROM t0;
| ---
@@ -314,14 +302,8 @@ SELECT quote(a) FROM t0;
-- gh-4462: LENGTH didn't take BOOLEAN arguments.
SELECT length(a) FROM t0;
| ---
- | - metadata:
- | - name: length(a)
- | type: integer
- | rows:
- | - [5]
- | - [4]
- | - [null]
- | - [null]
+ | - null
+ | - 'Type mismatch: can not convert FALSE to string'
| ...
SELECT typeof(a) FROM t0;
| ---
diff --git a/test/sql/gh-4159-function-argumens.result b/test/sql/gh-4159-function-argumens.result
index 48bd550a4..4a276d65b 100644
--- a/test/sql/gh-4159-function-argumens.result
+++ b/test/sql/gh-4159-function-argumens.result
@@ -19,6 +19,54 @@ SELECT abs(1, 2);
| - 'Wrong number of arguments is passed to ABS(): expected 1, got 2'
| ...
+SELECT abs(NULL);
+ | ---
+ | - metadata:
+ | - name: abs(NULL)
+ | type: number
+ | rows:
+ | - [null]
+ | ...
+SELECT abs(1);
+ | ---
+ | - metadata:
+ | - name: abs(1)
+ | type: number
+ | rows:
+ | - [1]
+ | ...
+SELECT abs(-1);
+ | ---
+ | - metadata:
+ | - name: abs(-1)
+ | type: number
+ | rows:
+ | - [1]
+ | ...
+SELECT abs(-1.5);
+ | ---
+ | - metadata:
+ | - name: abs(-1.5)
+ | type: number
+ | rows:
+ | - [1.5]
+ | ...
+SELECT abs(true);
+ | ---
+ | - null
+ | - 'Type mismatch: can not convert TRUE to number'
+ | ...
+SELECT abs('abc');
+ | ---
+ | - null
+ | - 'Type mismatch: can not convert abc to number'
+ | ...
+SELECT abs(X'3334');
+ | ---
+ | - null
+ | - 'Type mismatch: can not convert varbinary to number'
+ | ...
+
-- Function char().
SELECT char();
| ---
@@ -45,6 +93,36 @@ SELECT char(1, 2);
| - ["\x01\x02"]
| ...
+SELECT char(NULL);
+ | ---
+ | - metadata:
+ | - name: char(NULL)
+ | type: string
+ | rows:
+ | - [null]
+ | ...
+SELECT char(1);
+ | ---
+ | - metadata:
+ | - name: char(1)
+ | type: string
+ | rows:
+ | - ["\x01"]
+ | ...
+SELECT char(-1);
+ | ---
+ | - null
+ | - 'Type mismatch: can not convert -1 to unsigned'
+ | ...
+SELECT char(-1.5);
+ | ---
+ | - null
+ | - 'Type mismatch: can not convert -1.5 to unsigned'
+ | ...
+-- SELECT char(true);
+-- SELECT char('abc');
+-- SELECT char(X'3334');
+
-- Function character_length().
SELECT character_length();
| ---
@@ -65,6 +143,51 @@ SELECT character_length('1', '2');
| - 'Wrong number of arguments is passed to CHARACTER_LENGTH(): expected 1, got 2'
| ...
+SELECT character_length(NULL);
+ | ---
+ | - metadata:
+ | - name: character_length(NULL)
+ | type: integer
+ | rows:
+ | - [null]
+ | ...
+SELECT character_length(1);
+ | ---
+ | - null
+ | - 'Type mismatch: can not convert 1 to string'
+ | ...
+SELECT character_length(-1);
+ | ---
+ | - null
+ | - 'Type mismatch: can not convert -1 to string'
+ | ...
+SELECT character_length(-1.5);
+ | ---
+ | - null
+ | - 'Type mismatch: can not convert -1.5 to string'
+ | ...
+SELECT character_length(true);
+ | ---
+ | - null
+ | - 'Type mismatch: can not convert TRUE to string'
+ | ...
+SELECT character_length('abc');
+ | ---
+ | - metadata:
+ | - name: character_length('abc')
+ | type: integer
+ | rows:
+ | - [3]
+ | ...
+SELECT character_length(X'3334');
+ | ---
+ | - metadata:
+ | - name: character_length(X'3334')
+ | type: integer
+ | rows:
+ | - [2]
+ | ...
+
-- Function char_length().
SELECT char_length();
| ---
@@ -85,6 +208,51 @@ SELECT char_length('1', '2');
| - 'Wrong number of arguments is passed to CHAR_LENGTH(): expected 1, got 2'
| ...
+SELECT char_length(NULL);
+ | ---
+ | - metadata:
+ | - name: char_length(NULL)
+ | type: integer
+ | rows:
+ | - [null]
+ | ...
+SELECT char_length(1);
+ | ---
+ | - null
+ | - 'Type mismatch: can not convert 1 to string'
+ | ...
+SELECT char_length(-1);
+ | ---
+ | - null
+ | - 'Type mismatch: can not convert -1 to string'
+ | ...
+SELECT char_length(-1.5);
+ | ---
+ | - null
+ | - 'Type mismatch: can not convert -1.5 to string'
+ | ...
+SELECT char_length(true);
+ | ---
+ | - null
+ | - 'Type mismatch: can not convert TRUE to string'
+ | ...
+SELECT char_length('abc');
+ | ---
+ | - metadata:
+ | - name: char_length('abc')
+ | type: integer
+ | rows:
+ | - [3]
+ | ...
+SELECT char_length(X'3334');
+ | ---
+ | - metadata:
+ | - name: char_length(X'3334')
+ | type: integer
+ | rows:
+ | - [2]
+ | ...
+
-- Function coalesce().
SELECT coalesce();
| ---
@@ -115,6 +283,63 @@ SELECT coalesce('1', '2', '3');
| - ['1']
| ...
+SELECT coalesce(NULL, NULL);
+ | ---
+ | - metadata:
+ | - name: coalesce(NULL, NULL)
+ | type: scalar
+ | rows:
+ | - [null]
+ | ...
+SELECT coalesce(1, 1);
+ | ---
+ | - metadata:
+ | - name: coalesce(1, 1)
+ | type: scalar
+ | rows:
+ | - [1]
+ | ...
+SELECT coalesce(-1, -1);
+ | ---
+ | - metadata:
+ | - name: coalesce(-1, -1)
+ | type: scalar
+ | rows:
+ | - [-1]
+ | ...
+SELECT coalesce(-1.5, -1.5);
+ | ---
+ | - metadata:
+ | - name: coalesce(-1.5, -1.5)
+ | type: scalar
+ | rows:
+ | - [-1.5]
+ | ...
+SELECT coalesce(true, true);
+ | ---
+ | - metadata:
+ | - name: coalesce(true, true)
+ | type: scalar
+ | rows:
+ | - [true]
+ | ...
+SELECT coalesce('abc', 'abc');
+ | ---
+ | - metadata:
+ | - name: coalesce('abc', 'abc')
+ | type: scalar
+ | rows:
+ | - ['abc']
+ | ...
+SELECT coalesce(X'3334', X'3334');
+ | ---
+ | - metadata:
+ | - name: coalesce(X'3334', X'3334')
+ | type: scalar
+ | rows:
+ | - ['34']
+ | ...
+
-- Function greatest().
SELECT greatest();
| ---
@@ -137,6 +362,63 @@ SELECT greatest('1', '2');
| - ['2']
| ...
+SELECT greatest(NULL, NULL);
+ | ---
+ | - metadata:
+ | - name: greatest(NULL, NULL)
+ | type: scalar
+ | rows:
+ | - [null]
+ | ...
+SELECT greatest(1, 1);
+ | ---
+ | - metadata:
+ | - name: greatest(1, 1)
+ | type: scalar
+ | rows:
+ | - [1]
+ | ...
+SELECT greatest(-1, -1);
+ | ---
+ | - metadata:
+ | - name: greatest(-1, -1)
+ | type: scalar
+ | rows:
+ | - [-1]
+ | ...
+SELECT greatest(-1.5, -1.5);
+ | ---
+ | - metadata:
+ | - name: greatest(-1.5, -1.5)
+ | type: scalar
+ | rows:
+ | - [-1.5]
+ | ...
+SELECT greatest(true, true);
+ | ---
+ | - metadata:
+ | - name: greatest(true, true)
+ | type: scalar
+ | rows:
+ | - [true]
+ | ...
+SELECT greatest('abc', 'abc');
+ | ---
+ | - metadata:
+ | - name: greatest('abc', 'abc')
+ | type: scalar
+ | rows:
+ | - ['abc']
+ | ...
+SELECT greatest(X'3334', X'3334');
+ | ---
+ | - metadata:
+ | - name: greatest(X'3334', X'3334')
+ | type: scalar
+ | rows:
+ | - ['34']
+ | ...
+
-- Function hex().
SELECT hex();
| ---
@@ -157,6 +439,51 @@ SELECT hex(X'33', X'33');
| - 'Wrong number of arguments is passed to HEX(): expected 1, got 2'
| ...
+SELECT hex(NULL);
+ | ---
+ | - metadata:
+ | - name: hex(NULL)
+ | type: string
+ | rows:
+ | - ['']
+ | ...
+SELECT hex(1);
+ | ---
+ | - null
+ | - 'Type mismatch: can not convert 1 to string'
+ | ...
+SELECT hex(-1);
+ | ---
+ | - null
+ | - 'Type mismatch: can not convert -1 to string'
+ | ...
+SELECT hex(-1.5);
+ | ---
+ | - null
+ | - 'Type mismatch: can not convert -1.5 to string'
+ | ...
+SELECT hex(true);
+ | ---
+ | - null
+ | - 'Type mismatch: can not convert TRUE to string'
+ | ...
+SELECT hex('abc');
+ | ---
+ | - metadata:
+ | - name: hex('abc')
+ | type: string
+ | rows:
+ | - ['616263']
+ | ...
+SELECT hex(X'3334');
+ | ---
+ | - metadata:
+ | - name: hex(X'3334')
+ | type: string
+ | rows:
+ | - ['3334']
+ | ...
+
-- Function ifnull
SELECT ifnull();
| ---
@@ -182,46 +509,205 @@ SELECT ifnull(1, 2, 3);
| - 'Wrong number of arguments is passed to IFNULL(): expected 2, got 3'
| ...
--- Function least().
-SELECT least();
+SELECT ifnull(NULL, NULL);
| ---
- | - null
- | - 'Wrong number of arguments is passed to LEAST(): expected from 2 to 127, got 0'
+ | - metadata:
+ | - name: ifnull(NULL, NULL)
+ | type: integer
+ | rows:
+ | - [null]
| ...
-SELECT least('1');
+SELECT ifnull(1, 1);
| ---
- | - null
- | - 'Wrong number of arguments is passed to LEAST(): expected from 2 to 127, got 1'
+ | - metadata:
+ | - name: ifnull(1, 1)
+ | type: integer
+ | rows:
+ | - [1]
| ...
-SELECT least('1', '2');
+SELECT ifnull(-1, -1);
| ---
| - metadata:
- | - name: least('1', '2')
- | type: scalar
+ | - name: ifnull(-1, -1)
+ | type: integer
| rows:
- | - ['1']
+ | - [-1]
| ...
-
--- Function length().
-SELECT length();
+SELECT ifnull(-1.5, -1.5);
| ---
- | - null
- | - 'Wrong number of arguments is passed to LENGTH(): expected 1, got 0'
+ | - metadata:
+ | - name: ifnull(-1.5, -1.5)
+ | type: integer
+ | rows:
+ | - [-1.5]
| ...
-SELECT length('1');
+SELECT ifnull(true, true);
| ---
| - metadata:
- | - name: length('1')
+ | - name: ifnull(true, true)
| type: integer
| rows:
- | - [1]
+ | - [true]
| ...
-SELECT length('1', '2');
+SELECT ifnull('abc', 'abc');
| ---
- | - null
+ | - metadata:
+ | - name: ifnull('abc', 'abc')
+ | type: integer
+ | rows:
+ | - ['abc']
+ | ...
+SELECT ifnull(X'3334', X'3334');
+ | ---
+ | - metadata:
+ | - name: ifnull(X'3334', X'3334')
+ | type: integer
+ | rows:
+ | - ['34']
+ | ...
+
+-- Function least().
+SELECT least();
+ | ---
+ | - null
+ | - 'Wrong number of arguments is passed to LEAST(): expected from 2 to 127, got 0'
+ | ...
+SELECT least('1');
+ | ---
+ | - null
+ | - 'Wrong number of arguments is passed to LEAST(): expected from 2 to 127, got 1'
+ | ...
+SELECT least('1', '2');
+ | ---
+ | - metadata:
+ | - name: least('1', '2')
+ | type: scalar
+ | rows:
+ | - ['1']
+ | ...
+
+SELECT least(NULL, NULL);
+ | ---
+ | - metadata:
+ | - name: least(NULL, NULL)
+ | type: scalar
+ | rows:
+ | - [null]
+ | ...
+SELECT least(1, 1);
+ | ---
+ | - metadata:
+ | - name: least(1, 1)
+ | type: scalar
+ | rows:
+ | - [1]
+ | ...
+SELECT least(-1, -1);
+ | ---
+ | - metadata:
+ | - name: least(-1, -1)
+ | type: scalar
+ | rows:
+ | - [-1]
+ | ...
+SELECT least(-1.5, -1.5);
+ | ---
+ | - metadata:
+ | - name: least(-1.5, -1.5)
+ | type: scalar
+ | rows:
+ | - [-1.5]
+ | ...
+SELECT least(true, true);
+ | ---
+ | - metadata:
+ | - name: least(true, true)
+ | type: scalar
+ | rows:
+ | - [true]
+ | ...
+SELECT least('abc', 'abc');
+ | ---
+ | - metadata:
+ | - name: least('abc', 'abc')
+ | type: scalar
+ | rows:
+ | - ['abc']
+ | ...
+SELECT least(X'3334', X'3334');
+ | ---
+ | - metadata:
+ | - name: least(X'3334', X'3334')
+ | type: scalar
+ | rows:
+ | - ['34']
+ | ...
+
+-- Function length().
+SELECT length();
+ | ---
+ | - null
+ | - 'Wrong number of arguments is passed to LENGTH(): expected 1, got 0'
+ | ...
+SELECT length('1');
+ | ---
+ | - metadata:
+ | - name: length('1')
+ | type: integer
+ | rows:
+ | - [1]
+ | ...
+SELECT length('1', '2');
+ | ---
+ | - null
| - 'Wrong number of arguments is passed to LENGTH(): expected 1, got 2'
| ...
+SELECT length(NULL);
+ | ---
+ | - metadata:
+ | - name: length(NULL)
+ | type: integer
+ | rows:
+ | - [null]
+ | ...
+SELECT length(1);
+ | ---
+ | - null
+ | - 'Type mismatch: can not convert 1 to string'
+ | ...
+SELECT length(-1);
+ | ---
+ | - null
+ | - 'Type mismatch: can not convert -1 to string'
+ | ...
+SELECT length(-1.5);
+ | ---
+ | - null
+ | - 'Type mismatch: can not convert -1.5 to string'
+ | ...
+SELECT length(true);
+ | ---
+ | - null
+ | - 'Type mismatch: can not convert TRUE to string'
+ | ...
+SELECT length('abc');
+ | ---
+ | - metadata:
+ | - name: length('abc')
+ | type: integer
+ | rows:
+ | - [3]
+ | ...
+SELECT length(X'3334');
+ | ---
+ | - metadata:
+ | - name: length(X'3334')
+ | type: integer
+ | rows:
+ | - [2]
+ | ...
+
-- Function likelihood
SELECT likelihood();
| ---
@@ -247,6 +733,49 @@ SELECT likelihood(1, 0.5, 3);
| - 'Wrong number of arguments is passed to LIKELIHOOD(): expected 2, got 3'
| ...
+SELECT likelihood(NULL, NULL);
+ | ---
+ | - null
+ | - Illegal parameters, second argument to likelihood() must be a constant between 0.0
+ | and 1.0
+ | ...
+SELECT likelihood(1, 1);
+ | ---
+ | - null
+ | - Illegal parameters, second argument to likelihood() must be a constant between 0.0
+ | and 1.0
+ | ...
+SELECT likelihood(-1, -1);
+ | ---
+ | - null
+ | - Illegal parameters, second argument to likelihood() must be a constant between 0.0
+ | and 1.0
+ | ...
+SELECT likelihood(-1.5, -1.5);
+ | ---
+ | - null
+ | - Illegal parameters, second argument to likelihood() must be a constant between 0.0
+ | and 1.0
+ | ...
+SELECT likelihood(true, true);
+ | ---
+ | - null
+ | - Illegal parameters, second argument to likelihood() must be a constant between 0.0
+ | and 1.0
+ | ...
+SELECT likelihood('abc', 'abc');
+ | ---
+ | - null
+ | - Illegal parameters, second argument to likelihood() must be a constant between 0.0
+ | and 1.0
+ | ...
+SELECT likelihood(X'3334', X'3334');
+ | ---
+ | - null
+ | - Illegal parameters, second argument to likelihood() must be a constant between 0.0
+ | and 1.0
+ | ...
+
-- Function likely
SELECT likely();
| ---
@@ -267,6 +796,63 @@ SELECT likely(1, 2);
| - 'Wrong number of arguments is passed to LIKELY(): expected 1, got 2'
| ...
+SELECT likely(NULL);
+ | ---
+ | - metadata:
+ | - name: likely(NULL)
+ | type: scalar
+ | rows:
+ | - [null]
+ | ...
+SELECT likely(1);
+ | ---
+ | - metadata:
+ | - name: likely(1)
+ | type: integer
+ | rows:
+ | - [1]
+ | ...
+SELECT likely(-1);
+ | ---
+ | - metadata:
+ | - name: likely(-1)
+ | type: integer
+ | rows:
+ | - [-1]
+ | ...
+SELECT likely(-1.5);
+ | ---
+ | - metadata:
+ | - name: likely(-1.5)
+ | type: double
+ | rows:
+ | - [-1.5]
+ | ...
+SELECT likely(true);
+ | ---
+ | - metadata:
+ | - name: likely(true)
+ | type: boolean
+ | rows:
+ | - [true]
+ | ...
+SELECT likely('abc');
+ | ---
+ | - metadata:
+ | - name: likely('abc')
+ | type: string
+ | rows:
+ | - ['abc']
+ | ...
+SELECT likely(X'3334');
+ | ---
+ | - metadata:
+ | - name: likely(X'3334')
+ | type: varbinary
+ | rows:
+ | - ['34']
+ | ...
+
-- Function lower
SELECT lower();
| ---
@@ -287,6 +873,51 @@ SELECT lower('a', 2);
| - 'Wrong number of arguments is passed to LOWER(): expected 1, got 2'
| ...
+SELECT lower(NULL);
+ | ---
+ | - metadata:
+ | - name: lower(NULL)
+ | type: string
+ | rows:
+ | - [null]
+ | ...
+SELECT lower(1);
+ | ---
+ | - null
+ | - 'Type mismatch: can not convert 1 to string'
+ | ...
+SELECT lower(-1);
+ | ---
+ | - null
+ | - 'Type mismatch: can not convert -1 to string'
+ | ...
+SELECT lower(-1.5);
+ | ---
+ | - null
+ | - 'Type mismatch: can not convert -1.5 to string'
+ | ...
+SELECT lower(true);
+ | ---
+ | - null
+ | - 'Type mismatch: can not convert TRUE to string'
+ | ...
+SELECT lower('abc');
+ | ---
+ | - metadata:
+ | - name: lower('abc')
+ | type: string
+ | rows:
+ | - ['abc']
+ | ...
+SELECT lower(X'3334');
+ | ---
+ | - metadata:
+ | - name: lower(X'3334')
+ | type: string
+ | rows:
+ | - ['34']
+ | ...
+
-- Function nullif
SELECT nullif();
| ---
@@ -312,6 +943,63 @@ SELECT nullif(1, 2, 3);
| - 'Wrong number of arguments is passed to NULLIF(): expected 2, got 3'
| ...
+SELECT nullif(NULL, NULL);
+ | ---
+ | - metadata:
+ | - name: nullif(NULL, NULL)
+ | type: scalar
+ | rows:
+ | - [null]
+ | ...
+SELECT nullif(1, 1);
+ | ---
+ | - metadata:
+ | - name: nullif(1, 1)
+ | type: scalar
+ | rows:
+ | - [null]
+ | ...
+SELECT nullif(-1, -1);
+ | ---
+ | - metadata:
+ | - name: nullif(-1, -1)
+ | type: scalar
+ | rows:
+ | - [null]
+ | ...
+SELECT nullif(-1.5, -1.5);
+ | ---
+ | - metadata:
+ | - name: nullif(-1.5, -1.5)
+ | type: scalar
+ | rows:
+ | - [null]
+ | ...
+SELECT nullif(true, true);
+ | ---
+ | - metadata:
+ | - name: nullif(true, true)
+ | type: scalar
+ | rows:
+ | - [null]
+ | ...
+SELECT nullif('abc', 'abc');
+ | ---
+ | - metadata:
+ | - name: nullif('abc', 'abc')
+ | type: scalar
+ | rows:
+ | - [null]
+ | ...
+SELECT nullif(X'3334', X'3334');
+ | ---
+ | - metadata:
+ | - name: nullif(X'3334', X'3334')
+ | type: scalar
+ | rows:
+ | - [null]
+ | ...
+
-- Function position
SELECT position();
| ---
@@ -337,366 +1025,1075 @@ SELECT position('12345', '2', 3);
| - 'Wrong number of arguments is passed to POSITION(): expected 2, got 3'
| ...
--- Function printf
-SELECT printf();
+SELECT position(NULL, NULL);
+ | ---
+ | - metadata:
+ | - name: position(NULL, NULL)
+ | type: integer
+ | rows:
+ | - [null]
+ | ...
+SELECT position(1, 1);
+ | ---
+ | - null
+ | - 'Type mismatch: can not convert 1 to string'
+ | ...
+SELECT position(-1, -1);
+ | ---
+ | - null
+ | - 'Type mismatch: can not convert -1 to string'
+ | ...
+SELECT position(-1.5, -1.5);
+ | ---
+ | - null
+ | - 'Type mismatch: can not convert -1.5 to string'
+ | ...
+SELECT position(true, true);
+ | ---
+ | - null
+ | - 'Type mismatch: can not convert TRUE to string'
+ | ...
+SELECT position('abc', 'abc');
+ | ---
+ | - metadata:
+ | - name: position('abc', 'abc')
+ | type: integer
+ | rows:
+ | - [1]
+ | ...
+SELECT position(X'3334', X'3334');
+ | ---
+ | - metadata:
+ | - name: position(X'3334', X'3334')
+ | type: integer
+ | rows:
+ | - [1]
+ | ...
+
+-- Function printf
+SELECT printf();
+ | ---
+ | - metadata:
+ | - name: printf()
+ | type: string
+ | rows:
+ | - [null]
+ | ...
+SELECT printf('1');
+ | ---
+ | - metadata:
+ | - name: printf('1')
+ | type: string
+ | rows:
+ | - ['1']
+ | ...
+SELECT printf('1', 2);
+ | ---
+ | - metadata:
+ | - name: printf('1', 2)
+ | type: string
+ | rows:
+ | - ['1']
+ | ...
+SELECT printf('1', 2, 3);
+ | ---
+ | - metadata:
+ | - name: printf('1', 2, 3)
+ | type: string
+ | rows:
+ | - ['1']
+ | ...
+
+SELECT printf(NULL, NULL);
+ | ---
+ | - metadata:
+ | - name: printf(NULL, NULL)
+ | type: string
+ | rows:
+ | - [null]
+ | ...
+SELECT printf(1, 1);
+ | ---
+ | - metadata:
+ | - name: printf(1, 1)
+ | type: string
+ | rows:
+ | - ['1']
+ | ...
+SELECT printf(-1, -1);
+ | ---
+ | - metadata:
+ | - name: printf(-1, -1)
+ | type: string
+ | rows:
+ | - ['-1']
+ | ...
+SELECT printf(-1.5, -1.5);
+ | ---
+ | - metadata:
+ | - name: printf(-1.5, -1.5)
+ | type: string
+ | rows:
+ | - ['-1.5']
+ | ...
+SELECT printf(true, true);
+ | ---
+ | - metadata:
+ | - name: printf(true, true)
+ | type: string
+ | rows:
+ | - ['TRUE']
+ | ...
+SELECT printf('abc', 'abc');
+ | ---
+ | - metadata:
+ | - name: printf('abc', 'abc')
+ | type: string
+ | rows:
+ | - ['abc']
+ | ...
+SELECT printf(X'3334', X'3334');
+ | ---
+ | - metadata:
+ | - name: printf(X'3334', X'3334')
+ | type: string
+ | rows:
+ | - ['34']
+ | ...
+
+-- Function quote
+SELECT quote();
+ | ---
+ | - null
+ | - 'Wrong number of arguments is passed to QUOTE(): expected 1, got 0'
+ | ...
+SELECT quote('1');
+ | ---
+ | - metadata:
+ | - name: quote('1')
+ | type: string
+ | rows:
+ | - ['''1''']
+ | ...
+SELECT quote('1', 2);
+ | ---
+ | - null
+ | - 'Wrong number of arguments is passed to QUOTE(): expected 1, got 2'
+ | ...
+
+SELECT quote(NULL);
+ | ---
+ | - metadata:
+ | - name: quote(NULL)
+ | type: string
+ | rows:
+ | - ['NULL']
+ | ...
+SELECT quote(1);
+ | ---
+ | - metadata:
+ | - name: quote(1)
+ | type: string
+ | rows:
+ | - [1]
+ | ...
+SELECT quote(-1);
+ | ---
+ | - metadata:
+ | - name: quote(-1)
+ | type: string
+ | rows:
+ | - [-1]
+ | ...
+SELECT quote(-1.5);
+ | ---
+ | - metadata:
+ | - name: quote(-1.5)
+ | type: string
+ | rows:
+ | - ['-1.5']
+ | ...
+SELECT quote(true);
+ | ---
+ | - metadata:
+ | - name: quote(true)
+ | type: string
+ | rows:
+ | - ['TRUE']
+ | ...
+SELECT quote('abc');
+ | ---
+ | - metadata:
+ | - name: quote('abc')
+ | type: string
+ | rows:
+ | - ['''abc''']
+ | ...
+SELECT quote(X'3334');
+ | ---
+ | - metadata:
+ | - name: quote(X'3334')
+ | type: string
+ | rows:
+ | - ['X''3334''']
+ | ...
+
+-- Function random
+SELECT typeof(random());
+ | ---
+ | - metadata:
+ | - name: typeof(random())
+ | type: string
+ | rows:
+ | - ['integer']
+ | ...
+SELECT typeof(random(1));
+ | ---
+ | - null
+ | - 'Wrong number of arguments is passed to RANDOM(): expected 0, got 1'
+ | ...
+
+-- Function randomblob
+SELECT typeof(randomblob());
+ | ---
+ | - null
+ | - 'Wrong number of arguments is passed to RANDOMBLOB(): expected 1, got 0'
+ | ...
+SELECT typeof(randomblob(1));
+ | ---
+ | - metadata:
+ | - name: typeof(randomblob(1))
+ | type: string
+ | rows:
+ | - ['varbinary']
+ | ...
+SELECT typeof(randomblob(1, 2));
+ | ---
+ | - null
+ | - 'Wrong number of arguments is passed to RANDOMBLOB(): expected 1, got 2'
+ | ...
+
+SELECT randomblob(NULL);
+ | ---
+ | - metadata:
+ | - name: randomblob(NULL)
+ | type: varbinary
+ | rows:
+ | - [null]
+ | ...
+SELECT typeof(randomblob(1));
+ | ---
+ | - metadata:
+ | - name: typeof(randomblob(1))
+ | type: string
+ | rows:
+ | - ['varbinary']
+ | ...
+SELECT typeof(randomblob(-1));
+ | ---
+ | - null
+ | - 'Type mismatch: can not convert -1 to unsigned'
+ | ...
+SELECT typeof(randomblob(-1.5));
+ | ---
+ | - null
+ | - 'Type mismatch: can not convert -1.5 to unsigned'
+ | ...
+SELECT typeof(randomblob(true));
+ | ---
+ | - null
+ | - 'Type mismatch: can not convert TRUE to unsigned'
+ | ...
+SELECT typeof(randomblob('abc'));
+ | ---
+ | - null
+ | - 'Type mismatch: can not convert abc to unsigned'
+ | ...
+SELECT typeof(randomblob(X'3334'));
+ | ---
+ | - null
+ | - 'Type mismatch: can not convert varbinary to unsigned'
+ | ...
+
+-- Function replace
+SELECT replace();
+ | ---
+ | - null
+ | - 'Wrong number of arguments is passed to REPLACE(): expected 3, got 0'
+ | ...
+SELECT replace('12345');
+ | ---
+ | - null
+ | - 'Wrong number of arguments is passed to REPLACE(): expected 3, got 1'
+ | ...
+SELECT replace('12345', '2');
+ | ---
+ | - null
+ | - 'Wrong number of arguments is passed to REPLACE(): expected 3, got 2'
+ | ...
+SELECT replace('12345', '2', '3');
+ | ---
+ | - metadata:
+ | - name: replace('12345', '2', '3')
+ | type: string
+ | rows:
+ | - ['13345']
+ | ...
+SELECT replace('12345', '2', '3', 4);
+ | ---
+ | - null
+ | - 'Wrong number of arguments is passed to REPLACE(): expected 3, got 4'
+ | ...
+
+SELECT replace(NULL, NULL, NULL);
+ | ---
+ | - metadata:
+ | - name: replace(NULL, NULL, NULL)
+ | type: string
+ | rows:
+ | - [null]
+ | ...
+SELECT replace(1, 1, 1);
+ | ---
+ | - null
+ | - 'Type mismatch: can not convert 1 to string'
+ | ...
+SELECT replace(-1, -1, -1);
+ | ---
+ | - null
+ | - 'Type mismatch: can not convert -1 to string'
+ | ...
+SELECT replace(-1.5, -1.5, -1.5);
+ | ---
+ | - null
+ | - 'Type mismatch: can not convert -1.5 to string'
+ | ...
+SELECT replace(true, true, true);
+ | ---
+ | - null
+ | - 'Type mismatch: can not convert TRUE to string'
+ | ...
+SELECT replace('abc', 'abc', 'abc');
+ | ---
+ | - metadata:
+ | - name: replace('abc', 'abc', 'abc')
+ | type: string
+ | rows:
+ | - ['abc']
+ | ...
+SELECT replace(X'3334', X'3334', X'3334');
+ | ---
+ | - metadata:
+ | - name: replace(X'3334', X'3334', X'3334')
+ | type: string
+ | rows:
+ | - ['34']
+ | ...
+
+-- Function round
+SELECT round();
+ | ---
+ | - null
+ | - 'Wrong number of arguments is passed to ROUND(): expected from 1 to 2, got 0'
+ | ...
+SELECT round(1.1245);
+ | ---
+ | - metadata:
+ | - name: round(1.1245)
+ | type: integer
+ | rows:
+ | - [1]
+ | ...
+SELECT round(1.1245, 2);
+ | ---
+ | - metadata:
+ | - name: round(1.1245, 2)
+ | type: integer
+ | rows:
+ | - [1.12]
+ | ...
+SELECT round(1.1245, 2, 3);
+ | ---
+ | - null
+ | - 'Wrong number of arguments is passed to ROUND(): expected from 1 to 2, got 3'
+ | ...
+
+SELECT round(NULL, NULL);
+ | ---
+ | - metadata:
+ | - name: round(NULL, NULL)
+ | type: integer
+ | rows:
+ | - [null]
+ | ...
+SELECT round(1, 1);
+ | ---
+ | - metadata:
+ | - name: round(1, 1)
+ | type: integer
+ | rows:
+ | - [1]
+ | ...
+SELECT round(-1, -1);
+ | ---
+ | - null
+ | - 'Type mismatch: can not convert -1 to unsigned'
+ | ...
+SELECT round(-1.5, -1.5);
+ | ---
+ | - null
+ | - 'Type mismatch: can not convert -1.5 to unsigned'
+ | ...
+SELECT round(true, true);
+ | ---
+ | - null
+ | - 'Type mismatch: can not convert TRUE to double'
+ | ...
+SELECT round('abc', 'abc');
+ | ---
+ | - null
+ | - 'Type mismatch: can not convert abc to double'
+ | ...
+SELECT round(X'3334', X'3334');
+ | ---
+ | - null
+ | - 'Type mismatch: can not convert varbinary to double'
+ | ...
+
+-- Function row_count
+SELECT row_count();
+ | ---
+ | - metadata:
+ | - name: row_count()
+ | type: integer
+ | rows:
+ | - [0]
+ | ...
+SELECT row_count(1);
+ | ---
+ | - null
+ | - 'Wrong number of arguments is passed to ROW_COUNT(): expected 0, got 1'
+ | ...
+
+-- Function soundex
+SELECT soundex();
+ | ---
+ | - null
+ | - 'Wrong number of arguments is passed to SOUNDEX(): expected 1, got 0'
+ | ...
+SELECT soundex(1);
+ | ---
+ | - null
+ | - 'Type mismatch: can not convert 1 to string'
+ | ...
+SELECT soundex(1, 2);
+ | ---
+ | - null
+ | - 'Wrong number of arguments is passed to SOUNDEX(): expected 1, got 2'
+ | ...
+
+SELECT soundex(NULL);
+ | ---
+ | - metadata:
+ | - name: soundex(NULL)
+ | type: string
+ | rows:
+ | - ['?000']
+ | ...
+SELECT soundex(1);
+ | ---
+ | - null
+ | - 'Type mismatch: can not convert 1 to string'
+ | ...
+SELECT soundex(-1);
+ | ---
+ | - null
+ | - 'Type mismatch: can not convert -1 to string'
+ | ...
+SELECT soundex(-1.5);
+ | ---
+ | - null
+ | - 'Type mismatch: can not convert -1.5 to string'
+ | ...
+SELECT soundex(true);
+ | ---
+ | - null
+ | - 'Type mismatch: can not convert TRUE to string'
+ | ...
+SELECT soundex('abc');
+ | ---
+ | - metadata:
+ | - name: soundex('abc')
+ | type: string
+ | rows:
+ | - ['A120']
+ | ...
+SELECT soundex(X'3334');
+ | ---
+ | - metadata:
+ | - name: soundex(X'3334')
+ | type: string
+ | rows:
+ | - ['?000']
+ | ...
+
+-- Function substr
+SELECT substr();
+ | ---
+ | - null
+ | - 'Wrong number of arguments is passed to SUBSTR(): expected from 2 to 3, got 0'
+ | ...
+SELECT substr('12345');
+ | ---
+ | - null
+ | - 'Wrong number of arguments is passed to SUBSTR(): expected from 2 to 3, got 1'
+ | ...
+SELECT substr('12345', 2);
+ | ---
+ | - metadata:
+ | - name: substr('12345', 2)
+ | type: string
+ | rows:
+ | - ['2345']
+ | ...
+SELECT substr('12345', 2, 3);
+ | ---
+ | - metadata:
+ | - name: substr('12345', 2, 3)
+ | type: string
+ | rows:
+ | - ['234']
+ | ...
+SELECT substr('12345', 2, 3, 4);
+ | ---
+ | - null
+ | - 'Wrong number of arguments is passed to SUBSTR(): expected from 2 to 3, got 4'
+ | ...
+
+SELECT substr(NULL, NULL);
+ | ---
+ | - metadata:
+ | - name: substr(NULL, NULL)
+ | type: string
+ | rows:
+ | - [null]
+ | ...
+SELECT substr(1, 1);
+ | ---
+ | - null
+ | - 'Type mismatch: can not convert 1 to string'
+ | ...
+SELECT substr(-1, -1);
+ | ---
+ | - null
+ | - 'Type mismatch: can not convert -1 to string'
+ | ...
+SELECT substr(-1.5, -1.5);
+ | ---
+ | - null
+ | - 'Type mismatch: can not convert -1.5 to string'
+ | ...
+SELECT substr(true, true);
+ | ---
+ | - null
+ | - 'Type mismatch: can not convert TRUE to string'
+ | ...
+SELECT substr('abc', 'abc');
+ | ---
+ | - null
+ | - 'Type mismatch: can not convert abc to integer'
+ | ...
+SELECT substr(X'3334', X'3334');
+ | ---
+ | - null
+ | - 'Type mismatch: can not convert varbinary to integer'
+ | ...
+
+-- Function typeof
+SELECT typeof();
+ | ---
+ | - null
+ | - 'Wrong number of arguments is passed to TYPEOF(): expected 1, got 0'
+ | ...
+SELECT typeof(1);
+ | ---
+ | - metadata:
+ | - name: typeof(1)
+ | type: string
+ | rows:
+ | - ['integer']
+ | ...
+SELECT typeof(1, 2);
+ | ---
+ | - null
+ | - 'Wrong number of arguments is passed to TYPEOF(): expected 1, got 2'
+ | ...
+
+SELECT typeof(NULL);
+ | ---
+ | - metadata:
+ | - name: typeof(NULL)
+ | type: string
+ | rows:
+ | - ['boolean']
+ | ...
+SELECT typeof(1);
+ | ---
+ | - metadata:
+ | - name: typeof(1)
+ | type: string
+ | rows:
+ | - ['integer']
+ | ...
+SELECT typeof(-1);
+ | ---
+ | - metadata:
+ | - name: typeof(-1)
+ | type: string
+ | rows:
+ | - ['integer']
+ | ...
+SELECT typeof(-1.5);
+ | ---
+ | - metadata:
+ | - name: typeof(-1.5)
+ | type: string
+ | rows:
+ | - ['double']
+ | ...
+SELECT typeof(true);
+ | ---
+ | - metadata:
+ | - name: typeof(true)
+ | type: string
+ | rows:
+ | - ['boolean']
+ | ...
+SELECT typeof('abc');
+ | ---
+ | - metadata:
+ | - name: typeof('abc')
+ | type: string
+ | rows:
+ | - ['string']
+ | ...
+SELECT typeof(X'3334');
+ | ---
+ | - metadata:
+ | - name: typeof(X'3334')
+ | type: string
+ | rows:
+ | - ['varbinary']
+ | ...
+
+-- Function unicode
+SELECT unicode();
+ | ---
+ | - null
+ | - 'Wrong number of arguments is passed to UNICODE(): expected 1, got 0'
+ | ...
+SELECT unicode('1');
+ | ---
+ | - metadata:
+ | - name: unicode('1')
+ | type: string
+ | rows:
+ | - [49]
+ | ...
+SELECT unicode('1', '2');
+ | ---
+ | - null
+ | - 'Wrong number of arguments is passed to UNICODE(): expected 1, got 2'
+ | ...
+
+SELECT unicode(NULL);
+ | ---
+ | - metadata:
+ | - name: unicode(NULL)
+ | type: string
+ | rows:
+ | - [null]
+ | ...
+SELECT unicode(1);
+ | ---
+ | - null
+ | - 'Type mismatch: can not convert 1 to string'
+ | ...
+SELECT unicode(-1);
+ | ---
+ | - null
+ | - 'Type mismatch: can not convert -1 to string'
+ | ...
+SELECT unicode(-1.5);
+ | ---
+ | - null
+ | - 'Type mismatch: can not convert -1.5 to string'
+ | ...
+SELECT unicode(true);
+ | ---
+ | - null
+ | - 'Type mismatch: can not convert TRUE to string'
+ | ...
+SELECT unicode('abc');
+ | ---
+ | - metadata:
+ | - name: unicode('abc')
+ | type: string
+ | rows:
+ | - [97]
+ | ...
+SELECT unicode(X'3334');
+ | ---
+ | - metadata:
+ | - name: unicode(X'3334')
+ | type: string
+ | rows:
+ | - [51]
+ | ...
+
+-- Function unlikely
+SELECT unlikely();
+ | ---
+ | - null
+ | - 'Wrong number of arguments is passed to UNLIKELY(): expected 1, got 0'
+ | ...
+SELECT unlikely(1);
+ | ---
+ | - metadata:
+ | - name: unlikely(1)
+ | type: integer
+ | rows:
+ | - [1]
+ | ...
+SELECT unlikely(1, 2);
+ | ---
+ | - null
+ | - 'Wrong number of arguments is passed to UNLIKELY(): expected 1, got 2'
+ | ...
+
+SELECT unlikely(NULL);
+ | ---
+ | - metadata:
+ | - name: unlikely(NULL)
+ | type: scalar
+ | rows:
+ | - [null]
+ | ...
+SELECT unlikely(1);
+ | ---
+ | - metadata:
+ | - name: unlikely(1)
+ | type: integer
+ | rows:
+ | - [1]
+ | ...
+SELECT unlikely(-1);
| ---
| - metadata:
- | - name: printf()
- | type: string
+ | - name: unlikely(-1)
+ | type: integer
| rows:
- | - [null]
+ | - [-1]
| ...
-SELECT printf('1');
+SELECT unlikely(-1.5);
| ---
| - metadata:
- | - name: printf('1')
- | type: string
+ | - name: unlikely(-1.5)
+ | type: double
| rows:
- | - ['1']
+ | - [-1.5]
| ...
-SELECT printf('1', 2);
+SELECT unlikely(true);
| ---
| - metadata:
- | - name: printf('1', 2)
- | type: string
+ | - name: unlikely(true)
+ | type: boolean
| rows:
- | - ['1']
+ | - [true]
| ...
-SELECT printf('1', 2, 3);
+SELECT unlikely('abc');
| ---
| - metadata:
- | - name: printf('1', 2, 3)
+ | - name: unlikely('abc')
| type: string
| rows:
- | - ['1']
+ | - ['abc']
| ...
-
--- Function quote
-SELECT quote();
- | ---
- | - null
- | - 'Wrong number of arguments is passed to QUOTE(): expected 1, got 0'
- | ...
-SELECT quote('1');
+SELECT unlikely(X'3334');
| ---
| - metadata:
- | - name: quote('1')
- | type: string
+ | - name: unlikely(X'3334')
+ | type: varbinary
| rows:
- | - ['''1''']
+ | - ['34']
| ...
-SELECT quote('1', 2);
+
+-- Function upper
+SELECT upper();
| ---
| - null
- | - 'Wrong number of arguments is passed to QUOTE(): expected 1, got 2'
+ | - 'Wrong number of arguments is passed to UPPER(): expected 1, got 0'
| ...
-
--- Function random
-SELECT typeof(random());
+SELECT upper('a');
| ---
| - metadata:
- | - name: typeof(random())
+ | - name: upper('a')
| type: string
| rows:
- | - ['integer']
+ | - ['A']
| ...
-SELECT typeof(random(1));
+SELECT upper('a', 2);
| ---
| - null
- | - 'Wrong number of arguments is passed to RANDOM(): expected 0, got 1'
+ | - 'Wrong number of arguments is passed to UPPER(): expected 1, got 2'
| ...
--- Function randomblob
-SELECT typeof(randomblob());
- | ---
- | - null
- | - 'Wrong number of arguments is passed to RANDOMBLOB(): expected 1, got 0'
- | ...
-SELECT typeof(randomblob(1));
+SELECT upper(NULL);
| ---
| - metadata:
- | - name: typeof(randomblob(1))
+ | - name: upper(NULL)
| type: string
| rows:
- | - ['varbinary']
+ | - [null]
| ...
-SELECT typeof(randomblob(1, 2));
+SELECT upper(1);
| ---
| - null
- | - 'Wrong number of arguments is passed to RANDOMBLOB(): expected 1, got 2'
+ | - 'Type mismatch: can not convert 1 to string'
| ...
-
--- Function replace
-SELECT replace();
+SELECT upper(-1);
| ---
| - null
- | - 'Wrong number of arguments is passed to REPLACE(): expected 3, got 0'
+ | - 'Type mismatch: can not convert -1 to string'
| ...
-SELECT replace('12345');
+SELECT upper(-1.5);
| ---
| - null
- | - 'Wrong number of arguments is passed to REPLACE(): expected 3, got 1'
+ | - 'Type mismatch: can not convert -1.5 to string'
| ...
-SELECT replace('12345', '2');
+SELECT upper(true);
| ---
| - null
- | - 'Wrong number of arguments is passed to REPLACE(): expected 3, got 2'
+ | - 'Type mismatch: can not convert TRUE to string'
| ...
-SELECT replace('12345', '2', '3');
+SELECT upper('abc');
| ---
| - metadata:
- | - name: replace('12345', '2', '3')
+ | - name: upper('abc')
| type: string
| rows:
- | - ['13345']
- | ...
-SELECT replace('12345', '2', '3', 4);
- | ---
- | - null
- | - 'Wrong number of arguments is passed to REPLACE(): expected 3, got 4'
- | ...
-
--- Function round
-SELECT round();
- | ---
- | - null
- | - 'Wrong number of arguments is passed to ROUND(): expected from 1 to 2, got 0'
- | ...
-SELECT round(1.1245);
- | ---
- | - metadata:
- | - name: round(1.1245)
- | type: integer
- | rows:
- | - [1]
+ | - ['ABC']
| ...
-SELECT round(1.1245, 2);
+SELECT upper(X'3334');
| ---
| - metadata:
- | - name: round(1.1245, 2)
- | type: integer
+ | - name: upper(X'3334')
+ | type: string
| rows:
- | - [1.12]
- | ...
-SELECT round(1.1245, 2, 3);
- | ---
- | - null
- | - 'Wrong number of arguments is passed to ROUND(): expected from 1 to 2, got 3'
+ | - ['34']
| ...
--- Function row_count
-SELECT row_count();
+-- Function version
+SELECT typeof(version());
| ---
| - metadata:
- | - name: row_count()
- | type: integer
+ | - name: typeof(version())
+ | type: string
| rows:
- | - [0]
+ | - ['string']
| ...
-SELECT row_count(1);
+SELECT typeof(version(1));
| ---
| - null
- | - 'Wrong number of arguments is passed to ROW_COUNT(): expected 0, got 1'
+ | - 'Wrong number of arguments is passed to VERSION(): expected 0, got 1'
| ...
--- Function soundex
-SELECT soundex();
+-- Function zeroblob
+SELECT zeroblob();
| ---
| - null
- | - 'Wrong number of arguments is passed to SOUNDEX(): expected 1, got 0'
+ | - 'Wrong number of arguments is passed to ZEROBLOB(): expected 1, got 0'
| ...
-SELECT soundex(1);
+SELECT zeroblob(1);
| ---
| - metadata:
- | - name: soundex(1)
- | type: string
+ | - name: zeroblob(1)
+ | type: varbinary
| rows:
- | - ['?000']
+ | - ["\0"]
| ...
-SELECT soundex(1, 2);
+SELECT zeroblob(1, 2);
| ---
| - null
- | - 'Wrong number of arguments is passed to SOUNDEX(): expected 1, got 2'
+ | - 'Wrong number of arguments is passed to ZEROBLOB(): expected 1, got 2'
| ...
--- Function substr
-SELECT substr();
- | ---
- | - null
- | - 'Wrong number of arguments is passed to SUBSTR(): expected from 2 to 3, got 0'
- | ...
-SELECT substr('12345');
- | ---
- | - null
- | - 'Wrong number of arguments is passed to SUBSTR(): expected from 2 to 3, got 1'
- | ...
-SELECT substr('12345', 2);
+SELECT zeroblob(NULL);
| ---
| - metadata:
- | - name: substr('12345', 2)
- | type: string
+ | - name: zeroblob(NULL)
+ | type: varbinary
| rows:
- | - ['2345']
+ | - ['']
| ...
-SELECT substr('12345', 2, 3);
+SELECT zeroblob(1);
| ---
| - metadata:
- | - name: substr('12345', 2, 3)
- | type: string
+ | - name: zeroblob(1)
+ | type: varbinary
| rows:
- | - ['234']
+ | - ["\0"]
| ...
-SELECT substr('12345', 2, 3, 4);
+SELECT zeroblob(-1);
| ---
| - null
- | - 'Wrong number of arguments is passed to SUBSTR(): expected from 2 to 3, got 4'
+ | - 'Type mismatch: can not convert -1 to unsigned'
| ...
-
--- Function typeof
-SELECT typeof();
+SELECT zeroblob(-1.5);
| ---
| - null
- | - 'Wrong number of arguments is passed to TYPEOF(): expected 1, got 0'
+ | - 'Type mismatch: can not convert -1.5 to unsigned'
| ...
-SELECT typeof(1);
+SELECT zeroblob(true);
| ---
- | - metadata:
- | - name: typeof(1)
- | type: string
- | rows:
- | - ['integer']
+ | - null
+ | - 'Type mismatch: can not convert TRUE to unsigned'
| ...
-SELECT typeof(1, 2);
+SELECT zeroblob('abc');
| ---
| - null
- | - 'Wrong number of arguments is passed to TYPEOF(): expected 1, got 2'
+ | - 'Type mismatch: can not convert abc to unsigned'
+ | ...
+SELECT zeroblob(X'3334');
+ | ---
+ | - null
+ | - 'Type mismatch: can not convert varbinary to unsigned'
| ...
--- Function unicode
-SELECT unicode();
+-- Function avg
+SELECT avg() FROM (values(1), (2), (3));
| ---
| - null
- | - 'Wrong number of arguments is passed to UNICODE(): expected 1, got 0'
+ | - 'Wrong number of arguments is passed to AVG(): expected 1, got 0'
| ...
-SELECT unicode('1');
+SELECT avg("_auto_field_") FROM (values(1), (2), (3));
| ---
| - metadata:
- | - name: unicode('1')
- | type: string
+ | - name: avg("_auto_field_")
+ | type: number
| rows:
- | - [49]
+ | - [2]
| ...
-SELECT unicode('1', '2');
+SELECT avg("_auto_field_", 2) FROM (values(1), (2), (3));
| ---
| - null
- | - 'Wrong number of arguments is passed to UNICODE(): expected 1, got 2'
+ | - 'Wrong number of arguments is passed to AVG(): expected 1, got 2'
| ...
--- Function unlikely
-SELECT unlikely();
+SELECT avg("_auto_field_") FROM (values(NULL), (NULL), (NULL));
| ---
- | - null
- | - 'Wrong number of arguments is passed to UNLIKELY(): expected 1, got 0'
+ | - metadata:
+ | - name: avg("_auto_field_")
+ | type: number
+ | rows:
+ | - [null]
| ...
-SELECT unlikely(1);
+SELECT avg("_auto_field_") FROM (values(1), (1), (1));
| ---
| - metadata:
- | - name: unlikely(1)
- | type: integer
+ | - name: avg("_auto_field_")
+ | type: number
| rows:
| - [1]
| ...
-SELECT unlikely(1, 2);
- | ---
- | - null
- | - 'Wrong number of arguments is passed to UNLIKELY(): expected 1, got 2'
- | ...
-
--- Function upper
-SELECT upper();
+SELECT avg("_auto_field_") FROM (values(-1), (-1), (-1));
| ---
- | - null
- | - 'Wrong number of arguments is passed to UPPER(): expected 1, got 0'
+ | - metadata:
+ | - name: avg("_auto_field_")
+ | type: number
+ | rows:
+ | - [-1]
| ...
-SELECT upper('a');
+SELECT avg("_auto_field_") FROM (values(-1.5), (-1.5), (-1.5));
| ---
| - metadata:
- | - name: upper('a')
- | type: string
+ | - name: avg("_auto_field_")
+ | type: number
| rows:
- | - ['A']
+ | - [-1.5]
| ...
-SELECT upper('a', 2);
+SELECT avg("_auto_field_") FROM (values(true), (true), (true));
| ---
| - null
- | - 'Wrong number of arguments is passed to UPPER(): expected 1, got 2'
+ | - 'Type mismatch: can not convert TRUE to number'
| ...
-
--- Function version
-SELECT typeof(version());
+SELECT avg("_auto_field_") FROM (values('abc'), ('abc'), ('abc'));
| ---
- | - metadata:
- | - name: typeof(version())
- | type: string
- | rows:
- | - ['string']
+ | - null
+ | - 'Type mismatch: can not convert abc to number'
| ...
-SELECT typeof(version(1));
+SELECT avg("_auto_field_") FROM (values(X'3334'), (X'3334'), (X'3334'));
| ---
| - null
- | - 'Wrong number of arguments is passed to VERSION(): expected 0, got 1'
+ | - 'Type mismatch: can not convert varbinary to number'
| ...
--- Function zeroblob
-SELECT zeroblob();
+-- Function count
+SELECT count() FROM (values(1), (2), (3));
| ---
- | - null
- | - 'Wrong number of arguments is passed to ZEROBLOB(): expected 1, got 0'
+ | - metadata:
+ | - name: count()
+ | type: integer
+ | rows:
+ | - [3]
| ...
-SELECT zeroblob(1);
+SELECT count("_auto_field_") FROM (values(1), (2), (3));
| ---
| - metadata:
- | - name: zeroblob(1)
- | type: varbinary
+ | - name: count("_auto_field_")
+ | type: integer
| rows:
- | - ["\0"]
+ | - [3]
| ...
-SELECT zeroblob(1, 2);
+SELECT count("_auto_field_", 2) FROM (values(1), (2), (3));
| ---
| - null
- | - 'Wrong number of arguments is passed to ZEROBLOB(): expected 1, got 2'
+ | - 'Wrong number of arguments is passed to COUNT(): expected from 0 to 1, got 2'
| ...
--- Function avg
-SELECT avg() FROM (values(1), (2), (3));
+SELECT count("_auto_field_") FROM (values(NULL), (NULL), (NULL));
| ---
- | - null
- | - 'Wrong number of arguments is passed to AVG(): expected 1, got 0'
+ | - metadata:
+ | - name: count("_auto_field_")
+ | type: integer
+ | rows:
+ | - [0]
| ...
-SELECT avg("_auto_field_") FROM (values(1), (2), (3));
+SELECT count("_auto_field_") FROM (values(1), (1), (1));
| ---
| - metadata:
- | - name: avg("_auto_field_")
- | type: number
+ | - name: count("_auto_field_")
+ | type: integer
| rows:
- | - [2]
+ | - [3]
| ...
-SELECT avg("_auto_field_", 2) FROM (values(1), (2), (3));
+SELECT count("_auto_field_") FROM (values(-1), (-1), (-1));
| ---
- | - null
- | - 'Wrong number of arguments is passed to AVG(): expected 1, got 2'
+ | - metadata:
+ | - name: count("_auto_field_")
+ | type: integer
+ | rows:
+ | - [3]
| ...
-
--- Function count
-SELECT count() FROM (values(1), (2), (3));
+SELECT count("_auto_field_") FROM (values(-1.5), (-1.5), (-1.5));
| ---
| - metadata:
- | - name: count()
+ | - name: count("_auto_field_")
| type: integer
| rows:
| - [3]
| ...
-SELECT count("_auto_field_") FROM (values(1), (2), (3));
+SELECT count("_auto_field_") FROM (values(true), (true), (true));
| ---
| - metadata:
| - name: count("_auto_field_")
@@ -704,10 +2101,21 @@ SELECT count("_auto_field_") FROM (values(1), (2), (3));
| rows:
| - [3]
| ...
-SELECT count("_auto_field_", 2) FROM (values(1), (2), (3));
+SELECT count("_auto_field_") FROM (values('abc'), ('abc'), ('abc'));
| ---
- | - null
- | - 'Wrong number of arguments is passed to COUNT(): expected from 0 to 1, got 2'
+ | - metadata:
+ | - name: count("_auto_field_")
+ | type: integer
+ | rows:
+ | - [3]
+ | ...
+SELECT count("_auto_field_") FROM (values(X'3334'), (X'3334'), (X'3334'));
+ | ---
+ | - metadata:
+ | - name: count("_auto_field_")
+ | type: integer
+ | rows:
+ | - [3]
| ...
-- Function group_concat
@@ -740,6 +2148,63 @@ SELECT group_concat("_auto_field_", '2', '3') FROM (values('1'), ('2'), ('3'));
| 3'
| ...
+SELECT group_concat("_auto_field_") FROM (values(NULL), (NULL), (NULL));
+ | ---
+ | - metadata:
+ | - name: group_concat("_auto_field_")
+ | type: string
+ | rows:
+ | - [null]
+ | ...
+SELECT group_concat("_auto_field_") FROM (values(1), (1), (1));
+ | ---
+ | - metadata:
+ | - name: group_concat("_auto_field_")
+ | type: string
+ | rows:
+ | - ['1,1,1']
+ | ...
+SELECT group_concat("_auto_field_") FROM (values(-1), (-1), (-1));
+ | ---
+ | - metadata:
+ | - name: group_concat("_auto_field_")
+ | type: string
+ | rows:
+ | - ['-1,-1,-1']
+ | ...
+SELECT group_concat("_auto_field_") FROM (values(-1.5), (-1.5), (-1.5));
+ | ---
+ | - metadata:
+ | - name: group_concat("_auto_field_")
+ | type: string
+ | rows:
+ | - ['-1.5,-1.5,-1.5']
+ | ...
+SELECT group_concat("_auto_field_") FROM (values(true), (true), (true));
+ | ---
+ | - metadata:
+ | - name: group_concat("_auto_field_")
+ | type: string
+ | rows:
+ | - ['TRUE,TRUE,TRUE']
+ | ...
+SELECT group_concat("_auto_field_") FROM (values('abc'), ('abc'), ('abc'));
+ | ---
+ | - metadata:
+ | - name: group_concat("_auto_field_")
+ | type: string
+ | rows:
+ | - ['abc,abc,abc']
+ | ...
+SELECT group_concat("_auto_field_") FROM (values(X'3334'), (X'3334'), (X'3334'));
+ | ---
+ | - metadata:
+ | - name: group_concat("_auto_field_")
+ | type: string
+ | rows:
+ | - ['34,34,34']
+ | ...
+
-- Function max
SELECT max() FROM (values(1), (2), (3));
| ---
@@ -760,6 +2225,63 @@ SELECT max("_auto_field_", 2) FROM (values(1), (2), (3));
| - 'Wrong number of arguments is passed to MAX(): expected 1, got 2'
| ...
+SELECT max("_auto_field_") FROM (values(NULL), (NULL), (NULL));
+ | ---
+ | - metadata:
+ | - name: max("_auto_field_")
+ | type: scalar
+ | rows:
+ | - [null]
+ | ...
+SELECT max("_auto_field_") FROM (values(1), (1), (1));
+ | ---
+ | - metadata:
+ | - name: max("_auto_field_")
+ | type: scalar
+ | rows:
+ | - [1]
+ | ...
+SELECT max("_auto_field_") FROM (values(-1), (-1), (-1));
+ | ---
+ | - metadata:
+ | - name: max("_auto_field_")
+ | type: scalar
+ | rows:
+ | - [-1]
+ | ...
+SELECT max("_auto_field_") FROM (values(-1.5), (-1.5), (-1.5));
+ | ---
+ | - metadata:
+ | - name: max("_auto_field_")
+ | type: scalar
+ | rows:
+ | - [-1.5]
+ | ...
+SELECT max("_auto_field_") FROM (values(true), (true), (true));
+ | ---
+ | - metadata:
+ | - name: max("_auto_field_")
+ | type: scalar
+ | rows:
+ | - [true]
+ | ...
+SELECT max("_auto_field_") FROM (values('abc'), ('abc'), ('abc'));
+ | ---
+ | - metadata:
+ | - name: max("_auto_field_")
+ | type: scalar
+ | rows:
+ | - ['abc']
+ | ...
+SELECT max("_auto_field_") FROM (values(X'3334'), (X'3334'), (X'3334'));
+ | ---
+ | - metadata:
+ | - name: max("_auto_field_")
+ | type: scalar
+ | rows:
+ | - ['34']
+ | ...
+
-- Function min
SELECT min() FROM (values(1), (2), (3));
| ---
@@ -780,6 +2302,63 @@ SELECT min("_auto_field_", 2) FROM (values(1), (2), (3));
| - 'Wrong number of arguments is passed to MIN(): expected 1, got 2'
| ...
+SELECT min("_auto_field_") FROM (values(NULL), (NULL), (NULL));
+ | ---
+ | - metadata:
+ | - name: min("_auto_field_")
+ | type: scalar
+ | rows:
+ | - [null]
+ | ...
+SELECT min("_auto_field_") FROM (values(1), (1), (1));
+ | ---
+ | - metadata:
+ | - name: min("_auto_field_")
+ | type: scalar
+ | rows:
+ | - [1]
+ | ...
+SELECT min("_auto_field_") FROM (values(-1), (-1), (-1));
+ | ---
+ | - metadata:
+ | - name: min("_auto_field_")
+ | type: scalar
+ | rows:
+ | - [-1]
+ | ...
+SELECT min("_auto_field_") FROM (values(-1.5), (-1.5), (-1.5));
+ | ---
+ | - metadata:
+ | - name: min("_auto_field_")
+ | type: scalar
+ | rows:
+ | - [-1.5]
+ | ...
+SELECT min("_auto_field_") FROM (values(true), (true), (true));
+ | ---
+ | - metadata:
+ | - name: min("_auto_field_")
+ | type: scalar
+ | rows:
+ | - [true]
+ | ...
+SELECT min("_auto_field_") FROM (values('abc'), ('abc'), ('abc'));
+ | ---
+ | - metadata:
+ | - name: min("_auto_field_")
+ | type: scalar
+ | rows:
+ | - ['abc']
+ | ...
+SELECT min("_auto_field_") FROM (values(X'3334'), (X'3334'), (X'3334'));
+ | ---
+ | - metadata:
+ | - name: min("_auto_field_")
+ | type: scalar
+ | rows:
+ | - ['34']
+ | ...
+
-- Function sum
SELECT sum() FROM (values(1), (2), (3));
| ---
@@ -800,6 +2379,54 @@ SELECT sum("_auto_field_", 2) FROM (values(1), (2), (3));
| - 'Wrong number of arguments is passed to SUM(): expected 1, got 2'
| ...
+SELECT sum("_auto_field_") FROM (values(NULL), (NULL), (NULL));
+ | ---
+ | - metadata:
+ | - name: sum("_auto_field_")
+ | type: number
+ | rows:
+ | - [null]
+ | ...
+SELECT sum("_auto_field_") FROM (values(1), (1), (1));
+ | ---
+ | - metadata:
+ | - name: sum("_auto_field_")
+ | type: number
+ | rows:
+ | - [3]
+ | ...
+SELECT sum("_auto_field_") FROM (values(-1), (-1), (-1));
+ | ---
+ | - metadata:
+ | - name: sum("_auto_field_")
+ | type: number
+ | rows:
+ | - [-3]
+ | ...
+SELECT sum("_auto_field_") FROM (values(-1.5), (-1.5), (-1.5));
+ | ---
+ | - metadata:
+ | - name: sum("_auto_field_")
+ | type: number
+ | rows:
+ | - [-4.5]
+ | ...
+SELECT sum("_auto_field_") FROM (values(true), (true), (true));
+ | ---
+ | - null
+ | - 'Type mismatch: can not convert TRUE to number'
+ | ...
+SELECT sum("_auto_field_") FROM (values('abc'), ('abc'), ('abc'));
+ | ---
+ | - null
+ | - 'Type mismatch: can not convert abc to number'
+ | ...
+SELECT sum("_auto_field_") FROM (values(X'3334'), (X'3334'), (X'3334'));
+ | ---
+ | - null
+ | - 'Type mismatch: can not convert varbinary to number'
+ | ...
+
-- Function total
SELECT total() FROM (values(1), (2), (3));
| ---
@@ -819,3 +2446,51 @@ SELECT total("_auto_field_", 2) FROM (values(1), (2), (3));
| - null
| - 'Wrong number of arguments is passed to TOTAL(): expected 1, got 2'
| ...
+
+SELECT total("_auto_field_") FROM (values(NULL), (NULL), (NULL));
+ | ---
+ | - metadata:
+ | - name: total("_auto_field_")
+ | type: number
+ | rows:
+ | - [0]
+ | ...
+SELECT total("_auto_field_") FROM (values(1), (1), (1));
+ | ---
+ | - metadata:
+ | - name: total("_auto_field_")
+ | type: number
+ | rows:
+ | - [3]
+ | ...
+SELECT total("_auto_field_") FROM (values(-1), (-1), (-1));
+ | ---
+ | - metadata:
+ | - name: total("_auto_field_")
+ | type: number
+ | rows:
+ | - [-3]
+ | ...
+SELECT total("_auto_field_") FROM (values(-1.5), (-1.5), (-1.5));
+ | ---
+ | - metadata:
+ | - name: total("_auto_field_")
+ | type: number
+ | rows:
+ | - [-4.5]
+ | ...
+SELECT total("_auto_field_") FROM (values(true), (true), (true));
+ | ---
+ | - null
+ | - 'Type mismatch: can not convert TRUE to number'
+ | ...
+SELECT total("_auto_field_") FROM (values('abc'), ('abc'), ('abc'));
+ | ---
+ | - null
+ | - 'Type mismatch: can not convert abc to number'
+ | ...
+SELECT total("_auto_field_") FROM (values(X'3334'), (X'3334'), (X'3334'));
+ | ---
+ | - null
+ | - 'Type mismatch: can not convert varbinary to number'
+ | ...
diff --git a/test/sql/gh-4159-function-argumens.test.sql b/test/sql/gh-4159-function-argumens.test.sql
index 7578768cb..e45f62d91 100644
--- a/test/sql/gh-4159-function-argumens.test.sql
+++ b/test/sql/gh-4159-function-argumens.test.sql
@@ -3,92 +3,228 @@ SELECT abs();
SELECT abs(1);
SELECT abs(1, 2);
+SELECT abs(NULL);
+SELECT abs(1);
+SELECT abs(-1);
+SELECT abs(-1.5);
+SELECT abs(true);
+SELECT abs('abc');
+SELECT abs(X'3334');
+
-- Function char().
SELECT char();
SELECT char(1);
SELECT char(1, 2);
+SELECT char(NULL);
+SELECT char(1);
+SELECT char(-1);
+SELECT char(-1.5);
+-- SELECT char(true);
+-- SELECT char('abc');
+-- SELECT char(X'3334');
+
-- Function character_length().
SELECT character_length();
SELECT character_length('1');
SELECT character_length('1', '2');
+SELECT character_length(NULL);
+SELECT character_length(1);
+SELECT character_length(-1);
+SELECT character_length(-1.5);
+SELECT character_length(true);
+SELECT character_length('abc');
+SELECT character_length(X'3334');
+
-- Function char_length().
SELECT char_length();
SELECT char_length('1');
SELECT char_length('1', '2');
+SELECT char_length(NULL);
+SELECT char_length(1);
+SELECT char_length(-1);
+SELECT char_length(-1.5);
+SELECT char_length(true);
+SELECT char_length('abc');
+SELECT char_length(X'3334');
+
-- Function coalesce().
SELECT coalesce();
SELECT coalesce('1');
SELECT coalesce('1', '2');
SELECT coalesce('1', '2', '3');
+SELECT coalesce(NULL, NULL);
+SELECT coalesce(1, 1);
+SELECT coalesce(-1, -1);
+SELECT coalesce(-1.5, -1.5);
+SELECT coalesce(true, true);
+SELECT coalesce('abc', 'abc');
+SELECT coalesce(X'3334', X'3334');
+
-- Function greatest().
SELECT greatest();
SELECT greatest('1');
SELECT greatest('1', '2');
+SELECT greatest(NULL, NULL);
+SELECT greatest(1, 1);
+SELECT greatest(-1, -1);
+SELECT greatest(-1.5, -1.5);
+SELECT greatest(true, true);
+SELECT greatest('abc', 'abc');
+SELECT greatest(X'3334', X'3334');
+
-- Function hex().
SELECT hex();
SELECT hex(X'33');
SELECT hex(X'33', X'33');
+SELECT hex(NULL);
+SELECT hex(1);
+SELECT hex(-1);
+SELECT hex(-1.5);
+SELECT hex(true);
+SELECT hex('abc');
+SELECT hex(X'3334');
+
-- Function ifnull
SELECT ifnull();
SELECT ifnull(1);
SELECT ifnull(1, 2);
SELECT ifnull(1, 2, 3);
+SELECT ifnull(NULL, NULL);
+SELECT ifnull(1, 1);
+SELECT ifnull(-1, -1);
+SELECT ifnull(-1.5, -1.5);
+SELECT ifnull(true, true);
+SELECT ifnull('abc', 'abc');
+SELECT ifnull(X'3334', X'3334');
+
-- Function least().
SELECT least();
SELECT least('1');
SELECT least('1', '2');
+SELECT least(NULL, NULL);
+SELECT least(1, 1);
+SELECT least(-1, -1);
+SELECT least(-1.5, -1.5);
+SELECT least(true, true);
+SELECT least('abc', 'abc');
+SELECT least(X'3334', X'3334');
+
-- Function length().
SELECT length();
SELECT length('1');
SELECT length('1', '2');
+SELECT length(NULL);
+SELECT length(1);
+SELECT length(-1);
+SELECT length(-1.5);
+SELECT length(true);
+SELECT length('abc');
+SELECT length(X'3334');
+
-- Function likelihood
SELECT likelihood();
SELECT likelihood(1);
SELECT likelihood(1, 0.5);
SELECT likelihood(1, 0.5, 3);
+SELECT likelihood(NULL, NULL);
+SELECT likelihood(1, 1);
+SELECT likelihood(-1, -1);
+SELECT likelihood(-1.5, -1.5);
+SELECT likelihood(true, true);
+SELECT likelihood('abc', 'abc');
+SELECT likelihood(X'3334', X'3334');
+
-- Function likely
SELECT likely();
SELECT likely(1);
SELECT likely(1, 2);
+SELECT likely(NULL);
+SELECT likely(1);
+SELECT likely(-1);
+SELECT likely(-1.5);
+SELECT likely(true);
+SELECT likely('abc');
+SELECT likely(X'3334');
+
-- Function lower
SELECT lower();
SELECT lower('a');
SELECT lower('a', 2);
+SELECT lower(NULL);
+SELECT lower(1);
+SELECT lower(-1);
+SELECT lower(-1.5);
+SELECT lower(true);
+SELECT lower('abc');
+SELECT lower(X'3334');
+
-- Function nullif
SELECT nullif();
SELECT nullif(1);
SELECT nullif(1, 2);
SELECT nullif(1, 2, 3);
+SELECT nullif(NULL, NULL);
+SELECT nullif(1, 1);
+SELECT nullif(-1, -1);
+SELECT nullif(-1.5, -1.5);
+SELECT nullif(true, true);
+SELECT nullif('abc', 'abc');
+SELECT nullif(X'3334', X'3334');
+
-- Function position
SELECT position();
SELECT position('12345');
SELECT position('12345', '2');
SELECT position('12345', '2', 3);
+SELECT position(NULL, NULL);
+SELECT position(1, 1);
+SELECT position(-1, -1);
+SELECT position(-1.5, -1.5);
+SELECT position(true, true);
+SELECT position('abc', 'abc');
+SELECT position(X'3334', X'3334');
+
-- Function printf
SELECT printf();
SELECT printf('1');
SELECT printf('1', 2);
SELECT printf('1', 2, 3);
+SELECT printf(NULL, NULL);
+SELECT printf(1, 1);
+SELECT printf(-1, -1);
+SELECT printf(-1.5, -1.5);
+SELECT printf(true, true);
+SELECT printf('abc', 'abc');
+SELECT printf(X'3334', X'3334');
+
-- Function quote
SELECT quote();
SELECT quote('1');
SELECT quote('1', 2);
+SELECT quote(NULL);
+SELECT quote(1);
+SELECT quote(-1);
+SELECT quote(-1.5);
+SELECT quote(true);
+SELECT quote('abc');
+SELECT quote(X'3334');
+
-- Function random
SELECT typeof(random());
SELECT typeof(random(1));
@@ -98,6 +234,14 @@ SELECT typeof(randomblob());
SELECT typeof(randomblob(1));
SELECT typeof(randomblob(1, 2));
+SELECT randomblob(NULL);
+SELECT typeof(randomblob(1));
+SELECT typeof(randomblob(-1));
+SELECT typeof(randomblob(-1.5));
+SELECT typeof(randomblob(true));
+SELECT typeof(randomblob('abc'));
+SELECT typeof(randomblob(X'3334'));
+
-- Function replace
SELECT replace();
SELECT replace('12345');
@@ -105,12 +249,28 @@ SELECT replace('12345', '2');
SELECT replace('12345', '2', '3');
SELECT replace('12345', '2', '3', 4);
+SELECT replace(NULL, NULL, NULL);
+SELECT replace(1, 1, 1);
+SELECT replace(-1, -1, -1);
+SELECT replace(-1.5, -1.5, -1.5);
+SELECT replace(true, true, true);
+SELECT replace('abc', 'abc', 'abc');
+SELECT replace(X'3334', X'3334', X'3334');
+
-- Function round
SELECT round();
SELECT round(1.1245);
SELECT round(1.1245, 2);
SELECT round(1.1245, 2, 3);
+SELECT round(NULL, NULL);
+SELECT round(1, 1);
+SELECT round(-1, -1);
+SELECT round(-1.5, -1.5);
+SELECT round(true, true);
+SELECT round('abc', 'abc');
+SELECT round(X'3334', X'3334');
+
-- Function row_count
SELECT row_count();
SELECT row_count(1);
@@ -120,6 +280,14 @@ SELECT soundex();
SELECT soundex(1);
SELECT soundex(1, 2);
+SELECT soundex(NULL);
+SELECT soundex(1);
+SELECT soundex(-1);
+SELECT soundex(-1.5);
+SELECT soundex(true);
+SELECT soundex('abc');
+SELECT soundex(X'3334');
+
-- Function substr
SELECT substr();
SELECT substr('12345');
@@ -127,26 +295,66 @@ SELECT substr('12345', 2);
SELECT substr('12345', 2, 3);
SELECT substr('12345', 2, 3, 4);
+SELECT substr(NULL, NULL);
+SELECT substr(1, 1);
+SELECT substr(-1, -1);
+SELECT substr(-1.5, -1.5);
+SELECT substr(true, true);
+SELECT substr('abc', 'abc');
+SELECT substr(X'3334', X'3334');
+
-- Function typeof
SELECT typeof();
SELECT typeof(1);
SELECT typeof(1, 2);
+SELECT typeof(NULL);
+SELECT typeof(1);
+SELECT typeof(-1);
+SELECT typeof(-1.5);
+SELECT typeof(true);
+SELECT typeof('abc');
+SELECT typeof(X'3334');
+
-- Function unicode
SELECT unicode();
SELECT unicode('1');
SELECT unicode('1', '2');
+SELECT unicode(NULL);
+SELECT unicode(1);
+SELECT unicode(-1);
+SELECT unicode(-1.5);
+SELECT unicode(true);
+SELECT unicode('abc');
+SELECT unicode(X'3334');
+
-- Function unlikely
SELECT unlikely();
SELECT unlikely(1);
SELECT unlikely(1, 2);
+SELECT unlikely(NULL);
+SELECT unlikely(1);
+SELECT unlikely(-1);
+SELECT unlikely(-1.5);
+SELECT unlikely(true);
+SELECT unlikely('abc');
+SELECT unlikely(X'3334');
+
-- Function upper
SELECT upper();
SELECT upper('a');
SELECT upper('a', 2);
+SELECT upper(NULL);
+SELECT upper(1);
+SELECT upper(-1);
+SELECT upper(-1.5);
+SELECT upper(true);
+SELECT upper('abc');
+SELECT upper(X'3334');
+
-- Function version
SELECT typeof(version());
SELECT typeof(version(1));
@@ -156,38 +364,102 @@ SELECT zeroblob();
SELECT zeroblob(1);
SELECT zeroblob(1, 2);
+SELECT zeroblob(NULL);
+SELECT zeroblob(1);
+SELECT zeroblob(-1);
+SELECT zeroblob(-1.5);
+SELECT zeroblob(true);
+SELECT zeroblob('abc');
+SELECT zeroblob(X'3334');
+
-- Function avg
SELECT avg() FROM (values(1), (2), (3));
SELECT avg("_auto_field_") FROM (values(1), (2), (3));
SELECT avg("_auto_field_", 2) FROM (values(1), (2), (3));
+SELECT avg("_auto_field_") FROM (values(NULL), (NULL), (NULL));
+SELECT avg("_auto_field_") FROM (values(1), (1), (1));
+SELECT avg("_auto_field_") FROM (values(-1), (-1), (-1));
+SELECT avg("_auto_field_") FROM (values(-1.5), (-1.5), (-1.5));
+SELECT avg("_auto_field_") FROM (values(true), (true), (true));
+SELECT avg("_auto_field_") FROM (values('abc'), ('abc'), ('abc'));
+SELECT avg("_auto_field_") FROM (values(X'3334'), (X'3334'), (X'3334'));
+
-- Function count
SELECT count() FROM (values(1), (2), (3));
SELECT count("_auto_field_") FROM (values(1), (2), (3));
SELECT count("_auto_field_", 2) FROM (values(1), (2), (3));
+SELECT count("_auto_field_") FROM (values(NULL), (NULL), (NULL));
+SELECT count("_auto_field_") FROM (values(1), (1), (1));
+SELECT count("_auto_field_") FROM (values(-1), (-1), (-1));
+SELECT count("_auto_field_") FROM (values(-1.5), (-1.5), (-1.5));
+SELECT count("_auto_field_") FROM (values(true), (true), (true));
+SELECT count("_auto_field_") FROM (values('abc'), ('abc'), ('abc'));
+SELECT count("_auto_field_") FROM (values(X'3334'), (X'3334'), (X'3334'));
+
-- Function group_concat
SELECT group_concat() FROM (values('1'), ('2'), ('3'));
SELECT group_concat("_auto_field_") FROM (values('1'), ('2'), ('3'));
SELECT group_concat("_auto_field_", '2') FROM (values('1'), ('2'), ('3'));
SELECT group_concat("_auto_field_", '2', '3') FROM (values('1'), ('2'), ('3'));
+SELECT group_concat("_auto_field_") FROM (values(NULL), (NULL), (NULL));
+SELECT group_concat("_auto_field_") FROM (values(1), (1), (1));
+SELECT group_concat("_auto_field_") FROM (values(-1), (-1), (-1));
+SELECT group_concat("_auto_field_") FROM (values(-1.5), (-1.5), (-1.5));
+SELECT group_concat("_auto_field_") FROM (values(true), (true), (true));
+SELECT group_concat("_auto_field_") FROM (values('abc'), ('abc'), ('abc'));
+SELECT group_concat("_auto_field_") FROM (values(X'3334'), (X'3334'), (X'3334'));
+
-- Function max
SELECT max() FROM (values(1), (2), (3));
SELECT max("_auto_field_") FROM (values(1), (2), (3));
SELECT max("_auto_field_", 2) FROM (values(1), (2), (3));
+SELECT max("_auto_field_") FROM (values(NULL), (NULL), (NULL));
+SELECT max("_auto_field_") FROM (values(1), (1), (1));
+SELECT max("_auto_field_") FROM (values(-1), (-1), (-1));
+SELECT max("_auto_field_") FROM (values(-1.5), (-1.5), (-1.5));
+SELECT max("_auto_field_") FROM (values(true), (true), (true));
+SELECT max("_auto_field_") FROM (values('abc'), ('abc'), ('abc'));
+SELECT max("_auto_field_") FROM (values(X'3334'), (X'3334'), (X'3334'));
+
-- Function min
SELECT min() FROM (values(1), (2), (3));
SELECT min("_auto_field_") FROM (values(1), (2), (3));
SELECT min("_auto_field_", 2) FROM (values(1), (2), (3));
+SELECT min("_auto_field_") FROM (values(NULL), (NULL), (NULL));
+SELECT min("_auto_field_") FROM (values(1), (1), (1));
+SELECT min("_auto_field_") FROM (values(-1), (-1), (-1));
+SELECT min("_auto_field_") FROM (values(-1.5), (-1.5), (-1.5));
+SELECT min("_auto_field_") FROM (values(true), (true), (true));
+SELECT min("_auto_field_") FROM (values('abc'), ('abc'), ('abc'));
+SELECT min("_auto_field_") FROM (values(X'3334'), (X'3334'), (X'3334'));
+
-- Function sum
SELECT sum() FROM (values(1), (2), (3));
SELECT sum("_auto_field_") FROM (values(1), (2), (3));
SELECT sum("_auto_field_", 2) FROM (values(1), (2), (3));
+SELECT sum("_auto_field_") FROM (values(NULL), (NULL), (NULL));
+SELECT sum("_auto_field_") FROM (values(1), (1), (1));
+SELECT sum("_auto_field_") FROM (values(-1), (-1), (-1));
+SELECT sum("_auto_field_") FROM (values(-1.5), (-1.5), (-1.5));
+SELECT sum("_auto_field_") FROM (values(true), (true), (true));
+SELECT sum("_auto_field_") FROM (values('abc'), ('abc'), ('abc'));
+SELECT sum("_auto_field_") FROM (values(X'3334'), (X'3334'), (X'3334'));
+
-- Function total
SELECT total() FROM (values(1), (2), (3));
SELECT total("_auto_field_") FROM (values(1), (2), (3));
SELECT total("_auto_field_", 2) FROM (values(1), (2), (3));
+
+SELECT total("_auto_field_") FROM (values(NULL), (NULL), (NULL));
+SELECT total("_auto_field_") FROM (values(1), (1), (1));
+SELECT total("_auto_field_") FROM (values(-1), (-1), (-1));
+SELECT total("_auto_field_") FROM (values(-1.5), (-1.5), (-1.5));
+SELECT total("_auto_field_") FROM (values(true), (true), (true));
+SELECT total("_auto_field_") FROM (values('abc'), ('abc'), ('abc'));
+SELECT total("_auto_field_") FROM (values(X'3334'), (X'3334'), (X'3334'));
diff --git a/test/sql/types.result b/test/sql/types.result
index 70fbbc5a2..0fdfa1283 100644
--- a/test/sql/types.result
+++ b/test/sql/types.result
@@ -208,32 +208,29 @@ box.execute("CREATE TABLE t1 (s SCALAR PRIMARY KEY);")
---
- row_count: 1
...
-box.execute("INSERT INTO t1 VALUES (randomblob(5));")
+box.execute("INSERT INTO t1 VALUES (X'FF3435');")
---
- row_count: 1
...
box.execute("SELECT * FROM t1 WHERE s LIKE 'blob';")
---
- null
-- 'Inconsistent types: expected text got varbinary'
+- 'Type mismatch: can not convert varbinary to string'
...
box.execute("SELECT * FROM t1 WHERE 'blob' LIKE s;")
---
- null
-- 'Inconsistent types: expected text got varbinary'
+- 'Type mismatch: can not convert varbinary to string'
...
box.execute("SELECT * FROM t1 WHERE 'blob' LIKE x'0000';")
---
- null
-- 'Inconsistent types: expected text got varbinary'
+- 'Type mismatch: can not convert varbinary to string'
...
box.execute("SELECT s LIKE NULL FROM t1;")
---
-- metadata:
- - name: s LIKE NULL
- type: integer
- rows:
- - [null]
+- null
+- 'Type mismatch: can not convert varbinary to string'
...
box.execute("DELETE FROM t1;")
---
@@ -246,20 +243,17 @@ box.execute("INSERT INTO t1 VALUES (1);")
box.execute("SELECT * FROM t1 WHERE s LIKE 'int';")
---
- null
-- 'Inconsistent types: expected text got unsigned'
+- 'Type mismatch: can not convert 1 to string'
...
box.execute("SELECT * FROM t1 WHERE 'int' LIKE 4;")
---
- null
-- 'Inconsistent types: expected text got unsigned'
+- 'Type mismatch: can not convert 4 to string'
...
box.execute("SELECT NULL LIKE s FROM t1;")
---
-- metadata:
- - name: NULL LIKE s
- type: integer
- rows:
- - [null]
+- null
+- 'Type mismatch: can not convert 1 to string'
...
box.space.T1:drop()
---
@@ -830,19 +824,13 @@ box.execute("DELETE FROM t WHERE i < 18446744073709551613;")
...
box.execute("SELECT lower(i) FROM t;")
---
-- metadata:
- - name: lower(i)
- type: string
- rows:
- - ['18446744073709551613']
+- null
+- 'Type mismatch: can not convert 18446744073709551613 to string'
...
box.execute("SELECT upper(i) FROM t;")
---
-- metadata:
- - name: upper(i)
- type: string
- rows:
- - ['18446744073709551613']
+- null
+- 'Type mismatch: can not convert 18446744073709551613 to string'
...
box.execute("SELECT abs(i) FROM t;")
---
@@ -1315,18 +1303,24 @@ box.execute("SELECT group_concat(v) FROM t;")
...
box.execute("SELECT lower(v) FROM t;")
---
-- null
-- 'Inconsistent types: expected text got varbinary'
+- metadata:
+ - name: lower(v)
+ type: string
+ rows:
+ - ['abc']
...
box.execute("SELECT upper(v) FROM t;")
---
-- null
-- 'Inconsistent types: expected text got varbinary'
+- metadata:
+ - name: upper(v)
+ type: string
+ rows:
+ - ['ABC']
...
box.execute("SELECT abs(v) FROM t;")
---
- null
-- 'Inconsistent types: expected number got varbinary'
+- 'Type mismatch: can not convert varbinary to number'
...
box.execute("SELECT typeof(v) FROM t;")
---
@@ -1883,25 +1877,13 @@ box.execute("SELECT group_concat(d) FROM t;")
...
box.execute("SELECT lower(d) FROM t;")
---
-- metadata:
- - name: lower(d)
- type: string
- rows:
- - ['10.0']
- - ['-2.0']
- - ['3.3']
- - ['1.8e+19']
+- null
+- 'Type mismatch: can not convert 10.0 to string'
...
box.execute("SELECT upper(d) FROM t;")
---
-- metadata:
- - name: upper(d)
- type: string
- rows:
- - ['10.0']
- - ['-2.0']
- - ['3.3']
- - ['1.8E+19']
+- null
+- 'Type mismatch: can not convert 10.0 to string'
...
box.execute("SELECT abs(d) FROM t;")
---
diff --git a/test/sql/types.test.lua b/test/sql/types.test.lua
index 2dc70f3c5..07158708e 100644
--- a/test/sql/types.test.lua
+++ b/test/sql/types.test.lua
@@ -59,7 +59,7 @@ box.execute("VALUES (TYPEOF(randomblob(5) || zeroblob(5)));")
-- gh-3954: LIKE accepts only arguments of type TEXT and NULLs.
--
box.execute("CREATE TABLE t1 (s SCALAR PRIMARY KEY);")
-box.execute("INSERT INTO t1 VALUES (randomblob(5));")
+box.execute("INSERT INTO t1 VALUES (X'FF3435');")
box.execute("SELECT * FROM t1 WHERE s LIKE 'blob';")
box.execute("SELECT * FROM t1 WHERE 'blob' LIKE s;")
box.execute("SELECT * FROM t1 WHERE 'blob' LIKE x'0000';")
--
2.25.1
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [Tarantool-patches] [PATCH v4 5/5] sql: properly check arguments of functions
2020-07-13 5:33 ` [Tarantool-patches] [PATCH v4 5/5] sql: properly check arguments of functions imeevma
@ 2020-07-13 14:56 ` Nikita Pettik
0 siblings, 0 replies; 11+ messages in thread
From: Nikita Pettik @ 2020-07-13 14:56 UTC (permalink / raw)
To: imeevma; +Cc: tarantool-patches
On 13 Jul 08:33, imeevma@tarantool.org wrote:
> After this patch, the function arguments will be checked using ApplyType
> opcode before they are passed to the function. This means that the rules
> for implicitly casting values that were specified as arguments are
> defined by this opcode.
>
> Closes #4159
> ---
> src/box/sql/expr.c | 5 +
> src/box/sql/func.c | 360 +++-
> src/box/sql/select.c | 31 +
> src/box/sql/sqlInt.h | 32 +
> src/box/sql/vdbe.c | 12 +-
> test/sql-tap/cse.test.lua | 8 +-
> test/sql-tap/func.test.lua | 48 +-
> test/sql-tap/orderby1.test.lua | 2 +-
> test/sql-tap/position.test.lua | 6 +-
> test/sql/boolean.result | 32 +-
> test/sql/gh-4159-function-argumens.result | 2131 +++++++++++++++++--
> test/sql/gh-4159-function-argumens.test.sql | 272 +++
> test/sql/types.result | 76 +-
> test/sql/types.test.lua | 2 +-
> 14 files changed, 2584 insertions(+), 433 deletions(-)
>
> diff --git a/src/box/sql/expr.c b/src/box/sql/expr.c
> index 7aee240a3..aa5477c6a 100644
> --- a/src/box/sql/expr.c
> +++ b/src/box/sql/expr.c
> @@ -4104,6 +4104,11 @@ sqlExprCodeTarget(Parse * pParse, Expr * pExpr, int target)
> } else {
> r1 = 0;
> }
> + if (func->def->language == FUNC_LANGUAGE_SQL_BUILTIN) {
> + struct func_sql_builtin *f =
> + (struct func_sql_builtin *)func;
> + sql_emit_func_types(v, &f->args, r1, nFarg);
> + }
> if (sql_func_flag_is_set(func, SQL_FUNC_NEEDCOLL)) {
> sqlVdbeAddOp4(v, OP_CollSeq, 0, 0, 0,
> (char *)coll, P4_COLLSEQ);
> diff --git a/src/box/sql/func.c b/src/box/sql/func.c
> index 9d4c26081..66edc3792 100644
> --- a/src/box/sql/func.c
> +++ b/src/box/sql/func.c
> @@ -459,17 +459,10 @@ static void
> lengthFunc(sql_context * context, int argc, sql_value ** argv)
> {
> int len;
> -
> assert(argc == 1);
> UNUSED_PARAMETER(argc);
> switch (sql_value_type(argv[0])) {
> - case MP_BIN:
> - case MP_ARRAY:
> - case MP_MAP:
> - case MP_INT:
> - case MP_UINT:
> - case MP_BOOL:
> - case MP_DOUBLE:{
> + case MP_BIN: {
Let's get rid of switch-cases, they are really redundant now.
If you are afraid that diff will become big enough, you can
refactor each function in a separate patch.
> @@ -1550,6 +1514,15 @@ replaceFunc(sql_context * context, int argc, sql_value ** argv)
>
> assert(argc == 3);
> UNUSED_PARAMETER(argc);
> + assert(sql_value_type(argv[0]) == MP_NIL ||
> + sql_value_type(argv[0]) == MP_STR ||
> + sql_value_type(argv[0]) == MP_BIN);
Let's evaluate each type of argv only once.
> + assert(sql_value_type(argv[1]) == MP_NIL ||
> + sql_value_type(argv[1]) == MP_STR ||
> + sql_value_type(argv[1]) == MP_BIN);
> + assert(sql_value_type(argv[2]) == MP_NIL ||
> + sql_value_type(argv[2]) == MP_STR ||
> + sql_value_type(argv[2]) == MP_BIN);
> zStr = sql_value_text(argv[0]);
> if (zStr == 0)
> return;
> @@ -1858,13 +1831,9 @@ soundexFunc(sql_context * context, int argc, sql_value ** argv)
> 1, 2, 6, 2, 3, 0, 1, 0, 2, 0, 2, 0, 0, 0, 0, 0,
> };
> assert(argc == 1);
> - enum mp_type mp_type = sql_value_type(argv[0]);
> - if (mp_type_is_bloblike(mp_type)) {
> - diag_set(ClientError, ER_SQL_TYPE_MISMATCH,
> - sql_value_to_diag_str(argv[0]), "text");
> - context->is_aborted = true;
> - return;
> - }
> + assert(sql_value_type(argv[0]) == MP_NIL ||
> + sql_value_type(argv[0]) == MP_STR ||
> + sql_value_type(argv[0]) == MP_BIN);
Ditto
> zIn = (u8 *) sql_value_text(argv[0]);
> if (zIn == 0)
> zIn = (u8 *) "";
> int param_count;
> uint32_t min_count;
> uint32_t max_count;
> + enum field_type *types;
> + enum field_type recurrent_type;
> + bool is_blob_like_str;
What are these members supposed to mean?
> enum field_type returns;
> enum func_aggregate aggregate;
> bool export_to_sql;
> diff --git a/src/box/sql/select.c b/src/box/sql/select.c
> index 4b069addb..fe56ede1b 100644
> --- a/src/box/sql/select.c
> +++ b/src/box/sql/select.c
> @@ -124,6 +124,34 @@ clearSelect(sql * db, Select * p, int bFree)
> }
> }
>
> +void
> +sql_emit_func_types(struct Vdbe *vdbe, struct sql_builtin_func_args *args,
> + int reg, uint32_t argc)
-> sql_emit_func_arg_type_check()
> +{
> + assert(argc <= args->max_count);
> + enum field_type recurrent_type = args->recurrent_type;
> + assert(args->max_count > 0 || recurrent_type == FIELD_TYPE_ANY);
> + /*
> + * It makes no sense to check types of the MEMs if all
> + * arguments should be of type ANY.
> + */
> + if (recurrent_type == FIELD_TYPE_ANY)
> + return;
> + size_t size = (argc + 1) * sizeof(enum field_type);
> + enum field_type *types = sqlDbMallocZero(sql_get(), size);
> + for (uint32_t i = 0; i < argc; ++i) {
> + if (args->types == NULL)
> + types[i] = args->recurrent_type;
> + else
> + types[i] = args->types[i];
> + }
I don't understand what's going on here.
> + types[argc] = field_type_MAX;
> + sqlVdbeAddOp4(vdbe, OP_ApplyType, reg, argc, 0, (char *)types,
> + P4_DYNAMIC);
> + if (args->is_blob_like_str)
> + sqlVdbeChangeP5(vdbe, OPFLAG_BLOB_LIKE_STRING);
> +}
> +
> /*
> * Initialize a SelectDest structure.
> */
> void
> sql_emit_table_types(struct Vdbe *v, struct space_def *def, int reg);
>
> +/**
> + * Code an OP_ApplyType opcode that will force types for given
Not force, but try to cast implicitly. Force cast is implemented as CAST
operator.
> + * range of register starting from @a reg. These values then will
> + * be used as arguments of a function.
> + *
> + * @param vdbe VDBE.
> + * @param args Information about arguments of the function.
> + * @param reg Register where types will be placed.
> + * @param argc Number of arguments.
> + */
> +void
> +sql_emit_func_types(struct Vdbe *vdbe, struct sql_builtin_func_args *args,
> + int reg, uint32_t argc);
> +
> enum field_type
> sql_type_result(enum field_type lhs, enum field_type rhs);
>
> @@ -4406,6 +4424,20 @@ struct sql_builtin_func_args {
> uint32_t min_count;
> /** Max number of arguments. */
> uint32_t max_count;
> + /**
> + * If function arguments may not be of the same type, all
> + * argument types are described here.
> + */
> + enum field_type *types;
> + /**
> + * Contains the type of arguments if all arguments to the
> + * function are of the same type.
> + */
> + enum field_type recurrent_type;
Recurrent is quite unsuitable name. Why not always use array
of type? Is it big deal to fill and store it?
> + /**
> + * TRUE if the function should treat the BLOB as STRING.
> + */
> + bool is_blob_like_str;
Why do you need this attribute?
> };
>
> struct func_sql_builtin {
> diff --git a/src/box/sql/vdbe.c b/src/box/sql/vdbe.c
> index 863f38f5d..a4bf84bcc 100644
> --- a/src/box/sql/vdbe.c
> +++ b/src/box/sql/vdbe.c
> @@ -2882,7 +2882,7 @@ case OP_Fetch: {
> break;
> }
>
> -/* Opcode: ApplyType P1 P2 * P4 *
> +/* Opcode: ApplyType P1 P2 * P4 P5
> * Synopsis: type(r[P1@P2])
> *
> * Check that types of P2 registers starting from register P1 are
> @@ -2890,6 +2890,9 @@ case OP_Fetch: {
> * value and the given type are incompatible according to
> * field_mp_plain_type_is_compatible(), but both are numeric,
> * this opcode attempts to convert the value to the type.
> + *
> + * If P5 contains the OPFLAG_BLOB_LIKE_STRING flag, the BLOB
> + * values are processed as if they had the field type STRING.
I see this unprintable symbols for the third time. Did you review
your own patch?
> case OP_ApplyType: {
> enum field_type *types = pOp->p4.types;
> @@ -2904,6 +2907,13 @@ case OP_ApplyType: {
> pIn1++;
> continue;
> }
Need more info for this chunk.
> + if ((pOp->p5 & OPFLAG_BLOB_LIKE_STRING) != 0) {
> + if (type == FIELD_TYPE_STRING &&
> + mem_mp_type(pIn1) == MP_BIN) {
> + pIn1++;
> + continue;
> + }
> + }
> if (!mp_type_is_numeric(mem_mp_type(pIn1)) ||
> !sql_type_is_numeric(type) ||
> mem_convert_to_numeric(pIn1, type, false) != 0) {
> diff --git a/test/sql-tap/cse.test.lua b/test/sql-tap/cse.test.lua
> index 341b6de01..3c2076a1d 100755
> --- a/test/sql-tap/cse.test.lua
> +++ b/test/sql-tap/cse.test.lua
> @@ -195,23 +195,23 @@ test:do_execsql_test(
>
>
>
> -test:do_execsql_test(
> +test:do_catchsql_test(
> "cse-1.13",
> [[
> SELECT upper(b), typeof(b), b FROM t1
> ]], {
> -- <cse-1.13>
> - "11", "integer", 11, "21", "integer", 21
> + 1, "Type mismatch: can not convert 11 to string"
> -- </cse-1.13>
> })
DId not look at test changes yet. Please, elaborate on notes above
firstly.
>
^ permalink raw reply [flat|nested] 11+ messages in thread