[tarantool-patches] [PATCH v4 4/5] sql: get rid of FuncDef function hash
Kirill Shcherbatov
kshcherbatov at tarantool.org
Fri Aug 23 18:02:10 MSK 2019
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, while
port API call is not supported and protected with stubs.
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 are going to be fixed in the next 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
@TarantoolBot document
Title: SQL builtins priveleges
All SQL built-ins are executed on SQL privilege level that is
undefined yet.
---
src/box/lua/lua_sql.h | 39 --
src/box/sql/sqlInt.h | 220 +++------
src/box/sql/vdbe.h | 9 +-
src/box/sql/vdbeInt.h | 23 +-
src/box/func.c | 33 +-
src/box/lua/call.c | 2 -
src/box/lua/lua_sql.c | 205 ---------
src/box/sql/analyze.c | 34 +-
src/box/sql/callback.c | 204 ---------
src/box/sql/date.c | 28 --
src/box/sql/expr.c | 75 ++--
src/box/sql/func.c | 874 +++++++++++++++++++++++++++++++-----
src/box/sql/global.c | 7 -
src/box/sql/main.c | 137 ------
src/box/sql/resolve.c | 132 +++---
src/box/sql/select.c | 10 +-
src/box/sql/vdbe.c | 18 +-
src/box/sql/vdbeapi.c | 17 +-
src/box/sql/vdbeaux.c | 31 +-
src/box/sql/vdbemem.c | 65 ++-
src/box/sql/whereexpr.c | 2 +-
src/box/CMakeLists.txt | 1 -
src/box/alter.cc | 6 +
test/box/function1.result | 20 +
test/box/function1.test.lua | 7 +
25 files changed, 1057 insertions(+), 1142 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 <COPYRIGHT HOLDER> ``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
- * <COPYRIGHT HOLDER> 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/sqlInt.h b/src/box/sql/sqlInt.h
index a3a255a47..e617edd79 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"
@@ -541,9 +543,6 @@ void
sql_row_count(struct sql_context *context, MAYBE_UNUSED int unused1,
MAYBE_UNUSED sql_value **unused2);
-void *
-sql_user_data(sql_context *);
-
void *
sql_aggregate_context(sql_context *,
int nBytes);
@@ -566,26 +565,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 +1018,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;
@@ -1122,18 +1098,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.
*/
@@ -1243,65 +1207,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 {
- i8 nArg; /* Number of arguments. -1 means unlimited */
- 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()):
* 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
@@ -1315,7 +1226,6 @@ struct FuncDestructor {
#define SQL_FUNC_COUNT 0x0100
#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 */
/** Built-in min() or least() function. */
#define SQL_FUNC_MIN 0x1000
/** Built-in max() or greatest() function. */
@@ -1339,61 +1249,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, nArg, iArg, bNC, xFunc)
- * Used to create a scalar function definition of a function zName
- * implemented by C function xFunc that accepts nArg 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, nArg, iArg, bNC, xFunc)
- * Like FUNCTION except it omits the sql_FUNC_CONSTANT flag.
- *
- * AGGREGATE(zName, nArg, 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, nArg, pArg, flags)
- * Used to create a scalar function definition of a function zName
- * that accepts nArg 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, nArg, iArg, bNC, xFunc, type) \
- {nArg, SQL_FUNC_CONSTANT|(bNC*SQL_FUNC_NEEDCOLL), \
- SQL_INT_TO_PTR(iArg), 0, xFunc, 0, #zName, {0}, type}
-#define FUNCTION_COLL(zName, nArg, iArg, bNC, xFunc) \
- {nArg, 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, nArg, iArg, bNC, xFunc, type) \
- {nArg, (bNC*SQL_FUNC_NEEDCOLL), \
- SQL_INT_TO_PTR(iArg), 0, xFunc, 0, #zName, {0}, type}
-#define FUNCTION2(zName, nArg, iArg, bNC, xFunc, extraFlags, type) \
- {nArg,SQL_FUNC_CONSTANT|(bNC*SQL_FUNC_NEEDCOLL)|extraFlags,\
- SQL_INT_TO_PTR(iArg), 0, xFunc, 0, #zName, {0}, type}
-#define LIKEFUNC(zName, nArg, arg, flags, type) \
- {nArg, SQL_FUNC_NEEDCOLL|SQL_FUNC_CONSTANT|flags, \
- (void *)(SQL_INT_TO_PTR(arg)), 0, likeFunc, 0, #zName, {0}, type}
-#define AGGREGATE(zName, nArg, arg, nc, xStep, xFinal, type) \
- {nArg, (nc*SQL_FUNC_NEEDCOLL), \
- SQL_INT_TO_PTR(arg), 0, xStep,xFinal,#zName, {0}, type}
-#define AGGREGATE2(zName, nArg, arg, nc, xStep, xFinal, extraFlags, type) \
- {nArg, (nc*SQL_FUNC_NEEDCOLL)|extraFlags, \
- SQL_INT_TO_PTR(arg), 0, xStep,xFinal,#zName, {0}, type}
-
/*
* The following are used as the second parameter to sqlSavepoint(),
* and as the P1 argument to the OP_Savepoint instruction.
@@ -1574,7 +1429,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 */
/**
@@ -3522,10 +3378,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.
@@ -4098,7 +3950,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;
/**
@@ -4234,21 +4085,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
@@ -4285,7 +4128,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,
@@ -4476,9 +4318,55 @@ 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 of SQL flags. */
+ uint16_t flags;
+ /**
+ * A VDBE-memory-compatible call method.
+ * SQL built-ins don't use func base class "call"
+ * method to provide a best performance for SQL requests.
+ * Access checks are redundant, because all SQL built-ins
+ * are predefined and are executed on SQL privilege level.
+ */
+ void (*call)(sql_context *ctx, int argc, sql_value **argv);
+ /**
+ * A VDBE-memory-compatible finalize method
+ * (is valid only for aggregate function).
+ */
+ void (*finalize)(sql_context *ctx);
+};
+
+/**
+ * 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_is_set(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;
+}
+
+/**
+ * A SQL method to find a function in a hash by its 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 is found and NULL otherwise.
+ */
+struct func *
+sql_func_by_signature(const char *name, int argc);
/**
* 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 b97aa6ada..29ff99867 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 3a416aea5..104a05613 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. */
@@ -512,7 +519,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 mem 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 mem.
+ *
+ * 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..e8cc99081 100644
--- a/src/box/func.c
+++ b/src/box/func.c
@@ -381,38 +381,9 @@ 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,
-};
+extern struct func *
+func_sql_builtin_new(struct func_def *def);
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 <COPYRIGHT HOLDER> ``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
- * <COPYRIGHT HOLDER> 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/analyze.c b/src/box/sql/analyze.c
index bd52d12df..b9858c8d6 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,10 @@ 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 +854,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 +955,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 +1745,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 6c272de71..290363db6 100644
--- a/src/box/sql/callback.c
+++ b/src/box/sql/callback.c
@@ -56,207 +56,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 */
- )
-{
- int match;
-
- /* nArg of -2 is a special case */
- if (nArg == (-2))
- return (p->xSFunc == 0) ? 0 : FUNC_PERFECT_MATCH;
-
- /* Wrong number of arguments means "no match" */
- if (p->nArg != nArg && p->nArg >= 0)
- return 0;
-
- /* Give a better score to a function with a specific number of arguments
- * than to function that accepts any number of arguments.
- */
- if (p->nArg == nArg) {
- match = 4;
- } else {
- match = 1;
- }
-
- return match;
-}
-
-/*
- * 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->nArg = (u16) 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 1837817d6..f4b3c1e8c 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_is_set(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,8 @@ 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) {
diag_set(ClientError, ER_NO_SUCH_FUNCTION,
zId);
pParse->is_aborted = true;
@@ -4002,13 +3999,13 @@ 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_is_set(func, SQL_FUNC_COALESCE)) {
int endCoalesce = sqlVdbeMakeLabel(v);
if (nFarg < 2) {
diag_set(ClientError,
ER_FUNC_WRONG_ARG_COUNT,
- pDef->zName, "at least two",
- nFarg);
+ func->def->name,
+ "at least two", nFarg);
pParse->is_aborted = true;
break;
}
@@ -4033,12 +4030,12 @@ 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_is_set(func, SQL_FUNC_UNLIKELY)) {
if (nFarg < 1) {
diag_set(ClientError,
- ER_FUNC_WRONG_ARG_COUNT,
- pDef->zName, "at least one",
- nFarg);
+ ER_FUNC_WRONG_ARG_COUNT,
+ func->def->name,
+ "at least one", nFarg);
pParse->is_aborted = true;
break;
}
@@ -4063,7 +4060,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_is_set(func, SQL_FUNC_NEEDCOLL)) {
struct coll *unused = NULL;
uint32_t curr_id = COLL_NONE;
bool is_curr_forced = false;
@@ -4110,9 +4107,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_is_set(func, SQL_FUNC_LENGTH |
+ SQL_FUNC_TYPEOF)) {
u8 exprOp;
assert(nFarg == 1);
assert(pFarg->a[0].pExpr != 0);
@@ -4123,14 +4119,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;
}
}
@@ -4142,12 +4131,15 @@ sqlExprCodeTarget(Parse * pParse, Expr * pExpr, int target)
} else {
r1 = 0;
}
- if (pDef->funcFlags & SQL_FUNC_NEEDCOLL) {
+ if (sql_func_flag_is_set(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);
@@ -5455,12 +5447,21 @@ 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);
+ const char *name =
+ pExpr->u.zToken;
+ uint32_t argc =
+ pExpr->x.pList != NULL ?
+ pExpr->x.pList->nExpr : 0;
+ pItem->func =
+ sql_func_by_signature(
+ name, argc);
+ assert(pItem->func != NULL);
+ 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 9ce645698..363768764 100644
--- a/src/box/sql/func.c
+++ b/src/box/sql/func.c
@@ -45,6 +45,8 @@
#include <unicode/uchar.h>
#include <unicode/ucol.h>
#include "box/coll_id_cache.h"
+#include "box/schema.h"
+#include "box/func.h"
/*
* Return the collating function associated with a function.
@@ -79,10 +81,11 @@ minmaxFunc(sql_context * context, int argc, sql_value ** argv)
int i;
int iBest;
struct coll *pColl;
- struct FuncDef *func = context->pFunc;
+ struct func_sql_builtin *func =
+ (struct func_sql_builtin *)context->func;
assert(argc > 1);
- int mask = (func->funcFlags & SQL_FUNC_MAX) != 0 ? -1 : 0;
+ int mask = (func->flags & SQL_FUNC_MAX) != 0 ? -1 : 0;
pColl = sqlGetFuncCollSeq(context);
assert(mask == -1 || mask == 0);
iBest = 0;
@@ -1737,6 +1740,8 @@ minmaxStep(sql_context * context, int NotUsed, sql_value ** argv)
Mem *pBest;
UNUSED_PARAMETER(NotUsed);
+ struct func_sql_builtin *func =
+ (struct func_sql_builtin *)context->func;
pBest = (Mem *) sql_aggregate_context(context, sizeof(*pBest));
if (!pBest)
return;
@@ -1753,7 +1758,7 @@ minmaxStep(sql_context * context, int NotUsed, sql_value ** argv)
* between the two being that the sense of the
* comparison is inverted.
*/
- bool is_max = (context->pFunc->funcFlags & SQL_FUNC_MAX) != 0;
+ bool is_max = (func->flags & SQL_FUNC_MAX) != 0;
cmp = sqlMemCompare(pBest, pArg, pColl);
if ((is_max && cmp < 0) || (!is_max && cmp > 0)) {
sqlVdbeMemCopy(pBest, pArg);
@@ -1843,116 +1848,783 @@ 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);
- assert(func != NULL);
- if ((func->funcFlags & SQL_FUNC_LIKE) == 0)
+ struct func *func = sql_func_by_signature(expr->u.zToken, 2);
+ if (func == NULL || !sql_func_flag_is_set(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
+struct func *
+sql_func_by_signature(const char *name, int argc)
+{
+ struct func *base = func_by_name(name, strlen(name));
+ if (base == NULL || !base->def->exports.sql)
+ return NULL;
+
+ if (base->def->param_count != -1 && base->def->param_count != argc)
+ return NULL;
+ return base;
+}
+
+static int
+func_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;
+}
+
+/**
+ * A sequence of SQL builtins definitions in
+ * lexicographic order.
*/
-void
-sqlRegisterBuiltinFunctions(void)
-{
- /*
- * 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 struct {
+ /**
+ * Name is used to find corresponding entry in array
+ * sql_builtins applying binary search.
*/
- static FuncDef aBuiltinFunc[] = {
- FUNCTION(SOUNDEX, 1, 0, 0, soundexFunc, FIELD_TYPE_STRING),
- FUNCTION2(UNLIKELY, 1, 0, 0, noopFunc, SQL_FUNC_UNLIKELY,
- FIELD_TYPE_BOOLEAN),
- FUNCTION2(LIKELIHOOD, 2, 0, 0, noopFunc, SQL_FUNC_UNLIKELY,
- FIELD_TYPE_BOOLEAN),
- FUNCTION2(LIKELY, 1, 0, 0, noopFunc, SQL_FUNC_UNLIKELY,
- FIELD_TYPE_BOOLEAN),
- FUNCTION_COLL(TRIM, -1, 3, 0, trim_func),
- FUNCTION2(LEAST, -1, 0, 1, minmaxFunc, SQL_FUNC_MIN,
- FIELD_TYPE_SCALAR),
- AGGREGATE2(MIN, 1, 0, 1, minmaxStep, minMaxFinalize,
- SQL_FUNC_MIN, FIELD_TYPE_SCALAR),
- FUNCTION2(greatest, -1, 1, 1, minmaxFunc, SQL_FUNC_MAX,
- FIELD_TYPE_SCALAR),
- AGGREGATE2(MAX, 1, 1, 1, minmaxStep, minMaxFinalize,
- SQL_FUNC_MAX, FIELD_TYPE_SCALAR),
- FUNCTION2(TYPEOF, 1, 0, 0, typeofFunc, SQL_FUNC_TYPEOF,
- FIELD_TYPE_STRING),
- FUNCTION2(LENGTH, 1, 0, 0, lengthFunc, SQL_FUNC_LENGTH,
- FIELD_TYPE_INTEGER),
- FUNCTION(CHAR_LENGTH, 1, 0, 0, lengthFunc, FIELD_TYPE_INTEGER),
- FUNCTION(CHARACTER_LENGTH, 1, 0, 0, lengthFunc,
- FIELD_TYPE_INTEGER),
- FUNCTION(POSITION, 2, 0, 1, position_func, FIELD_TYPE_INTEGER),
- FUNCTION(PRINTF, -1, 0, 0, printfFunc, FIELD_TYPE_STRING),
- FUNCTION(UNICODE, 1, 0, 0, unicodeFunc, FIELD_TYPE_STRING),
- FUNCTION(CHAR, -1, 0, 0, charFunc, FIELD_TYPE_STRING),
- FUNCTION(ABS, 1, 0, 0, absFunc, FIELD_TYPE_NUMBER),
- FUNCTION(ROUND, -1, 0, 0, roundFunc, FIELD_TYPE_INTEGER),
- FUNCTION_COLL(UPPER, 1, 0, 1, UpperICUFunc),
- FUNCTION_COLL(LOWER, 1, 0, 1, LowerICUFunc),
- FUNCTION(HEX, 1, 0, 0, hexFunc, FIELD_TYPE_STRING),
- FUNCTION2(IFNULL, 2, 0, 0, noopFunc, SQL_FUNC_COALESCE,
- FIELD_TYPE_INTEGER),
- VFUNCTION(RANDOM, 0, 0, 0, randomFunc, FIELD_TYPE_INTEGER),
- VFUNCTION(RANDOMBLOB, 1, 0, 0, randomBlob, FIELD_TYPE_VARBINARY),
- FUNCTION(NULLIF, 2, 0, 1, nullifFunc, FIELD_TYPE_SCALAR),
- FUNCTION(VERSION, 0, 0, 0, sql_func_version, FIELD_TYPE_STRING),
- FUNCTION(QUOTE, 1, 0, 0, quoteFunc, FIELD_TYPE_STRING),
- VFUNCTION(ROW_COUNT, 0, 0, 0, sql_row_count, FIELD_TYPE_INTEGER),
- FUNCTION_COLL(REPLACE, 3, 0, 0, replaceFunc),
- FUNCTION(ZEROBLOB, 1, 0, 0, zeroblobFunc, FIELD_TYPE_VARBINARY),
- FUNCTION_COLL(SUBSTR, -1, 0, 0, substrFunc),
- AGGREGATE(SUM, 1, 0, 0, sum_step, sumFinalize,
- FIELD_TYPE_NUMBER),
- AGGREGATE(TOTAL, 1, 0, 0, sum_step, totalFinalize,
- FIELD_TYPE_NUMBER),
- AGGREGATE(AVG, 1, 0, 0, sum_step, avgFinalize,
- FIELD_TYPE_NUMBER),
- AGGREGATE2(COUNT, -1, 0, 0, countStep, countFinalize,
- SQL_FUNC_COUNT, FIELD_TYPE_INTEGER),
- AGGREGATE(GROUP_CONCAT, -1, 0, 0, groupConcatStep,
- groupConcatFinalize, FIELD_TYPE_STRING),
- LIKEFUNC(LIKE, -1, 1, SQL_FUNC_LIKE,
- FIELD_TYPE_INTEGER),
- FUNCTION2(COALESCE, -1, 0, 0, noopFunc, SQL_FUNC_COALESCE,
- FIELD_TYPE_SCALAR),
- };
- sql_register_analyze_builtins();
- sqlRegisterDateTimeFunctions();
- sqlInsertBuiltinFuncs(aBuiltinFunc, ArraySize(aBuiltinFunc));
+ const char *name;
+ /** Members below are related to struct func_sql_builtin. */
+ uint16_t flags;
+ void (*call)(sql_context *ctx, int argc, sql_value **argv);
+ void (*finalize)(sql_context *ctx);
+ /** Members below are related to struct func_def. */
+ bool is_deterministic;
+ int param_count;
+ enum field_type returns;
+ enum func_aggregate aggregate;
+ bool export_to_sql;
+} sql_builtins[] = {
+ {.name = "ABS",
+ .param_count = 1,
+ .returns = FIELD_TYPE_NUMBER,
+ .aggregate = FUNC_AGGREGATE_NONE,
+ .is_deterministic = true,
+ .flags = 0,
+ .call = absFunc,
+ .finalize = NULL,
+ .export_to_sql = true,
+ }, {
+ .name = "AVG",
+ .param_count = 1,
+ .returns = FIELD_TYPE_NUMBER,
+ .is_deterministic = false,
+ .aggregate = FUNC_AGGREGATE_GROUP,
+ .flags = 0,
+ .call = sum_step,
+ .finalize = avgFinalize,
+ .export_to_sql = true,
+ }, {
+ .name = "CEIL",
+ .call = sql_builtin_stub,
+ .export_to_sql = false,
+ .param_count = -1,
+ .returns = FIELD_TYPE_ANY,
+ .aggregate = FUNC_AGGREGATE_NONE,
+ .is_deterministic = false,
+ .flags = 0,
+ .finalize = NULL,
+ }, {
+ .name = "CEILING",
+ .call = sql_builtin_stub,
+ .export_to_sql = false,
+ .param_count = -1,
+ .returns = FIELD_TYPE_ANY,
+ .aggregate = FUNC_AGGREGATE_NONE,
+ .is_deterministic = false,
+ .flags = 0,
+ .finalize = NULL,
+ }, {
+ .name = "CHAR",
+ .param_count = -1,
+ .returns = FIELD_TYPE_STRING,
+ .is_deterministic = true,
+ .aggregate = FUNC_AGGREGATE_NONE,
+ .flags = 0,
+ .call = charFunc,
+ .finalize = NULL,
+ .export_to_sql = true,
+ }, {
+ .name = "CHARACTER_LENGTH",
+ .param_count = 1,
+ .returns = FIELD_TYPE_INTEGER,
+ .aggregate = FUNC_AGGREGATE_NONE,
+ .is_deterministic = true,
+ .flags = 0,
+ .call = lengthFunc,
+ .finalize = NULL,
+ .export_to_sql = true,
+ }, {
+ .name = "CHAR_LENGTH",
+ .param_count = 1,
+ .returns = FIELD_TYPE_INTEGER,
+ .aggregate = FUNC_AGGREGATE_NONE,
+ .is_deterministic = true,
+ .flags = 0,
+ .call = lengthFunc,
+ .finalize = NULL,
+ .export_to_sql = true,
+ }, {
+ .name = "COALESCE",
+ .param_count = -1,
+ .returns = FIELD_TYPE_SCALAR,
+ .aggregate = FUNC_AGGREGATE_NONE,
+ .is_deterministic = true,
+ .flags = SQL_FUNC_COALESCE,
+ .call = sql_builtin_stub,
+ .finalize = NULL,
+ .export_to_sql = true,
+ }, {
+ .name = "COUNT",
+ .param_count = -1,
+ .returns = FIELD_TYPE_INTEGER,
+ .aggregate = FUNC_AGGREGATE_GROUP,
+ .is_deterministic = false,
+ .flags = 0,
+ .call = countStep,
+ .finalize = countFinalize,
+ .export_to_sql = true,
+ }, {
+ .name = "CURRENT_DATE",
+ .call = sql_builtin_stub,
+ .export_to_sql = false,
+ .param_count = -1,
+ .returns = FIELD_TYPE_ANY,
+ .aggregate = FUNC_AGGREGATE_NONE,
+ .is_deterministic = false,
+ .flags = 0,
+ .finalize = NULL,
+ }, {
+ .name = "CURRENT_TIME",
+ .call = sql_builtin_stub,
+ .export_to_sql = false,
+ .param_count = -1,
+ .returns = FIELD_TYPE_ANY,
+ .aggregate = FUNC_AGGREGATE_NONE,
+ .is_deterministic = false,
+ .flags = 0,
+ .finalize = NULL,
+ }, {
+ .name = "CURRENT_TIMESTAMP",
+ .call = sql_builtin_stub,
+ .export_to_sql = false,
+ .param_count = -1,
+ .returns = FIELD_TYPE_ANY,
+ .aggregate = FUNC_AGGREGATE_NONE,
+ .is_deterministic = false,
+ .flags = 0,
+ .finalize = NULL,
+ }, {
+ .name = "DATE",
+ .call = sql_builtin_stub,
+ .export_to_sql = false,
+ .param_count = -1,
+ .returns = FIELD_TYPE_ANY,
+ .aggregate = FUNC_AGGREGATE_NONE,
+ .is_deterministic = false,
+ .flags = 0,
+ .finalize = NULL,
+ }, {
+ .name = "DATETIME",
+ .call = sql_builtin_stub,
+ .export_to_sql = false,
+ .param_count = -1,
+ .returns = FIELD_TYPE_ANY,
+ .aggregate = FUNC_AGGREGATE_NONE,
+ .is_deterministic = false,
+ .flags = 0,
+ .finalize = NULL,
+ }, {
+ .name = "EVERY",
+ .call = sql_builtin_stub,
+ .export_to_sql = false,
+ .param_count = -1,
+ .returns = FIELD_TYPE_ANY,
+ .aggregate = FUNC_AGGREGATE_NONE,
+ .is_deterministic = false,
+ .flags = 0,
+ .finalize = NULL,
+ }, {
+ .name = "EXISTS",
+ .call = sql_builtin_stub,
+ .export_to_sql = false,
+ .param_count = -1,
+ .returns = FIELD_TYPE_ANY,
+ .aggregate = FUNC_AGGREGATE_NONE,
+ .is_deterministic = false,
+ .flags = 0,
+ .finalize = NULL,
+ }, {
+ .name = "EXP",
+ .call = sql_builtin_stub,
+ .export_to_sql = false,
+ .param_count = -1,
+ .returns = FIELD_TYPE_ANY,
+ .aggregate = FUNC_AGGREGATE_NONE,
+ .is_deterministic = false,
+ .flags = 0,
+ .finalize = NULL,
+ }, {
+ .name = "EXTRACT",
+ .call = sql_builtin_stub,
+ .export_to_sql = false,
+ .param_count = -1,
+ .returns = FIELD_TYPE_ANY,
+ .aggregate = FUNC_AGGREGATE_NONE,
+ .is_deterministic = false,
+ .flags = 0,
+ .finalize = NULL,
+ }, {
+ .name = "FLOOR",
+ .call = sql_builtin_stub,
+ .export_to_sql = false,
+ .param_count = -1,
+ .returns = FIELD_TYPE_ANY,
+ .aggregate = FUNC_AGGREGATE_NONE,
+ .is_deterministic = false,
+ .flags = 0,
+ .finalize = NULL,
+ }, {
+ .name = "GREATER",
+ .call = sql_builtin_stub,
+ .export_to_sql = false,
+ .param_count = -1,
+ .returns = FIELD_TYPE_ANY,
+ .aggregate = FUNC_AGGREGATE_NONE,
+ .is_deterministic = false,
+ .flags = 0,
+ .finalize = NULL,
+ }, {
+ .name = "GREATEST",
+ .param_count = -1,
+ .returns = FIELD_TYPE_SCALAR,
+ .aggregate = FUNC_AGGREGATE_NONE,
+ .is_deterministic = true,
+ .flags = SQL_FUNC_NEEDCOLL | SQL_FUNC_MAX,
+ .call = minmaxFunc,
+ .finalize = NULL,
+ .export_to_sql = true,
+ }, {
+ .name = "GROUP_CONCAT",
+ .param_count = -1,
+ .returns = FIELD_TYPE_STRING,
+ .aggregate = FUNC_AGGREGATE_GROUP,
+ .is_deterministic = false,
+ .flags = 0,
+ .call = groupConcatStep,
+ .finalize = groupConcatFinalize,
+ .export_to_sql = true,
+ }, {
+ .name = "HEX",
+ .param_count = 1,
+ .returns = FIELD_TYPE_STRING,
+ .aggregate = FUNC_AGGREGATE_NONE,
+ .is_deterministic = true,
+ .flags = 0,
+ .call = hexFunc,
+ .finalize = NULL,
+ .export_to_sql = true,
+ }, {
+ .name = "IFNULL",
+ .param_count = 2,
+ .returns = FIELD_TYPE_INTEGER,
+ .aggregate = FUNC_AGGREGATE_NONE,
+ .is_deterministic = true,
+ .flags = SQL_FUNC_COALESCE,
+ .call = sql_builtin_stub,
+ .finalize = NULL,
+ .export_to_sql = true,
+ }, {
+ .name = "JULIANDAY",
+ .call = sql_builtin_stub,
+ .export_to_sql = false,
+ .param_count = -1,
+ .returns = FIELD_TYPE_ANY,
+ .aggregate = FUNC_AGGREGATE_NONE,
+ .is_deterministic = false,
+ .flags = 0,
+ .finalize = NULL,
+ }, {
+ .name = "LEAST",
+ .param_count = -1,
+ .returns = FIELD_TYPE_SCALAR,
+ .aggregate = FUNC_AGGREGATE_NONE,
+ .is_deterministic = true,
+ .flags = SQL_FUNC_NEEDCOLL | SQL_FUNC_MIN,
+ .call = minmaxFunc,
+ .finalize = NULL,
+ .export_to_sql = true,
+ }, {
+ .name = "LENGTH",
+ .param_count = 1,
+ .returns = FIELD_TYPE_INTEGER,
+ .aggregate = FUNC_AGGREGATE_NONE,
+ .is_deterministic = true,
+ .flags = SQL_FUNC_LENGTH,
+ .call = lengthFunc,
+ .finalize = NULL,
+ .export_to_sql = true,
+ }, {
+ .name = "LESSER",
+ .call = sql_builtin_stub,
+ .export_to_sql = false,
+ .param_count = -1,
+ .returns = FIELD_TYPE_ANY,
+ .aggregate = FUNC_AGGREGATE_NONE,
+ .is_deterministic = false,
+ .flags = 0,
+ .finalize = NULL,
+ }, {
+ .name = "LIKE",
+ .param_count = -1,
+ .returns = FIELD_TYPE_INTEGER,
+ .aggregate = FUNC_AGGREGATE_NONE,
+ .is_deterministic = true,
+ .flags = SQL_FUNC_NEEDCOLL | SQL_FUNC_LIKE,
+ .call = likeFunc,
+ .finalize = NULL,
+ .export_to_sql = true,
+ }, {
+ .name = "LIKELIHOOD",
+ .param_count = 2,
+ .returns = FIELD_TYPE_BOOLEAN,
+ .aggregate = FUNC_AGGREGATE_NONE,
+ .is_deterministic = true,
+ .flags = SQL_FUNC_UNLIKELY,
+ .call = sql_builtin_stub,
+ .finalize = NULL,
+ .export_to_sql = true,
+ }, {
+ .name = "LIKELY",
+ .param_count = 1,
+ .returns = FIELD_TYPE_BOOLEAN,
+ .aggregate = FUNC_AGGREGATE_NONE,
+ .is_deterministic = true,
+ .flags = SQL_FUNC_UNLIKELY,
+ .call = sql_builtin_stub,
+ .finalize = NULL,
+ .export_to_sql = true,
+ }, {
+ .name = "LN",
+ .call = sql_builtin_stub,
+ .export_to_sql = false,
+ .param_count = -1,
+ .returns = FIELD_TYPE_ANY,
+ .aggregate = FUNC_AGGREGATE_NONE,
+ .is_deterministic = false,
+ .flags = 0,
+ .finalize = NULL,
+ }, {
+ .name = "LOWER",
+ .param_count = 1,
+ .returns = FIELD_TYPE_STRING,
+ .aggregate = FUNC_AGGREGATE_NONE,
+ .is_deterministic = true,
+ .flags = SQL_FUNC_DERIVEDCOLL | SQL_FUNC_NEEDCOLL,
+ .call = LowerICUFunc,
+ .finalize = NULL,
+ .export_to_sql = true,
+ }, {
+ .name = "MAX",
+ .param_count = 1,
+ .returns = FIELD_TYPE_SCALAR,
+ .aggregate = FUNC_AGGREGATE_GROUP,
+ .is_deterministic = false,
+ .flags = SQL_FUNC_NEEDCOLL | SQL_FUNC_MAX,
+ .call = minmaxStep,
+ .finalize = minMaxFinalize,
+ .export_to_sql = true,
+ }, {
+ .name = "MIN",
+ .param_count = 1,
+ .returns = FIELD_TYPE_SCALAR,
+ .aggregate = FUNC_AGGREGATE_GROUP,
+ .is_deterministic = false,
+ .flags = SQL_FUNC_NEEDCOLL | SQL_FUNC_MIN,
+ .call = minmaxStep,
+ .finalize = minMaxFinalize,
+ .export_to_sql = true,
+ }, {
+ .name = "MOD",
+ .call = sql_builtin_stub,
+ .export_to_sql = false,
+ .param_count = -1,
+ .returns = FIELD_TYPE_ANY,
+ .aggregate = FUNC_AGGREGATE_NONE,
+ .is_deterministic = false,
+ .flags = 0,
+ .finalize = NULL,
+ }, {
+ .name = "NULLIF",
+ .param_count = 2,
+ .returns = FIELD_TYPE_SCALAR,
+ .aggregate = FUNC_AGGREGATE_NONE,
+ .is_deterministic = true,
+ .flags = SQL_FUNC_NEEDCOLL,
+ .call = nullifFunc,
+ .finalize = NULL,
+ .export_to_sql = true,
+ }, {
+ .name = "OCTET_LENGTH",
+ .call = sql_builtin_stub,
+ .export_to_sql = false,
+ .param_count = -1,
+ .returns = FIELD_TYPE_ANY,
+ .aggregate = FUNC_AGGREGATE_NONE,
+ .is_deterministic = false,
+ .flags = 0,
+ .finalize = NULL,
+ }, {
+ .name = "POSITION",
+ .param_count = 2,
+ .returns = FIELD_TYPE_INTEGER,
+ .aggregate = FUNC_AGGREGATE_NONE,
+ .is_deterministic = true,
+ .flags = SQL_FUNC_NEEDCOLL,
+ .call = position_func,
+ .finalize = NULL,
+ .export_to_sql = true,
+ }, {
+ .name = "POWER",
+ .call = sql_builtin_stub,
+ .export_to_sql = false,
+ .param_count = -1,
+ .returns = FIELD_TYPE_ANY,
+ .aggregate = FUNC_AGGREGATE_NONE,
+ .is_deterministic = false,
+ .flags = 0,
+ .finalize = NULL,
+ }, {
+ .name = "PRINTF",
+ .param_count = -1,
+ .returns = FIELD_TYPE_STRING,
+ .aggregate = FUNC_AGGREGATE_NONE,
+ .is_deterministic = true,
+ .flags = 0,
+ .call = printfFunc,
+ .finalize = NULL,
+ .export_to_sql = true,
+ }, {
+ .name = "QUOTE",
+ .param_count = 1,
+ .returns = FIELD_TYPE_STRING,
+ .aggregate = FUNC_AGGREGATE_NONE,
+ .is_deterministic = true,
+ .flags = 0,
+ .call = quoteFunc,
+ .finalize = NULL,
+ .export_to_sql = true,
+ }, {
+ .name = "RANDOM",
+ .param_count = 0,
+ .returns = FIELD_TYPE_INTEGER,
+ .aggregate = FUNC_AGGREGATE_NONE,
+ .is_deterministic = false,
+ .flags = 0,
+ .call = randomFunc,
+ .finalize = NULL,
+ .export_to_sql = true,
+ }, {
+ .name = "RANDOMBLOB",
+ .param_count = 1,
+ .returns = FIELD_TYPE_VARBINARY,
+ .aggregate = FUNC_AGGREGATE_NONE,
+ .is_deterministic = false,
+ .flags = 0,
+ .call = randomBlob,
+ .finalize = NULL,
+ .export_to_sql = true,
+ }, {
+ .name = "REPLACE",
+ .param_count = 3,
+ .returns = FIELD_TYPE_STRING,
+ .aggregate = FUNC_AGGREGATE_NONE,
+ .is_deterministic = true,
+ .flags = SQL_FUNC_DERIVEDCOLL,
+ .call = replaceFunc,
+ .finalize = NULL,
+ .export_to_sql = true,
+ }, {
+ .name = "ROUND",
+ .param_count = -1,
+ .returns = FIELD_TYPE_INTEGER,
+ .aggregate = FUNC_AGGREGATE_NONE,
+ .is_deterministic = true,
+ .flags = 0,
+ .call = roundFunc,
+ .finalize = NULL,
+ .export_to_sql = true,
+ }, {
+ .name = "ROW_COUNT",
+ .param_count = 0,
+ .returns = FIELD_TYPE_INTEGER,
+ .aggregate = FUNC_AGGREGATE_NONE,
+ .is_deterministic = true,
+ .flags = 0,
+ .call = sql_row_count,
+ .finalize = NULL,
+ .export_to_sql = true,
+ }, {
+ .name = "SOME",
+ .call = sql_builtin_stub,
+ .export_to_sql = false,
+ .param_count = -1,
+ .returns = FIELD_TYPE_ANY,
+ .aggregate = FUNC_AGGREGATE_NONE,
+ .is_deterministic = false,
+ .flags = 0,
+ .finalize = NULL,
+ }, {
+ .name = "SOUNDEX",
+ .param_count = 1,
+ .returns = FIELD_TYPE_STRING,
+ .aggregate = FUNC_AGGREGATE_NONE,
+ .is_deterministic = true,
+ .flags = 0,
+ .call = soundexFunc,
+ .finalize = NULL,
+ .export_to_sql = true,
+ }, {
+ .name = "SQRT",
+ .call = sql_builtin_stub,
+ .export_to_sql = false,
+ .param_count = -1,
+ .returns = FIELD_TYPE_ANY,
+ .aggregate = FUNC_AGGREGATE_NONE,
+ .is_deterministic = false,
+ .flags = 0,
+ .finalize = NULL,
+ }, {
+ .name = "STRFTIME",
+ .call = sql_builtin_stub,
+ .export_to_sql = false,
+ .param_count = -1,
+ .returns = FIELD_TYPE_ANY,
+ .aggregate = FUNC_AGGREGATE_NONE,
+ .is_deterministic = false,
+ .flags = 0,
+ .finalize = NULL,
+ }, {
+ .name = "SUBSTR",
+ .param_count = -1,
+ .returns = FIELD_TYPE_STRING,
+ .aggregate = FUNC_AGGREGATE_NONE,
+ .is_deterministic = true,
+ .flags = SQL_FUNC_DERIVEDCOLL,
+ .call = substrFunc,
+ .finalize = NULL,
+ .export_to_sql = true,
+ }, {
+ .name = "SUM",
+ .param_count = 1,
+ .returns = FIELD_TYPE_NUMBER,
+ .aggregate = FUNC_AGGREGATE_GROUP,
+ .is_deterministic = false,
+ .flags = 0,
+ .call = sum_step,
+ .finalize = sumFinalize,
+ .export_to_sql = true,
+ }, {
+ .name = "TIME",
+ .call = sql_builtin_stub,
+ .export_to_sql = false,
+ .param_count = -1,
+ .returns = FIELD_TYPE_ANY,
+ .aggregate = FUNC_AGGREGATE_NONE,
+ .is_deterministic = false,
+ .flags = 0,
+ .finalize = NULL,
+ }, {
+ .name = "TOTAL",
+ .param_count = 1,
+ .returns = FIELD_TYPE_NUMBER,
+ .aggregate = FUNC_AGGREGATE_GROUP,
+ .is_deterministic = false,
+ .flags = 0,
+ .call = sum_step,
+ .finalize = totalFinalize,
+ .export_to_sql = true,
+ }, {
+ .name = "TRIM",
+ .param_count = -1,
+ .returns = FIELD_TYPE_STRING,
+ .aggregate = FUNC_AGGREGATE_NONE,
+ .is_deterministic = true,
+ .flags = SQL_FUNC_DERIVEDCOLL,
+ .call = trim_func,
+ .finalize = NULL,
+ .export_to_sql = true,
+ }, {
+ .name = "TYPEOF",
+ .param_count = 1,
+ .returns = FIELD_TYPE_STRING,
+ .aggregate = FUNC_AGGREGATE_NONE,
+ .is_deterministic = true,
+ .flags = SQL_FUNC_TYPEOF,
+ .call = typeofFunc,
+ .finalize = NULL,
+ .export_to_sql = true,
+ }, {
+ .name = "UNICODE",
+ .param_count = 1,
+ .returns = FIELD_TYPE_STRING,
+ .aggregate = FUNC_AGGREGATE_NONE,
+ .is_deterministic = true,
+ .flags = 0,
+ .call = unicodeFunc,
+ .finalize = NULL,
+ .export_to_sql = true,
+ }, {
+ .name = "UNLIKELY",
+ .param_count = 1,
+ .returns = FIELD_TYPE_BOOLEAN,
+ .aggregate = FUNC_AGGREGATE_NONE,
+ .is_deterministic = true,
+ .flags = SQL_FUNC_UNLIKELY,
+ .call = sql_builtin_stub,
+ .finalize = NULL,
+ .export_to_sql = true,
+ }, {
+ .name = "UPPER",
+ .param_count = 1,
+ .returns = FIELD_TYPE_STRING,
+ .aggregate = FUNC_AGGREGATE_NONE,
+ .is_deterministic = true,
+ .flags = SQL_FUNC_DERIVEDCOLL | SQL_FUNC_NEEDCOLL,
+ .call = UpperICUFunc,
+ .finalize = NULL,
+ .export_to_sql = true,
+ }, {
+ .name = "VERSION",
+ .param_count = 0,
+ .returns = FIELD_TYPE_STRING,
+ .aggregate = FUNC_AGGREGATE_NONE,
+ .is_deterministic = true,
+ .flags = 0,
+ .call = sql_func_version,
+ .finalize = NULL,
+ .export_to_sql = true,
+ }, {
+ .name = "ZEROBLOB",
+ .param_count = 1,
+ .returns = FIELD_TYPE_VARBINARY,
+ .aggregate = FUNC_AGGREGATE_NONE,
+ .is_deterministic = true,
+ .flags = 0,
+ .call = zeroblobFunc,
+ .finalize = NULL,
+ .export_to_sql = true,
+ }, {
+ .name = "_sql_stat_get",
+ .call = sql_builtin_stub,
+ .export_to_sql = false,
+ .param_count = -1,
+ .returns = FIELD_TYPE_ANY,
+ .aggregate = FUNC_AGGREGATE_NONE,
+ .is_deterministic = false,
+ .flags = 0,
+ .finalize = NULL,
+ }, {
+ .name = "_sql_stat_init",
+ .call = sql_builtin_stub,
+ .export_to_sql = false,
+ .param_count = -1,
+ .returns = FIELD_TYPE_ANY,
+ .aggregate = FUNC_AGGREGATE_NONE,
+ .is_deterministic = false,
+ .flags = 0,
+ .finalize = NULL,
+ }, {
+ .name = "_sql_stat_push",
+ .call = sql_builtin_stub,
+ .export_to_sql = false,
+ .param_count = -1,
+ .returns = FIELD_TYPE_ANY,
+ .aggregate = FUNC_AGGREGATE_NONE,
+ .is_deterministic = false,
+ .flags = 0,
+ .finalize = NULL,
+ },
+};
-#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");
+static struct func_vtab func_sql_builtin_vtab;
+
+struct func *
+func_sql_builtin_new(struct func_def *def)
+{
+ assert(def->language == FUNC_LANGUAGE_SQL_BUILTIN);
+ /** 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
+ /*
+ * All SQL built-in(s) (stubs) are defined in a snapshot.
+ * Implementation-specific metadata is defined in
+ * sql_builtins list. When a definition were not found
+ * above, the function name is invalid, i.e. it is
+ * not built-in function.
+ */
+ if (idx == -1) {
+ diag_set(ClientError, ER_CREATE_FUNCTION, def->name,
+ "given built-in is not predefined");
+ return NULL;
+ }
+ struct func_sql_builtin *func =
+ (struct func_sql_builtin *) calloc(1, sizeof(*func));
+ if (func == NULL) {
+ diag_set(OutOfMemory, sizeof(*func), "malloc", "func");
+ return NULL;
+ }
+ func->base.def = def;
+ func->base.vtab = &func_sql_builtin_vtab;
+ func->flags = sql_builtins[idx].flags;
+ func->call = sql_builtins[idx].call;
+ func->finalize = sql_builtins[idx].finalize;
+ def->param_count = sql_builtins[idx].param_count;
+ def->is_deterministic = sql_builtins[idx].is_deterministic;
+ def->returns = sql_builtins[idx].returns;
+ def->aggregate = sql_builtins[idx].aggregate;
+ def->exports.sql = sql_builtins[idx].export_to_sql;
+ 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 = func_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 748dc1b8e..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 && p->nArg == nArg) {
- 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->nArg = (u16) 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 853d42b58..c9c8e26f1 100644
--- a/src/box/sql/resolve.c
+++ b/src/box/sql/resolve.c
@@ -38,6 +38,7 @@
#include "sqlInt.h"
#include <stdlib.h>
#include <string.h>
+#include "box/schema.h"
/*
* Walk the expression tree pExpr and increase the aggregate function
@@ -591,83 +592,71 @@ resolveExprStep(Walker * pWalker, Expr * pExpr)
case TK_FUNCTION:{
ExprList *pList = pExpr->x.pList; /* The argument list */
int n = pList ? pList->nExpr : 0; /* Number of arguments */
- 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) {
- diag_set(ClientError,
- ER_NO_SUCH_FUNCTION, zId);
- } else {
- uint32_t argc = pDef->nArg;
- const char *err = tt_sprintf("%d", argc);
- diag_set(ClientError,
- ER_FUNC_WRONG_ARG_COUNT,
- pDef->zName, err, n);
- }
+ struct func *func = func_by_name(zId, nId);
+ if (func == NULL) {
+ diag_set(ClientError, ER_NO_SUCH_FUNCTION, zId);
pParse->is_aborted = true;
pNC->nErr++;
- } else {
- is_agg = pDef->xFinalize != 0;
- pExpr->type = pDef->ret_type;
- const char *err =
- "second argument to likelihood() must "\
- "be a constant between 0.0 and 1.0";
- if (pDef->funcFlags & SQL_FUNC_UNLIKELY) {
- ExprSetProperty(pExpr,
- EP_Unlikely | EP_Skip);
- if (n == 2) {
- pExpr->iTable =
- exprProbability(pList->a[1].
- pExpr);
- if (pExpr->iTable < 0) {
- diag_set(ClientError,
- ER_ILLEGAL_PARAMS,
- err);
- pParse->is_aborted =
- true;
- pNC->nErr++;
- }
- } else {
- /* EVIDENCE-OF: R-61304-29449 The unlikely(X) function is
- * equivalent to likelihood(X, 0.0625).
- * EVIDENCE-OF: R-01283-11636 The unlikely(X) function is
- * short-hand for likelihood(X,0.0625).
- * EVIDENCE-OF: R-36850-34127 The likely(X) function is short-hand
- * for likelihood(X,0.9375).
- * EVIDENCE-OF: R-53436-40973 The likely(X) function is equivalent
- * to likelihood(X,0.9375).
- */
- /* TUNING: unlikely() probability is 0.0625. likely() is 0.9375 */
- pExpr->iTable =
- pDef->zName[0] ==
- 'u' ? 8388608 : 125829120;
- }
- }
- if ((pDef->funcFlags & SQL_FUNC_CONSTANT) != 0) {
- /* 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) {
- /* Date/time functions that use 'now', and other functions
- * that might change over time cannot be used
- * in an index.
- */
- assert((pNC->ncFlags & NC_IdxExpr) == 0);
+ return WRC_Abort;
+ }
+ if (!func->def->exports.sql) {
+ diag_set(ClientError, ER_SQL_PARSER_GENERIC,
+ tt_sprintf("function %.*s() is not "
+ "available in SQL",
+ nId, zId));
+ pParse->is_aborted = true;
+ pNC->nErr++;
+ return WRC_Abort;
+ }
+ if (func->def->param_count != -1 &&
+ func->def->param_count != n) {
+ uint32_t argc = func->def->param_count;
+ const char *err = tt_sprintf("%d", argc);
+ diag_set(ClientError, ER_FUNC_WRONG_ARG_COUNT,
+ func->def->name, err, n);
+ pParse->is_aborted = true;
+ pNC->nErr++;
+ return WRC_Abort;
+ }
+ bool is_agg = func->def->aggregate ==
+ FUNC_AGGREGATE_GROUP;
+ assert(!is_agg || func->def->language ==
+ FUNC_LANGUAGE_SQL_BUILTIN);
+ pExpr->type = func->def->returns;
+ if (sql_func_flag_is_set(func, SQL_FUNC_UNLIKELY) &&
+ n == 2) {
+ ExprSetProperty(pExpr, EP_Unlikely | EP_Skip);
+ pExpr->iTable =
+ exprProbability(pList->a[1].pExpr);
+ if (pExpr->iTable < 0) {
+ diag_set(ClientError, ER_ILLEGAL_PARAMS,
+ "second argument to "
+ "likelihood() must be a "
+ "constant between 0.0 and 1.0");
+ pParse->is_aborted = true;
+ pNC->nErr++;
+ return WRC_Abort;
}
+ } else if (sql_func_flag_is_set(func,
+ SQL_FUNC_UNLIKELY)) {
+ ExprSetProperty(pExpr, EP_Unlikely | EP_Skip);
+ /*
+ * unlikely() probability is
+ * 0.0625, likely() is 0.9375
+ */
+ pExpr->iTable = func->def->name[0] == 'u' ?
+ 8388608 : 125829120;
}
+ assert(!func->def->is_deterministic ||
+ (pNC->ncFlags & NC_IdxExpr) == 0);
+ if (func->def->is_deterministic)
+ ExprSetProperty(pExpr, EP_ConstFunc);
if (is_agg && (pNC->ncFlags & NC_AllowAgg) == 0) {
const char *err =
tt_sprintf("misuse of aggregate "\
@@ -692,13 +681,12 @@ resolveExprStep(Walker * pWalker, Expr * pExpr)
pExpr->op2++;
pNC2 = pNC2->pNext;
}
- assert(pDef != 0);
+ assert(func != NULL);
if (pNC2) {
pNC2->ncFlags |= NC_HasAgg;
- if ((pDef->funcFlags &
- SQL_FUNC_MIN) != 0 ||
- (pDef->funcFlags &
- SQL_FUNC_MAX) != 0)
+ if (sql_func_flag_is_set(func,
+ SQL_FUNC_MIN |
+ SQL_FUNC_MAX))
pNC2->ncFlags |= NC_MinMaxAgg;
}
pNC->ncFlags |= NC_AllowAgg;
diff --git a/src/box/sql/select.c b/src/box/sql/select.c
index ddb5de87e..cdcdbaf2b 100644
--- a/src/box/sql/select.c
+++ b/src/box/sql/select.c
@@ -4381,7 +4381,9 @@ is_simple_count(struct Select *select, struct AggInfo *agg_info)
return NULL;
if (NEVER(agg_info->nFunc == 0))
return NULL;
- if ((agg_info->aFunc->pFunc->funcFlags & SQL_FUNC_COUNT) == 0 ||
+ assert(agg_info->aFunc->func->def->language ==
+ FUNC_LANGUAGE_SQL_BUILTIN);
+ if (sql_func_flag_is_set(agg_info->aFunc->func, SQL_FUNC_COUNT) ||
(agg_info->aFunc->pExpr->x.pList != NULL &&
agg_info->aFunc->pExpr->x.pList->nExpr > 0))
return NULL;
@@ -5278,7 +5280,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);
}
}
@@ -5319,7 +5321,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_is_set(pF->func, SQL_FUNC_NEEDCOLL)) {
struct coll *coll = NULL;
struct ExprList_item *pItem;
int j;
@@ -5338,7 +5340,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 bfa8c1e6f..f9f996929 100644
--- a/src/box/sql/vdbe.c
+++ b/src/box/sql/vdbe.c
@@ -1733,7 +1733,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));
@@ -1741,7 +1741,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;
@@ -1776,7 +1776,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 *func = (struct func_sql_builtin *)pCtx->func;
+ func->call(pCtx, pCtx->argc, pCtx->argv);
/* If the function returned an error, throw an exception */
if (pCtx->is_aborted)
@@ -4994,7 +4996,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));
@@ -5002,7 +5004,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;
@@ -5044,7 +5046,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 *func = (struct func_sql_builtin *)pCtx->func;
+ func->call(pCtx, pCtx->argc, pCtx->argv);
if (pCtx->is_aborted) {
sqlVdbeMemRelease(&t);
goto abort_due_to_error;
@@ -5076,7 +5080,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 dfaff9365..bf40b44fa 100644
--- a/src/box/sql/vdbeapi.c
+++ b/src/box/sql/vdbeapi.c
@@ -477,17 +477,6 @@ sql_step(sql_stmt * pStmt)
return sqlStep(v);
}
-/*
- * Extract the user data from a sql_context structure and return a
- * pointer to it.
- */
-void *
-sql_user_data(sql_context * p)
-{
- assert(p && p->pFunc);
- return p->pFunc->pUserData;
-}
-
/*
* Extract the user data from a sql_context structure and return a
* pointer to it.
@@ -547,7 +536,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 +552,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);
+ assert(p->func->def->language == FUNC_LANGUAGE_SQL_BUILTIN);
+ assert(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 62df76c26..40918e7e7 100644
--- a/src/box/sql/vdbeaux.c
+++ b/src/box/sql/vdbeaux.c
@@ -620,24 +620,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);
}
@@ -661,13 +648,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;
}
}
@@ -1121,15 +1106,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->nArg);
+ 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->nArg);
+ 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 5516d7fb1..ac0dfa333 100644
--- a/src/box/sql/vdbemem.c
+++ b/src/box/sql/vdbemem.c
@@ -312,37 +312,29 @@ 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)) {
- sql_context ctx;
- Mem t;
- assert((pMem->flags & MEM_Null) != 0 || pFunc == pMem->u.pDef);
- memset(&ctx, 0, sizeof(ctx));
- memset(&t, 0, sizeof(t));
- t.flags = MEM_Null;
- t.db = pMem->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));
- if (ctx.is_aborted)
- return -1;
- }
- return 0;
+ assert(func != NULL);
+ assert(func->def->language == FUNC_LANGUAGE_SQL_BUILTIN);
+ assert(func->def->aggregate == FUNC_AGGREGATE_GROUP);
+ assert((mem->flags & MEM_Null) != 0 || func == mem->u.func);
+ sql_context ctx;
+ memset(&ctx, 0, sizeof(ctx));
+ Mem t;
+ memset(&t, 0, sizeof(t));
+ t.flags = MEM_Null;
+ t.db = mem->db;
+ t.field_type = field_type_MAX;
+ ctx.pOut = &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));
+ return ctx.is_aborted ? -1 : 0;
}
/*
@@ -359,7 +351,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 +1281,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,10 +1291,10 @@ 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) == 0 ||
- (pFunc->funcFlags & SQL_FUNC_NEEDCOLL))
+ struct func *func = sql_func_by_signature(p->u.zToken, nVal);
+ if (func == NULL || func->def->language != FUNC_LANGUAGE_SQL_BUILTIN ||
+ !func->def->is_deterministic ||
+ sql_func_flag_is_set(func, SQL_FUNC_NEEDCOLL))
return 0;
if (pList) {
@@ -1332,8 +1323,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/src/box/alter.cc b/src/box/alter.cc
index 4f2e34bf0..c0780d6da 100644
--- a/src/box/alter.cc
+++ b/src/box/alter.cc
@@ -2926,6 +2926,12 @@ on_replace_dd_func(struct trigger * /* trigger */, void *event)
(unsigned) old_func->def->uid,
"function has references");
}
+ /* Can't' drop a builtin function. */
+ if (old_func->def->language == FUNC_LANGUAGE_SQL_BUILTIN) {
+ tnt_raise(ClientError, ER_DROP_FUNCTION,
+ (unsigned) old_func->def->uid,
+ "function is SQL built-in");
+ }
struct trigger *on_commit =
txn_alter_trigger_new(on_drop_func_commit, old_func);
struct trigger *on_rollback =
diff --git a/test/box/function1.result b/test/box/function1.result
index 5b091f72b..0ece5448a 100644
--- a/test/box/function1.result
+++ b/test/box/function1.result
@@ -825,6 +825,26 @@ box.func.LUA:call({"return 1 + 1"})
---
- 2
...
+box.schema.user.grant('guest', 'execute', 'function', 'SUM')
+---
+...
+c = net.connect(box.cfg.listen)
+---
+...
+c:call("SUM")
+---
+- error: sql builtin function does not support Lua frontend
+...
+c:close()
+---
+...
+box.schema.user.revoke('guest', 'execute', 'function', 'SUM')
+---
+...
+box.schema.func.drop("SUM")
+---
+- error: 'Can''t drop function 1: function is SQL built-in'
+...
-- Introduce function options
box.schema.func.create('test', {body = "function(tuple) return tuple end", is_deterministic = true, opts = {is_multikey = true}})
---
diff --git a/test/box/function1.test.lua b/test/box/function1.test.lua
index f894472f8..665972eda 100644
--- a/test/box/function1.test.lua
+++ b/test/box/function1.test.lua
@@ -294,6 +294,13 @@ ok == true
box.func.LUA:call({"return 1 + 1"})
+box.schema.user.grant('guest', 'execute', 'function', 'SUM')
+c = net.connect(box.cfg.listen)
+c:call("SUM")
+c:close()
+box.schema.user.revoke('guest', 'execute', 'function', 'SUM')
+box.schema.func.drop("SUM")
+
-- Introduce function options
box.schema.func.create('test', {body = "function(tuple) return tuple end", is_deterministic = true, opts = {is_multikey = true}})
box.func['test'].is_multikey == true
--
2.22.1
More information about the Tarantool-patches
mailing list