From: Safin Timur via Tarantool-patches <tarantool-patches@dev.tarantool.org>
To: Vladimir Davydov <vdavydov@tarantool.org>, imeevma@tarantool.org
Cc: tarantool-patches@dev.tarantool.org
Subject: Re: [Tarantool-patches] [PATCH v2 2/5] sql: introduce SQL built-in functions to parser
Date: Sat, 21 Aug 2021 03:27:13 +0300 [thread overview]
Message-ID: <d8faaee1-720c-8d64-5098-eea30ef4766c@tarantool.org> (raw)
In-Reply-To: <20210819083514.i4hlig526gy6domj@esperanza>
[-- Attachment #1: Type: text/plain, Size: 11058 bytes --]
On 19.08.2021 11:35, Vladimir Davydov via Tarantool-patches wrote:
> On Wed, Aug 18, 2021 at 05:34:59PM +0300, imeevma@tarantool.org wrote:
>> diff --git a/extra/mkkeywordhash.c b/extra/mkkeywordhash.c
>> index 0d998506c..369d9e1dd 100644
>> --- a/extra/mkkeywordhash.c
>> +++ b/extra/mkkeywordhash.c
>> @@ -184,7 +184,6 @@ static Keyword aKeywordTable[] = {
>> { "BLOB", "TK_STANDARD", true },
>> { "BINARY", "TK_ID", true },
>> { "CALL", "TK_STANDARD", true },
>> - { "CHAR", "TK_CHAR", true },
>> { "CHARACTER", "TK_ID", true },
>> { "CONDITION", "TK_STANDARD", true },
>> { "CONNECT", "TK_STANDARD", true },
>> @@ -251,6 +250,42 @@ static Keyword aKeywordTable[] = {
>> { "LEADING", "TK_LEADING", true },
>> { "TRAILING", "TK_TRAILING", true },
>> { "BOTH", "TK_BOTH", true },
>> + { "ABS", "TK_ABS", true },
>> + { "AVG", "TK_AVG", true },
>> + { "CHAR", "TK_CHAR", true },
>> + { "CHAR_LENGTH", "TK_CHAR_LEN", true },
>> + { "CHARACTER_LENGTH", "TK_CHAR_LEN", true },
>> + { "COALESCE", "TK_COALESCE", true },
>> + { "COUNT", "TK_COUNT", true },
>> + { "GREATEST", "TK_GREATEST", true },
>> + { "GROUP_CONCAT", "TK_GROUP_CONCAT",true },
>> + { "HEX", "TK_HEX", true },
>> + { "IFNULL", "TK_IFNULL", true },
>> + { "LEAST", "TK_LEAST", true },
>> + { "LENGTH", "TK_LENGTH", true },
>> + { "LIKELIHOOD", "TK_LIKELIHOOD", true },
>> + { "LIKELY", "TK_LIKELY", true },
>> + { "LOWER", "TK_LOWER", true },
>> + { "MAX", "TK_MAX", true },
>> + { "MIN", "TK_MIN", true },
>> + { "NULLIF", "TK_NULLIF", true },
>> + { "POSITION", "TK_POSITION", true },
>> + { "PRINTF", "TK_PRINTF", true },
>> + { "QUOTE", "TK_QUOTE", true },
>> + { "RANDOM", "TK_RANDOM", true },
>> + { "RANDOMBLOB", "TK_RANDOMBLOB", true },
>> + { "ROUND", "TK_ROUND", true },
>> + { "ROW_COUNT", "TK_ROW_COUNT", true },
>> + { "SOUNDEX", "TK_SOUNDEX", true },
>> + { "SUBSTR", "TK_SUBSTR", true },
>> + { "SUM", "TK_SUM", true },
>> + { "TOTAL", "TK_TOTAL", true },
>> + { "TYPEOF", "TK_TYPEOF", true },
>> + { "UNICODE", "TK_UNICODE", true },
>> + { "UNLIKELY", "TK_UNLIKELY", true },
>> + { "UPPER", "TK_UPPER", true },
>> + { "VERSION", "TK_VERSION", true },
>> + { "ZEROBLOB", "TK_ZEROBLOB", true },
>
> Should be sorted?
They all made reserved (last true) - that's one of a problems in a patch.
>
>> };
>>
>> /* Number of keywords */
>> diff --git a/src/box/sql/parse.y b/src/box/sql/parse.y
>> index bd041e862..cb2e627db 100644
>> --- a/src/box/sql/parse.y
>> +++ b/src/box/sql/parse.y
>> @@ -1172,27 +1172,506 @@ trim_specification(A) ::= LEADING. { A =
> TRIM_LEADING; }
>> trim_specification(A) ::= TRAILING. { A = TRIM_TRAILING; }
>> trim_specification(A) ::= BOTH. { A = TRIM_BOTH; }
>>
>> -expr(A) ::= id(X) LP distinct(D) exprlist(Y) RP(E). {
>> - if( Y && Y->nExpr>pParse->db->aLimit[SQL_LIMIT_FUNCTION_ARG] ){
>> - const char *err =
>> - tt_sprintf("Number of arguments to function %.*s", X.n, X.z);
>> - diag_set(ClientError, ER_SQL_PARSER_LIMIT, err, Y->nExpr,
>> - pParse->db->aLimit[SQL_LIMIT_FUNCTION_ARG]);
>> +expr(A) ::= ABS(X) LP distinct(D) exprlist(Y) RP(E). {
>> + if (Y == NULL || Y->nExpr != 1) {
>> + int n = Y == NULL ? 0 : Y->nExpr;
>> + diag_set(ClientError, ER_FUNC_WRONG_ARG_COUNT, "ABS", "1", n);
>> pParse->is_aborted = true;
>> + return;
>> }
>> A.pExpr = sqlExprFunction(pParse, Y, &X);
>> - spanSet(&A,&X,&E);
>> - if( D==SF_Distinct && A.pExpr ){
>> + spanSet(&A, &X, &E);
>> + if(D == SF_Distinct && A.pExpr)
>> A.pExpr->flags |= EP_Distinct;
>> +}
>> +
>> +expr(A) ::= AVG(X) LP distinct(D) exprlist(Y) RP(E). {
>> + if (Y == NULL || Y->nExpr != 1) {
>> + int n = Y == NULL ? 0 : Y->nExpr;
>> + diag_set(ClientError, ER_FUNC_WRONG_ARG_COUNT, "AVG", "1", n);
>> + pParse->is_aborted = true;
>> + return;
>> }
>> + A.pExpr = sqlExprFunction(pParse, Y, &X);
>> + spanSet(&A, &X, &E);
>> + if(D == SF_Distinct && A.pExpr)
>> + A.pExpr->flags |= EP_Distinct;
>> }
>>
>> -/*
>> - * type_func(A) ::= DATE(A) .
>> - * type_func(A) ::= DATETIME(A) .
>> - */
>> -type_func(A) ::= CHAR(A) .
>> -expr(A) ::= type_func(X) LP distinct(D) exprlist(Y) RP(E). {
>> +expr(A) ::= CHAR(X) LP distinct(D) exprlist(Y) RP(E). {
>> + if (Y != NULL && Y->nExpr >
> pParse->db->aLimit[SQL_LIMIT_FUNCTION_ARG]) {
>> + const char *str = tt_sprintf("from %d to %d", 0,
>> +
> pParse->db->aLimit[SQL_LIMIT_FUNCTION_ARG]);
>> + diag_set(ClientError, ER_FUNC_WRONG_ARG_COUNT, "CHAR", str,
> Y->nExpr);
>> + pParse->is_aborted = true;
>> + return;
>> + }
>> + A.pExpr = sqlExprFunction(pParse, Y, &X);
>> + spanSet(&A, &X, &E);
>> + if(D == SF_Distinct && A.pExpr)
>> + A.pExpr->flags |= EP_Distinct;
>> +}
>> +
>> +expr(A) ::= CHAR_LEN(X) LP distinct(D) exprlist(Y) RP(E). {
>> + if (Y == NULL || Y->nExpr != 1) {
>> + int n = Y == NULL ? 0 : Y->nExpr;
>> + const char *name = X.n == strlen("CHAR_LENGTH") ? "CHAR_LENGTH" :
>> + "CHARACTER_LENGTH";
>> + diag_set(ClientError, ER_FUNC_WRONG_ARG_COUNT, name, "1", n);
>> + pParse->is_aborted = true;
>> + return;
>> + }
>> + A.pExpr = sqlExprFunction(pParse, Y, &X);
>> + spanSet(&A, &X, &E);
>> + if(D == SF_Distinct && A.pExpr)
>> + A.pExpr->flags |= EP_Distinct;
>> +}
>> +
>> +expr(A) ::= COALESCE(X) LP distinct(D) exprlist(Y) RP(E). {
>> + if (Y == NULL || Y->nExpr < 2 ||
>> + Y->nExpr > pParse->db->aLimit[SQL_LIMIT_FUNCTION_ARG]) {
>> + int n = Y == NULL ? 0 : Y->nExpr;
>> + const char *str = tt_sprintf("from %d to %d", 2,
>> +
> pParse->db->aLimit[SQL_LIMIT_FUNCTION_ARG]);
>> + diag_set(ClientError, ER_FUNC_WRONG_ARG_COUNT, "COALESCE", str, n);
>> + pParse->is_aborted = true;
>> + return;
>> + }
>> + A.pExpr = sqlExprFunction(pParse, Y, &X);
>> + spanSet(&A, &X, &E);
>> + if(D == SF_Distinct && A.pExpr)
>> + A.pExpr->flags |= EP_Distinct;
>> +}
>
> Can you the code to a helper function to avoid copy-paste?
Yup, a lot of copy-paste. Hard to distibgush the core of each. This all
could make significantly shorter:
-------------------------------------------------------------------
expr(A) ::= builtin_fn(X) LP distinct(D) exprlist(Y) RP(E). {
/* variable number of arguments */
int args = X.args, maxargs = X.maxargs;
int n = Y == NULL ? 0 : Y->nExpr;
if (X.vararg) {
int limit_args = pParse->db->aLimit[SQL_LIMIT_FUNCTION_ARG];
/* less than minimal # of expected or more than maximal */
if (n < args || n > limit_args) {
const char *str = tt_sprintf("from %d to %d", 0, limit_args);
diag_set(ClientError, ER_FUNC_WRONG_ARG_COUNT,
TOKEN_STR(X.token), str, Y->nExpr);
pParse->is_aborted = true;
return;
}
} else {
/* not expected number - args */
if (maxargs == 0 && n != args) {
diag_set(ClientError, ER_FUNC_WRONG_ARG_COUNT, TOKEN_STR(X.token),
tt_sprintf("%d", args), n);
pParse->is_aborted = true;
return;
}
/* from args till maxargs */
if (n < args || (maxargs != 0 && n > maxargs)) {/* or more than
expected */
const char *str = tt_sprintf("from %d to %d", args, maxargs);
diag_set(ClientError, ER_FUNC_WRONG_ARG_COUNT,
TOKEN_STR(X.token), str, n);
pParse->is_aborted = true;
return;
}
}
A.pExpr = sql_expr_new_built_in(pParse, Y, &X.token, X.id);
spanSet(&A, &X.token, &E);
if (D == SF_Distinct && A.pExpr)
A.pExpr->flags |= EP_Distinct;
}
%type builtin_fn {struct builtin_fn_def}
builtin_fn(A) ::= ABS(T). { FUNC_ARG_N(A, T, TK_ABS, 1); }
builtin_fn(A) ::= AVG(T). { FUNC_ARG_N(A, T, TK_AVG, 1); }
builtin_fn(A) ::= CHAR(T). { FUNC_ARG_VAR(A, T, TK_CHAR); }
builtin_fn(A) ::= CHAR_LEN(T). { FUNC_ARG_N(A, T, TK_CHAR_LENGTH, 1); }
builtin_fn(A) ::= COALESCE(T). { FUNC_ARG_VAR_N(A, T, TK_COALESCE, 2); }
/*builtin_fn(A) ::=(T) COUNT. { FUNC_ARG_N(A, T, TK_COUNT, 1); }*/
builtin_fn(A) ::= GREATEST(T). { FUNC_ARG_VAR_N(A, T, TK_GREATEST, 2); }
builtin_fn(A) ::= GROUP_CONCAT(T). { FUNC_ARG_N_M(A, T, TK_GROUP_CONCAT,
1, 2); }
....
-------------------------------------------------------------------
More details you could see in apatch attached.
>
>> diff --git a/test/box/tx_man.result b/test/box/tx_man.result
>> index 786d7fc30..b99fbc2ca 100644
>> --- a/test/box/tx_man.result
>> +++ b/test/box/tx_man.result
>> @@ -2129,11 +2129,11 @@ tx1:rollback()
>>
>> -- gh-6095: SQL query may crash in MVCC mode if it involves ephemeral
> spaces.
>> --
>> -box.execute([[ CREATE TABLE test (id INT NOT NULL PRIMARY KEY, count
> INT NOT NULL)]])
>> +box.execute([[ CREATE TABLE test (id INT NOT NULL PRIMARY KEY, "COUNT"
> INT NOT NULL)]])
>> | ---
>> | - row_count: 1
>> | ...
>> -box.execute([[ UPDATE test SET count = count + 1 WHERE id = 0 ]])
>> +box.execute([[ UPDATE test SET "COUNT" = "COUNT" + 1 WHERE id = 0 ]])
>
> This looks bad. MySQL and PostgreSQL allow that.
I've fixed it in a patch suggested...
>
>> | ---
>> | - row_count: 0
>> | ...
>> diff --git a/test/sql-tap/func.test.lua b/test/sql-tap/func.test.lua
>> index e7b35c9d9..7dd85025a 100755
>> --- a/test/sql-tap/func.test.lua
>> +++ b/test/sql-tap/func.test.lua
>> @@ -68,7 +68,7 @@ test:do_catchsql_test(
>> SELECT length(*) FROM tbl1 ORDER BY t1
>> ]], {
>> -- <func-1.1>
>> - 1, "Wrong number of arguments is passed to LENGTH(): expected
> 1, got 0"
>> + 1, "Syntax error at line 1 near '*'"
>
> This is probably okay.
>
>> -- </func-1.1>
>> })
>>
>> @@ -2483,7 +2483,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"
>
> And this too.
>
[Patch attached is for branch imeevma/gh-6105-check-types-no-test]
Best Regards,
Timur
[-- Attachment #2: 0001-sql-handle-sql-builtins-in-parser.patch --]
[-- Type: text/plain, Size: 31193 bytes --]
From f06233a02a22ad0d5d97f6c9fdccf58e536182a3 Mon Sep 17 00:00:00 2001
Message-Id: <f06233a02a22ad0d5d97f6c9fdccf58e536182a3.1629505300.git.tsafin@tarantool.org>
From: Timur Safin <tsafin@tarantool.org>
Date: Sat, 21 Aug 2021 01:34:17 +0300
Subject: [PATCH] sql: handle sql builtins in parser
To: imeevma@tarantool.org
Refactored a way how builtins declared in parse.y:
- made handling code to not duplicate in each case;
- properly initialized id fallback so they all
could be used in non-functions context.
---
| 72 ++---
src/box/sql/parse.y | 571 ++++++---------------------------------
src/box/sql/parse_def.h | 53 ++++
test/box/tx_man.result | 13 +-
test/box/tx_man.test.lua | 4 +-
5 files changed, 189 insertions(+), 524 deletions(-)
--git a/extra/mkkeywordhash.c b/extra/mkkeywordhash.c
index 235010da4..3d6f5c9bc 100644
--- a/extra/mkkeywordhash.c
+++ b/extra/mkkeywordhash.c
@@ -251,42 +251,42 @@ static Keyword aKeywordTable[] = {
{ "LEADING", "TK_LEADING", true },
{ "TRAILING", "TK_TRAILING", true },
{ "BOTH", "TK_BOTH", true },
- { "ABS", "TK_ABS", true },
- { "AVG", "TK_AVG", true },
- { "CHAR", "TK_CHAR", true },
- { "CHAR_LENGTH", "TK_CHAR_LEN", true },
- { "CHARACTER_LENGTH", "TK_CHAR_LEN", true },
- { "COALESCE", "TK_COALESCE", true },
- { "COUNT", "TK_COUNT", true },
- { "GREATEST", "TK_GREATEST", true },
- { "GROUP_CONCAT", "TK_GROUP_CONCAT",true },
- { "HEX", "TK_HEX", true },
- { "IFNULL", "TK_IFNULL", true },
- { "LEAST", "TK_LEAST", true },
- { "LENGTH", "TK_LENGTH", true },
- { "LIKELIHOOD", "TK_LIKELIHOOD", true },
- { "LIKELY", "TK_LIKELY", true },
- { "LOWER", "TK_LOWER", true },
- { "MAX", "TK_MAX", true },
- { "MIN", "TK_MIN", true },
- { "NULLIF", "TK_NULLIF", true },
- { "POSITION", "TK_POSITION", true },
- { "PRINTF", "TK_PRINTF", true },
- { "QUOTE", "TK_QUOTE", true },
- { "RANDOM", "TK_RANDOM", true },
- { "RANDOMBLOB", "TK_RANDOMBLOB", true },
- { "ROUND", "TK_ROUND", true },
- { "ROW_COUNT", "TK_ROW_COUNT", true },
- { "SOUNDEX", "TK_SOUNDEX", true },
- { "SUBSTR", "TK_SUBSTR", true },
- { "SUM", "TK_SUM", true },
- { "TOTAL", "TK_TOTAL", true },
- { "TYPEOF", "TK_TYPEOF", true },
- { "UNICODE", "TK_UNICODE", true },
- { "UNLIKELY", "TK_UNLIKELY", true },
- { "UPPER", "TK_UPPER", true },
- { "VERSION", "TK_VERSION", true },
- { "ZEROBLOB", "TK_ZEROBLOB", true },
+ { "ABS", "TK_ABS", false },
+ { "AVG", "TK_AVG", false },
+ { "CHAR", "TK_CHAR", false },
+ { "CHAR_LENGTH", "TK_CHAR_LEN", false },
+ { "CHARACTER_LENGTH", "TK_CHAR_LEN", false },
+ { "COALESCE", "TK_COALESCE", false },
+ { "COUNT", "TK_COUNT", false },
+ { "GREATEST", "TK_GREATEST", false },
+ { "GROUP_CONCAT", "TK_GROUP_CONCAT",false },
+ { "HEX", "TK_HEX", false },
+ { "IFNULL", "TK_IFNULL", false },
+ { "LEAST", "TK_LEAST", false },
+ { "LENGTH", "TK_LENGTH", false },
+ { "LIKELIHOOD", "TK_LIKELIHOOD", false },
+ { "LIKELY", "TK_LIKELY", false },
+ { "LOWER", "TK_LOWER", false },
+ { "MAX", "TK_MAX", false },
+ { "MIN", "TK_MIN", false },
+ { "NULLIF", "TK_NULLIF", false },
+ { "POSITION", "TK_POSITION", false },
+ { "PRINTF", "TK_PRINTF", false },
+ { "QUOTE", "TK_QUOTE", false },
+ { "RANDOM", "TK_RANDOM", false },
+ { "RANDOMBLOB", "TK_RANDOMBLOB", false },
+ { "ROUND", "TK_ROUND", false },
+ { "ROW_COUNT", "TK_ROW_COUNT", false },
+ { "SOUNDEX", "TK_SOUNDEX", false },
+ { "SUBSTR", "TK_SUBSTR", false },
+ { "SUM", "TK_SUM", false },
+ { "TOTAL", "TK_TOTAL", false },
+ { "TYPEOF", "TK_TYPEOF", false },
+ { "UNICODE", "TK_UNICODE", false },
+ { "UNLIKELY", "TK_UNLIKELY", false },
+ { "UPPER", "TK_UPPER", false },
+ { "VERSION", "TK_VERSION", false },
+ { "ZEROBLOB", "TK_ZEROBLOB", false },
};
/* Number of keywords */
diff --git a/src/box/sql/parse.y b/src/box/sql/parse.y
index 6d65788e3..00c755a4b 100644
--- a/src/box/sql/parse.y
+++ b/src/box/sql/parse.y
@@ -267,9 +267,17 @@ columnlist ::= tcons.
ABORT ACTION ADD AFTER AUTOINCREMENT BEFORE CASCADE
CONFLICT DEFERRED END ENGINE FAIL
IGNORE INITIALLY INSTEAD NO MATCH PLAN
- QUERY KEY OFFSET RAISE RELEASE REPLACE RESTRICT
- RENAME CTIME_KW IF ENABLE DISABLE UUID
+ QUERY KEY OFFSET RAISE RELEASE RESTRICT
+ RENAME CTIME_KW IF ENABLE DISABLE
+ ABS
+
+ AVG CHAR CHAR_LENGTH CHARACTER_LENGTH COALESCE COUNT
+ GREATEST GROUP_CONCAT HEX IFNULL LEAST LENGTH LIKELIHOOD
+ LIKELY LOWER MAX MIN NULLIF POSITION PRINTF QUOTE RANDOM
+ RANDOMBLOB REPLACE ROUND ROW_COUNT SOUNDEX SUBSTR SUM
+ TOTAL TYPEOF UNICODE UNLIKELY UPPER UUID VERSION
.
+
%wildcard ANY.
@@ -1172,87 +1180,98 @@ trim_specification(A) ::= LEADING. { A = TRIM_LEADING; }
trim_specification(A) ::= TRAILING. { A = TRIM_TRAILING; }
trim_specification(A) ::= BOTH. { A = TRIM_BOTH; }
-expr(A) ::= ABS(X) LP distinct(D) exprlist(Y) RP(E). {
- if (Y == NULL || Y->nExpr != 1) {
- int n = Y == NULL ? 0 : Y->nExpr;
- diag_set(ClientError, ER_FUNC_WRONG_ARG_COUNT, "ABS", "1", n);
- pParse->is_aborted = true;
- return;
- }
- A.pExpr = sql_expr_new_built_in(pParse, Y, &X, TK_ABS);
- spanSet(&A, &X, &E);
- if(D == SF_Distinct && A.pExpr)
- A.pExpr->flags |= EP_Distinct;
-}
+expr(A) ::= builtin_fn(X) LP distinct(D) exprlist(Y) RP(E). {
+ /* variable number of arguments */
+ int args = X.args, maxargs = X.maxargs;
+ int n = Y == NULL ? 0 : Y->nExpr;
-expr(A) ::= AVG(X) LP distinct(D) exprlist(Y) RP(E). {
- if (Y == NULL || Y->nExpr != 1) {
- int n = Y == NULL ? 0 : Y->nExpr;
- diag_set(ClientError, ER_FUNC_WRONG_ARG_COUNT, "AVG", "1", n);
- pParse->is_aborted = true;
- return;
- }
- A.pExpr = sql_expr_new_built_in(pParse, Y, &X, TK_AVG);
- spanSet(&A, &X, &E);
- if(D == SF_Distinct && A.pExpr)
- A.pExpr->flags |= EP_Distinct;
-}
+ if (X.vararg) {
+ int limit_args = pParse->db->aLimit[SQL_LIMIT_FUNCTION_ARG];
-expr(A) ::= CHAR(X) LP distinct(D) exprlist(Y) RP(E). {
- if (Y != NULL && Y->nExpr > pParse->db->aLimit[SQL_LIMIT_FUNCTION_ARG]) {
- const char *str = tt_sprintf("from %d to %d", 0,
- pParse->db->aLimit[SQL_LIMIT_FUNCTION_ARG]);
- diag_set(ClientError, ER_FUNC_WRONG_ARG_COUNT, "CHAR", str, Y->nExpr);
- pParse->is_aborted = true;
- return;
- }
- A.pExpr = sql_expr_new_built_in(pParse, Y, &X, TK_CHAR);
- spanSet(&A, &X, &E);
- if(D == SF_Distinct && A.pExpr)
- A.pExpr->flags |= EP_Distinct;
-}
-
-expr(A) ::= CHAR_LEN(X) LP distinct(D) exprlist(Y) RP(E). {
- if (Y == NULL || Y->nExpr != 1) {
- int n = Y == NULL ? 0 : Y->nExpr;
- const char *name = X.n == strlen("CHAR_LENGTH") ? "CHAR_LENGTH" :
- "CHARACTER_LENGTH";
- diag_set(ClientError, ER_FUNC_WRONG_ARG_COUNT, name, "1", n);
- pParse->is_aborted = true;
- return;
- }
- A.pExpr = sql_expr_new_built_in(pParse, Y, &X, TK_CHAR_LEN);
- spanSet(&A, &X, &E);
- if(D == SF_Distinct && A.pExpr)
- A.pExpr->flags |= EP_Distinct;
-}
-
-expr(A) ::= COALESCE(X) LP distinct(D) exprlist(Y) RP(E). {
- if (Y == NULL || Y->nExpr < 2 ||
- Y->nExpr > pParse->db->aLimit[SQL_LIMIT_FUNCTION_ARG]) {
- int n = Y == NULL ? 0 : Y->nExpr;
- const char *str = tt_sprintf("from %d to %d", 2,
- pParse->db->aLimit[SQL_LIMIT_FUNCTION_ARG]);
- diag_set(ClientError, ER_FUNC_WRONG_ARG_COUNT, "COALESCE", str, n);
- pParse->is_aborted = true;
- return;
+ /* less than minimal # of expected or more than maximal */
+ if (n < args || n > limit_args) {
+ const char *str = tt_sprintf("from %d to %d", 0, limit_args);
+ diag_set(ClientError, ER_FUNC_WRONG_ARG_COUNT,
+ TOKEN_STR(X.token), str, Y->nExpr);
+ pParse->is_aborted = true;
+ return;
+ }
+ } else {
+ /* not expected number - args */
+ if (maxargs == 0 && n != args) {
+ diag_set(ClientError, ER_FUNC_WRONG_ARG_COUNT, TOKEN_STR(X.token),
+ tt_sprintf("%d", args), n);
+ pParse->is_aborted = true;
+ return;
+ }
+ /* from args till maxargs */
+ if (n < args || (maxargs != 0 && n > maxargs)) {/* or more than expected */
+ const char *str = tt_sprintf("from %d to %d", args, maxargs);
+ diag_set(ClientError, ER_FUNC_WRONG_ARG_COUNT, TOKEN_STR(X.token), str, n);
+ pParse->is_aborted = true;
+ return;
+ }
}
- A.pExpr = sql_expr_new_built_in(pParse, Y, &X, TK_COALESCE);
- spanSet(&A, &X, &E);
- if(D == SF_Distinct && A.pExpr)
+ A.pExpr = sql_expr_new_built_in(pParse, Y, &X.token, X.id);
+ spanSet(&A, &X.token, &E);
+ if (D == SF_Distinct && A.pExpr)
A.pExpr->flags |= EP_Distinct;
}
+%type builtin_fn {struct builtin_fn_def}
+
+builtin_fn(A) ::= ABS(T). { FUNC_ARG_N(A, T, TK_ABS, 1); }
+builtin_fn(A) ::= AVG(T). { FUNC_ARG_N(A, T, TK_AVG, 1); }
+builtin_fn(A) ::= CHAR(T). { FUNC_ARG_VAR(A, T, TK_CHAR); }
+builtin_fn(A) ::= CHAR_LEN(T). { FUNC_ARG_N(A, T, TK_CHAR_LENGTH, 1); }
+builtin_fn(A) ::= COALESCE(T). { FUNC_ARG_VAR_N(A, T, TK_COALESCE, 2); }
+/*builtin_fn(A) ::=(T) COUNT. { FUNC_ARG_N(A, T, TK_COUNT, 1); }*/
+builtin_fn(A) ::= GREATEST(T). { FUNC_ARG_VAR_N(A, T, TK_GREATEST, 2); }
+builtin_fn(A) ::= GROUP_CONCAT(T). { FUNC_ARG_N_M(A, T, TK_GROUP_CONCAT, 1, 2); }
+builtin_fn(A) ::= HEX(T). { FUNC_ARG_N(A, T, TK_HEX, 1); }
+builtin_fn(A) ::= IFNULL(T). { FUNC_ARG_N(A, T, TK_IFNULL, 2); }
+builtin_fn(A) ::= LEAST(T). { FUNC_ARG_VAR_N(A, T, TK_LEAST, 2); }
+builtin_fn(A) ::= LENGTH(T). { FUNC_ARG_N(A, T, TK_LENGTH, 1); }
+builtin_fn(A) ::= LIKELIHOOD(T). { FUNC_ARG_N(A, T, TK_LIKELIHOOD, 2); }
+builtin_fn(A) ::= LIKELY(T). { FUNC_ARG_N(A, T, TK_LIKELY, 1); }
+builtin_fn(A) ::= LOWER(T). { FUNC_ARG_N(A, T, TK_LOWER, 1);}
+builtin_fn(A) ::= MAX(T). { FUNC_ARG_N(A, T, TK_MAX, 1); }
+builtin_fn(A) ::= MIN(T). { FUNC_ARG_N(A, T, TK_MIN, 1); }
+builtin_fn(A) ::= NULLIF(T). { FUNC_ARG_N(A, T, TK_NULLIF, 2); }
+builtin_fn(A) ::= POSITION(T). { FUNC_ARG_N(A, T, TK_POSITION, 2); }
+builtin_fn(A) ::= PRINTF(T). { FUNC_ARG_VAR(A, T, TK_PRINTF); }
+builtin_fn(A) ::= QUOTE(T). { FUNC_ARG_N(A, T, TK_QUOTE, 1); }
+builtin_fn(A) ::= RANDOM(T). { FUNC_ARG_N(A, T, TK_RANDOM, 0); }
+builtin_fn(A) ::= RANDOMBLOB(T). { FUNC_ARG_N(A, T, TK_RANDOMBLOB, 1); }
+builtin_fn(A) ::= REPLACE(T). { FUNC_ARG_N(A, T, TK_REPLACE, 3); }
+builtin_fn(A) ::= ROUND(T). { FUNC_ARG_N_M(A, T, TK_ROUND, 1, 2); }
+builtin_fn(A) ::= ROW_COUNT(T). { FUNC_ARG_N(A, T, TK_ROW_COUNT, 0); }
+builtin_fn(A) ::= SOUNDEX(T). { FUNC_ARG_N(A, T, TK_SOUNDEX, 1); }
+builtin_fn(A) ::= SUBSTR(T). { FUNC_ARG_N_M(A, T, TK_SUBSTR, 2, 3); }
+builtin_fn(A) ::= SUM(T). { FUNC_ARG_N(A, T, TK_SUM, 1); }
+builtin_fn(A) ::= TOTAL(T). { FUNC_ARG_N(A, T, TK_TOTAL, 1); }
+builtin_fn(A) ::= TYPEOF(T). { FUNC_ARG_N(A, T, TK_TYPEOF, 1); }
+builtin_fn(A) ::= UNICODE(T). { FUNC_ARG_N(A, T, TK_UNICODE, 1); }
+builtin_fn(A) ::= UNLIKELY(T). { FUNC_ARG_N(A, T, TK_UNLIKELY, 1); }
+builtin_fn(A) ::= UPPER(T). { FUNC_ARG_N(A, T, TK_UPPER, 1); }
+builtin_fn(A) ::= UUID(T). { FUNC_ARG_N(A, T, TK_UUID, 1); }
+builtin_fn(A) ::= VERSION(T). { FUNC_ARG_N(A, T, TK_VERSION, 0); }
+builtin_fn(A) ::= ZEROBLOB(T). { FUNC_ARG_N(A, T, TK_ZEROBLOB, 1); }
+
+/* to avoid shift-reduce conflict here we have to expand this rule
+ manually for TK_COUNT */
expr(A) ::= COUNT(X) LP distinct(D) exprlist(Y) RP(E). {
- if (Y != NULL && Y->nExpr > 1) {
- const char *str = tt_sprintf("from %d to %d", 0, 1);
- diag_set(ClientError, ER_FUNC_WRONG_ARG_COUNT, "COUNT", str, Y->nExpr);
+ int n = Y == NULL ? 0 : Y->nExpr;
+
+ /* not expected number of arguments */
+ if (n != 1) {
+ diag_set(ClientError, ER_FUNC_WRONG_ARG_COUNT, TOKEN_STR(X), "1", n);
pParse->is_aborted = true;
return;
}
A.pExpr = sql_expr_new_built_in(pParse, Y, &X, TK_COUNT);
spanSet(&A, &X, &E);
- if(D == SF_Distinct && A.pExpr)
+ if (D == SF_Distinct && A.pExpr)
A.pExpr->flags |= EP_Distinct;
}
@@ -1261,416 +1280,6 @@ expr(A) ::= COUNT(X) LP STAR RP(E). {
spanSet(&A, &X, &E);
}
-expr(A) ::= GREATEST(X) LP distinct(D) exprlist(Y) RP(E). {
- if (Y == NULL || Y->nExpr < 2 ||
- Y->nExpr > pParse->db->aLimit[SQL_LIMIT_FUNCTION_ARG]) {
- int n = Y == NULL ? 0 : Y->nExpr;
- const char *str = tt_sprintf("from %d to %d", 2,
- pParse->db->aLimit[SQL_LIMIT_FUNCTION_ARG]);
- diag_set(ClientError, ER_FUNC_WRONG_ARG_COUNT, "GREATEST", str, n);
- pParse->is_aborted = true;
- return;
- }
- A.pExpr = sql_expr_new_built_in(pParse, Y, &X, TK_GREATEST);
- spanSet(&A, &X, &E);
- if(D == SF_Distinct && A.pExpr)
- A.pExpr->flags |= EP_Distinct;
-}
-
-expr(A) ::= GROUP_CONCAT(X) LP distinct(D) exprlist(Y) RP(E). {
- if (Y == NULL || Y->nExpr > 2) {
- int n = Y == NULL ? 0 : Y->nExpr;
- const char *str = tt_sprintf("from %d to %d", 1, 2);
- diag_set(ClientError, ER_FUNC_WRONG_ARG_COUNT, "GROUP_CONCAT", str, n);
- pParse->is_aborted = true;
- return;
- }
- A.pExpr = sql_expr_new_built_in(pParse, Y, &X, TK_GROUP_CONCAT);
- spanSet(&A, &X, &E);
- if(D == SF_Distinct && A.pExpr)
- A.pExpr->flags |= EP_Distinct;
-}
-
-expr(A) ::= HEX(X) LP distinct(D) exprlist(Y) RP(E). {
- if (Y == NULL || Y->nExpr != 1) {
- int n = Y == NULL ? 0 : Y->nExpr;
- diag_set(ClientError, ER_FUNC_WRONG_ARG_COUNT, "HEX", "1", n);
- pParse->is_aborted = true;
- return;
- }
- A.pExpr = sql_expr_new_built_in(pParse, Y, &X, TK_HEX);
- spanSet(&A, &X, &E);
- if(D == SF_Distinct && A.pExpr)
- A.pExpr->flags |= EP_Distinct;
-}
-
-expr(A) ::= IFNULL(X) LP distinct(D) exprlist(Y) RP(E). {
- if (Y == NULL || Y->nExpr != 2) {
- int n = Y == NULL ? 0 : Y->nExpr;
- diag_set(ClientError, ER_FUNC_WRONG_ARG_COUNT, "IFNULL", "2", n);
- pParse->is_aborted = true;
- return;
- }
- A.pExpr = sql_expr_new_built_in(pParse, Y, &X, TK_IFNULL);
- spanSet(&A, &X, &E);
- if(D == SF_Distinct && A.pExpr)
- A.pExpr->flags |= EP_Distinct;
-}
-
-expr(A) ::= LEAST(X) LP distinct(D) exprlist(Y) RP(E). {
- if (Y == NULL || Y->nExpr < 2 ||
- Y->nExpr > pParse->db->aLimit[SQL_LIMIT_FUNCTION_ARG]) {
- int n = Y == NULL ? 0 : Y->nExpr;
- const char *str = tt_sprintf("from %d to %d", 2,
- pParse->db->aLimit[SQL_LIMIT_FUNCTION_ARG]);
- diag_set(ClientError, ER_FUNC_WRONG_ARG_COUNT, "LEAST", str, n);
- pParse->is_aborted = true;
- return;
- }
- A.pExpr = sql_expr_new_built_in(pParse, Y, &X, TK_LEAST);
- spanSet(&A, &X, &E);
- if(D == SF_Distinct && A.pExpr)
- A.pExpr->flags |= EP_Distinct;
-}
-
-expr(A) ::= LENGTH(X) LP distinct(D) exprlist(Y) RP(E). {
- if (Y == NULL || Y->nExpr != 1) {
- int n = Y == NULL ? 0 : Y->nExpr;
- diag_set(ClientError, ER_FUNC_WRONG_ARG_COUNT, "LENGTH", "1", n);
- pParse->is_aborted = true;
- return;
- }
- A.pExpr = sql_expr_new_built_in(pParse, Y, &X, TK_LENGTH);
- spanSet(&A, &X, &E);
- if(D == SF_Distinct && A.pExpr)
- A.pExpr->flags |= EP_Distinct;
-}
-
-expr(A) ::= LIKELIHOOD(X) LP distinct(D) exprlist(Y) RP(E). {
- if (Y == NULL || Y->nExpr != 2) {
- int n = Y == NULL ? 0 : Y->nExpr;
- diag_set(ClientError, ER_FUNC_WRONG_ARG_COUNT, "LIKELIHOOD", "2", n);
- pParse->is_aborted = true;
- return;
- }
- A.pExpr = sql_expr_new_built_in(pParse, Y, &X, TK_LIKELIHOOD);
- spanSet(&A, &X, &E);
- if(D == SF_Distinct && A.pExpr)
- A.pExpr->flags |= EP_Distinct;
-}
-
-expr(A) ::= LIKELY(X) LP distinct(D) exprlist(Y) RP(E). {
- if (Y == NULL || Y->nExpr != 1) {
- int n = Y == NULL ? 0 : Y->nExpr;
- diag_set(ClientError, ER_FUNC_WRONG_ARG_COUNT, "LIKELY", "1", n);
- pParse->is_aborted = true;
- return;
- }
- A.pExpr = sql_expr_new_built_in(pParse, Y, &X, TK_LIKELY);
- spanSet(&A, &X, &E);
- if(D == SF_Distinct && A.pExpr)
- A.pExpr->flags |= EP_Distinct;
-}
-
-expr(A) ::= LOWER(X) LP distinct(D) exprlist(Y) RP(E). {
- if (Y == NULL || Y->nExpr != 1) {
- int n = Y == NULL ? 0 : Y->nExpr;
- diag_set(ClientError, ER_FUNC_WRONG_ARG_COUNT, "LOWER", "1", n);
- pParse->is_aborted = true;
- return;
- }
- A.pExpr = sql_expr_new_built_in(pParse, Y, &X, TK_LOWER);
- spanSet(&A, &X, &E);
- if(D == SF_Distinct && A.pExpr)
- A.pExpr->flags |= EP_Distinct;
-}
-
-expr(A) ::= MAX(X) LP distinct(D) exprlist(Y) RP(E). {
- if (Y == NULL || Y->nExpr != 1) {
- int n = Y == NULL ? 0 : Y->nExpr;
- diag_set(ClientError, ER_FUNC_WRONG_ARG_COUNT, "MAX", "1", n);
- pParse->is_aborted = true;
- return;
- }
- A.pExpr = sql_expr_new_built_in(pParse, Y, &X, TK_MAX);
- spanSet(&A, &X, &E);
- if(D == SF_Distinct && A.pExpr)
- A.pExpr->flags |= EP_Distinct;
-}
-
-expr(A) ::= MIN(X) LP distinct(D) exprlist(Y) RP(E). {
- if (Y == NULL || Y->nExpr != 1) {
- int n = Y == NULL ? 0 : Y->nExpr;
- diag_set(ClientError, ER_FUNC_WRONG_ARG_COUNT, "MIN", "1", n);
- pParse->is_aborted = true;
- return;
- }
- A.pExpr = sql_expr_new_built_in(pParse, Y, &X, TK_MIN);
- spanSet(&A, &X, &E);
- if(D == SF_Distinct && A.pExpr)
- A.pExpr->flags |= EP_Distinct;
-}
-
-expr(A) ::= NULLIF(X) LP distinct(D) exprlist(Y) RP(E). {
- if (Y == NULL || Y->nExpr != 2) {
- int n = Y == NULL ? 0 : Y->nExpr;
- diag_set(ClientError, ER_FUNC_WRONG_ARG_COUNT, "NULLIF", "2", n);
- pParse->is_aborted = true;
- return;
- }
- A.pExpr = sql_expr_new_built_in(pParse, Y, &X, TK_NULLIF);
- spanSet(&A, &X, &E);
- if(D == SF_Distinct && A.pExpr)
- A.pExpr->flags |= EP_Distinct;
-}
-
-expr(A) ::= POSITION(X) LP distinct(D) exprlist(Y) RP(E). {
- if (Y == NULL || Y->nExpr != 2) {
- int n = Y == NULL ? 0 : Y->nExpr;
- diag_set(ClientError, ER_FUNC_WRONG_ARG_COUNT, "POSITION", "2", n);
- pParse->is_aborted = true;
- return;
- }
- A.pExpr = sql_expr_new_built_in(pParse, Y, &X, TK_POSITION);
- spanSet(&A, &X, &E);
- if(D == SF_Distinct && A.pExpr)
- A.pExpr->flags |= EP_Distinct;
-}
-
-expr(A) ::= PRINTF(X) LP distinct(D) exprlist(Y) RP(E). {
- if (Y != NULL && Y->nExpr > pParse->db->aLimit[SQL_LIMIT_FUNCTION_ARG]) {
- const char *str = tt_sprintf("from %d to %d", 0,
- pParse->db->aLimit[SQL_LIMIT_FUNCTION_ARG]);
- diag_set(ClientError, ER_FUNC_WRONG_ARG_COUNT, "PRINTF", str, Y->nExpr);
- pParse->is_aborted = true;
- return;
- }
- A.pExpr = sql_expr_new_built_in(pParse, Y, &X, TK_PRINTF);
- spanSet(&A, &X, &E);
- if(D == SF_Distinct && A.pExpr)
- A.pExpr->flags |= EP_Distinct;
-}
-
-expr(A) ::= QUOTE(X) LP distinct(D) exprlist(Y) RP(E). {
- if (Y == NULL || Y->nExpr != 1) {
- int n = Y == NULL ? 0 : Y->nExpr;
- diag_set(ClientError, ER_FUNC_WRONG_ARG_COUNT, "QUOTE", "1", n);
- pParse->is_aborted = true;
- return;
- }
- A.pExpr = sql_expr_new_built_in(pParse, Y, &X, TK_QUOTE);
- spanSet(&A, &X, &E);
- if(D == SF_Distinct && A.pExpr)
- A.pExpr->flags |= EP_Distinct;
-}
-
-expr(A) ::= RANDOM(X) LP distinct(D) exprlist(Y) RP(E). {
- if (Y != NULL) {
- diag_set(ClientError, ER_FUNC_WRONG_ARG_COUNT, "RANDOM", "0", Y->nExpr);
- pParse->is_aborted = true;
- return;
- }
- A.pExpr = sql_expr_new_built_in(pParse, Y, &X, TK_RANDOM);
- spanSet(&A, &X, &E);
- if(D == SF_Distinct && A.pExpr)
- A.pExpr->flags |= EP_Distinct;
-}
-
-expr(A) ::= RANDOMBLOB(X) LP distinct(D) exprlist(Y) RP(E). {
- if (Y == NULL || Y->nExpr != 1) {
- int n = Y == NULL ? 0 : Y->nExpr;
- diag_set(ClientError, ER_FUNC_WRONG_ARG_COUNT, "RANDOMBLOB", "1", n);
- pParse->is_aborted = true;
- return;
- }
- A.pExpr = sql_expr_new_built_in(pParse, Y, &X, TK_RANDOMBLOB);
- spanSet(&A, &X, &E);
- if(D == SF_Distinct && A.pExpr)
- A.pExpr->flags |= EP_Distinct;
-}
-
-expr(A) ::= REPLACE(X) LP distinct(D) exprlist(Y) RP(E). {
- if (Y == NULL || Y->nExpr != 3) {
- int n = Y == NULL ? 0 : Y->nExpr;
- diag_set(ClientError, ER_FUNC_WRONG_ARG_COUNT, "REPLACE", "3", n);
- pParse->is_aborted = true;
- return;
- }
- A.pExpr = sql_expr_new_built_in(pParse, Y, &X, TK_REPLACE);
- spanSet(&A, &X, &E);
- if(D == SF_Distinct && A.pExpr)
- A.pExpr->flags |= EP_Distinct;
-}
-
-expr(A) ::= ROUND(X) LP distinct(D) exprlist(Y) RP(E). {
- if (Y == NULL || Y->nExpr > 2) {
- int n = Y == NULL ? 0 : Y->nExpr;
- const char *str = tt_sprintf("from %d to %d", 1, 2);
- diag_set(ClientError, ER_FUNC_WRONG_ARG_COUNT, "ROUND", str, n);
- pParse->is_aborted = true;
- return;
- }
- A.pExpr = sql_expr_new_built_in(pParse, Y, &X, TK_ROUND);
- spanSet(&A, &X, &E);
- if(D == SF_Distinct && A.pExpr)
- A.pExpr->flags |= EP_Distinct;
-}
-
-expr(A) ::= ROW_COUNT(X) LP distinct(D) exprlist(Y) RP(E). {
- if (Y != NULL) {
- diag_set(ClientError, ER_FUNC_WRONG_ARG_COUNT, "ROW_COUNT", "0", Y->nExpr);
- pParse->is_aborted = true;
- return;
- }
- A.pExpr = sql_expr_new_built_in(pParse, Y, &X, TK_ROW_COUNT);
- spanSet(&A, &X, &E);
- if(D == SF_Distinct && A.pExpr)
- A.pExpr->flags |= EP_Distinct;
-}
-
-expr(A) ::= SOUNDEX(X) LP distinct(D) exprlist(Y) RP(E). {
- if (Y == NULL || Y->nExpr != 1) {
- int n = Y == NULL ? 0 : Y->nExpr;
- diag_set(ClientError, ER_FUNC_WRONG_ARG_COUNT, "SOUNDEX", "1", n);
- pParse->is_aborted = true;
- return;
- }
- A.pExpr = sql_expr_new_built_in(pParse, Y, &X, TK_SOUNDEX);
- spanSet(&A, &X, &E);
- if(D == SF_Distinct && A.pExpr)
- A.pExpr->flags |= EP_Distinct;
-}
-
-expr(A) ::= SUBSTR(X) LP distinct(D) exprlist(Y) RP(E). {
- if (Y == NULL || Y->nExpr < 2 || Y->nExpr > 3) {
- int n = Y == NULL ? 0 : Y->nExpr;
- const char *str = tt_sprintf("from %d to %d", 2, 3);
- diag_set(ClientError, ER_FUNC_WRONG_ARG_COUNT, "SUBSTR", str, n);
- pParse->is_aborted = true;
- return;
- }
- A.pExpr = sql_expr_new_built_in(pParse, Y, &X, TK_SUBSTR);
- spanSet(&A, &X, &E);
- if(D == SF_Distinct && A.pExpr)
- A.pExpr->flags |= EP_Distinct;
-}
-
-expr(A) ::= SUM(X) LP distinct(D) exprlist(Y) RP(E). {
- if (Y == NULL || Y->nExpr != 1) {
- int n = Y == NULL ? 0 : Y->nExpr;
- diag_set(ClientError, ER_FUNC_WRONG_ARG_COUNT, "SUM", "1", n);
- pParse->is_aborted = true;
- return;
- }
- A.pExpr = sql_expr_new_built_in(pParse, Y, &X, TK_SUM);
- spanSet(&A, &X, &E);
- if(D == SF_Distinct && A.pExpr)
- A.pExpr->flags |= EP_Distinct;
-}
-
-expr(A) ::= TOTAL(X) LP distinct(D) exprlist(Y) RP(E). {
- if (Y == NULL || Y->nExpr != 1) {
- int n = Y == NULL ? 0 : Y->nExpr;
- diag_set(ClientError, ER_FUNC_WRONG_ARG_COUNT, "TOTAL", "1", n);
- pParse->is_aborted = true;
- return;
- }
- A.pExpr = sql_expr_new_built_in(pParse, Y, &X, TK_TOTAL);
- spanSet(&A, &X, &E);
- if(D == SF_Distinct && A.pExpr)
- A.pExpr->flags |= EP_Distinct;
-}
-
-expr(A) ::= TYPEOF(X) LP distinct(D) exprlist(Y) RP(E). {
- if (Y == NULL || Y->nExpr != 1) {
- int n = Y == NULL ? 0 : Y->nExpr;
- diag_set(ClientError, ER_FUNC_WRONG_ARG_COUNT, "TYPEOF", "1", n);
- pParse->is_aborted = true;
- return;
- }
- A.pExpr = sql_expr_new_built_in(pParse, Y, &X, TK_TYPEOF);
- spanSet(&A, &X, &E);
- if(D == SF_Distinct && A.pExpr)
- A.pExpr->flags |= EP_Distinct;
-}
-
-expr(A) ::= UNICODE(X) LP distinct(D) exprlist(Y) RP(E). {
- if (Y == NULL || Y->nExpr != 1) {
- int n = Y == NULL ? 0 : Y->nExpr;
- diag_set(ClientError, ER_FUNC_WRONG_ARG_COUNT, "UNICODE", "1", n);
- pParse->is_aborted = true;
- return;
- }
- A.pExpr = sql_expr_new_built_in(pParse, Y, &X, TK_UNICODE);
- spanSet(&A, &X, &E);
- if(D == SF_Distinct && A.pExpr)
- A.pExpr->flags |= EP_Distinct;
-}
-
-expr(A) ::= UNLIKELY(X) LP distinct(D) exprlist(Y) RP(E). {
- if (Y == NULL || Y->nExpr != 1) {
- int n = Y == NULL ? 0 : Y->nExpr;
- diag_set(ClientError, ER_FUNC_WRONG_ARG_COUNT, "UNLIKELY", "1", n);
- pParse->is_aborted = true;
- return;
- }
- A.pExpr = sql_expr_new_built_in(pParse, Y, &X, TK_UNLIKELY);
- spanSet(&A, &X, &E);
- if(D == SF_Distinct && A.pExpr)
- A.pExpr->flags |= EP_Distinct;
-}
-
-expr(A) ::= UPPER(X) LP distinct(D) exprlist(Y) RP(E). {
- if (Y == NULL || Y->nExpr != 1) {
- int n = Y == NULL ? 0 : Y->nExpr;
- diag_set(ClientError, ER_FUNC_WRONG_ARG_COUNT, "UPPER", "1", n);
- pParse->is_aborted = true;
- return;
- }
- A.pExpr = sql_expr_new_built_in(pParse, Y, &X, TK_UPPER);
- spanSet(&A, &X, &E);
- if(D == SF_Distinct && A.pExpr)
- A.pExpr->flags |= EP_Distinct;
-}
-
-expr(A) ::= UUID(X) LP distinct(D) exprlist(Y) RP(E). {
- if (Y != NULL && Y->nExpr > 1) {
- const char *str = tt_sprintf("from %d to %d", 0, 1);
- diag_set(ClientError, ER_FUNC_WRONG_ARG_COUNT, "UUID", str, Y->nExpr);
- pParse->is_aborted = true;
- return;
- }
- A.pExpr = sql_expr_new_built_in(pParse, Y, &X, TK_UUID);
- spanSet(&A, &X, &E);
- if(D == SF_Distinct && A.pExpr)
- A.pExpr->flags |= EP_Distinct;
-}
-
-expr(A) ::= VERSION(X) LP distinct(D) exprlist(Y) RP(E). {
- if (Y != NULL) {
- diag_set(ClientError, ER_FUNC_WRONG_ARG_COUNT, "VERSION", "0", Y->nExpr);
- pParse->is_aborted = true;
- return;
- }
- A.pExpr = sql_expr_new_built_in(pParse, Y, &X, TK_VERSION);
- spanSet(&A, &X, &E);
- if(D == SF_Distinct && A.pExpr)
- A.pExpr->flags |= EP_Distinct;
-}
-
-expr(A) ::= ZEROBLOB(X) LP distinct(D) exprlist(Y) RP(E). {
- if (Y == NULL || Y->nExpr != 1) {
- int n = Y == NULL ? 0 : Y->nExpr;
- diag_set(ClientError, ER_FUNC_WRONG_ARG_COUNT, "ZEROBLOB", "1", n);
- pParse->is_aborted = true;
- return;
- }
- A.pExpr = sql_expr_new_built_in(pParse, Y, &X, TK_ZEROBLOB);
- spanSet(&A, &X, &E);
- if(D == SF_Distinct && A.pExpr)
- A.pExpr->flags |= EP_Distinct;
-}
-
expr(A) ::= id(X) LP distinct(D) exprlist(Y) RP(E). {
if( Y && Y->nExpr>pParse->db->aLimit[SQL_LIMIT_FUNCTION_ARG] ){
const char *err =
diff --git a/src/box/sql/parse_def.h b/src/box/sql/parse_def.h
index c5e093517..904d15dfb 100644
--- a/src/box/sql/parse_def.h
+++ b/src/box/sql/parse_def.h
@@ -320,6 +320,59 @@ struct create_index_def {
enum sort_order sort_order;
};
+struct builtin_fn_def {
+ struct Token token;
+ /* Token ID */
+ uint8_t id;
+ /** variable number of arguments */
+ bool vararg;
+ /** for variargs = minimal number of arguments
+ * otherwise - exat number of arguments
+ */
+ int args;
+ /** maximum # of arguments if defined
+ * otherwise should be euqal to args
+ */
+ int maxargs;
+};
+
+#define TOKEN_STR(token) tt_sprintf("%.*s", (token).n, (token).z)
+
+/** exactly N args, most frequently N==1 */
+#define FUNC_ARG_N(A, T, t, N) { \
+ A.id = t; \
+ A.token = T; \
+ A.vararg = false; \
+ A.args = N; \
+ A.maxargs = 0; \
+}
+
+/** from N, to M number of args */
+#define FUNC_ARG_N_M(A, T, t, N, M) { \
+ A.id = t; \
+ A.token = T; \
+ A.vararg = false; \
+ A.args = N; \
+ A.maxargs = M; \
+}
+
+/** variable # of args */
+#define FUNC_ARG_VAR(A, T, t) { \
+ A.id = t; \
+ A.token = T; \
+ A.vararg = true; \
+ A.args = A.maxargs = 0; \
+}
+
+/** variable # of args, at least N */
+#define FUNC_ARG_VAR_N(A, T, t, N) { \
+ A.id = t; \
+ A.token = T; \
+ A.vararg = true; \
+ A.args = N; \
+ A.maxargs = 0; \
+}
+
/** Basic initialisers of parse structures.*/
static inline void
alter_entity_def_init(struct alter_entity_def *alter_def,
diff --git a/test/box/tx_man.result b/test/box/tx_man.result
index 22d8cf450..3b691ae88 100644
--- a/test/box/tx_man.result
+++ b/test/box/tx_man.result
@@ -2129,13 +2129,14 @@ tx1:rollback()
-- gh-6095: SQL query may crash in MVCC mode if it involves ephemeral spaces.
--
-box.execute([[ CREATE TABLE test (id INT NOT NULL PRIMARY KEY, "COUNT" INT NOT NULL)]])
+box.execute([[ CREATE TABLE test (id INT NOT NULL PRIMARY KEY, count INT NOT NULL)]])
| ---
| - row_count: 1
| ...
-box.execute([[ UPDATE test SET "COUNT" = "COUNT" + 1 WHERE id = 0 ]])
+box.execute([[ UPDATE test SET count = count + 1 WHERE id = 0 ]])
| ---
- | - row_count: 0
+ | - null
+ | - Syntax error at line 1 near '+'
| ...
box.execute([[ DROP TABLE test]])
| ---
@@ -3641,7 +3642,8 @@ tx1:begin()
| ...
tx1('box.execute([[SELECT COUNT() from k1]])')
| ---
- | - - {'rows': [[0]], 'metadata': [{'type': 'integer', 'name': 'COLUMN_1'}]}
+ | - - null
+ | - 'Wrong number of arguments is passed to COUNT(): expected 1, got 0'
| ...
box.execute([[INSERT INTO k1 VALUES (1);]])
| ---
@@ -3649,7 +3651,8 @@ box.execute([[INSERT INTO k1 VALUES (1);]])
| ...
tx1('box.execute([[SELECT COUNT() from k1]])')
| ---
- | - - {'rows': [[0]], 'metadata': [{'type': 'integer', 'name': 'COLUMN_1'}]}
+ | - - null
+ | - 'Wrong number of arguments is passed to COUNT(): expected 1, got 0'
| ...
tx1:commit()
| ---
diff --git a/test/box/tx_man.test.lua b/test/box/tx_man.test.lua
index 3d488c2de..19bc14a67 100644
--- a/test/box/tx_man.test.lua
+++ b/test/box/tx_man.test.lua
@@ -661,8 +661,8 @@ tx1:rollback()
-- gh-6095: SQL query may crash in MVCC mode if it involves ephemeral spaces.
--
-box.execute([[ CREATE TABLE test (id INT NOT NULL PRIMARY KEY, "COUNT" INT NOT NULL)]])
-box.execute([[ UPDATE test SET "COUNT" = "COUNT" + 1 WHERE id = 0 ]])
+box.execute([[ CREATE TABLE test (id INT NOT NULL PRIMARY KEY, count INT NOT NULL)]])
+box.execute([[ UPDATE test SET count = count + 1 WHERE id = 0 ]])
box.execute([[ DROP TABLE test]])
-- https://github.com/tarantool/tarantool/issues/5515
--
2.29.2
next prev parent reply other threads:[~2021-08-21 0:27 UTC|newest]
Thread overview: 8+ messages / expand[flat|nested] mbox.gz Atom feed top
2021-08-18 14:34 [Tarantool-patches] [PATCH v2 0/5] Prepare for static arguments type check Mergen Imeev via Tarantool-patches
2021-08-18 14:34 ` [Tarantool-patches] [PATCH v2 1/5] sql: modify arithmetic aggregate functions Mergen Imeev via Tarantool-patches
2021-08-18 14:34 ` [Tarantool-patches] [PATCH v2 2/5] sql: introduce SQL built-in functions to parser Mergen Imeev via Tarantool-patches
2021-08-19 8:35 ` Vladimir Davydov via Tarantool-patches
2021-08-21 0:27 ` Safin Timur via Tarantool-patches [this message]
2021-08-18 14:35 ` [Tarantool-patches] [PATCH v2 3/5] sql: separate functions in parser Mergen Imeev via Tarantool-patches
2021-08-18 14:35 ` [Tarantool-patches] [PATCH v2 4/5] sql: separate function flags from functions Mergen Imeev via Tarantool-patches
2021-08-18 14:35 ` [Tarantool-patches] [PATCH v2 5/5] sql: encapsulate SQL built-in functions opcodes Mergen Imeev via Tarantool-patches
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=d8faaee1-720c-8d64-5098-eea30ef4766c@tarantool.org \
--to=tarantool-patches@dev.tarantool.org \
--cc=imeevma@tarantool.org \
--cc=tsafin@tarantool.org \
--cc=vdavydov@tarantool.org \
--subject='Re: [Tarantool-patches] [PATCH v2 2/5] sql: introduce SQL built-in functions to parser' \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox