From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from [87.239.111.99] (localhost [127.0.0.1]) by dev.tarantool.org (Postfix) with ESMTP id 306336EC40; Wed, 18 Aug 2021 17:36:27 +0300 (MSK) DKIM-Filter: OpenDKIM Filter v2.11.0 dev.tarantool.org 306336EC40 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=tarantool.org; s=dev; t=1629297387; bh=dA6TfFtVDLjm2/IhSkidD8QUWF8esudsDYOGz2G3pcY=; h=To:Cc:Date:In-Reply-To:References:Subject:List-Id: List-Unsubscribe:List-Archive:List-Post:List-Help:List-Subscribe: From:Reply-To:From; b=J7MaK3f9i2wO9nLH07B77dryTPz+g+oD4DSRrUyuww10UMtffzsT+if3xbQCOD6A8 oPqbPqAkix1GwAKa1eWJbhBqBhn74gKsVGQrpGQCwMogGyPKhXX0TJlE18S/idvWU8 anZcofLRUA3sHzGqZ6P+M+dnz5de54sQBj0IIUE8= Received: from smtpng1.i.mail.ru (smtpng1.i.mail.ru [94.100.181.251]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by dev.tarantool.org (Postfix) with ESMTPS id 44A926EC41 for ; Wed, 18 Aug 2021 17:35:03 +0300 (MSK) DKIM-Filter: OpenDKIM Filter v2.11.0 dev.tarantool.org 44A926EC41 Received: by smtpng1.m.smailru.net with esmtpa (envelope-from ) id 1mGMek-0002yg-5p; Wed, 18 Aug 2021 17:35:02 +0300 To: vdavydov@tarantool.org Cc: tarantool-patches@dev.tarantool.org Date: Wed, 18 Aug 2021 17:35:01 +0300 Message-Id: <86911c88f1d235024c8841fcf520359f68a988f5.1629297142.git.imeevma@gmail.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: References: MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-4EC0790: 10 X-7564579A: 646B95376F6C166E X-77F55803: 4F1203BC0FB41BD92087353F0EC44DD906AB4890CDABF0C5CB76CEE71D3E4007182A05F5380850404F26954FDAAFBEE4DD3AD3A4975D9A1274C26D7D013E38CBD8E324D00FFE9B75 X-7FA49CB5: FF5795518A3D127A4AD6D5ED66289B5278DA827A17800CE741724E8C4892374EEA1F7E6F0F101C67BD4B6F7A4D31EC0BCC500DACC3FED6E28638F802B75D45FF8AA50765F790063742E71BDDD354FC988638F802B75D45FF36EB9D2243A4F8B5A6FCA7DBDB1FC311F39EFFDF887939037866D6147AF826D81925FDE1F67C1458A086A2FF315D56CC117882F4460429724CE54428C33FAD305F5C1EE8F4F765FCAA867293B0326636D2E47CDBA5A96583BD4B6F7A4D31EC0BC014FD901B82EE079FA2833FD35BB23D27C277FBC8AE2E8B2EE5AD8F952D28FBA471835C12D1D977C4224003CC8364762BB6847A3DEAEFB0F43C7A68FF6260569E8FC8737B5C2249EC8D19AE6D49635B68655334FD4449CB9ECD01F8117BC8BEAAAE862A0553A39223F8577A6DFFEA7CFA80D66F452D417A43847C11F186F3C59DAA53EE0834AAEE X-B7AD71C0: AC4F5C86D027EB782CDD5689AFBDA7A213B5FB47DCBC3458834459D11680B5050C8E7512C1707E8FC2A2FC08BC309E6D X-C1DE0DAB: C20DE7B7AB408E4181F030C43753B8186998911F362727C414F749A5E30D975CF160826E4E1956AEB42741A4AA51F9BE38E600C98C67E4219C2B6934AE262D3EE7EAB7254005DCED7532B743992DF240BDC6A1CF3F042BAD6DF99611D93F60EFCE66FDB1904541E0699F904B3F4130E343918A1A30D5E7FCCB5012B2E24CD356 X-C8649E89: 4E36BF7865823D7055A7F0CF078B5EC49A30900B95165D343FB425EC7F4D4A4BEDE1D10CF27C63567197B20EB08213D1071CF7BD42972D231662295EF3C69CDF1D7E09C32AA3244C0C5DD729AEB5FDB1AA3AA8248150AF2E8894E9C85370243E729B2BEF169E0186 X-D57D3AED: 3ZO7eAau8CL7WIMRKs4sN3D3tLDjz0dLbV79QFUyzQ2Ujvy7cMT6pYYqY16iZVKkSc3dCLJ7zSJH7+u4VD18S7Vl4ZUrpaVfd2+vE6kuoey4m4VkSEu530nj6fImhcD4MUrOEAnl0W826KZ9Q+tr5ycPtXkTV4k65bRjmOUUP8cvGozZ33TWg5HZplvhhXbhDGzqmQDTd6OAevLeAnq3Ra9uf7zvY2zzsIhlcp/Y7m53TZgf2aB4JOg4gkr2biojuRQ/H5n28tq2JlAUj2EX6A== X-Mailru-Sender: 689FA8AB762F7393C37E3C1AEC41BA5D6F1FF0279E82EB21E74413C3DC9358ED83D72C36FC87018B9F80AB2734326CD2FB559BB5D741EB96352A0ABBE4FDA4210A04DAD6CC59E33667EA787935ED9F1B X-Mras: Ok Subject: [Tarantool-patches] [PATCH v2 3/5] sql: separate functions in parser X-BeenThere: tarantool-patches@dev.tarantool.org X-Mailman-Version: 2.1.34 Precedence: list List-Id: Tarantool development patches List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , From: Mergen Imeev via Tarantool-patches Reply-To: imeevma@tarantool.org Errors-To: tarantool-patches-bounces@dev.tarantool.org Sender: "Tarantool-patches" 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