[Tarantool-patches] [PATCH v2 3/5] sql: separate functions in parser
imeevma at tarantool.org
imeevma at tarantool.org
Wed Aug 18 17:35:01 MSK 2021
This patch separates SQL built-in functions from user-defined functions
when creating a VDBE. This makes it easier to validate user-defined
functions, and we can now modify built-in SQL functions without breaking
user-defined functions.
Part of #6105
---
extra/addopcodes.sh | 1 +
src/box/sql/expr.c | 113 +++++++++++++++++++++++++++++++++++------
src/box/sql/func.c | 2 +-
src/box/sql/parse.y | 82 +++++++++++++++---------------
src/box/sql/resolve.c | 24 ++++++++-
src/box/sql/select.c | 2 +-
src/box/sql/sqlInt.h | 9 ++++
src/box/sql/treeview.c | 1 +
src/box/sql/vdbemem.c | 2 +-
9 files changed, 174 insertions(+), 62 deletions(-)
diff --git a/extra/addopcodes.sh b/extra/addopcodes.sh
index 3f8cfdf02..e07a97ae9 100755
--- a/extra/addopcodes.sh
+++ b/extra/addopcodes.sh
@@ -53,6 +53,7 @@ extras=" \
LINEFEED \
SPACE \
ILLEGAL \
+ BUILT_IN_FUNC \
"
IFS=" "
diff --git a/src/box/sql/expr.c b/src/box/sql/expr.c
index 8902c648f..6c24dc09a 100644
--- a/src/box/sql/expr.c
+++ b/src/box/sql/expr.c
@@ -137,6 +137,29 @@ sql_expr_type(struct Expr *pExpr)
return pExpr->type;
}
+struct func *
+sql_func_by_signature(const char *name, uint32_t argc)
+{
+ struct func *func = func_by_name(name, strlen(name));
+ if (func == NULL) {
+ diag_set(ClientError, ER_NO_SUCH_FUNCTION, name);
+ return NULL;
+ }
+ if (!func->def->exports.sql) {
+ diag_set(ClientError, ER_SQL_PARSER_GENERIC,
+ tt_sprintf("function %s() is not available in "
+ "SQL", name));
+ return NULL;
+ }
+ if (func->def->param_count != (int)argc) {
+ diag_set(ClientError, ER_FUNC_WRONG_ARG_COUNT, name,
+ tt_sprintf("%d", func->def->param_count),
+ argc);
+ return NULL;
+ }
+ return func;
+}
+
enum field_type *
field_type_sequence_dup(struct Parse *parse, enum field_type *types,
uint32_t len)
@@ -202,7 +225,7 @@ sqlExprSkipCollate(Expr * pExpr)
if (ExprHasProperty(pExpr, EP_Unlikely)) {
assert(!ExprHasProperty(pExpr, EP_xIsSelect));
assert(pExpr->x.pList->nExpr > 0);
- assert(pExpr->op == TK_FUNCTION);
+ assert(pExpr->op == TK_BUILT_IN_FUNC);
pExpr = pExpr->x.pList->a[0].pExpr;
} else {
assert(pExpr->op == TK_COLLATE);
@@ -325,7 +348,7 @@ sql_expr_coll(Parse *parse, Expr *p, bool *is_explicit_coll, uint32_t *coll_id,
*coll_id = lhs_coll_id;
break;
}
- if (op == TK_FUNCTION) {
+ if (op == TK_BUILT_IN_FUNC) {
uint32_t arg_count = p->x.pList == NULL ? 0 :
p->x.pList->nExpr;
uint32_t flags = sql_func_flags(p->u.zToken);
@@ -1029,7 +1052,8 @@ sql_expr_new_dequoted(struct sql *db, int op, const struct Token *token)
e->u.zToken = (char *) &e[1];
if (token->z[0] == '"')
e->flags |= EP_DblQuoted;
- if (op != TK_ID && op != TK_COLLATE && op != TK_FUNCTION) {
+ if (op != TK_ID && op != TK_COLLATE && op != TK_FUNCTION &&
+ op != TK_BUILT_IN_FUNC) {
memcpy(e->u.zToken, token->z, token->n);
e->u.zToken[token->n] = '\0';
sqlDequote(e->u.zToken);
@@ -1193,6 +1217,25 @@ sqlExprFunction(Parse * pParse, ExprList * pList, Token * pToken)
return new_expr;
}
+struct Expr *
+sql_expr_new_built_in(struct Parse *parser, struct ExprList *list,
+ struct Token *token)
+{
+ struct sql *db = parser->db;
+ assert(token != NULL);
+ struct Expr *new_expr = sql_expr_new_dequoted(db, TK_BUILT_IN_FUNC,
+ token);
+ if (new_expr == NULL) {
+ sql_expr_list_delete(db, list);
+ parser->is_aborted = true;
+ return NULL;
+ }
+ new_expr->x.pList = list;
+ assert(!ExprHasProperty(new_expr, EP_xIsSelect));
+ sqlExprSetHeightAndFlags(parser, new_expr);
+ return new_expr;
+}
+
/*
* Assign a variable number to an expression that encodes a
* wildcard in the original SQL statement.
@@ -2050,6 +2093,7 @@ exprNodeIsConstant(Walker * pWalker, Expr * pExpr)
* and either pWalker->eCode==4 or 5 or the function has the
* SQL_FUNC_CONST flag.
*/
+ case TK_BUILT_IN_FUNC:
case TK_FUNCTION:
if (pWalker->eCode >= 4 || ExprHasProperty(pExpr, EP_ConstFunc)) {
return WRC_Continue;
@@ -3917,6 +3961,53 @@ sqlExprCodeTarget(Parse * pParse, Expr * pExpr, int target)
break;
}
case TK_FUNCTION:{
+ struct ExprList *args;
+ uint32_t argc;
+ /* Mask of function arguments that are constant */
+ uint32_t mask = 0;
+
+ assert(!ExprHasProperty(pExpr, EP_xIsSelect));
+ if (ExprHasProperty(pExpr, EP_TokenOnly)) {
+ args = NULL;
+ } else {
+ args = pExpr->x.pList;
+ }
+ argc = args != NULL ? args->nExpr : 0;
+ assert(!ExprHasProperty(pExpr, EP_IntValue));
+ const char *name = pExpr->u.zToken;
+ struct func *func = sql_func_by_signature(name, argc);
+ if (func == NULL) {
+ pParse->is_aborted = true;
+ break;
+ }
+ for (uint32_t i = 0; i < argc; i++) {
+ if (i < 32 && sqlExprIsConstant(args->a[i].pExpr))
+ mask |= MASKBIT32(i);
+ }
+ if (args != NULL) {
+ if (mask != 0) {
+ r1 = pParse->nMem + 1;
+ pParse->nMem += argc;
+ } else {
+ r1 = sqlGetTempRange(pParse, argc);
+ }
+
+ sqlExprCachePush(pParse);
+ sqlExprCodeExprList(pParse, args, r1, 0,
+ SQL_ECEL_DUP | SQL_ECEL_FACTOR);
+ sqlExprCachePop(pParse);
+ } else {
+ r1 = 0;
+ }
+ sqlVdbeAddOp4(v, OP_FunctionByName, mask, r1, target,
+ sqlDbStrNDup(pParse->db, name, strlen(name)),
+ P4_DYNAMIC);
+ sqlVdbeChangeP5(v, argc);
+ if (argc != 0 && mask == 0)
+ sqlReleaseTempRange(pParse, r1, argc);
+ return target;
+ }
+ case TK_BUILT_IN_FUNC: {
ExprList *pFarg; /* List of function arguments */
int nFarg; /* Number of function arguments */
u32 constMask = 0; /* Mask of function arguments that are constant */
@@ -4077,18 +4168,8 @@ sqlExprCodeTarget(Parse * pParse, Expr * pExpr, int target)
sqlVdbeAddOp4(v, OP_CollSeq, 0, 0, 0,
(char *)coll, P4_COLLSEQ);
}
- if (func->def->language == FUNC_LANGUAGE_SQL_BUILTIN) {
- sqlVdbeAddOp4(v, OP_BuiltinFunction0, constMask,
- r1, target, (char *)func,
- P4_FUNC);
- } else {
- sqlVdbeAddOp4(v, OP_FunctionByName, constMask,
- r1, target,
- sqlDbStrNDup(pParse->db,
- func->def->name,
- func->def->name_len),
- P4_DYNAMIC);
- }
+ sqlVdbeAddOp4(v, OP_BuiltinFunction0, constMask, r1,
+ target, (char *)func, P4_FUNC);
sqlVdbeChangeP5(v, (u8) nFarg);
if (nFarg && constMask == 0) {
sqlReleaseTempRange(pParse, r1, nFarg);
@@ -5028,7 +5109,7 @@ sqlExprCompare(Expr * pA, Expr * pB, int iTab)
}
if (pA->op != TK_COLUMN_REF && pA->op != TK_AGG_COLUMN &&
pA->u.zToken) {
- if (pA->op == TK_FUNCTION) {
+ if (pA->op == TK_BUILT_IN_FUNC) {
if (sqlStrICmp(pA->u.zToken, pB->u.zToken) != 0)
return 2;
} else if (strcmp(pA->u.zToken, pB->u.zToken) != 0) {
diff --git a/src/box/sql/func.c b/src/box/sql/func.c
index 3267d101e..492960443 100644
--- a/src/box/sql/func.c
+++ b/src/box/sql/func.c
@@ -1903,7 +1903,7 @@ groupConcatFinalize(sql_context * context)
int
sql_is_like_func(struct Expr *expr)
{
- if (expr->op != TK_FUNCTION || !expr->x.pList ||
+ if (expr->op != TK_BUILT_IN_FUNC || !expr->x.pList ||
expr->x.pList->nExpr != 2)
return 0;
assert(!ExprHasProperty(expr, EP_xIsSelect));
diff --git a/src/box/sql/parse.y b/src/box/sql/parse.y
index cb2e627db..2327482ec 100644
--- a/src/box/sql/parse.y
+++ b/src/box/sql/parse.y
@@ -1125,7 +1125,7 @@ expr(A) ::= CAST(X) LP expr(E) AS typedef(T) RP(Y). {
}
expr(A) ::= TRIM(X) LP trim_operands(Y) RP(E). {
- A.pExpr = sqlExprFunction(pParse, Y, &X);
+ A.pExpr = sql_expr_new_built_in(pParse, Y, &X);
spanSet(&A, &X, &E);
}
@@ -1179,7 +1179,7 @@ expr(A) ::= ABS(X) LP distinct(D) exprlist(Y) RP(E). {
pParse->is_aborted = true;
return;
}
- A.pExpr = sqlExprFunction(pParse, Y, &X);
+ A.pExpr = sql_expr_new_built_in(pParse, Y, &X);
spanSet(&A, &X, &E);
if(D == SF_Distinct && A.pExpr)
A.pExpr->flags |= EP_Distinct;
@@ -1192,7 +1192,7 @@ expr(A) ::= AVG(X) LP distinct(D) exprlist(Y) RP(E). {
pParse->is_aborted = true;
return;
}
- A.pExpr = sqlExprFunction(pParse, Y, &X);
+ A.pExpr = sql_expr_new_built_in(pParse, Y, &X);
spanSet(&A, &X, &E);
if(D == SF_Distinct && A.pExpr)
A.pExpr->flags |= EP_Distinct;
@@ -1206,7 +1206,7 @@ expr(A) ::= CHAR(X) LP distinct(D) exprlist(Y) RP(E). {
pParse->is_aborted = true;
return;
}
- A.pExpr = sqlExprFunction(pParse, Y, &X);
+ A.pExpr = sql_expr_new_built_in(pParse, Y, &X);
spanSet(&A, &X, &E);
if(D == SF_Distinct && A.pExpr)
A.pExpr->flags |= EP_Distinct;
@@ -1221,7 +1221,7 @@ expr(A) ::= CHAR_LEN(X) LP distinct(D) exprlist(Y) RP(E). {
pParse->is_aborted = true;
return;
}
- A.pExpr = sqlExprFunction(pParse, Y, &X);
+ A.pExpr = sql_expr_new_built_in(pParse, Y, &X);
spanSet(&A, &X, &E);
if(D == SF_Distinct && A.pExpr)
A.pExpr->flags |= EP_Distinct;
@@ -1237,7 +1237,7 @@ expr(A) ::= COALESCE(X) LP distinct(D) exprlist(Y) RP(E). {
pParse->is_aborted = true;
return;
}
- A.pExpr = sqlExprFunction(pParse, Y, &X);
+ A.pExpr = sql_expr_new_built_in(pParse, Y, &X);
spanSet(&A, &X, &E);
if(D == SF_Distinct && A.pExpr)
A.pExpr->flags |= EP_Distinct;
@@ -1250,14 +1250,14 @@ expr(A) ::= COUNT(X) LP distinct(D) exprlist(Y) RP(E). {
pParse->is_aborted = true;
return;
}
- A.pExpr = sqlExprFunction(pParse, Y, &X);
+ A.pExpr = sql_expr_new_built_in(pParse, Y, &X);
spanSet(&A, &X, &E);
if(D == SF_Distinct && A.pExpr)
A.pExpr->flags |= EP_Distinct;
}
expr(A) ::= COUNT(X) LP STAR RP(E). {
- A.pExpr = sqlExprFunction(pParse, NULL, &X);
+ A.pExpr = sql_expr_new_built_in(pParse, NULL, &X);
spanSet(&A, &X, &E);
}
@@ -1271,7 +1271,7 @@ expr(A) ::= GREATEST(X) LP distinct(D) exprlist(Y) RP(E). {
pParse->is_aborted = true;
return;
}
- A.pExpr = sqlExprFunction(pParse, Y, &X);
+ A.pExpr = sql_expr_new_built_in(pParse, Y, &X);
spanSet(&A, &X, &E);
if(D == SF_Distinct && A.pExpr)
A.pExpr->flags |= EP_Distinct;
@@ -1285,7 +1285,7 @@ expr(A) ::= GROUP_CONCAT(X) LP distinct(D) exprlist(Y) RP(E). {
pParse->is_aborted = true;
return;
}
- A.pExpr = sqlExprFunction(pParse, Y, &X);
+ A.pExpr = sql_expr_new_built_in(pParse, Y, &X);
spanSet(&A, &X, &E);
if(D == SF_Distinct && A.pExpr)
A.pExpr->flags |= EP_Distinct;
@@ -1298,7 +1298,7 @@ expr(A) ::= HEX(X) LP distinct(D) exprlist(Y) RP(E). {
pParse->is_aborted = true;
return;
}
- A.pExpr = sqlExprFunction(pParse, Y, &X);
+ A.pExpr = sql_expr_new_built_in(pParse, Y, &X);
spanSet(&A, &X, &E);
if(D == SF_Distinct && A.pExpr)
A.pExpr->flags |= EP_Distinct;
@@ -1311,7 +1311,7 @@ expr(A) ::= IFNULL(X) LP distinct(D) exprlist(Y) RP(E). {
pParse->is_aborted = true;
return;
}
- A.pExpr = sqlExprFunction(pParse, Y, &X);
+ A.pExpr = sql_expr_new_built_in(pParse, Y, &X);
spanSet(&A, &X, &E);
if(D == SF_Distinct && A.pExpr)
A.pExpr->flags |= EP_Distinct;
@@ -1327,7 +1327,7 @@ expr(A) ::= LEAST(X) LP distinct(D) exprlist(Y) RP(E). {
pParse->is_aborted = true;
return;
}
- A.pExpr = sqlExprFunction(pParse, Y, &X);
+ A.pExpr = sql_expr_new_built_in(pParse, Y, &X);
spanSet(&A, &X, &E);
if(D == SF_Distinct && A.pExpr)
A.pExpr->flags |= EP_Distinct;
@@ -1340,7 +1340,7 @@ expr(A) ::= LENGTH(X) LP distinct(D) exprlist(Y) RP(E). {
pParse->is_aborted = true;
return;
}
- A.pExpr = sqlExprFunction(pParse, Y, &X);
+ A.pExpr = sql_expr_new_built_in(pParse, Y, &X);
spanSet(&A, &X, &E);
if(D == SF_Distinct && A.pExpr)
A.pExpr->flags |= EP_Distinct;
@@ -1353,7 +1353,7 @@ expr(A) ::= LIKELIHOOD(X) LP distinct(D) exprlist(Y) RP(E). {
pParse->is_aborted = true;
return;
}
- A.pExpr = sqlExprFunction(pParse, Y, &X);
+ A.pExpr = sql_expr_new_built_in(pParse, Y, &X);
spanSet(&A, &X, &E);
if(D == SF_Distinct && A.pExpr)
A.pExpr->flags |= EP_Distinct;
@@ -1366,7 +1366,7 @@ expr(A) ::= LIKELY(X) LP distinct(D) exprlist(Y) RP(E). {
pParse->is_aborted = true;
return;
}
- A.pExpr = sqlExprFunction(pParse, Y, &X);
+ A.pExpr = sql_expr_new_built_in(pParse, Y, &X);
spanSet(&A, &X, &E);
if(D == SF_Distinct && A.pExpr)
A.pExpr->flags |= EP_Distinct;
@@ -1379,7 +1379,7 @@ expr(A) ::= LOWER(X) LP distinct(D) exprlist(Y) RP(E). {
pParse->is_aborted = true;
return;
}
- A.pExpr = sqlExprFunction(pParse, Y, &X);
+ A.pExpr = sql_expr_new_built_in(pParse, Y, &X);
spanSet(&A, &X, &E);
if(D == SF_Distinct && A.pExpr)
A.pExpr->flags |= EP_Distinct;
@@ -1392,7 +1392,7 @@ expr(A) ::= MAX(X) LP distinct(D) exprlist(Y) RP(E). {
pParse->is_aborted = true;
return;
}
- A.pExpr = sqlExprFunction(pParse, Y, &X);
+ A.pExpr = sql_expr_new_built_in(pParse, Y, &X);
spanSet(&A, &X, &E);
if(D == SF_Distinct && A.pExpr)
A.pExpr->flags |= EP_Distinct;
@@ -1405,7 +1405,7 @@ expr(A) ::= MIN(X) LP distinct(D) exprlist(Y) RP(E). {
pParse->is_aborted = true;
return;
}
- A.pExpr = sqlExprFunction(pParse, Y, &X);
+ A.pExpr = sql_expr_new_built_in(pParse, Y, &X);
spanSet(&A, &X, &E);
if(D == SF_Distinct && A.pExpr)
A.pExpr->flags |= EP_Distinct;
@@ -1418,7 +1418,7 @@ expr(A) ::= NULLIF(X) LP distinct(D) exprlist(Y) RP(E). {
pParse->is_aborted = true;
return;
}
- A.pExpr = sqlExprFunction(pParse, Y, &X);
+ A.pExpr = sql_expr_new_built_in(pParse, Y, &X);
spanSet(&A, &X, &E);
if(D == SF_Distinct && A.pExpr)
A.pExpr->flags |= EP_Distinct;
@@ -1431,7 +1431,7 @@ expr(A) ::= POSITION(X) LP distinct(D) exprlist(Y) RP(E). {
pParse->is_aborted = true;
return;
}
- A.pExpr = sqlExprFunction(pParse, Y, &X);
+ A.pExpr = sql_expr_new_built_in(pParse, Y, &X);
spanSet(&A, &X, &E);
if(D == SF_Distinct && A.pExpr)
A.pExpr->flags |= EP_Distinct;
@@ -1445,7 +1445,7 @@ expr(A) ::= PRINTF(X) LP distinct(D) exprlist(Y) RP(E). {
pParse->is_aborted = true;
return;
}
- A.pExpr = sqlExprFunction(pParse, Y, &X);
+ A.pExpr = sql_expr_new_built_in(pParse, Y, &X);
spanSet(&A, &X, &E);
if(D == SF_Distinct && A.pExpr)
A.pExpr->flags |= EP_Distinct;
@@ -1458,7 +1458,7 @@ expr(A) ::= QUOTE(X) LP distinct(D) exprlist(Y) RP(E). {
pParse->is_aborted = true;
return;
}
- A.pExpr = sqlExprFunction(pParse, Y, &X);
+ A.pExpr = sql_expr_new_built_in(pParse, Y, &X);
spanSet(&A, &X, &E);
if(D == SF_Distinct && A.pExpr)
A.pExpr->flags |= EP_Distinct;
@@ -1470,7 +1470,7 @@ expr(A) ::= RANDOM(X) LP distinct(D) exprlist(Y) RP(E). {
pParse->is_aborted = true;
return;
}
- A.pExpr = sqlExprFunction(pParse, Y, &X);
+ A.pExpr = sql_expr_new_built_in(pParse, Y, &X);
spanSet(&A, &X, &E);
if(D == SF_Distinct && A.pExpr)
A.pExpr->flags |= EP_Distinct;
@@ -1483,7 +1483,7 @@ expr(A) ::= RANDOMBLOB(X) LP distinct(D) exprlist(Y) RP(E). {
pParse->is_aborted = true;
return;
}
- A.pExpr = sqlExprFunction(pParse, Y, &X);
+ A.pExpr = sql_expr_new_built_in(pParse, Y, &X);
spanSet(&A, &X, &E);
if(D == SF_Distinct && A.pExpr)
A.pExpr->flags |= EP_Distinct;
@@ -1496,7 +1496,7 @@ expr(A) ::= REPLACE(X) LP distinct(D) exprlist(Y) RP(E). {
pParse->is_aborted = true;
return;
}
- A.pExpr = sqlExprFunction(pParse, Y, &X);
+ A.pExpr = sql_expr_new_built_in(pParse, Y, &X);
spanSet(&A, &X, &E);
if(D == SF_Distinct && A.pExpr)
A.pExpr->flags |= EP_Distinct;
@@ -1510,7 +1510,7 @@ expr(A) ::= ROUND(X) LP distinct(D) exprlist(Y) RP(E). {
pParse->is_aborted = true;
return;
}
- A.pExpr = sqlExprFunction(pParse, Y, &X);
+ A.pExpr = sql_expr_new_built_in(pParse, Y, &X);
spanSet(&A, &X, &E);
if(D == SF_Distinct && A.pExpr)
A.pExpr->flags |= EP_Distinct;
@@ -1522,7 +1522,7 @@ expr(A) ::= ROW_COUNT(X) LP distinct(D) exprlist(Y) RP(E). {
pParse->is_aborted = true;
return;
}
- A.pExpr = sqlExprFunction(pParse, Y, &X);
+ A.pExpr = sql_expr_new_built_in(pParse, Y, &X);
spanSet(&A, &X, &E);
if(D == SF_Distinct && A.pExpr)
A.pExpr->flags |= EP_Distinct;
@@ -1535,7 +1535,7 @@ expr(A) ::= SOUNDEX(X) LP distinct(D) exprlist(Y) RP(E). {
pParse->is_aborted = true;
return;
}
- A.pExpr = sqlExprFunction(pParse, Y, &X);
+ A.pExpr = sql_expr_new_built_in(pParse, Y, &X);
spanSet(&A, &X, &E);
if(D == SF_Distinct && A.pExpr)
A.pExpr->flags |= EP_Distinct;
@@ -1549,7 +1549,7 @@ expr(A) ::= SUBSTR(X) LP distinct(D) exprlist(Y) RP(E). {
pParse->is_aborted = true;
return;
}
- A.pExpr = sqlExprFunction(pParse, Y, &X);
+ A.pExpr = sql_expr_new_built_in(pParse, Y, &X);
spanSet(&A, &X, &E);
if(D == SF_Distinct && A.pExpr)
A.pExpr->flags |= EP_Distinct;
@@ -1562,7 +1562,7 @@ expr(A) ::= SUM(X) LP distinct(D) exprlist(Y) RP(E). {
pParse->is_aborted = true;
return;
}
- A.pExpr = sqlExprFunction(pParse, Y, &X);
+ A.pExpr = sql_expr_new_built_in(pParse, Y, &X);
spanSet(&A, &X, &E);
if(D == SF_Distinct && A.pExpr)
A.pExpr->flags |= EP_Distinct;
@@ -1575,7 +1575,7 @@ expr(A) ::= TOTAL(X) LP distinct(D) exprlist(Y) RP(E). {
pParse->is_aborted = true;
return;
}
- A.pExpr = sqlExprFunction(pParse, Y, &X);
+ A.pExpr = sql_expr_new_built_in(pParse, Y, &X);
spanSet(&A, &X, &E);
if(D == SF_Distinct && A.pExpr)
A.pExpr->flags |= EP_Distinct;
@@ -1588,7 +1588,7 @@ expr(A) ::= TYPEOF(X) LP distinct(D) exprlist(Y) RP(E). {
pParse->is_aborted = true;
return;
}
- A.pExpr = sqlExprFunction(pParse, Y, &X);
+ A.pExpr = sql_expr_new_built_in(pParse, Y, &X);
spanSet(&A, &X, &E);
if(D == SF_Distinct && A.pExpr)
A.pExpr->flags |= EP_Distinct;
@@ -1601,7 +1601,7 @@ expr(A) ::= UNICODE(X) LP distinct(D) exprlist(Y) RP(E). {
pParse->is_aborted = true;
return;
}
- A.pExpr = sqlExprFunction(pParse, Y, &X);
+ A.pExpr = sql_expr_new_built_in(pParse, Y, &X);
spanSet(&A, &X, &E);
if(D == SF_Distinct && A.pExpr)
A.pExpr->flags |= EP_Distinct;
@@ -1614,7 +1614,7 @@ expr(A) ::= UNLIKELY(X) LP distinct(D) exprlist(Y) RP(E). {
pParse->is_aborted = true;
return;
}
- A.pExpr = sqlExprFunction(pParse, Y, &X);
+ A.pExpr = sql_expr_new_built_in(pParse, Y, &X);
spanSet(&A, &X, &E);
if(D == SF_Distinct && A.pExpr)
A.pExpr->flags |= EP_Distinct;
@@ -1627,7 +1627,7 @@ expr(A) ::= UPPER(X) LP distinct(D) exprlist(Y) RP(E). {
pParse->is_aborted = true;
return;
}
- A.pExpr = sqlExprFunction(pParse, Y, &X);
+ A.pExpr = sql_expr_new_built_in(pParse, Y, &X);
spanSet(&A, &X, &E);
if(D == SF_Distinct && A.pExpr)
A.pExpr->flags |= EP_Distinct;
@@ -1640,7 +1640,7 @@ expr(A) ::= UUID(X) LP distinct(D) exprlist(Y) RP(E). {
pParse->is_aborted = true;
return;
}
- A.pExpr = sqlExprFunction(pParse, Y, &X);
+ A.pExpr = sql_expr_new_built_in(pParse, Y, &X);
spanSet(&A, &X, &E);
if(D == SF_Distinct && A.pExpr)
A.pExpr->flags |= EP_Distinct;
@@ -1652,7 +1652,7 @@ expr(A) ::= VERSION(X) LP distinct(D) exprlist(Y) RP(E). {
pParse->is_aborted = true;
return;
}
- A.pExpr = sqlExprFunction(pParse, Y, &X);
+ A.pExpr = sql_expr_new_built_in(pParse, Y, &X);
spanSet(&A, &X, &E);
if(D == SF_Distinct && A.pExpr)
A.pExpr->flags |= EP_Distinct;
@@ -1665,7 +1665,7 @@ expr(A) ::= ZEROBLOB(X) LP distinct(D) exprlist(Y) RP(E). {
pParse->is_aborted = true;
return;
}
- A.pExpr = sqlExprFunction(pParse, Y, &X);
+ A.pExpr = sql_expr_new_built_in(pParse, Y, &X);
spanSet(&A, &X, &E);
if(D == SF_Distinct && A.pExpr)
A.pExpr->flags |= EP_Distinct;
@@ -1753,7 +1753,7 @@ expr(A) ::= expr(A) likeop(OP) expr(Y). [LIKE_KW] {
OP.n &= 0x7fffffff;
pList = sql_expr_list_append(pParse->db,NULL, Y.pExpr);
pList = sql_expr_list_append(pParse->db,pList, A.pExpr);
- A.pExpr = sqlExprFunction(pParse, pList, &OP);
+ A.pExpr = sql_expr_new_built_in(pParse, pList, &OP);
exprNot(pParse, bNot, &A);
A.zEnd = Y.zEnd;
if( A.pExpr ) A.pExpr->flags |= EP_InfixFunc;
@@ -1765,7 +1765,7 @@ expr(A) ::= expr(A) likeop(OP) expr(Y) ESCAPE expr(E). [LIKE_KW] {
pList = sql_expr_list_append(pParse->db,NULL, Y.pExpr);
pList = sql_expr_list_append(pParse->db,pList, A.pExpr);
pList = sql_expr_list_append(pParse->db,pList, E.pExpr);
- A.pExpr = sqlExprFunction(pParse, pList, &OP);
+ A.pExpr = sql_expr_new_built_in(pParse, pList, &OP);
exprNot(pParse, bNot, &A);
A.zEnd = E.zEnd;
if( A.pExpr ) A.pExpr->flags |= EP_InfixFunc;
diff --git a/src/box/sql/resolve.c b/src/box/sql/resolve.c
index 35faddab5..32ab1ac68 100644
--- a/src/box/sql/resolve.c
+++ b/src/box/sql/resolve.c
@@ -589,7 +589,27 @@ resolveExprStep(Walker * pWalker, Expr * pExpr)
/* Resolve function names
*/
- case TK_FUNCTION:{
+ case TK_FUNCTION: {
+ struct ExprList *args = pExpr->x.pList;
+ uint32_t argc = args == NULL ? 0 : args->nExpr;
+
+ assert(!ExprHasProperty(pExpr, EP_xIsSelect));
+ const char *name = pExpr->u.zToken;
+ struct func *func = sql_func_by_signature(name, argc);
+ if (func == NULL) {
+ pParse->is_aborted = true;
+ pNC->nErr++;
+ return WRC_Abort;
+ }
+ pExpr->type = func->def->returns;
+ assert(!func->def->is_deterministic ||
+ (pNC->ncFlags & NC_IdxExpr) == 0);
+ if (func->def->is_deterministic)
+ ExprSetProperty(pExpr, EP_ConstFunc);
+ sqlWalkExprList(pWalker, args);
+ return WRC_Prune;
+ }
+ case TK_BUILT_IN_FUNC: {
ExprList *pList = pExpr->x.pList; /* The argument list */
int n = pList ? pList->nExpr : 0; /* Number of arguments */
int nId; /* Number of characters in function name */
@@ -1453,7 +1473,7 @@ resolveSelectStep(Walker * pWalker, Select * p)
* Function calls are checked to make sure that the function is
* defined and that the correct number of arguments are specified.
* If the function is an aggregate function, then the NC_HasAgg flag is
- * set and the opcode is changed from TK_FUNCTION to TK_AGG_FUNCTION.
+ * set and the opcode is changed from TK_BUILT_IN_FUNC to TK_AGG_FUNCTION.
* If an expression contains aggregate functions then the EP_Agg
* property on the expression is set.
*
diff --git a/src/box/sql/select.c b/src/box/sql/select.c
index 021e0ebd5..8003703a1 100644
--- a/src/box/sql/select.c
+++ b/src/box/sql/select.c
@@ -758,7 +758,7 @@ setJoinExpr(Expr * p, int iTable)
assert(!ExprHasProperty(p, EP_TokenOnly | EP_Reduced));
ExprSetVVAProperty(p, EP_NoReduce);
p->iRightJoinTable = (i16) iTable;
- if (p->op == TK_FUNCTION && p->x.pList) {
+ if (p->op == TK_BUILT_IN_FUNC && p->x.pList) {
int i;
for (i = 0; i < p->x.pList->nExpr; i++) {
setJoinExpr(p->x.pList->a[i].pExpr, iTable);
diff --git a/src/box/sql/sqlInt.h b/src/box/sql/sqlInt.h
index 540c3a2ff..1fd9d2bbf 100644
--- a/src/box/sql/sqlInt.h
+++ b/src/box/sql/sqlInt.h
@@ -2702,6 +2702,11 @@ Expr *sqlExprFunction(Parse *, ExprList *, Token *);
void sqlExprAssignVarNumber(Parse *, Expr *, u32);
ExprList *sqlExprListAppendVector(Parse *, ExprList *, IdList *, Expr *);
+/** Construct a new expression node for a built-in function. */
+struct Expr *
+sql_expr_new_built_in(struct Parse *parser, struct ExprList *list,
+ struct Token *token);
+
/**
* Set the sort order for the last element on the given ExprList.
*
@@ -4391,6 +4396,10 @@ sql_func_flag_is_set(struct func *func, uint16_t flag)
struct func *
sql_func_find(struct Expr *expr);
+/** Return user-defined function with given name and number of arguments. */
+struct func *
+sql_func_by_signature(const char *name, uint32_t argc);
+
/**
* Return the parameters of the function with the given name. If the function
* with the given name does not exist, or the function is not a built-in SQL
diff --git a/src/box/sql/treeview.c b/src/box/sql/treeview.c
index 5f042ce8b..4cf43073c 100644
--- a/src/box/sql/treeview.c
+++ b/src/box/sql/treeview.c
@@ -481,6 +481,7 @@ sqlTreeViewExpr(TreeView * pView, const Expr * pExpr, u8 moreToFollow)
}
case TK_AGG_FUNCTION:
+ case TK_BUILT_IN_FUNC:
case TK_FUNCTION:{
ExprList *pFarg; /* List of function arguments */
if (ExprHasProperty(pExpr, EP_TokenOnly)) {
diff --git a/src/box/sql/vdbemem.c b/src/box/sql/vdbemem.c
index 499089c8d..d2de8dc3d 100644
--- a/src/box/sql/vdbemem.c
+++ b/src/box/sql/vdbemem.c
@@ -332,7 +332,7 @@ valueFromExpr(sql * db, /* The database connection */
}
#endif
- else if (op == TK_FUNCTION && pCtx != 0) {
+ else if (op == TK_BUILT_IN_FUNC && pCtx != 0) {
rc = valueFromFunction(db, pExpr, type, &pVal, pCtx);
}
--
2.25.1
More information about the Tarantool-patches
mailing list