From f06233a02a22ad0d5d97f6c9fdccf58e536182a3 Mon Sep 17 00:00:00 2001 Message-Id: From: Timur Safin 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. --- extra/mkkeywordhash.c | 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(-) diff --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