[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