From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from localhost (localhost [127.0.0.1]) by turing.freelists.org (Avenir Technologies Mail Multiplex) with ESMTP id 3770B26A29 for ; Thu, 8 Aug 2019 10:51:03 -0400 (EDT) Received: from turing.freelists.org ([127.0.0.1]) by localhost (turing.freelists.org [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id NDzoixlnzNMQ for ; Thu, 8 Aug 2019 10:51:03 -0400 (EDT) Received: from smtp5.mail.ru (smtp5.mail.ru [94.100.179.24]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by turing.freelists.org (Avenir Technologies Mail Multiplex) with ESMTPS id 74112269D6 for ; Thu, 8 Aug 2019 10:51:02 -0400 (EDT) From: Kirill Shcherbatov Subject: [tarantool-patches] [PATCH v2 7/8] sql: get rid of FuncDef function hash Date: Thu, 8 Aug 2019 17:50:51 +0300 Message-Id: <29c44d7790720584498ca1763cb28ff98e35c71d.1565275470.git.kshcherbatov@tarantool.org> In-Reply-To: References: MIME-Version: 1.0 Content-Transfer-Encoding: 8bit Sender: tarantool-patches-bounce@freelists.org Errors-to: tarantool-patches-bounce@freelists.org Reply-To: tarantool-patches@freelists.org List-Help: List-Unsubscribe: List-software: Ecartis version 1.0.0 List-Id: tarantool-patches List-Subscribe: List-Owner: List-post: List-Archive: To: tarantool-patches@freelists.org, korablev@tarantool.org Cc: Kirill Shcherbatov Now it is possible to move all SQL builtin functions to Tarantool's function hash. An existent FuncDef function representation was replaced with func_sql_builtin class. It has a sql-specific method :call and :finalize typica while port API call is not supported and protected with stub. This patch removes FuncDef hash and sql_function_create endpoint, but doesn't introduce something instead. Therefore few affected tests are disabled. A required functionality would be fixed in the next patch. Following tests using sql_create_function are broken now. They would be fixed in the following commit: sql-tap/alias.test.lua sql-tap/check.test.lua sql-tap/func5.test.lua sql-tap/lua_sql.test.lua sql-tap/subquery.test.lua sql-tap/trigger9.test.lua sql/errinj.result sql/errinj.test.lua sql/func-recreate.test.lua Part of #2200, #4113, #2233 --- src/box/lua/lua_sql.h | 39 ----- src/box/sql.h | 5 + src/box/sql/sqlInt.h | 248 +++++++--------------------- src/box/sql/vdbe.h | 9 +- src/box/sql/vdbeInt.h | 23 ++- src/box/func.c | 34 +--- src/box/lua/call.c | 2 - src/box/lua/lua_sql.c | 205 ----------------------- src/box/sql.c | 23 +++ src/box/sql/analyze.c | 35 ++-- src/box/sql/callback.c | 191 ---------------------- src/box/sql/date.c | 28 ---- src/box/sql/expr.c | 64 ++++---- src/box/sql/func.c | 309 ++++++++++++++++++++++++----------- src/box/sql/global.c | 7 - src/box/sql/main.c | 137 ---------------- src/box/sql/resolve.c | 50 +++--- src/box/sql/select.c | 6 +- src/box/sql/vdbe.c | 20 ++- src/box/sql/vdbeapi.c | 11 +- src/box/sql/vdbeaux.c | 33 +--- src/box/sql/vdbemem.c | 50 +++--- src/box/sql/whereexpr.c | 2 +- src/box/CMakeLists.txt | 1 - test/sql-tap/where2.test.lua | 4 +- 25 files changed, 456 insertions(+), 1080 deletions(-) delete mode 100644 src/box/lua/lua_sql.h delete mode 100644 src/box/lua/lua_sql.c diff --git a/src/box/lua/lua_sql.h b/src/box/lua/lua_sql.h deleted file mode 100644 index b81093eca..000000000 --- a/src/box/lua/lua_sql.h +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright 2010-2017, Tarantool AUTHORS, please see AUTHORS file. - * - * Redistribution and use in source and binary forms, with or - * without modification, are permitted provided that the following - * conditions are met: - * - * 1. Redistributions of source code must retain the above - * copyright notice, this list of conditions and the - * following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following - * disclaimer in the documentation and/or other materials - * provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED - * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL - * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR - * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF - * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#ifndef TARANTOOL_LUA_SQL_H -#define TARANTOOL_LUA_SQL_H - -int -lbox_sql_create_function(struct lua_State *L); - -#endif //TARANTOOL_LUA_SQL_H - diff --git a/src/box/sql.h b/src/box/sql.h index 9ccecf28c..6eaf5ba3a 100644 --- a/src/box/sql.h +++ b/src/box/sql.h @@ -68,6 +68,7 @@ struct Expr; struct Parse; struct Select; struct Table; +struct func_def; struct sql_trigger; struct space_def; @@ -348,6 +349,10 @@ sql_src_list_entry_name(const struct SrcList *list, int i); void sqlSrcListDelete(struct sql *db, struct SrcList *list); +/** Construct a SQL builtin function object. */ +struct func * +func_sql_builtin_new(struct func_def *def); + /** * Auxiliary VDBE structure to speed-up tuple data field access. * A memory allocation that manage this structure must have diff --git a/src/box/sql/sqlInt.h b/src/box/sql/sqlInt.h index 114ac0e4b..6ef1a1d4a 100644 --- a/src/box/sql/sqlInt.h +++ b/src/box/sql/sqlInt.h @@ -70,6 +70,8 @@ #include "box/column_mask.h" #include "parse_def.h" #include "box/field_def.h" +#include "box/func.h" +#include "box/func_def.h" #include "box/sql.h" #include "box/txn.h" #include "trivia/util.h" @@ -566,26 +568,6 @@ sql_initialize(void); #define SQL_TRACE_ROW 0x04 #define SQL_TRACE_CLOSE 0x08 -#define SQL_DETERMINISTIC 0x800 - -int -sql_create_function_v2(sql * db, - const char *zFunctionName, - enum field_type type, - int nArg, - int flags, - void *pApp, - void (*xFunc) (sql_context *, - int, - sql_value **), - void (*xStep) (sql_context *, - int, - sql_value **), - void (*xFinal) - (sql_context *), - void (*xDestroy) (void *) - ); - #define SQL_OPEN_READONLY 0x00000001 /* Ok for sql_open_v2() */ #define SQL_OPEN_READWRITE 0x00000002 /* Ok for sql_open_v2() */ #define SQL_OPEN_CREATE 0x00000004 /* Ok for sql_open_v2() */ @@ -1039,9 +1021,6 @@ typedef struct Column Column; typedef struct Expr Expr; typedef struct ExprList ExprList; typedef struct ExprSpan ExprSpan; -typedef struct FuncDestructor FuncDestructor; -typedef struct FuncDef FuncDef; -typedef struct FuncDefHash FuncDefHash; typedef struct IdList IdList; typedef struct KeyClass KeyClass; typedef struct Lookaside Lookaside; @@ -1123,18 +1102,6 @@ struct LookasideSlot { LookasideSlot *pNext; /* Next buffer in the list of free buffers */ }; -/* - * A hash table for built-in function definitions. (Application-defined - * functions use a regular table table from hash.h.) - * - * Hash each FuncDef structure into one of the FuncDefHash.a[] slots. - * Collisions are on the FuncDef.u.pHash chain. - */ -#define SQL_FUNC_HASH_SZ 23 -struct FuncDefHash { - FuncDef *a[SQL_FUNC_HASH_SZ]; /* Hash table for functions */ -}; - /* * Each database connection is an instance of the following structure. */ @@ -1244,78 +1211,12 @@ struct type_def { }; /* - * Each SQL function is defined by an instance of the following - * structure. For global built-in functions (ex: substr(), max(), count()) - * a pointer to this structure is held in the sqlBuiltinFunctions object. - * For per-connection application-defined functions, a pointer to this - * structure is held in the db->aHash hash table. - * - * The u.pHash field is used by the global built-ins. The u.pDestructor - * field is used by per-connection app-def functions. - */ -struct FuncDef { - /** - * A bitmask representing all supported function - * overloads. The function supports argc == n iff this - * bitmask has bit n set 1. In particular case, a bitmask - * (~0) means this function works with any possible - * argument. - * - * The count of arguments for function is limited with - * (CHAR_BITS*sizeof(uint64_t) - 1). When the highest bit - * of the mask is set, this means that greater values - * are supported. E.g. greatest function works correctly - * with any number of input arguments. - */ - uint64_t signature_mask; - u16 funcFlags; /* Some combination of sql_FUNC_* */ - void *pUserData; /* User data parameter */ - FuncDef *pNext; /* Next function with same name */ - void (*xSFunc) (sql_context *, int, sql_value **); /* func or agg-step */ - void (*xFinalize) (sql_context *); /* Agg finalizer */ - const char *zName; /* SQL name of the function. */ - union { - FuncDef *pHash; /* Next with a different name but the same hash */ - FuncDestructor *pDestructor; /* Reference counted destructor function */ - } u; - /* Return type. */ - enum field_type ret_type; -}; - -/* - * This structure encapsulates a user-function destructor callback (as - * configured using create_function_v2()) and a reference counter. When - * create_function_v2() is called to create a function with a destructor, - * a single object of this type is allocated. FuncDestructor.nRef is set to - * the number of FuncDef objects created (either 1 or 3, depending on whether - * or not the specified encoding is sql_ANY). The FuncDef.pDestructor - * member of each of the new FuncDef objects is set to point to the allocated - * FuncDestructor. - * - * Thereafter, when one of the FuncDef objects is deleted, the reference - * count on this object is decremented. When it reaches 0, the destructor - * is invoked and the FuncDestructor structure freed. - */ -struct FuncDestructor { - int nRef; - void (*xDestroy) (void *); - void *pUserData; -}; - -/* - * Possible values for FuncDef.flags. Note that the _LENGTH and _TYPEOF - * values must correspond to OPFLAG_LENGTHARG and OPFLAG_TYPEOFARG. And - * sql_FUNC_CONSTANT must be the same as sql_DETERMINISTIC. There - * are assert() statements in the code to verify this. - * * Value constraints (enforced via assert()): * SQL_FUNC_MINMAX == NC_MinMaxAgg == SF_MinMaxAgg * SQL_FUNC_LENGTH == OPFLAG_LENGTHARG * SQL_FUNC_TYPEOF == OPFLAG_TYPEOFARG - * SQL_FUNC_CONSTANT == sql_DETERMINISTIC from the API */ #define SQL_FUNC_LIKE 0x0004 /* Candidate for the LIKE optimization */ -#define SQL_FUNC_EPHEM 0x0010 /* Ephemeral. Delete with VDBE */ #define SQL_FUNC_NEEDCOLL 0x0020 /* sqlGetFuncCollSeq() might be called. * The flag is set when the collation * of function arguments should be @@ -1327,7 +1228,6 @@ struct FuncDestructor { #define SQL_FUNC_TYPEOF 0x0080 /* Built-in typeof() function */ #define SQL_FUNC_COALESCE 0x0200 /* Built-in coalesce() or ifnull() */ #define SQL_FUNC_UNLIKELY 0x0400 /* Built-in unlikely() function */ -#define SQL_FUNC_CONSTANT 0x0800 /* Constant inputs give a constant output */ #define SQL_FUNC_MINMAX 0x1000 /* True for min() and max() aggregates */ #define SQL_FUNC_SLOCHNG 0x2000 /* "Slow Change". Value constant during a * single query - might change over time @@ -1351,72 +1251,6 @@ enum trim_side_mask { TRIM_BOTH = TRIM_LEADING | TRIM_TRAILING }; -/* - * The following three macros, FUNCTION(), LIKEFUNC() and AGGREGATE() are - * used to create the initializers for the FuncDef structures. - * - * FUNCTION(zName, mask, iArg, bNC, xFunc) - * Used to create a scalar function definition of a function zName - * implemented by C function xFunc that accepts mask arguments. The - * value passed as iArg is cast to a (void*) and made available - * as the user-data (sql_user_data()) for the function. If - * argument bNC is true, then the sql_FUNC_NEEDCOLL flag is set. - * - * FUNCTION_COLL - * Like FUNCTION except it assumes that function returns - * STRING which collation should be derived from first - * argument (trim, substr etc). - * - * VFUNCTION(zName, mask, iArg, bNC, xFunc) - * Like FUNCTION except it omits the sql_FUNC_CONSTANT flag. - * - * DFUNCTION(zName, mask, iArg, bNC, xFunc) - * Like FUNCTION except it omits the sql_FUNC_CONSTANT flag and - * adds the sql_FUNC_SLOCHNG flag. Used for date & time functions, - * but not during a single query. - * - * AGGREGATE(zName, mask, iArg, bNC, xStep, xFinal) - * Used to create an aggregate function definition implemented by - * the C functions xStep and xFinal. The first four parameters - * are interpreted in the same way as the first 4 parameters to - * FUNCTION(). - * - * LIKEFUNC(zName, mask, pArg, flags) - * Used to create a scalar function definition of a function zName - * that accepts mask arguments and is implemented by a call to C - * function likeFunc. Argument pArg is cast to a (void *) and made - * available as the function user-data (sql_user_data()). The - * FuncDef.flags variable is set to the value passed as the flags - * parameter. - */ -#define FUNCTION(zName, mask, iArg, bNC, xFunc, type) \ - {mask, SQL_FUNC_CONSTANT|(bNC*SQL_FUNC_NEEDCOLL), \ - SQL_INT_TO_PTR(iArg), 0, xFunc, 0, #zName, {0}, type} -#define FUNCTION_COLL(zName, mask, iArg, bNC, xFunc) \ - {mask, SQL_FUNC_CONSTANT|SQL_FUNC_DERIVEDCOLL|(bNC*SQL_FUNC_NEEDCOLL), \ - SQL_INT_TO_PTR(iArg), 0, xFunc, 0, #zName, {0}, FIELD_TYPE_STRING} -#define VFUNCTION(zName, mask, iArg, bNC, xFunc, type) \ - {mask, (bNC*SQL_FUNC_NEEDCOLL), \ - SQL_INT_TO_PTR(iArg), 0, xFunc, 0, #zName, {0}, type} -#define DFUNCTION(zName, mask, iArg, bNC, xFunc, type) \ - {mask, SQL_FUNC_SLOCHNG|(bNC*SQL_FUNC_NEEDCOLL), \ - SQL_INT_TO_PTR(iArg), 0, xFunc, 0, #zName, {0}, type} -#define FUNCTION2(zName, mask, iArg, bNC, xFunc, extraFlags, type) \ - {mask,SQL_FUNC_CONSTANT|(bNC*SQL_FUNC_NEEDCOLL)|extraFlags,\ - SQL_INT_TO_PTR(iArg), 0, xFunc, 0, #zName, {0}, type} -#define STR_FUNCTION(zName, mask, pArg, bNC, xFunc) \ - {mask, SQL_FUNC_SLOCHNG|(bNC*SQL_FUNC_NEEDCOLL), \ - pArg, 0, xFunc, 0, #zName, {SQL_AFF_STRING, {0}}} -#define LIKEFUNC(zName, mask, arg, flags, type) \ - {mask, SQL_FUNC_NEEDCOLL|SQL_FUNC_CONSTANT|flags, \ - (void *)(SQL_INT_TO_PTR(arg)), 0, likeFunc, 0, #zName, {0}, type} -#define AGGREGATE(zName, mask, arg, nc, xStep, xFinal, type) \ - {mask, (nc*SQL_FUNC_NEEDCOLL), \ - SQL_INT_TO_PTR(arg), 0, xStep,xFinal,#zName, {0}, type} -#define AGGREGATE2(zName, mask, arg, nc, xStep, xFinal, extraFlags, type) \ - {mask, (nc*SQL_FUNC_NEEDCOLL)|extraFlags, \ - SQL_INT_TO_PTR(arg), 0, xStep,xFinal,#zName, {0}, type} - #define ARGC_MASK_FULL (~0ULL) #define ARGC_MASK(a) (a < 0 ? ARGC_MASK_FULL : (1ULL << a)) #define ARGC_MASK2(a, b) (ARGC_MASK(a) | ARGC_MASK(b)) @@ -1614,7 +1448,8 @@ struct AggInfo { */ struct AggInfo_func { /* For each aggregate function */ Expr *pExpr; /* Expression encoding the function */ - FuncDef *pFunc; /* The aggregate function implementation */ + /** The aggregate function implementation. */ + struct func *func; int iMem; /* Memory location that acts as accumulator */ int iDistinct; /* Ephemeral table used to enforce DISTINCT */ /** @@ -3562,10 +3397,6 @@ void sqlSelectSetName(Select *, const char *); #else #define sqlSelectSetName(A,B) #endif -void sqlInsertBuiltinFuncs(FuncDef *, int); -FuncDef *sqlFindFunction(sql *, const char *, int, u8); -void sqlRegisterBuiltinFunctions(void); -void sqlRegisterDateTimeFunctions(void); /** * Evaluate a view and store its result in an ephemeral table. @@ -4138,7 +3969,6 @@ extern const unsigned char sqlUpperToLower[]; extern const unsigned char sqlCtypeMap[]; extern const Token sqlIntTokens[]; extern SQL_WSD struct sqlConfig sqlConfig; -extern FuncDefHash sqlBuiltinFunctions; extern int sqlPendingByte; /** @@ -4274,21 +4104,13 @@ sql_key_info_to_key_def(struct sql_key_info *key_info); * Check if the function implements LIKE-style comparison & if it * is appropriate to apply a LIKE query optimization. * - * @param db database structure. * @param pExpr pointer to a function-implementing expression. * @param[out] is_like_ci true if LIKE is case insensitive. * * @retval 1 if LIKE optimization can be used, 0 otherwise. */ int -sql_is_like_func(struct sql *db, struct Expr *expr); - -int sqlCreateFunc(sql *, const char *, enum field_type, - int, int, void *, - void (*)(sql_context *, int, sql_value **), - void (*)(sql_context *, int, sql_value **), - void (*)(sql_context *), - FuncDestructor * pDestructor); +sql_is_like_func(struct Expr *expr); /** Set OOM error flag. */ static inline void @@ -4325,7 +4147,6 @@ sql_expr_new_column(struct sql *db, struct SrcList *src_list, int src_idx, int sqlExprCheckIN(Parse *, Expr *); -void sqlAnalyzeFunctions(void); int sqlStat4ProbeSetValue(Parse *, struct index_def *, UnpackedRecord **, Expr *, int, int, int *); int sqlStat4ValueFromExpr(Parse *, Expr *, enum field_type type, @@ -4516,9 +4337,62 @@ Expr *sqlExprForVectorField(Parse *, Expr *, int); */ extern int sqlSubProgramsRemaining; -/** Register built-in functions to work with ANALYZE data. */ -void -sql_register_analyze_builtins(void); +struct func_sql_builtin { + /** Function object base class. */ + struct func base; + /** + * A bitmask representing all supported function + * overloads. The function supports argc == n iff this + * bitmask has bit n set 1. In particular case, a bitmask + * (~0) means this function works with any possible + * argument. + * + * The count of arguments for function is limited with + * (CHAR_BITS*sizeof(uint64_t) - 1). When the highest bit + * of the mask is set, this means that greater values + * are supported. E.g. greatest function works correctly + * with any number of input arguments. + */ + uint64_t signature_mask; + /** A bitmask of SQL flags. */ + uint16_t flags; + /** User data to pass in call method. */ + void *user_data; + /** A call method for a function. */ + void (*call)(sql_context *ctx, int argc, sql_value **argv); + /** Finalize method (only for aggregate function). */ + void (*finalize)(sql_context *ctx); +}; + +/** + * A SQL method to find a function in a hash by it's name and + * count of arguments. Only functions that have 'SQL' engine + * export field set true and have exactly the same signature + * are returned. + * + * Returns not NULL function pointer when a valid and exported + * to SQL engine function was found and NULL otherwise. + */ +struct func * +sql_func_by_signature(const char *name, uint32_t argc); + +/** + * Test whether SQL-specific flag is set for given function. + * Currently only SQL Builtin Functions have such hint flags, + * so function returns false for other functions. Such approach + * decreases code complexity and allows do not distinguish + * functions by implementation details where it is unnecessary. + * + * Returns true when given flag is set for a given function and + * false otherwise. + */ +static inline bool +sql_func_flag_test(struct func *func, uint16_t flag) +{ + if (func->def->language != FUNC_LANGUAGE_SQL_BUILTIN) + return false; + return (((struct func_sql_builtin *)func)->flags & flag) != 0; +} /** * Generate VDBE code to halt execution with correct error if diff --git a/src/box/sql/vdbe.h b/src/box/sql/vdbe.h index 8f16202ba..4f639ec38 100644 --- a/src/box/sql/vdbe.h +++ b/src/box/sql/vdbe.h @@ -72,7 +72,11 @@ struct VdbeOp { char *z; /* Pointer to data for string (char array) types */ i64 *pI64; /* Used when p4type is P4_INT64/UINT64 */ double *pReal; /* Used when p4type is P4_REAL */ - FuncDef *pFunc; /* Used when p4type is P4_FUNCDEF */ + /** + * A pointer to function implementation. + * Used when p4type is P4_FUNC. + */ + struct func *func; sql_context *pCtx; /* Used when p4type is P4_FUNCCTX */ struct coll *pColl; /* Used when p4type is P4_COLLSEQ */ Mem *pMem; /* Used when p4type is P4_MEM */ @@ -122,7 +126,8 @@ struct SubProgram { #define P4_DYNAMIC (-1) /* Pointer to a string obtained from sqlMalloc() */ #define P4_STATIC (-2) /* Pointer to a static string */ #define P4_COLLSEQ (-3) /* P4 is a pointer to a CollSeq structure */ -#define P4_FUNCDEF (-4) /* P4 is a pointer to a FuncDef structure */ +/** P4 is a pointer to a func structure. */ +#define P4_FUNC (-4) #define P4_MEM (-7) /* P4 is a pointer to a Mem* structure */ #define P4_TRANSIENT 0 /* P4 is a pointer to a transient string */ #define P4_REAL (-9) /* P4 is a 64-bit floating point value */ diff --git a/src/box/sql/vdbeInt.h b/src/box/sql/vdbeInt.h index f77c019fb..f085477c1 100644 --- a/src/box/sql/vdbeInt.h +++ b/src/box/sql/vdbeInt.h @@ -46,6 +46,8 @@ */ typedef struct VdbeOp Op; +struct func; + /* * Boolean values */ @@ -168,7 +170,11 @@ struct Mem { bool b; /* Boolean value used when MEM_Bool is set in flags */ int nZero; /* Used when bit MEM_Zero is set in flags */ void *p; /* Generic pointer */ - FuncDef *pDef; /* Used only when flags==MEM_Agg */ + /** + * A pointer to function implementation. + * Used only when flags==MEM_Agg. + */ + struct func *func; VdbeFrame *pFrame; /* Used when flags==MEM_Frame */ } u; u32 flags; /* Some combination of MEM_Null, MEM_Str, MEM_Dyn, etc. */ @@ -309,7 +315,8 @@ mem_apply_numeric_type(struct Mem *record); */ struct sql_context { Mem *pOut; /* The return value is stored here */ - FuncDef *pFunc; /* Pointer to function information */ + /* A pointer to function implementation. */ + struct func *func; Mem *pMem; /* Memory cell used to store aggregate context */ Vdbe *pVdbe; /* The VM that owns this context */ /** Instruction number of OP_BuiltinFunction0. */ @@ -516,7 +523,17 @@ int sqlVdbeMemNumerify(Mem *); int sqlVdbeMemCast(Mem *, enum field_type type); int sqlVdbeMemFromBtree(BtCursor *, u32, u32, Mem *); void sqlVdbeMemRelease(Mem * p); -int sqlVdbeMemFinalize(Mem *, FuncDef *); + +/** + * Memory cell pMem contains the context of an aggregate function. + * This routine calls the finalize method for that function. The + * result of the aggregate is stored back into pMem. + * + * Returns -1 if the finalizer reports an error. 0 otherwise. + */ +int +sql_vdbemem_finalize(struct Mem *mem, struct func *func); + const char *sqlOpcodeName(int); int sqlVdbeMemGrow(Mem * pMem, int n, int preserve); int sqlVdbeMemClearAndResize(Mem * pMem, int n); diff --git a/src/box/func.c b/src/box/func.c index b35d05dca..12e603b3f 100644 --- a/src/box/func.c +++ b/src/box/func.c @@ -39,6 +39,7 @@ #include "port.h" #include "schema.h" #include "session.h" +#include "sql.h" #include /** @@ -381,39 +382,6 @@ restore: static struct func * func_c_new(struct func_def *def); -/** A stub object for SQL builtins to avoid name clash with UDF. */ -static struct func_vtab func_sql_builtin_vtab; - -/** Construct a SQL builtin function object. */ -struct func * -func_sql_builtin_new(struct func_def *def) -{ - assert(def->language == FUNC_LANGUAGE_SQL_BUILTIN); - struct func *func = - (struct func *) malloc(sizeof(*func)); - if (func == NULL) { - diag_set(OutOfMemory, sizeof(*func), "malloc", "func"); - return NULL; - } - /** Don't export SQL builtins in Lua for now. */ - def->exports.lua = false; - func->vtab = &func_sql_builtin_vtab; - return func; -} - -static void -func_sql_builtin_destroy(struct func *func) -{ - assert(func->vtab == &func_sql_builtin_vtab); - assert(func->def->language == FUNC_LANGUAGE_SQL_BUILTIN); - free(func); -} - -static struct func_vtab func_sql_builtin_vtab = { - .call = NULL, - .destroy = func_sql_builtin_destroy, -}; - struct func * func_new(struct func_def *def) { diff --git a/src/box/lua/call.c b/src/box/lua/call.c index 0ac2eb7a6..001578b5a 100644 --- a/src/box/lua/call.c +++ b/src/box/lua/call.c @@ -44,7 +44,6 @@ #include "box/port.h" #include "box/lua/tuple.h" #include "small/obuf.h" -#include "lua_sql.h" #include "trivia/util.h" #include "mpstream.h" @@ -968,7 +967,6 @@ static struct trigger on_alter_func_in_lua = { static const struct luaL_Reg boxlib_internal[] = { {"call_loadproc", lbox_call_loadproc}, - {"sql_create_function", lbox_sql_create_function}, {"module_reload", lbox_module_reload}, {"func_call", lbox_func_call}, {NULL, NULL} diff --git a/src/box/lua/lua_sql.c b/src/box/lua/lua_sql.c deleted file mode 100644 index 67a51a82c..000000000 --- a/src/box/lua/lua_sql.c +++ /dev/null @@ -1,205 +0,0 @@ -/* - * Copyright 2010-2017, Tarantool AUTHORS, please see AUTHORS file. - * - * Redistribution and use in source and binary forms, with or - * without modification, are permitted provided that the following - * conditions are met: - * - * 1. Redistributions of source code must retain the above - * copyright notice, this list of conditions and the - * following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following - * disclaimer in the documentation and/or other materials - * provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED - * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL - * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR - * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF - * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#include "lua.h" -#include "lua/utils.h" - -#include "box/lua/call.h" -#include "box/sql/sqlInt.h" -#include "box/sql/vdbeInt.h" - -struct lua_sql_func_info { - int func_ref; -}; - -/** - * This function is callback which is called by sql engine. - * - * Purpose of this function is to call lua func from sql. - * Lua func should be previously registered in sql - * (see lbox_sql_create_function). - */ -static void -lua_sql_call(sql_context *pCtx, int nVal, sql_value **apVal) { - lua_State *L = lua_newthread(tarantool_L); - int coro_ref = luaL_ref(tarantool_L, LUA_REGISTRYINDEX); - struct lua_sql_func_info *func_info = sql_user_data(pCtx); - - lua_rawgeti(L, LUA_REGISTRYINDEX, func_info->func_ref); - for (int i = 0; i < nVal; i++) { - sql_value *param = apVal[i]; - switch (sql_value_type(param)) { - case MP_INT: - luaL_pushint64(L, sql_value_int64(param)); - break; - case MP_UINT: - luaL_pushuint64(L, sql_value_uint64(param)); - break; - case MP_DOUBLE: - lua_pushnumber(L, sql_value_double(param)); - break; - case MP_STR: - lua_pushstring(L, (const char *) sql_value_text(param)); - break; - case MP_BIN: - lua_pushlstring(L, sql_value_blob(param), - (size_t) sql_value_bytes(param)); - break; - case MP_NIL: - lua_rawgeti(L, LUA_REGISTRYINDEX, luaL_nil_ref); - break; - case MP_BOOL: - lua_pushboolean(L, sql_value_boolean(param)); - break; - default: - diag_set(ClientError, ER_SQL_EXECUTE, "Unsupported "\ - "type passed to Lua"); - pCtx->is_aborted = true; - goto error; - } - } - if (lua_pcall(L, lua_gettop(L) - 1, 1, 0) != 0){ - diag_set(ClientError, ER_SQL_EXECUTE, lua_tostring(L, -1)); - pCtx->is_aborted = true; - goto error; - } - switch(lua_type(L, -1)) { - case LUA_TBOOLEAN: - sql_result_bool(pCtx, lua_toboolean(L, -1)); - break; - case LUA_TNUMBER: - sql_result_double(pCtx, lua_tonumber(L, -1)); - break; - case LUA_TSTRING: - sql_result_text(pCtx, lua_tostring(L, -1), -1, - SQL_TRANSIENT); - break; - case LUA_TNIL: - sql_result_null(pCtx); - break; - default: - diag_set(ClientError, ER_SQL_EXECUTE, "Unsupported type "\ - "passed from Lua"); - pCtx->is_aborted = true; - goto error; - } -error: - luaL_unref(tarantool_L, LUA_REGISTRYINDEX, coro_ref); - return; -} - -static void -lua_sql_destroy(void *p) -{ - struct lua_sql_func_info *func_info = p; - luaL_unref(tarantool_L, LUA_REGISTRYINDEX, func_info->func_ref); - free(func_info); - return; -} - -/** - * A helper to register lua function in SQL during runtime. - * It makes available queries like this: "SELECT lua_func(arg);" - * - * sql_create_function *p argument is used to store func ref - * to lua function (it identifies actual lua func to call if there - * are many of them). SQL function must have name and type of - * returning value. Additionally, it can feature number of - * arguments and deterministic flag. - */ -int -lbox_sql_create_function(struct lua_State *L) -{ - struct sql *db = sql_get(); - if (db == NULL) - return luaL_error(L, "Please call box.cfg{} first"); - int argc = lua_gettop(L); - /* - * Three function prototypes are possible: - * 1. sql_create_function("func_name", "type", func); - * 2. sql_create_function("func_name", "type", func, - * func_arg_num); - * 3. sql_create_function("func_name", "type", func, - * func_arg_num, is_deterministic); - */ - if (!(argc == 3 && lua_isstring(L, 1) && lua_isstring(L, 2) && - lua_isfunction(L, 3)) && - !(argc == 4 && lua_isstring(L, 1) && lua_isstring(L, 2) && - lua_isfunction(L, 3) && lua_isnumber(L, 4)) && - !(argc == 5 && lua_isstring(L, 1) && lua_isstring(L, 2) && - lua_isfunction(L, 3) && lua_isnumber(L, 4) && - lua_isboolean(L, 5))) - return luaL_error(L, "Invalid arguments"); - enum field_type type; - const char *type_arg = lua_tostring(L, 2); - if (strcmp(type_arg, "INT") == 0 || strcmp(type_arg, "INTEGER") == 0) - type = FIELD_TYPE_INTEGER; - else if (strcmp(type_arg, "TEXT") == 0) - type = FIELD_TYPE_STRING; - else if (strcmp(type_arg, "NUMBER") == 0) - type = FIELD_TYPE_NUMBER; - else if (strcmp(type_arg, "VARBINARY") == 0) - type = FIELD_TYPE_SCALAR; - else if (strcmp(type_arg, "BOOL") == 0 || - strcmp(type_arg, "BOOLEAN") == 0) - type = FIELD_TYPE_BOOLEAN; - else - return luaL_error(L, "Unknown type"); - /* -1 indicates any number of arguments. */ - int func_arg_num = -1; - bool is_deterministic = false; - if (argc == 4) { - func_arg_num = lua_tointeger(L, 4); - lua_pop(L, 1); - } else if (argc == 5) { - is_deterministic = lua_toboolean(L, 5); - func_arg_num = lua_tointeger(L, 4); - lua_pop(L, 2); - } - size_t name_len; - const char *name = lua_tolstring(L, 1, &name_len); - char *normalized_name = - sql_normalized_name_region_new(&fiber()->gc, name, name_len); - if (normalized_name == NULL) - return luaT_error(L); - struct lua_sql_func_info *func_info = - (struct lua_sql_func_info *) malloc(sizeof(*func_info)); - if (func_info == NULL) - return luaL_error(L, "out of memory"); - func_info->func_ref = luaL_ref(L, LUA_REGISTRYINDEX); - int rc = sql_create_function_v2(db, normalized_name, type, func_arg_num, - is_deterministic ? SQL_DETERMINISTIC : 0, - func_info, lua_sql_call, NULL, NULL, - lua_sql_destroy); - if (rc != 0) - return luaT_error(L); - return 0; -} diff --git a/src/box/sql.c b/src/box/sql.c index 0ab3a506f..a731332c7 100644 --- a/src/box/sql.c +++ b/src/box/sql.c @@ -1262,3 +1262,26 @@ vdbe_field_ref_prepare_tuple(struct vdbe_field_ref *field_ref, vdbe_field_ref_create(field_ref, tuple, tuple_data(tuple), tuple->bsize); } + +struct func * +sql_func_by_signature(const char *name, uint32_t argc) +{ + struct func *func = func_by_name(name, strlen(name)); + if (func == NULL || !func->def->exports.sql) + return NULL; + if (func->def->language != FUNC_LANGUAGE_SQL_BUILTIN) { + return NULL; + } else { + /* + * The param_count field is not valid for sql + * builtin functions because they define an + * signature_mask with all supported overloads. + */ + struct func_sql_builtin *builtin = + (struct func_sql_builtin *)func; + if (!column_mask_fieldno_is_set(builtin->signature_mask, + argc)) + return NULL; + } + return func; +} diff --git a/src/box/sql/analyze.c b/src/box/sql/analyze.c index bd52d12df..b68d86dfe 100644 --- a/src/box/sql/analyze.c +++ b/src/box/sql/analyze.c @@ -263,7 +263,7 @@ stat4Destructor(void *pOld) * return value is BLOB, but it is really just a pointer to the Stat4Accum * object. */ -static void +MAYBE_UNUSED static void statInit(sql_context * context, int argc, sql_value ** argv) { Stat4Accum *p; @@ -535,7 +535,7 @@ samplePushPrevious(Stat4Accum * p, int iChng) * * The R parameter is only used for STAT4 */ -static void +MAYBE_UNUSED static void statPush(sql_context * context, int argc, sql_value ** argv) { int i; @@ -608,7 +608,7 @@ statPush(sql_context * context, int argc, sql_value ** argv) * The content to returned is determined by the parameter J * which is one of the STAT_GET_xxxx values defined above. */ -static void +MAYBE_UNUSED static void statGet(sql_context * context, int argc, sql_value ** argv) { Stat4Accum *p = (Stat4Accum *) sql_value_blob(argv[0]); @@ -715,11 +715,11 @@ callStatGet(Vdbe * v, int regStat4, int iParam, int regOut) { assert(regOut != regStat4 && regOut != regStat4 + 1); sqlVdbeAddOp2(v, OP_Integer, iParam, regStat4 + 1); - struct FuncDef *func = - sqlFindFunction(sql_get(), "_sql_stat_get", 2, 0); + struct func *func = + sql_func_by_signature("_sql_stat_get", 2); assert(func != NULL); sqlVdbeAddOp4(v, OP_BuiltinFunction0, 0, regStat4, regOut, - (char *)func, P4_FUNCDEF); + (char *)func, P4_FUNC); sqlVdbeChangeP5(v, 2); } @@ -855,11 +855,11 @@ vdbe_emit_analyze_space(struct Parse *parse, struct space *space) sqlVdbeAddOp2(v, OP_Count, idx_cursor, stat4_reg + 3); sqlVdbeAddOp2(v, OP_Integer, part_count, stat4_reg + 1); sqlVdbeAddOp2(v, OP_Integer, part_count, stat4_reg + 2); - struct FuncDef *init_func = - sqlFindFunction(sql_get(), "_sql_stat_init", 3, 0); + struct func *init_func = + sql_func_by_signature("_sql_stat_init", 3); assert(init_func != NULL); sqlVdbeAddOp4(v, OP_BuiltinFunction0, 0, stat4_reg + 1, - stat4_reg, (char *)init_func, P4_FUNCDEF); + stat4_reg, (char *)init_func, P4_FUNC); sqlVdbeChangeP5(v, 3); /* * Implementation of the following: @@ -956,11 +956,11 @@ vdbe_emit_analyze_space(struct Parse *parse, struct space *space) sqlVdbeAddOp3(v, OP_MakeRecord, stat_key_reg, pk_part_count, key_reg); assert(chng_reg == (stat4_reg + 1)); - struct FuncDef *push_func = - sqlFindFunction(sql_get(), "_sql_stat_push", 3, 0); + struct func *push_func = + sql_func_by_signature("_sql_stat_push", 3); assert(push_func != NULL); sqlVdbeAddOp4(v, OP_BuiltinFunction0, 1, stat4_reg, tmp_reg, - (char *)push_func, P4_FUNCDEF); + (char *)push_func, P4_FUNC); sqlVdbeChangeP5(v, 3); sqlVdbeAddOp2(v, OP_Next, idx_cursor, next_row_addr); /* Add the entry to the stat1 table. */ @@ -1746,14 +1746,3 @@ fail: box_txn_rollback(); return -1; } - -void -sql_register_analyze_builtins(void) -{ - static FuncDef funcs[] = { - FUNCTION(_sql_stat_get, 2, 0, 0, statGet, FIELD_TYPE_ANY), - FUNCTION(_sql_stat_push, 3, 0, 0, statPush, FIELD_TYPE_ANY), - FUNCTION(_sql_stat_init, 3, 0, 0, statInit, FIELD_TYPE_ANY), - }; - sqlInsertBuiltinFuncs(funcs, nelem(funcs)); -} diff --git a/src/box/sql/callback.c b/src/box/sql/callback.c index 737c24d98..b489f0c81 100644 --- a/src/box/sql/callback.c +++ b/src/box/sql/callback.c @@ -57,194 +57,3 @@ sql_get_coll_seq(Parse *parser, const char *name, uint32_t *coll_id) return p->coll; } } - -/* During the search for the best function definition, this procedure - * is called to test how well the function passed as the first argument - * matches the request for a function with nArg arguments in a system - * that uses encoding enc. The value returned indicates how well the - * request is matched. A higher value indicates a better match. - * - * If nArg is -1 that means to only return a match (non-zero) if p->nArg - * is also -1. In other words, we are searching for a function that - * takes a variable number of arguments. - * - * If nArg is -2 that means that we are searching for any function - * regardless of the number of arguments it uses, so return a positive - * match score for any - * - * The returned value is always between 0 and 6, as follows: - * - * 0: Not a match. - * 1: UTF8/16 conversion required and function takes any number of arguments. - * 2: UTF16 byte order change required and function takes any number of args. - * 3: encoding matches and function takes any number of arguments - * 4: UTF8/16 conversion required - argument count matches exactly - * 5: UTF16 byte order conversion required - argument count matches exactly - * 6: Perfect match: encoding and argument count match exactly. - * - * If nArg==(-2) then any function with a non-null xSFunc is - * a perfect match and any function with xSFunc NULL is - * a non-match. - */ -#define FUNC_PERFECT_MATCH 4 /* The score for a perfect match */ -static int -matchQuality(FuncDef * p, /* The function we are evaluating for match quality */ - int nArg /* Desired number of arguments. (-1)==any */ - ) -{ - /* nArg of -2 is a special case */ - if (nArg == (-2)) - return FUNC_PERFECT_MATCH; - /* Wrong number of arguments means "no match" */ - if (!column_mask_fieldno_is_set(p->signature_mask, (uint32_t)nArg)) - return 0; - return p->signature_mask == ARGC_MASK(nArg) ? FUNC_PERFECT_MATCH : 1; -} - -/* - * Search a FuncDefHash for a function with the given name. Return - * a pointer to the matching FuncDef if found, or 0 if there is no match. - */ -static FuncDef * -functionSearch(int h, /* Hash of the name */ - const char *zFunc /* Name of function */ - ) -{ - FuncDef *p; - for (p = sqlBuiltinFunctions.a[h]; p; p = p->u.pHash) { - if (sqlStrICmp(p->zName, zFunc) == 0) { - return p; - } - } - return 0; -} - -/* - * Insert a new FuncDef into a FuncDefHash hash table. - */ -void -sqlInsertBuiltinFuncs(FuncDef * aDef, /* List of global functions to be inserted */ - int nDef /* Length of the apDef[] list */ - ) -{ - int i; - for (i = 0; i < nDef; i++) { - FuncDef *pOther; - const char *zName = aDef[i].zName; - int nName = sqlStrlen30(zName); - int h = - (sqlUpperToLower[(u8) zName[0]] + - nName) % SQL_FUNC_HASH_SZ; - pOther = functionSearch(h, zName); - if (pOther) { - assert(pOther != &aDef[i] && pOther->pNext != &aDef[i]); - aDef[i].pNext = pOther->pNext; - pOther->pNext = &aDef[i]; - } else { - aDef[i].pNext = 0; - aDef[i].u.pHash = sqlBuiltinFunctions.a[h]; - sqlBuiltinFunctions.a[h] = &aDef[i]; - } - } -} - -/* - * Locate a user function given a name, a number of arguments and a flag - * indicating whether the function prefers UTF-16 over UTF-8. Return a - * pointer to the FuncDef structure that defines that function, or return - * NULL if the function does not exist. - * - * If the createFlag argument is true, then a new (blank) FuncDef - * structure is created and liked into the "db" structure if a - * no matching function previously existed. - * - * If nArg is -2, then the first valid function found is returned. A - * function is valid if xSFunc is non-zero. The nArg==(-2) - * case is used to see if zName is a valid function name for some number - * of arguments. If nArg is -2, then createFlag must be 0. - * - * If createFlag is false, then a function with the required name and - * number of arguments may be returned even if the eTextRep flag does not - * match that requested. - */ -FuncDef * -sqlFindFunction(sql * db, /* An open database */ - const char *zName, /* Name of the function. zero-terminated */ - int nArg, /* Number of arguments. -1 means any number */ - u8 createFlag /* Create new entry if true and does not otherwise exist */ - ) -{ - FuncDef *p; /* Iterator variable */ - FuncDef *pBest = 0; /* Best match found so far */ - int bestScore = 0; /* Score of best match */ - int h; /* Hash value */ - int nName; /* Length of the name */ - - assert(nArg >= (-2)); - assert(nArg >= (-1) || createFlag == 0); - nName = sqlStrlen30(zName); - - /* First search for a match amongst the application-defined functions. - */ - p = (FuncDef *) sqlHashFind(&db->aFunc, zName); - while (p) { - int score = matchQuality(p, nArg); - if (score > bestScore) { - pBest = p; - bestScore = score; - } - p = p->pNext; - } - - /* If no match is found, search the built-in functions. - * - * Except, if createFlag is true, that means that we are trying to - * install a new function. Whatever FuncDef structure is returned it will - * have fields overwritten with new information appropriate for the - * new function. But the FuncDefs for built-in functions are read-only. - * So we must not search for built-ins when creating a new function. - */ - if (!createFlag && (pBest == NULL)) { - bestScore = 0; - h = (sqlUpperToLower[(u8) zName[0]] + - nName) % SQL_FUNC_HASH_SZ; - p = functionSearch(h, zName); - while (p) { - int score = matchQuality(p, nArg); - if (score > bestScore) { - pBest = p; - bestScore = score; - } - p = p->pNext; - } - } - - /* If the createFlag parameter is true and the search did not reveal an - * exact match for the name, number of arguments and encoding, then add a - * new entry to the hash table and return it. - */ - if (createFlag && bestScore < FUNC_PERFECT_MATCH && - (pBest = - sqlDbMallocZero(db, sizeof(*pBest) + nName + 1)) != 0) { - FuncDef *pOther; - pBest->zName = (const char *)&pBest[1]; - pBest->signature_mask = ARGC_MASK(nArg); - pBest->funcFlags = 0; - memcpy((char *)&pBest[1], zName, nName + 1); - pOther = - (FuncDef *) sqlHashInsert(&db->aFunc, pBest->zName, - pBest); - if (pOther == pBest) { - sqlDbFree(db, pBest); - sqlOomFault(db); - return 0; - } else { - pBest->pNext = pOther; - } - } - - if (pBest && (pBest->xSFunc || createFlag)) { - return pBest; - } - return 0; -} diff --git a/src/box/sql/date.c b/src/box/sql/date.c index 2e2a71ad2..dffc23616 100644 --- a/src/box/sql/date.c +++ b/src/box/sql/date.c @@ -1290,31 +1290,3 @@ currentTimeFunc(sql_context * context, int argc, sql_value ** argv) } } #endif - -/* - * This function registered all of the above C functions as SQL - * functions. This should be the only routine in this file with - * external linkage. - */ -void -sqlRegisterDateTimeFunctions(void) -{ - static FuncDef aDateTimeFuncs[] = { -#if 0 - DFUNCTION(julianday, -1, 0, 0, juliandayFunc, FIELD_TYPE_NUMBER), - DFUNCTION(date, -1, 0, 0, dateFunc, FIELD_TYPE_STRING), - DFUNCTION(time, -1, 0, 0, timeFunc, FIELD_TYPE_STRING), - DFUNCTION(datetime, -1, 0, 0, datetimeFunc, FIELD_TYPE_STRING), - DFUNCTION(strftime, -1, 0, 0, strftimeFunc, FIELD_TYPE_STRING), - DFUNCTION(current_time, 0, 0, 0, ctimeFunc, FIELD_TYPE_STRING), - DFUNCTION(current_timestamp, 0, 0, 0, ctimestampFunc, - FIELD_TYPE_STRING), - DFUNCTION(current_date, 0, 0, 0, cdateFunc, FIELD_TYPE_STRING), - STR_FUNCTION(current_time, 0, "%H:%M:%S", 0, currentTimeFunc), - STR_FUNCTION(current_date, 0, "%Y-%m-%d", 0, currentTimeFunc), - STR_FUNCTION(current_timestamp, 0, "%Y-%m-%d %H:%M:%S", 0, - currentTimeFunc), -#endif - }; - sqlInsertBuiltinFuncs(aDateTimeFuncs, ArraySize(aDateTimeFuncs)); -} diff --git a/src/box/sql/expr.c b/src/box/sql/expr.c index 1f9d91705..64b3bc835 100644 --- a/src/box/sql/expr.c +++ b/src/box/sql/expr.c @@ -328,12 +328,11 @@ sql_expr_coll(Parse *parse, Expr *p, bool *is_explicit_coll, uint32_t *coll_id, if (op == TK_FUNCTION) { uint32_t arg_count = p->x.pList == NULL ? 0 : p->x.pList->nExpr; - struct FuncDef *func = sqlFindFunction(parse->db, - p->u.zToken, - arg_count, 0); + struct func *func = + sql_func_by_signature(p->u.zToken, arg_count); if (func == NULL) break; - if ((func->funcFlags & SQL_FUNC_DERIVEDCOLL) != 0) { + if (sql_func_flag_test(func, SQL_FUNC_DERIVEDCOLL)) { /* * Now we use quite straightforward * approach assuming that resulting @@ -342,7 +341,7 @@ sql_expr_coll(Parse *parse, Expr *p, bool *is_explicit_coll, uint32_t *coll_id, * built-in functions: trim, upper, * lower, replace, substr. */ - assert(func->ret_type == FIELD_TYPE_STRING); + assert(func->def->returns == FIELD_TYPE_STRING); p = p->x.pList->a->pExpr; continue; } @@ -3975,11 +3974,9 @@ sqlExprCodeTarget(Parse * pParse, Expr * pExpr, int target) case TK_FUNCTION:{ ExprList *pFarg; /* List of function arguments */ int nFarg; /* Number of function arguments */ - FuncDef *pDef; /* The function definition object */ const char *zId; /* The function name */ u32 constMask = 0; /* Mask of function arguments that are constant */ int i; /* Loop counter */ - sql *db = pParse->db; /* The database connection */ struct coll *coll = NULL; assert(!ExprHasProperty(pExpr, EP_xIsSelect)); @@ -3991,8 +3988,9 @@ sqlExprCodeTarget(Parse * pParse, Expr * pExpr, int target) nFarg = pFarg ? pFarg->nExpr : 0; assert(!ExprHasProperty(pExpr, EP_IntValue)); zId = pExpr->u.zToken; - pDef = sqlFindFunction(db, zId, nFarg, 0); - if (pDef == 0 || pDef->xFinalize != 0) { + struct func *func = sql_func_by_signature(zId, nFarg); + if (func == NULL || + func->def->aggregate == FUNC_AGGREGATE_GROUP) { diag_set(ClientError, ER_NO_SUCH_FUNCTION, zId); pParse->is_aborted = true; @@ -4002,7 +4000,7 @@ sqlExprCodeTarget(Parse * pParse, Expr * pExpr, int target) * IFNULL() functions. This avoids unnecessary evaluation of * arguments past the first non-NULL argument. */ - if (pDef->funcFlags & SQL_FUNC_COALESCE) { + if (sql_func_flag_test(func, SQL_FUNC_COALESCE)) { int endCoalesce = sqlVdbeMakeLabel(v); assert(nFarg >= 2); sqlExprCode(pParse, pFarg->a[0].pExpr, @@ -4026,7 +4024,7 @@ sqlExprCodeTarget(Parse * pParse, Expr * pExpr, int target) /* The UNLIKELY() function is a no-op. The result is the value * of the first argument. */ - if (pDef->funcFlags & SQL_FUNC_UNLIKELY) { + if (sql_func_flag_test(func, SQL_FUNC_UNLIKELY)) { assert(nFarg >= 1); return sqlExprCodeTarget(pParse, pFarg->a[0].pExpr, @@ -4049,7 +4047,7 @@ sqlExprCodeTarget(Parse * pParse, Expr * pExpr, int target) * is done using ANSI rules from * collations_check_compatibility(). */ - if ((pDef->funcFlags & SQL_FUNC_NEEDCOLL) != 0) { + if (sql_func_flag_test(func, SQL_FUNC_NEEDCOLL)) { struct coll *unused = NULL; uint32_t curr_id = COLL_NONE; bool is_curr_forced = false; @@ -4096,9 +4094,8 @@ sqlExprCodeTarget(Parse * pParse, Expr * pExpr, int target) * or OPFLAG_TYPEOFARG respectively, to avoid unnecessary data * loading. */ - if ((pDef-> - funcFlags & (SQL_FUNC_LENGTH | - SQL_FUNC_TYPEOF)) != 0) { + if (sql_func_flag_test(func, SQL_FUNC_LENGTH | + SQL_FUNC_TYPEOF)) { u8 exprOp; assert(nFarg == 1); assert(pFarg->a[0].pExpr != 0); @@ -4109,14 +4106,7 @@ sqlExprCodeTarget(Parse * pParse, Expr * pExpr, int target) OPFLAG_LENGTHARG); assert(SQL_FUNC_TYPEOF == OPFLAG_TYPEOFARG); - testcase(pDef-> - funcFlags & - OPFLAG_LENGTHARG); - pFarg->a[0].pExpr->op2 = - pDef-> - funcFlags & - (OPFLAG_LENGTHARG | - OPFLAG_TYPEOFARG); + pFarg->a[0].pExpr->op2 = true; } } @@ -4128,12 +4118,15 @@ sqlExprCodeTarget(Parse * pParse, Expr * pExpr, int target) } else { r1 = 0; } - if (pDef->funcFlags & SQL_FUNC_NEEDCOLL) { + if (sql_func_flag_test(func, SQL_FUNC_NEEDCOLL)) { sqlVdbeAddOp4(v, OP_CollSeq, 0, 0, 0, (char *)coll, P4_COLLSEQ); } - sqlVdbeAddOp4(v, OP_BuiltinFunction0, constMask, r1, - target, (char *)pDef, P4_FUNCDEF); + assert(func->def->language == + FUNC_LANGUAGE_SQL_BUILTIN); + int op = OP_BuiltinFunction0; + sqlVdbeAddOp4(v, op, constMask, r1, target, + (char *)func, P4_FUNC); sqlVdbeChangeP5(v, (u8) nFarg); if (nFarg && constMask == 0) { sqlReleaseTempRange(pParse, r1, nFarg); @@ -5441,12 +5434,19 @@ analyzeAggregate(Walker * pWalker, Expr * pExpr) pItem->iMem = ++pParse->nMem; assert(!ExprHasProperty (pExpr, EP_IntValue)); - pItem->pFunc = sqlFindFunction( - pParse->db, - pExpr->u.zToken, - pExpr->x.pList ? - pExpr->x.pList->nExpr : 0, - 0); + uint32_t argc = + pExpr->x.pList != NULL ? + pExpr->x.pList->nExpr : 0; + pItem->func = + sql_func_by_signature( + pExpr->u.zToken, + argc); + assert(pItem->func->def-> + language == + FUNC_LANGUAGE_SQL_BUILTIN && + pItem->func->def-> + aggregate == + FUNC_AGGREGATE_GROUP); if (pExpr->flags & EP_Distinct) { pItem->iDistinct = pParse->nTab++; diff --git a/src/box/sql/func.c b/src/box/sql/func.c index 8e07ce892..f07c52b95 100644 --- a/src/box/sql/func.c +++ b/src/box/sql/func.c @@ -1825,114 +1825,231 @@ groupConcatFinalize(sql_context * context) } int -sql_is_like_func(struct sql *db, struct Expr *expr) +sql_is_like_func(struct Expr *expr) { if (expr->op != TK_FUNCTION || !expr->x.pList || expr->x.pList->nExpr != 2) return 0; assert(!ExprHasProperty(expr, EP_xIsSelect)); - struct FuncDef *func = sqlFindFunction(db, expr->u.zToken, 2, 0); + struct func *func = sql_func_by_signature(expr->u.zToken, 2); assert(func != NULL); - if ((func->funcFlags & SQL_FUNC_LIKE) == 0) + if (!sql_func_flag_test(func, SQL_FUNC_LIKE)) return 0; return 1; } -/* - * All of the FuncDef structures in the aBuiltinFunc[] array above - * to the global function hash table. This occurs at start-time (as - * a consequence of calling sql_initialize()). - * - * After this routine runs +static int +sql_builtin_call_stub(struct func *func, struct port *args, struct port *ret) +{ + (void) func; (void) args; (void) ret; + diag_set(ClientError, ER_UNSUPPORTED, + "sql builtin function", "Lua frontend"); + return -1; +} + +static void +sql_builtin_stub(sql_context *ctx, int argc, sql_value **argv) +{ + (void) argc; (void) argv; + diag_set(ClientError, ER_SQL_EXECUTE, + tt_sprintf("function '%s' is not implemented", + ctx->func->def->name)); + ctx->is_aborted = true; +} + +#define REG_FUNC(name, signature_mask, returns, flags, \ + call, user_data, is_deterministic) \ + {name, signature_mask, returns, flags, call, NULL, \ + user_data, FUNC_AGGREGATE_NONE, is_deterministic} + +#define AGG_FUNC(name, signature_mask, returns, flags, \ + call, finalize, user_data) \ + {name, signature_mask, returns, flags, call, \ + finalize, user_data, FUNC_AGGREGATE_GROUP, false} + +#define STUB_FUNC(name) \ + {name, 0, FIELD_TYPE_ANY, 0, sql_builtin_stub, \ + NULL, NULL, FUNC_AGGREGATE_NONE, false} + +/** + * A sequence of SQL builtins definitions in + * lexicographic order. */ -void -sqlRegisterBuiltinFunctions(void) +static struct { + const char *name; + uint64_t signature_mask; + enum field_type returns; + uint16_t flags; + void (*call)(sql_context *ctx, int argc, sql_value **argv); + void (*finalize)(sql_context *ctx); + void *user_data; + enum func_aggregate aggregate; + bool is_deterministic; +} sql_builtins[] = { + REG_FUNC("ABS", ARGC_MASK(1), FIELD_TYPE_NUMBER, 0, + absFunc, NULL, true), + AGG_FUNC("AVG", ARGC_MASK(1), FIELD_TYPE_NUMBER, 0, + sum_step, avgFinalize, NULL), + STUB_FUNC("CEIL"), STUB_FUNC("CEILING"), + REG_FUNC("CHAR", ARGC_MASK_FULL, FIELD_TYPE_STRING, 0, + charFunc, NULL, true), + REG_FUNC("CHARACTER_LENGTH", ARGC_MASK(1), FIELD_TYPE_INTEGER, 0, + lengthFunc, NULL, true), + REG_FUNC("CHAR_LENGTH", ARGC_MASK(1), FIELD_TYPE_INTEGER, 0, + lengthFunc, NULL, true), + REG_FUNC("COALESCE", ARGC_MASK_FULL & ~ARGC_MASK2(0, 1), + FIELD_TYPE_SCALAR, SQL_FUNC_COALESCE, + sql_builtin_stub, NULL, true), + AGG_FUNC("COUNT", ARGC_MASK2(0, 1), FIELD_TYPE_INTEGER, 0, + countStep, countFinalize, NULL), + STUB_FUNC("CURRENT_DATE"), STUB_FUNC("CURRENT_TIME"), + STUB_FUNC("CURRENT_TIMESTAMP"), STUB_FUNC("DATE"), + STUB_FUNC("DATETIME"), STUB_FUNC("EVERY"), + STUB_FUNC("EXISTS"), STUB_FUNC("EXP"), STUB_FUNC("EXTRACT"), + STUB_FUNC("FLOOR"), STUB_FUNC("GREATER"), + REG_FUNC("GREATEST", ARGC_MASK_FULL, FIELD_TYPE_SCALAR, + SQL_FUNC_NEEDCOLL, minmaxFunc, SQL_INT_TO_PTR(1), true), + AGG_FUNC("GROUP_CONCAT", ARGC_MASK2(1, 2), FIELD_TYPE_STRING, 0, + groupConcatStep, groupConcatFinalize, NULL), + REG_FUNC("HEX", ARGC_MASK(1), FIELD_TYPE_STRING, 0, + hexFunc, NULL, true), + REG_FUNC("IFNULL", ARGC_MASK(2), FIELD_TYPE_INTEGER, SQL_FUNC_COALESCE, + sql_builtin_stub, NULL, true), + STUB_FUNC("JULIANDAY"), + REG_FUNC("LEAST", ARGC_MASK_FULL, FIELD_TYPE_SCALAR, SQL_FUNC_NEEDCOLL, + minmaxFunc, SQL_INT_TO_PTR(0), true), + REG_FUNC("LENGTH", ARGC_MASK(1), FIELD_TYPE_INTEGER, SQL_FUNC_LENGTH, + lengthFunc, NULL, true), + STUB_FUNC("LESSER"), + REG_FUNC("LIKE", ARGC_MASK2(2, 3), FIELD_TYPE_INTEGER, + SQL_FUNC_NEEDCOLL | SQL_FUNC_LIKE, + likeFunc, SQL_INT_TO_PTR(1), true), + REG_FUNC("LIKELIHOOD", ARGC_MASK(2), FIELD_TYPE_BOOLEAN, + SQL_FUNC_UNLIKELY, sql_builtin_stub, NULL, true), + REG_FUNC("LIKELY", ARGC_MASK(1), FIELD_TYPE_BOOLEAN, SQL_FUNC_UNLIKELY, + sql_builtin_stub, NULL, true), + STUB_FUNC("LN"), + REG_FUNC("LOWER", ARGC_MASK(1), FIELD_TYPE_STRING, + SQL_FUNC_DERIVEDCOLL | SQL_FUNC_NEEDCOLL, + LowerICUFunc, NULL, true), + AGG_FUNC("MAX", ARGC_MASK(1), FIELD_TYPE_SCALAR, + SQL_FUNC_NEEDCOLL | SQL_FUNC_MINMAX, + minmaxStep, minMaxFinalize, SQL_INT_TO_PTR(1)), + AGG_FUNC("MIN", ARGC_MASK(1), FIELD_TYPE_SCALAR, + SQL_FUNC_NEEDCOLL | SQL_FUNC_MINMAX, + minmaxStep, minMaxFinalize, SQL_INT_TO_PTR(0)), + STUB_FUNC("MOD"), + REG_FUNC("NULLIF", ARGC_MASK(2), FIELD_TYPE_SCALAR, SQL_FUNC_NEEDCOLL, + nullifFunc, NULL, true), + STUB_FUNC("OCTET_LENGTH"), + REG_FUNC("POSITION", ARGC_MASK(2), FIELD_TYPE_INTEGER, + SQL_FUNC_NEEDCOLL, position_func, NULL, true), + STUB_FUNC("POWER"), + REG_FUNC("PRINTF", ARGC_MASK_FULL, FIELD_TYPE_STRING, 0, + printfFunc, NULL, true), + REG_FUNC("QUOTE", ARGC_MASK(1), FIELD_TYPE_STRING, 0, + quoteFunc, NULL, true), + REG_FUNC("RANDOM", ARGC_MASK(0), FIELD_TYPE_INTEGER, 0, + randomFunc, NULL, false), + REG_FUNC("RANDOMBLOB", ARGC_MASK(1), FIELD_TYPE_VARBINARY, 0, + randomBlob, NULL, false), + REG_FUNC("REPLACE", ARGC_MASK(3), FIELD_TYPE_STRING, + SQL_FUNC_DERIVEDCOLL, replaceFunc, NULL, true), + REG_FUNC("ROUND", ARGC_MASK2(1, 2), FIELD_TYPE_INTEGER, 0, + roundFunc, NULL, true), + REG_FUNC("ROW_COUNT", ARGC_MASK(0), FIELD_TYPE_INTEGER, 0, + sql_row_count, NULL, true), + STUB_FUNC("SOME"), + REG_FUNC("SOUNDEX", ARGC_MASK(1), FIELD_TYPE_STRING, 0, + soundexFunc, NULL, true), + STUB_FUNC("SQRT"), STUB_FUNC("STRFTIME"), + REG_FUNC("SUBSTR", ARGC_MASK2(2, 3), FIELD_TYPE_STRING, + SQL_FUNC_DERIVEDCOLL, substrFunc, NULL, true), + AGG_FUNC("SUM", ARGC_MASK(1), FIELD_TYPE_NUMBER, 0, sum_step, + sumFinalize, NULL), + STUB_FUNC("TIME"), + AGG_FUNC("TOTAL", ARGC_MASK(1), FIELD_TYPE_NUMBER, 0, sum_step, + totalFinalize, NULL), + REG_FUNC("TRIM", ARGC_MASK3(1, 2, 3), FIELD_TYPE_STRING, + SQL_FUNC_DERIVEDCOLL, trim_func, NULL, true), + REG_FUNC("TYPEOF", ARGC_MASK(1), FIELD_TYPE_STRING, + SQL_FUNC_TYPEOF, typeofFunc, NULL, true), + REG_FUNC("UNICODE", ARGC_MASK(1), FIELD_TYPE_STRING, 0, + unicodeFunc, NULL, true), + REG_FUNC("UNLIKELY", ARGC_MASK(1), FIELD_TYPE_BOOLEAN, + SQL_FUNC_UNLIKELY, sql_builtin_stub, NULL, true), + REG_FUNC("UPPER", ARGC_MASK(1), FIELD_TYPE_STRING, + SQL_FUNC_DERIVEDCOLL | SQL_FUNC_NEEDCOLL, + UpperICUFunc, NULL, true), + REG_FUNC("VERSION", ARGC_MASK(0), FIELD_TYPE_STRING, 0, + sql_func_version, NULL, true), + REG_FUNC("ZEROBLOB", ARGC_MASK(1), FIELD_TYPE_VARBINARY, 0, + zeroblobFunc, NULL, true), + STUB_FUNC("_sql_stat_get"), STUB_FUNC("_sql_stat_init"), + STUB_FUNC("_sql_stat_push"), +}; + +static struct func_vtab func_sql_builtin_vtab; + +struct func * +func_sql_builtin_new(struct func_def *def) { - /* - * The following array holds FuncDef structures for all of the functions - * defined in this file. - * - * The array cannot be constant since changes are made to the - * FuncDef.pHash elements at start-time. The elements of this array - * are read-only after initialization is complete. - * - * For peak efficiency, put the most frequently used function last. - */ - static FuncDef aBuiltinFunc[] = { - FUNCTION(soundex, ARGC_MASK(1), 0, 0, soundexFunc, FIELD_TYPE_STRING), - FUNCTION2(unlikely, ARGC_MASK(1), 0, 0, noopFunc, SQL_FUNC_UNLIKELY, - FIELD_TYPE_BOOLEAN), - FUNCTION2(likelihood, ARGC_MASK(2), 0, 0, noopFunc, SQL_FUNC_UNLIKELY, - FIELD_TYPE_BOOLEAN), - FUNCTION2(likely, ARGC_MASK(1), 0, 0, noopFunc, SQL_FUNC_UNLIKELY, - FIELD_TYPE_BOOLEAN), - FUNCTION_COLL(trim, ARGC_MASK3(1, 2, 3), 3, 0, trim_func), - FUNCTION(least, ARGC_MASK_FULL, 0, 1, minmaxFunc, FIELD_TYPE_SCALAR), - AGGREGATE2(min, ARGC_MASK(1), 0, 1, minmaxStep, minMaxFinalize, - SQL_FUNC_MINMAX, FIELD_TYPE_SCALAR), - FUNCTION(greatest, ARGC_MASK_FULL, 1, 1, minmaxFunc, FIELD_TYPE_SCALAR), - AGGREGATE2(max, ARGC_MASK(1), 1, 1, minmaxStep, minMaxFinalize, - SQL_FUNC_MINMAX, FIELD_TYPE_SCALAR), - FUNCTION2(typeof, ARGC_MASK(1), 0, 0, typeofFunc, SQL_FUNC_TYPEOF, - FIELD_TYPE_STRING), - FUNCTION2(length, ARGC_MASK(1), 0, 0, lengthFunc, SQL_FUNC_LENGTH, - FIELD_TYPE_INTEGER), - FUNCTION(char_length, ARGC_MASK(1), 0, 0, lengthFunc, FIELD_TYPE_INTEGER), - FUNCTION(character_length, ARGC_MASK(1), 0, 0, lengthFunc, - FIELD_TYPE_INTEGER), - FUNCTION(position, ARGC_MASK(2), 0, 1, position_func, FIELD_TYPE_INTEGER), - FUNCTION(printf, ARGC_MASK_FULL, 0, 0, printfFunc, FIELD_TYPE_STRING), - FUNCTION(unicode, ARGC_MASK(1), 0, 0, unicodeFunc, FIELD_TYPE_STRING), - FUNCTION(char, ARGC_MASK_FULL, 0, 0, charFunc, FIELD_TYPE_STRING), - FUNCTION(abs, ARGC_MASK(1), 0, 0, absFunc, FIELD_TYPE_NUMBER), - FUNCTION(round, ARGC_MASK2(1, 2), 0, 0, roundFunc, FIELD_TYPE_INTEGER), - FUNCTION_COLL(upper, ARGC_MASK(1), 0, 1, UpperICUFunc), - FUNCTION_COLL(lower, ARGC_MASK(1), 0, 1, LowerICUFunc), - FUNCTION(hex, ARGC_MASK(1), 0, 0, hexFunc, FIELD_TYPE_STRING), - FUNCTION2(ifnull, ARGC_MASK(2), 0, 0, noopFunc, SQL_FUNC_COALESCE, - FIELD_TYPE_INTEGER), - VFUNCTION(random, ARGC_MASK(0), 0, 0, randomFunc, FIELD_TYPE_INTEGER), - VFUNCTION(randomblob, ARGC_MASK(1), 0, 0, randomBlob, FIELD_TYPE_VARBINARY), - FUNCTION(nullif, ARGC_MASK(2), 0, 1, nullifFunc, FIELD_TYPE_SCALAR), - FUNCTION(version, ARGC_MASK(0), 0, 0, sql_func_version, FIELD_TYPE_STRING), - FUNCTION(quote, ARGC_MASK(1), 0, 0, quoteFunc, FIELD_TYPE_STRING), - VFUNCTION(row_count, ARGC_MASK(0), 0, 0, sql_row_count, FIELD_TYPE_INTEGER), - FUNCTION_COLL(replace, ARGC_MASK(3), 0, 0, replaceFunc), - FUNCTION(zeroblob, ARGC_MASK(1), 0, 0, zeroblobFunc, FIELD_TYPE_VARBINARY), - FUNCTION_COLL(substr, ARGC_MASK2(2, 3), 0, 0, substrFunc), - AGGREGATE(sum, ARGC_MASK(1), 0, 0, sum_step, sumFinalize, - FIELD_TYPE_NUMBER), - AGGREGATE(total, ARGC_MASK(1), 0, 0, sum_step, totalFinalize, - FIELD_TYPE_NUMBER), - AGGREGATE(avg, ARGC_MASK(1), 0, 0, sum_step, avgFinalize, - FIELD_TYPE_NUMBER), - AGGREGATE(count, ARGC_MASK2(0, 1), 0, 0, countStep, countFinalize, - FIELD_TYPE_INTEGER), - AGGREGATE(group_concat, ARGC_MASK2(1, 2), 0, 0, groupConcatStep, - groupConcatFinalize, FIELD_TYPE_STRING), - LIKEFUNC(like, ARGC_MASK2(2, 3), 1, SQL_FUNC_LIKE, - FIELD_TYPE_INTEGER), - FUNCTION2(coalesce, ARGC_MASK_FULL & ~ARGC_MASK2(0, 1), 0, 0, noopFunc, SQL_FUNC_COALESCE, - FIELD_TYPE_SCALAR), - }; - sql_register_analyze_builtins(); - sqlRegisterDateTimeFunctions(); - sqlInsertBuiltinFuncs(aBuiltinFunc, ArraySize(aBuiltinFunc)); - -#if 0 /* Enable to print out how the built-in functions are hashed */ - { - int i; - FuncDef *p; - for (i = 0; i < SQL_FUNC_HASH_SZ; i++) { - printf("FUNC-HASH %02d:", i); - for (p = sqlBuiltinFunctions.a[i]; p; - p = p->u.pHash) { - int n = sqlStrlen30(p->zName); - int h = p->zName[0] + n; - printf(" %s(%d)", p->zName, h); - } - printf("\n"); + assert(def->language == FUNC_LANGUAGE_SQL_BUILTIN); + if (def->body != NULL || def->is_sandboxed) { + diag_set(ClientError, ER_CREATE_FUNCTION, def->name, + "body and is_sandboxed options are not compatible " + "with SQL language"); + return NULL; + } + /** Binary search for corresponding builtin entry. */ + int idx = -1, left = 0, right = nelem(sql_builtins) - 1; + while (left <= right) { + uint32_t mid = (left + right) / 2; + int rc = strcmp(def->name, sql_builtins[mid].name); + if (rc == 0) { + idx = mid; + break; } + if (rc < 0) + right = mid - 1; + else + left = mid + 1; } -#endif + if (idx == -1) { + diag_set(ClientError, ER_CREATE_FUNCTION, def->name, + "unknown sql builtin name"); + return NULL; + } + struct func_sql_builtin *func = + (struct func_sql_builtin *) malloc(sizeof(*func)); + if (func == NULL) { + diag_set(OutOfMemory, sizeof(*func), "malloc", "func"); + return NULL; + } + func->flags = sql_builtins[idx].flags; + func->user_data = sql_builtins[idx].user_data; + func->call = sql_builtins[idx].call; + func->finalize = sql_builtins[idx].finalize; + func->signature_mask = sql_builtins[idx].signature_mask; + func->base.vtab = &func_sql_builtin_vtab; + func->base.def = def; + + def->is_deterministic = sql_builtins[idx].is_deterministic; + def->returns = sql_builtins[idx].returns; + def->aggregate = sql_builtins[idx].aggregate; + def->exports.sql = true; + return &func->base; +} + +static void +func_sql_builtin_destroy(struct func *func) +{ + assert(func->vtab == &func_sql_builtin_vtab); + assert(func->def->language == FUNC_LANGUAGE_SQL_BUILTIN); + free(func); } + +static struct func_vtab func_sql_builtin_vtab = { + .call = sql_builtin_call_stub, + .destroy = func_sql_builtin_destroy, +}; diff --git a/src/box/sql/global.c b/src/box/sql/global.c index 6cadef809..c25b83de1 100644 --- a/src/box/sql/global.c +++ b/src/box/sql/global.c @@ -162,13 +162,6 @@ SQL_WSD struct sqlConfig sqlConfig = { 0x7ffffffe /* iOnceResetThreshold */ }; -/* - * Hash table for global functions - functions common to all - * database connections. After initialization, this table is - * read-only. - */ -FuncDefHash sqlBuiltinFunctions; - /* * The value of the "pending" byte must be 0x40000000 (1 byte past the * 1-gibabyte boundary) in a compatible database. sql never uses diff --git a/src/box/sql/main.c b/src/box/sql/main.c index eb6e4a7db..0b20f2132 100644 --- a/src/box/sql/main.c +++ b/src/box/sql/main.c @@ -131,9 +131,6 @@ sql_initialize(void) if (sqlGlobalConfig.isInit == 0 && sqlGlobalConfig.inProgress == 0) { sqlGlobalConfig.inProgress = 1; - memset(&sqlBuiltinFunctions, 0, - sizeof(sqlBuiltinFunctions)); - sqlRegisterBuiltinFunctions(); sql_os_init(); sqlGlobalConfig.isInit = 1; sqlGlobalConfig.inProgress = 0; @@ -242,25 +239,6 @@ sqlCloseSavepoints(Vdbe * pVdbe) pVdbe->anonymous_savepoint = NULL; } -/* - * Invoke the destructor function associated with FuncDef p, if any. Except, - * if this is not the last copy of the function, do not invoke it. Multiple - * copies of a single function are created when create_function() is called - * with SQL_ANY as the encoding. - */ -static void -functionDestroy(sql * db, FuncDef * p) -{ - FuncDestructor *pDestructor = p->u.pDestructor; - if (pDestructor) { - pDestructor->nRef--; - if (pDestructor->nRef == 0) { - pDestructor->xDestroy(pDestructor->pUserData); - sqlDbFree(db, pDestructor); - } - } -} - /* * Rollback all database files. If tripCode is not 0, then * any write cursors are invalidated ("tripped" - as in "tripping a circuit @@ -279,121 +257,6 @@ sqlRollbackAll(Vdbe * pVdbe) } } -/* - * This function is exactly the same as sql_create_function(), except - * that it is designed to be called by internal code. The difference is - * that if a malloc() fails in sql_create_function(), an error code - * is returned and the mallocFailed flag cleared. - */ -int -sqlCreateFunc(sql * db, - const char *zFunctionName, - enum field_type type, - int nArg, - int flags, - void *pUserData, - void (*xSFunc) (sql_context *, int, sql_value **), - void (*xStep) (sql_context *, int, sql_value **), - void (*xFinal) (sql_context *), - FuncDestructor * pDestructor) -{ - FuncDef *p; - int extraFlags; - - if (zFunctionName == 0 || - (xSFunc && (xFinal || xStep)) || - (!xSFunc && (xFinal && !xStep)) || - (!xSFunc && (!xFinal && xStep)) || - (nArg < -1 || nArg > SQL_MAX_FUNCTION_ARG) || - (255 < (sqlStrlen30(zFunctionName)))) { - diag_set(ClientError, ER_CREATE_FUNCTION, zFunctionName, - "wrong function definition"); - return -1; - } - - assert(SQL_FUNC_CONSTANT == SQL_DETERMINISTIC); - extraFlags = flags & SQL_DETERMINISTIC; - - - /* Check if an existing function is being overridden or deleted. If so, - * and there are active VMs, then return an error. If a function - * is being overridden/deleted but there are no active VMs, allow the - * operation to continue but invalidate all precompiled statements. - */ - p = sqlFindFunction(db, zFunctionName, nArg, 0); - if (p != NULL && (p->signature_mask & nArg) != 0) { - if (db->nVdbeActive) { - diag_set(ClientError, ER_CREATE_FUNCTION, zFunctionName, - "unable to create function due to active "\ - "statements"); - return -1; - } else { - sqlExpirePreparedStatements(db); - } - } - - p = sqlFindFunction(db, zFunctionName, nArg, 1); - assert(p || db->mallocFailed); - if (p == NULL) - return -1; - - /* If an older version of the function with a configured destructor is - * being replaced invoke the destructor function here. - */ - functionDestroy(db, p); - - if (pDestructor) { - pDestructor->nRef++; - } - p->u.pDestructor = pDestructor; - p->funcFlags = extraFlags; - testcase(p->funcFlags & SQL_DETERMINISTIC); - p->xSFunc = xSFunc ? xSFunc : xStep; - p->xFinalize = xFinal; - p->pUserData = pUserData; - p->signature_mask = ARGC_MASK(nArg); - p->ret_type = type; - return 0; -} - -int -sql_create_function_v2(sql * db, - const char *zFunc, - enum field_type type, - int nArg, - int flags, - void *p, - void (*xSFunc) (sql_context *, int, - sql_value **), - void (*xStep) (sql_context *, int, - sql_value **), - void (*xFinal) (sql_context *), - void (*xDestroy) (void *)) -{ - FuncDestructor *pArg = 0; - - if (xDestroy) { - pArg = - (FuncDestructor *) sqlDbMallocZero(db, - sizeof - (FuncDestructor)); - if (!pArg) { - xDestroy(p); - return -1; - } - pArg->xDestroy = xDestroy; - pArg->pUserData = p; - } - int rc = sqlCreateFunc(db, zFunc, type, nArg, flags, p, xSFunc, xStep, - xFinal, pArg); - if (pArg && pArg->nRef == 0) { - assert(rc != 0); - xDestroy(p); - sqlDbFree(db, pArg); - } - return rc; -} - /* * This array defines hard upper bounds on limit values. The * initializer must be kept in sync with the SQL_LIMIT_* diff --git a/src/box/sql/resolve.c b/src/box/sql/resolve.c index 0b90edd06..0c54afef6 100644 --- a/src/box/sql/resolve.c +++ b/src/box/sql/resolve.c @@ -38,6 +38,9 @@ #include "sqlInt.h" #include #include +#include "box/func.h" +#include "box/func_def.h" +#include "box/schema.h" /* * Walk the expression tree pExpr and increase the aggregate function @@ -596,27 +599,30 @@ resolveExprStep(Walker * pWalker, Expr * pExpr) int is_agg = 0; /* True if is an aggregate function */ int nId; /* Number of characters in function name */ const char *zId; /* The function name. */ - FuncDef *pDef; /* Information about the function */ assert(!ExprHasProperty(pExpr, EP_xIsSelect)); zId = pExpr->u.zToken; nId = sqlStrlen30(zId); - pDef = sqlFindFunction(pParse->db, zId, n, 0); - if (pDef == 0) { - pDef = - sqlFindFunction(pParse->db, zId, -2,0); - if (pDef == 0) { + struct func *func = sql_func_by_signature(zId, n); + if (func == NULL) { + func = func_by_name(zId, strlen(zId)); + if (func == NULL || !func->def->exports.sql) { + func = NULL; no_such_func = 1; } else { wrong_num_args = 1; } } else { - is_agg = pDef->xFinalize != 0; - pExpr->type = pDef->ret_type; + is_agg = func->def->language == + FUNC_LANGUAGE_SQL_BUILTIN && + func->def->aggregate == + FUNC_AGGREGATE_GROUP;; + pExpr->type = func->def->returns; const char *err = "second argument to likelihood() must "\ "be a constant between 0.0 and 1.0"; - if (pDef->funcFlags & SQL_FUNC_UNLIKELY) { + if (sql_func_flag_test(func, + SQL_FUNC_UNLIKELY)) { ExprSetProperty(pExpr, EP_Unlikely | EP_Skip); if (n == 2) { @@ -643,21 +649,19 @@ resolveExprStep(Walker * pWalker, Expr * pExpr) */ /* TUNING: unlikely() probability is 0.0625. likely() is 0.9375 */ pExpr->iTable = - pDef->zName[0] == - 'u' ? 8388608 : 125829120; + func->def->name[0] == 'u' ? + 8388608 : 125829120; } } - if (pDef-> - funcFlags & (SQL_FUNC_CONSTANT | - SQL_FUNC_SLOCHNG)) { + if (func->def->is_deterministic || + sql_func_flag_test(func, SQL_FUNC_SLOCHNG)) { /* For the purposes of the EP_ConstFunc flag, date and time * functions and other functions that change slowly are considered * constant because they are constant for the duration of one query */ ExprSetProperty(pExpr, EP_ConstFunc); } - if ((pDef->funcFlags & SQL_FUNC_CONSTANT) == - 0) { + if (!func->def->is_deterministic) { /* Date/time functions that use 'now', and other functions * that might change over time cannot be used * in an index. @@ -700,18 +704,14 @@ resolveExprStep(Walker * pWalker, Expr * pExpr) pExpr->op2++; pNC2 = pNC2->pNext; } - assert(pDef != 0); + assert(func != NULL); if (pNC2) { + pNC2->ncFlags |= NC_HasAgg; assert(SQL_FUNC_MINMAX == NC_MinMaxAgg); - testcase((pDef-> - funcFlags & - SQL_FUNC_MINMAX) != 0); - pNC2->ncFlags |= - NC_HasAgg | (pDef-> - funcFlags & - SQL_FUNC_MINMAX); - + if (sql_func_flag_test(func, + SQL_FUNC_MINMAX)) + pNC2->ncFlags |= NC_MinMaxAgg; } pNC->ncFlags |= NC_AllowAgg; } diff --git a/src/box/sql/select.c b/src/box/sql/select.c index 921a52150..bce5cef3b 100644 --- a/src/box/sql/select.c +++ b/src/box/sql/select.c @@ -5277,7 +5277,7 @@ finalizeAggFunctions(Parse * pParse, AggInfo * pAggInfo) assert(!ExprHasProperty(pF->pExpr, EP_xIsSelect)); sqlVdbeAddOp2(v, OP_AggFinal, pF->iMem, pList ? pList->nExpr : 0); - sqlVdbeAppendP4(v, pF->pFunc, P4_FUNCDEF); + sqlVdbeAppendP4(v, pF->func, P4_FUNC); } } @@ -5318,7 +5318,7 @@ updateAccumulator(Parse * pParse, AggInfo * pAggInfo) vdbe_insert_distinct(pParse, pF->iDistinct, pF->reg_eph, addrNext, 1, regAgg); } - if (pF->pFunc->funcFlags & SQL_FUNC_NEEDCOLL) { + if (sql_func_flag_test(pF->func, SQL_FUNC_NEEDCOLL)) { struct coll *coll = NULL; struct ExprList_item *pItem; int j; @@ -5337,7 +5337,7 @@ updateAccumulator(Parse * pParse, AggInfo * pAggInfo) (char *)coll, P4_COLLSEQ); } sqlVdbeAddOp3(v, OP_AggStep0, 0, regAgg, pF->iMem); - sqlVdbeAppendP4(v, pF->pFunc, P4_FUNCDEF); + sqlVdbeAppendP4(v, pF->func, P4_FUNC); sqlVdbeChangeP5(v, (u8) nArg); sql_expr_type_cache_change(pParse, regAgg, nArg); sqlReleaseTempRange(pParse, regAgg, nArg); diff --git a/src/box/sql/vdbe.c b/src/box/sql/vdbe.c index 28552f64a..a66becc89 100644 --- a/src/box/sql/vdbe.c +++ b/src/box/sql/vdbe.c @@ -41,6 +41,8 @@ */ #include "box/box.h" #include "box/error.h" +#include "box/func.h" +#include "box/func_def.h" #include "box/fk_constraint.h" #include "box/txn.h" #include "box/tuple.h" @@ -1704,7 +1706,7 @@ case OP_BuiltinFunction0: { int n; sql_context *pCtx; - assert(pOp->p4type==P4_FUNCDEF); + assert(pOp->p4type == P4_FUNC); n = pOp->p5; assert(pOp->p3>0 && pOp->p3<=(p->nMem+1 - p->nCursor)); assert(n==0 || (pOp->p2>0 && pOp->p2+n<=(p->nMem+1 - p->nCursor)+1)); @@ -1712,7 +1714,7 @@ case OP_BuiltinFunction0: { pCtx = sqlDbMallocRawNN(db, sizeof(*pCtx) + (n-1)*sizeof(sql_value*)); if (pCtx==0) goto no_mem; pCtx->pOut = 0; - pCtx->pFunc = pOp->p4.pFunc; + pCtx->func = pOp->p4.func; pCtx->iOp = (int)(pOp - aOp); pCtx->pVdbe = p; pCtx->argc = n; @@ -1747,7 +1749,9 @@ case OP_BuiltinFunction: { } #endif pCtx->is_aborted = false; - (*pCtx->pFunc->xSFunc)(pCtx, pCtx->argc, pCtx->argv);/* IMP: R-24505-23230 */ + assert(pCtx->func->def->language == FUNC_LANGUAGE_SQL_BUILTIN); + (*((struct func_sql_builtin *)pCtx->func)->call)(pCtx, pCtx->argc, + pCtx->argv); /* If the function returned an error, throw an exception */ if (pCtx->is_aborted) @@ -5005,7 +5009,7 @@ case OP_AggStep0: { int n; sql_context *pCtx; - assert(pOp->p4type==P4_FUNCDEF); + assert(pOp->p4type == P4_FUNC); n = pOp->p5; assert(pOp->p3>0 && pOp->p3<=(p->nMem+1 - p->nCursor)); assert(n==0 || (pOp->p2>0 && pOp->p2+n<=(p->nMem+1 - p->nCursor)+1)); @@ -5013,7 +5017,7 @@ case OP_AggStep0: { pCtx = sqlDbMallocRawNN(db, sizeof(*pCtx) + (n-1)*sizeof(sql_value*)); if (pCtx==0) goto no_mem; pCtx->pMem = 0; - pCtx->pFunc = pOp->p4.pFunc; + pCtx->func = pOp->p4.func; pCtx->iOp = (int)(pOp - aOp); pCtx->pVdbe = p; pCtx->argc = n; @@ -5055,7 +5059,9 @@ case OP_AggStep: { pCtx->pOut = &t; pCtx->is_aborted = false; pCtx->skipFlag = 0; - (pCtx->pFunc->xSFunc)(pCtx,pCtx->argc,pCtx->argv); /* IMP: R-24505-23230 */ + assert(pCtx->func->def->language == FUNC_LANGUAGE_SQL_BUILTIN); + (*((struct func_sql_builtin *)pCtx->func)->call)(pCtx, pCtx->argc, + pCtx->argv); if (pCtx->is_aborted) { sqlVdbeMemRelease(&t); goto abort_due_to_error; @@ -5087,7 +5093,7 @@ case OP_AggFinal: { assert(pOp->p1>0 && pOp->p1<=(p->nMem+1 - p->nCursor)); pMem = &aMem[pOp->p1]; assert((pMem->flags & ~(MEM_Null|MEM_Agg))==0); - if (sqlVdbeMemFinalize(pMem, pOp->p4.pFunc) != 0) + if (sql_vdbemem_finalize(pMem, pOp->p4.func) != 0) goto abort_due_to_error; UPDATE_MAX_BLOBSIZE(pMem); if (sqlVdbeMemTooBig(pMem)) { diff --git a/src/box/sql/vdbeapi.c b/src/box/sql/vdbeapi.c index 1aee3cf85..4ff8db621 100644 --- a/src/box/sql/vdbeapi.c +++ b/src/box/sql/vdbeapi.c @@ -484,8 +484,9 @@ sql_step(sql_stmt * pStmt) void * sql_user_data(sql_context * p) { - assert(p && p->pFunc); - return p->pFunc->pUserData; + assert(p != NULL && p->func != NULL); + assert(p->func->def->language == FUNC_LANGUAGE_SQL_BUILTIN); + return ((struct func_sql_builtin *)p->func)->user_data; } /* @@ -547,7 +548,7 @@ createAggContext(sql_context * p, int nByte) } else { sqlVdbeMemClearAndResize(pMem, nByte); pMem->flags = MEM_Agg; - pMem->u.pDef = p->pFunc; + pMem->u.func = p->func; if (pMem->z) { memset(pMem->z, 0, nByte); } @@ -563,7 +564,9 @@ createAggContext(sql_context * p, int nByte) void * sql_aggregate_context(sql_context * p, int nByte) { - assert(p && p->pFunc && p->pFunc->xFinalize); + assert(p != NULL && p->func != NULL && + p->func->def->language == FUNC_LANGUAGE_SQL_BUILTIN && + p->func->def->aggregate == FUNC_AGGREGATE_GROUP); testcase(nByte < 0); if ((p->pMem->flags & MEM_Agg) == 0) { return createAggContext(p, nByte); diff --git a/src/box/sql/vdbeaux.c b/src/box/sql/vdbeaux.c index d32404580..a44540b1d 100644 --- a/src/box/sql/vdbeaux.c +++ b/src/box/sql/vdbeaux.c @@ -648,24 +648,11 @@ sqlVdbeJumpHere(Vdbe * p, int addr) sqlVdbeChangeP2(p, addr, p->nOp); } -/* - * If the input FuncDef structure is ephemeral, then free it. If - * the FuncDef is not ephermal, then do nothing. - */ -static void -freeEphemeralFunction(sql * db, FuncDef * pDef) -{ - if ((pDef->funcFlags & SQL_FUNC_EPHEM) != 0) { - sqlDbFree(db, pDef); - } -} - static void vdbeFreeOpArray(sql *, Op *, int); static SQL_NOINLINE void freeP4FuncCtx(sql * db, sql_context * p) { - freeEphemeralFunction(db, p->pFunc); sqlDbFree(db, p); } @@ -689,13 +676,11 @@ freeP4(sql * db, int p4type, void *p4) case P4_KEYINFO: sql_key_info_unref(p4); break; - case P4_FUNCDEF:{ - freeEphemeralFunction(db, (FuncDef *) p4); - break; - } case P4_MEM: sqlValueFree((sql_value *) p4); break; + default: + break; } } @@ -1149,17 +1134,17 @@ displayP4(Op * pOp, char *zTemp, int nTemp) sqlXPrintf(&x, "(binary)"); break; } - case P4_FUNCDEF:{ - FuncDef *pDef = pOp->p4.pFunc; - sqlXPrintf(&x, "%s(%d)", pDef->zName, - pDef->signature_mask); + case P4_FUNC:{ + struct func *func = pOp->p4.func; + sqlXPrintf(&x, "%s(%d)", func->def->name, + func->def->param_count); break; } #if defined(SQL_DEBUG) || defined(VDBE_PROFILE) case P4_FUNCCTX:{ - FuncDef *pDef = pOp->p4.pCtx->pFunc; - sqlXPrintf(&x, "%s(%d)", pDef->zName, - pDef->signature_mask); + struct func *func = pOp->p4.func; + sqlXPrintf(&x, "%s(%d)", func->def->name, + func->def->param_count); break; } #endif diff --git a/src/box/sql/vdbemem.c b/src/box/sql/vdbemem.c index b8c31ecec..8b2e816f2 100644 --- a/src/box/sql/vdbemem.c +++ b/src/box/sql/vdbemem.c @@ -312,33 +312,28 @@ sqlVdbeMemStringify(Mem * pMem) return 0; } -/* - * Memory cell pMem contains the context of an aggregate function. - * This routine calls the finalize method for that function. The - * result of the aggregate is stored back into pMem. - * - * Return -1 if the finalizer reports an error. 0 otherwise. - */ int -sqlVdbeMemFinalize(Mem * pMem, FuncDef * pFunc) +sql_vdbemem_finalize(struct Mem *mem, struct func *func) { - if (ALWAYS(pFunc && pFunc->xFinalize)) { + if (ALWAYS(func != NULL && + func->def->language == FUNC_LANGUAGE_SQL_BUILTIN && + func->def->aggregate == FUNC_AGGREGATE_GROUP)) { sql_context ctx; Mem t; - assert((pMem->flags & MEM_Null) != 0 || pFunc == pMem->u.pDef); + assert((mem->flags & MEM_Null) != 0 || func == mem->u.func); memset(&ctx, 0, sizeof(ctx)); memset(&t, 0, sizeof(t)); t.flags = MEM_Null; - t.db = pMem->db; + t.db = mem->db; t.field_type = field_type_MAX; ctx.pOut = &t; - ctx.pMem = pMem; - ctx.pFunc = pFunc; - pFunc->xFinalize(&ctx); /* IMP: R-24505-23230 */ - assert((pMem->flags & MEM_Dyn) == 0); - if (pMem->szMalloc > 0) - sqlDbFree(pMem->db, pMem->zMalloc); - memcpy(pMem, &t, sizeof(t)); + ctx.pMem = mem; + ctx.func = func; + ((struct func_sql_builtin *)func)->finalize(&ctx); + assert((mem->flags & MEM_Dyn) == 0); + if (mem->szMalloc > 0) + sqlDbFree(mem->db, mem->zMalloc); + memcpy(mem, &t, sizeof(t)); if (ctx.is_aborted) return -1; } @@ -359,7 +354,7 @@ vdbeMemClearExternAndSetNull(Mem * p) { assert(VdbeMemDynamic(p)); if (p->flags & MEM_Agg) { - sqlVdbeMemFinalize(p, p->u.pDef); + sql_vdbemem_finalize(p, p->u.func); assert((p->flags & MEM_Agg) == 0); testcase(p->flags & MEM_Dyn); } @@ -1289,7 +1284,6 @@ valueFromFunction(sql * db, /* The database connection */ sql_context ctx; /* Context object for function invocation */ sql_value **apVal = 0; /* Function arguments */ int nVal = 0; /* Size of apVal[] array */ - FuncDef *pFunc = 0; /* Function definition */ sql_value *pVal = 0; /* New value */ int rc = 0; /* Return code */ ExprList *pList = 0; /* Function arguments */ @@ -1300,13 +1294,13 @@ valueFromFunction(sql * db, /* The database connection */ pList = p->x.pList; if (pList) nVal = pList->nExpr; - pFunc = sqlFindFunction(db, p->u.zToken, nVal, 0); - assert(pFunc); - if ((pFunc->funcFlags & (SQL_FUNC_CONSTANT | SQL_FUNC_SLOCHNG)) == - 0 || (pFunc->funcFlags & SQL_FUNC_NEEDCOLL) - ) { + struct func *func = sql_func_by_signature(p->u.zToken, nVal); + assert(func != NULL); + if (func->def->language != FUNC_LANGUAGE_SQL_BUILTIN || + (!func->def->is_deterministic && + !sql_func_flag_test(func, SQL_FUNC_SLOCHNG)) || + sql_func_flag_test(func, SQL_FUNC_NEEDCOLL)) return 0; - } if (pList) { apVal = @@ -1334,8 +1328,8 @@ valueFromFunction(sql * db, /* The database connection */ assert(!pCtx->pParse->is_aborted); memset(&ctx, 0, sizeof(ctx)); ctx.pOut = pVal; - ctx.pFunc = pFunc; - pFunc->xSFunc(&ctx, nVal, apVal); + ctx.func = func; + ((struct func_sql_builtin *)func)->call(&ctx, nVal, apVal); assert(!ctx.is_aborted); sql_value_apply_type(pVal, type); assert(rc == 0); diff --git a/src/box/sql/whereexpr.c b/src/box/sql/whereexpr.c index 8adf6a5f1..98615b118 100644 --- a/src/box/sql/whereexpr.c +++ b/src/box/sql/whereexpr.c @@ -273,7 +273,7 @@ like_optimization_is_valid(Parse *pParse, Expr *pExpr, Expr **ppPrefix, /* Result code to return. */ int rc; - if (!sql_is_like_func(db, pExpr)) { + if (!sql_is_like_func(pExpr)) { return 0; } pList = pExpr->x.pList; diff --git a/src/box/CMakeLists.txt b/src/box/CMakeLists.txt index 9bba37bcb..9d2fcea4b 100644 --- a/src/box/CMakeLists.txt +++ b/src/box/CMakeLists.txt @@ -130,7 +130,6 @@ add_library(box STATIC ${lua_sources} lua/init.c lua/call.c - lua/lua_sql.c lua/cfg.cc lua/console.c lua/tuple.c diff --git a/test/sql-tap/where2.test.lua b/test/sql-tap/where2.test.lua index 4116ca913..f267be8e6 100755 --- a/test/sql-tap/where2.test.lua +++ b/test/sql-tap/where2.test.lua @@ -231,7 +231,7 @@ test:do_execsql_test( EXPLAIN SELECT * FROM x1, x2 WHERE x=1 ORDER BY random(); ]], { -- - "/random/" + "/RANDOM/" -- }) @@ -254,7 +254,7 @@ test:do_execsql_test( EXPLAIN SELECT * FROM x1, x2 WHERE x=1 ORDER BY abs(5); ]], { -- - "~/abs/" + "~/ABS/" -- }) -- 2.22.0