[PATCH v1 12/12] sql: use schema's func hash instead of FuncDef hash
Kirill Shcherbatov
kshcherbatov at tarantool.org
Mon Jul 8 14:26:18 MSK 2019
Part of #2200
Closes #4113
Closes #2233
@TarantoolBot document
Title: Reworked SQL functions machinery
Now all functions in Tarantool use uniform API and calling
convention. Therefore it is possible to call C and Lua functions
from SQL.
To make a function available in SQL, you need to define it
with list of types of it's arguments 'param_list', the type
of returned value 'returns' and mention 'SQL' language in
exports parameter. Deterministic functions must be defined with
is_deterministic = true (the query planner use this information
to build an effective vdbe code).
Example:
-- C function is called in SQL
box.schema.func.create("function1.divide", {language = 'C',
returns = 'number', param_list = {'number', 'number'},
is_deterministic = true, exports = {'LUA', 'SQL'}})
box.execute('SELECT "function1.divide"(6, 3)')
---
- metadata:
- name: '"function1.divide"(6, 3)'
type: number
rows:
- [2]
...
-- Persistent Lua function is called in SQL
box.schema.func.create("SUMMARIZE", {language = 'LUA', returns = 'number',
body = 'function (a, b) return a + b end',
is_deterministic = true,
param_list = {'number', 'number'},
exports = {'LUA', 'SQL'}})
box.execute('SELECT summarize(1, 2)')
---
- metadata:
- name: summarize(1, 2)
type: number
rows:
- [3]
...
Moreover there is a predefined Lua function "LUA" that allows to
call Lua code in SQL. The argument of LUA function is a valid
Lua code that returns some scalar type.
Example:
box.execute('SELECT lua(\'return box.cfg.memtx_memory\')')
---
- metadata:
- name: lua('return box.cfg.memtx_memory')
type: any
rows:
- [268435456]
...
---
src/box/CMakeLists.txt | 1 -
src/box/alter.cc | 1 +
src/box/func.c | 3 +-
src/box/func.h | 5 +
src/box/func_def.c | 19 +
src/box/func_def.h | 2 +
src/box/lua/call.c | 8 +-
src/box/lua/lua_sql.c | 217 ----------
src/box/lua/lua_sql.h | 39 --
src/box/lua/schema.lua | 4 +-
src/box/port.c | 5 +-
src/box/sql.h | 13 +
src/box/sql/analyze.c | 44 +-
src/box/sql/callback.c | 208 ----------
src/box/sql/date.c | 28 --
src/box/sql/expr.c | 51 ++-
src/box/sql/func.c | 695 +++++++++++++++++++++++++-------
src/box/sql/global.c | 7 -
src/box/sql/main.c | 134 ------
src/box/sql/resolve.c | 55 ++-
src/box/sql/select.c | 10 +-
src/box/sql/sqlInt.h | 176 +-------
src/box/sql/vdbe.c | 17 +-
src/box/sql/vdbe.h | 6 +-
src/box/sql/vdbeInt.h | 20 +-
src/box/sql/vdbeapi.c | 11 +-
src/box/sql/vdbeaux.c | 31 +-
src/box/sql/vdbemem.c | 73 ++--
src/box/sql/whereexpr.c | 4 +-
src/lib/coll/coll.c | 1 +
test/box/function1.result | 119 ++++++
test/box/function1.test.lua | 35 ++
test/sql-tap/alias.test.lua | 11 +-
test/sql-tap/check.test.lua | 13 +-
test/sql-tap/func5.test.lua | 29 +-
test/sql-tap/lua_sql.test.lua | 120 +++---
test/sql-tap/subquery.test.lua | 21 +-
test/sql-tap/trigger9.test.lua | 6 +-
test/sql-tap/where2.test.lua | 4 +-
test/sql/errinj.result | 25 --
test/sql/errinj.test.lua | 10 -
test/sql/func-recreate.result | 41 +-
test/sql/func-recreate.test.lua | 28 +-
43 files changed, 1107 insertions(+), 1243 deletions(-)
delete mode 100644 src/box/lua/lua_sql.c
delete mode 100644 src/box/lua/lua_sql.h
diff --git a/src/box/CMakeLists.txt b/src/box/CMakeLists.txt
index 481842a39..a0160912d 100644
--- a/src/box/CMakeLists.txt
+++ b/src/box/CMakeLists.txt
@@ -128,7 +128,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 c92a1f710..cd85552a2 100644
--- a/src/box/alter.cc
+++ b/src/box/alter.cc
@@ -2786,6 +2786,7 @@ func_def_new_from_tuple(struct tuple *tuple)
def->exports.lua = true;
def->param_count = 0;
}
+ def->sql_flags = 0;
def_guard.is_active = false;
return def;
}
diff --git a/src/box/func.c b/src/box/func.c
index e36649d15..6e7d04f25 100644
--- a/src/box/func.c
+++ b/src/box/func.c
@@ -34,13 +34,13 @@
#include "assoc.h"
#include "lua/utils.h"
#include "lua/call.h"
-#include "lua/lua_sql.h"
#include "error.h"
#include "sql.h"
#include "diag.h"
#include "port.h"
#include "schema.h"
#include "session.h"
+#include "small/region.h"
#include <dlfcn.h>
/**
@@ -438,6 +438,7 @@ func_c_new(struct func_def *def)
return NULL;
}
func->base.vtab = func_c_vtab;
+ rlist_create(&func->base.signature);
func->func = NULL;
func->module = NULL;
return &func->base;
diff --git a/src/box/func.h b/src/box/func.h
index 7e4dd37a3..133172bf0 100644
--- a/src/box/func.h
+++ b/src/box/func.h
@@ -82,6 +82,11 @@ struct func {
* Cached runtime access information.
*/
struct access access[BOX_USER_MAX];
+ /**
+ * A list of other functions with given name.
+ * Is valid for SQL builtins.
+ */
+ struct rlist signature;
};
/**
diff --git a/src/box/func_def.c b/src/box/func_def.c
index fb9f77df8..ffd76a514 100644
--- a/src/box/func_def.c
+++ b/src/box/func_def.c
@@ -34,9 +34,28 @@ func_def_cmp(struct func_def *def1, struct func_def *def2)
return def1->aggregate - def2->aggregate;
if (def1->param_count != def2->param_count)
return def1->param_count - def2->param_count;
+ if (def1->sql_flags != def2->sql_flags)
+ return def1->sql_flags - def2->sql_flags;
if ((def1->comment != NULL) != (def2->comment != NULL))
return def1->comment - def2->comment;
if (def1->comment != NULL && strcmp(def1->comment, def2->comment) != 0)
return strcmp(def1->comment, def2->comment);
return 0;
}
+
+struct func_def *
+func_def_dup(struct func_def *def)
+{
+ uint32_t body_offset, comment_offset;
+ uint32_t sz = func_def_sizeof(def->name_len,
+ def->body != NULL ? strlen(def->body) : 0,
+ def->comment != NULL ? strlen(def->comment) : 0,
+ &body_offset, &comment_offset);
+ struct func_def *new = (struct func_def *) malloc(sz);
+ memcpy(new, def, sz);
+ if (new->body != NULL)
+ new->body = (char *)new + body_offset;
+ if (new->comment != NULL)
+ new->comment = (char *)new + comment_offset;
+ return new;
+}
diff --git a/src/box/func_def.h b/src/box/func_def.h
index 508580f78..809d74c42 100644
--- a/src/box/func_def.h
+++ b/src/box/func_def.h
@@ -89,6 +89,8 @@ struct func_def {
* available.
*/
bool is_sandboxed;
+ /** A set of SQL_FUNCTION_* flags. */
+ uint16_t sql_flags;
/** The count of function's input arguments. */
int param_count;
/** The type of the value returned by function. */
diff --git a/src/box/lua/call.c b/src/box/lua/call.c
index 752f05745..528c41310 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"
@@ -501,13 +500,16 @@ port_lua_destroy(struct port *base)
extern const char *
port_lua_dump_plain(struct port *port, uint32_t *size);
+extern struct sql_value *
+port_lua_get_vdbemem(struct port *base, uint32_t *size);
+
static const struct port_vtab port_lua_vtab = {
.dump_msgpack = port_lua_dump,
.dump_msgpack_16 = port_lua_dump_16,
.dump_lua = port_lua_dump_lua,
.dump_plain = port_lua_dump_plain,
.get_msgpack = port_lua_get_msgpack,
- .get_vdbemem = NULL,
+ .get_vdbemem = port_lua_get_vdbemem,
.get_context = NULL,
.destroy = port_lua_destroy,
};
@@ -717,6 +719,7 @@ func_lua_new(struct func_def *def)
func->lua_ref = LUA_REFNIL;
func->base.vtab = func_lua_vtab;
}
+ rlist_create(&func->base.signature);
return &func->base;
}
@@ -959,7 +962,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 0c5797fa2..000000000
--- a/src/box/lua/lua_sql.c
+++ /dev/null
@@ -1,217 +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/port.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 int
-lua_sql_call(struct func *func, struct port *args, struct port *ret)
-{
- (void) func;
- uint32_t argc;
- struct Mem *argv = (struct Mem *)port_get_vdbemem(args, &argc);
- if (argv == NULL)
- return -1;
- struct sql_context *ctx = (struct sql_context *) port_get_context(args);
- assert(ctx != NULL);
- struct Mem *val = vdbemem_alloc_on_region(1);
- if (val == NULL)
- return -1;
- port_vdbemem_create(ret, (struct sql_value *)val, 1, NULL);
-
- 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(ctx);
-
- lua_rawgeti(L, LUA_REGISTRYINDEX, func_info->func_ref);
- for (uint32_t i = 0; i < argc; i++) {
- sql_value *param = (sql_value *)&argv[i];
- switch (sql_value_type(param)) {
- case MP_INT:
- luaL_pushint64(L, sql_value_int64(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");
- goto error;
- }
- }
- if (lua_pcall(L, lua_gettop(L) - 1, 1, 0) != 0){
- diag_set(ClientError, ER_SQL_EXECUTE, lua_tostring(L, -1));
- goto error;
- }
- switch(lua_type(L, -1)) {
- case LUA_TBOOLEAN:
- mem_set_bool(val, lua_toboolean(L, -1));
- break;
- case LUA_TNUMBER:
- sqlVdbeMemSetDouble(val, lua_tonumber(L, -1));
- break;
- case LUA_TSTRING:
- if (sqlVdbeMemSetStr(val, lua_tostring(L, -1), -1,
- 1, SQL_TRANSIENT) != 0)
- return -1;
- break;
- case LUA_TNIL:
- sqlVdbeMemSetNull(val);
- break;
- default:
- diag_set(ClientError, ER_SQL_EXECUTE, "Unsupported type "\
- "passed from Lua");
- goto error;
- }
- return 0;
-error:
- luaL_unref(tarantool_L, LUA_REGISTRYINDEX, coro_ref);
- return -1;
-}
-
-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, "FLOAT") == 0)
- type = FIELD_TYPE_NUMBER;
- else if (strcmp(type_arg, "NUM") == 0)
- type = FIELD_TYPE_NUMBER;
- else if (strcmp(type_arg, "BLOB") == 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/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/lua/schema.lua b/src/box/lua/schema.lua
index aadcd3fa9..5c65eb792 100644
--- a/src/box/lua/schema.lua
+++ b/src/box/lua/schema.lua
@@ -2109,7 +2109,9 @@ box.schema.func.create = function(name, opts)
if_not_exists = 'boolean',
language = 'string', body = 'string',
is_deterministic = 'boolean',
- is_sandboxed = 'boolean', comment = 'string' })
+ is_sandboxed = 'boolean', comment = 'string',
+ returns = 'string', param_list = 'table',
+ exports = 'table'})
local _func = box.space[box.schema.FUNC_ID]
local _vfunc = box.space[box.schema.VFUNC_ID]
local func = _vfunc.index.name:get{name}
diff --git a/src/box/port.c b/src/box/port.c
index 9e4ab9453..02c6a2245 100644
--- a/src/box/port.c
+++ b/src/box/port.c
@@ -140,13 +140,16 @@ port_free(void)
mempool_destroy(&port_tuple_entry_pool);
}
+extern struct sql_value *
+port_tuple_get_vdbemem(struct port *base, uint32_t *size);
+
const struct port_vtab port_tuple_vtab = {
.dump_msgpack = port_tuple_dump_msgpack,
.dump_msgpack_16 = port_tuple_dump_msgpack_16,
.dump_lua = port_tuple_dump_lua,
.dump_plain = NULL,
.get_msgpack = NULL,
- .get_vdbemem = NULL,
+ .get_vdbemem = port_tuple_get_vdbemem,
.get_context = NULL,
.destroy = port_tuple_destroy,
};
diff --git a/src/box/sql.h b/src/box/sql.h
index a078bfdec..ac10ae400 100644
--- a/src/box/sql.h
+++ b/src/box/sql.h
@@ -33,6 +33,7 @@
#include <stdbool.h>
#include <stdint.h>
+#include "box/func.h"
#if defined(__cplusplus)
extern "C" {
@@ -409,6 +410,18 @@ vdbe_field_ref_prepare_tuple(struct vdbe_field_ref *field_ref,
struct func *
func_sql_builtin_new(struct func_def *def);
+struct func_sql_builtin {
+ /** Function object base class. */
+ struct func base;
+ /** User data to pass in call method. */
+ void *user_data;
+ /** Finilize method for aggregate function. */
+ int (*finalize)(struct func *func, struct port *args, struct port *ret);
+};
+
+struct func *
+sql_func_by_signature(const char *name, uint32_t name_len, int argc);
+
#if defined(__cplusplus)
} /* extern "C" { */
#endif
diff --git a/src/box/sql/analyze.c b/src/box/sql/analyze.c
index 34e68b355..1c38cbc4d 100644
--- a/src/box/sql/analyze.c
+++ b/src/box/sql/analyze.c
@@ -264,7 +264,7 @@ stat4Destructor(void *pOld)
* return value is BLOB, but it is really just a pointer to the Stat4Accum
* object.
*/
-static int
+MAYBE_UNUSED int
sql_builtin_stat_init(struct func *func, struct port *args, struct port *ret)
{
(void) func;
@@ -544,7 +544,7 @@ samplePushPrevious(Stat4Accum * p, int iChng)
*
* The R parameter is only used for STAT4
*/
-static int
+MAYBE_UNUSED int
sql_builtin_stat_push(struct func *func, struct port *args, struct port *ret)
{
(void) func;
@@ -623,7 +623,7 @@ sql_builtin_stat_push(struct func *func, struct port *args, struct port *ret)
* The content to returned is determined by the parameter J
* which is one of the STAT_GET_xxxx values defined above.
*/
-static int
+MAYBE_UNUSED int
sql_builtin_stat_get(struct func *func, struct port *args, struct port *ret)
{
(void) func;
@@ -736,11 +736,11 @@ vdbe_emit_analyze_stat_get(struct Vdbe * v, int regStat4, int iParam,
{
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", strlen("_sql_stat_get"), 2);
assert(func != NULL);
sqlVdbeAddOp4(v, OP_Function0, 0, regStat4, regOut,
- (char *)func, P4_FUNCDEF);
+ (char *)func, P4_FUNC);
sqlVdbeChangeP5(v, 2);
}
@@ -876,11 +876,12 @@ 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",
+ strlen("_sql_stat_init"), 3);
assert(init_func != NULL);
sqlVdbeAddOp4(v, OP_Function0, 0, stat4_reg + 1, stat4_reg,
- (char *)init_func, P4_FUNCDEF);
+ (char *)init_func, P4_FUNC);
sqlVdbeChangeP5(v, 3);
/*
* Implementation of the following:
@@ -977,11 +978,12 @@ 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",
+ strlen("_sql_stat_push"), 3);
assert(push_func != NULL);
sqlVdbeAddOp4(v, OP_Function0, 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. */
@@ -1779,7 +1781,7 @@ fail:
* column of the sql_stat4 table to the record format sql uses
* internally.
*/
-static int
+MAYBE_UNUSED int
sql_builtin_record(struct func *func, struct port *args, struct port *ret)
{
(void) func;
@@ -1816,19 +1818,3 @@ sql_builtin_record(struct func *func, struct port *args, struct port *ret)
}
return 0;
}
-
-#define FUNCTION_ANALYZE(zName, nArg, xFunc) \
- {nArg, SQL_FUNC_CONSTANT, NULL, 0, xFunc, 0, #zName, {0},\
- FIELD_TYPE_ANY}
-
-void
-sql_register_analyze_builtins(void)
-{
- static FuncDef aAnalyzeTableFuncs[] = {
- FUNCTION_ANALYZE(_sql_record, 1, sql_builtin_record),
- FUNCTION_ANALYZE(_sql_stat_get, 2, sql_builtin_stat_get),
- FUNCTION_ANALYZE(_sql_stat_push, 3, sql_builtin_stat_push),
- FUNCTION_ANALYZE(_sql_stat_init, 3, sql_builtin_stat_init),
- };
- sqlInsertBuiltinFuncs(aAnalyzeTableFuncs, nelem(aAnalyzeTableFuncs));
-}
diff --git a/src/box/sql/callback.c b/src/box/sql/callback.c
index 42fec2c6a..290363db6 100644
--- a/src/box/sql/callback.c
+++ b/src/box/sql/callback.c
@@ -56,211 +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.
- *
- * If the SQL_PreferBuiltin flag is set, then search the built-in
- * functions even if a prior app-defined function was found. And give
- * priority to 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 7ee3e992e..003e49840 100644
--- a/src/box/sql/expr.c
+++ b/src/box/sql/expr.c
@@ -35,6 +35,8 @@
*/
#include "box/coll_id_cache.h"
#include "coll/coll.h"
+#include "box/func.h"
+#include "box/field_def.h"
#include "sqlInt.h"
#include "tarantoolInt.h"
#include "box/schema.h"
@@ -275,12 +277,13 @@ 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,
+ strlen(p->u.zToken), arg_count);
if (func == NULL)
break;
- if ((func->funcFlags & SQL_FUNC_DERIVEDCOLL) != 0) {
+ if ((func->def->sql_flags &
+ SQL_FUNC_DERIVEDCOLL) != 0) {
/*
* Now we use quite straightforward
* approach assuming that resulting
@@ -289,7 +292,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;
}
@@ -3923,11 +3926,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));
@@ -3939,8 +3940,10 @@ 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, strlen(zId), nFarg);
+ if (func == NULL ||
+ func->def->aggregate == FUNC_AGGREGATE_GROUP) {
diag_set(ClientError, ER_NO_SUCH_FUNCTION,
zId);
pParse->is_aborted = true;
@@ -3950,7 +3953,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 (func->def->sql_flags & SQL_FUNC_COALESCE) {
int endCoalesce = sqlVdbeMakeLabel(v);
assert(nFarg >= 2);
sqlExprCode(pParse, pFarg->a[0].pExpr,
@@ -3987,7 +3990,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 ((func->def->sql_flags & SQL_FUNC_NEEDCOLL) != 0) {
struct coll *unused = NULL;
uint32_t curr_id = COLL_NONE;
bool is_curr_forced = false;
@@ -4034,9 +4037,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 ((func->def->sql_flags & (SQL_FUNC_LENGTH |
+ SQL_FUNC_TYPEOF)) != 0) {
u8 exprOp;
assert(nFarg == 1);
assert(pFarg->a[0].pExpr != 0);
@@ -4051,8 +4053,7 @@ sqlExprCodeTarget(Parse * pParse, Expr * pExpr, int target)
funcFlags &
OPFLAG_LENGTHARG);
pFarg->a[0].pExpr->op2 =
- pDef->
- funcFlags &
+ func->def->sql_flags &
(OPFLAG_LENGTHARG |
OPFLAG_TYPEOFARG);
}
@@ -4066,12 +4067,12 @@ sqlExprCodeTarget(Parse * pParse, Expr * pExpr, int target)
} else {
r1 = 0;
}
- if (pDef->funcFlags & SQL_FUNC_NEEDCOLL) {
+ if (func->def->sql_flags & SQL_FUNC_NEEDCOLL) {
sqlVdbeAddOp4(v, OP_CollSeq, 0, 0, 0,
(char *)coll, P4_COLLSEQ);
}
sqlVdbeAddOp4(v, OP_Function0, constMask, r1,
- target, (char *)pDef, P4_FUNCDEF);
+ target, (char *)func, P4_FUNC);
sqlVdbeChangeP5(v, (u8) nFarg);
if (nFarg && constMask == 0) {
sqlReleaseTempRange(pParse, r1, nFarg);
@@ -5376,12 +5377,18 @@ analyzeAggregate(Walker * pWalker, Expr * pExpr)
pItem->iMem = ++pParse->nMem;
assert(!ExprHasProperty
(pExpr, EP_IntValue));
- pItem->pFunc = sqlFindFunction(
- pParse->db,
+ pItem->func = sql_func_by_signature(
pExpr->u.zToken,
+ strlen(pExpr->u.zToken),
pExpr->x.pList ?
- pExpr->x.pList->nExpr : 0,
- 0);
+ pExpr->x.pList->
+ nExpr : 0);
+ 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 6000b7971..79068f20d 100644
--- a/src/box/sql/func.c
+++ b/src/box/sql/func.c
@@ -39,14 +39,19 @@
#include "version.h"
#include "coll/coll.h"
#include "box/func.h"
+#include "lua/utils.h"
+#include "box/tuple.h"
+#include "mpstream.h"
#include "box/port.h"
#include "tarantoolInt.h"
#include "box/session.h"
+#include "box/schema.h"
#include <unicode/ustring.h>
#include <unicode/ucasemap.h>
#include <unicode/ucnv.h>
#include <unicode/uchar.h>
#include <unicode/ucol.h>
+#include "small/rlist.h"
/*
* Return the collating function associated with a function.
@@ -101,17 +106,221 @@ port_vdbemem_get_context(struct port *base)
return port->ctx;
}
+void
+port_vdbemem_dump_lua(struct port *base, struct lua_State *L, bool is_flat)
+{
+ struct port_vdbemem *port = (struct port_vdbemem *) base;
+ assert(is_flat == true);
+ for (uint32_t i = 0; i < port->size; i++) {
+ sql_value *param =
+ (sql_value *)((struct Mem *)port->mem + i);
+ switch (sql_value_type(param)) {
+ case MP_INT:
+ luaL_pushint64(L, sql_value_int64(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_pushnil(L);
+ break;
+ case MP_BOOL:
+ lua_pushboolean(L, sql_value_boolean(param));
+ break;
+ default:
+ unreachable();
+ }
+ }
+}
+
+const char *
+port_vdbemem_get_msgpack(struct port *base, uint32_t *size)
+{
+ struct port_vdbemem *port = (struct port_vdbemem *) base;
+ struct region *region = &fiber()->gc;
+ size_t region_svp = region_used(region);
+ bool is_error = false;
+ struct mpstream stream;
+ mpstream_init(&stream, region, region_reserve_cb, region_alloc_cb,
+ set_encode_error, &is_error);
+ mpstream_encode_array(&stream, port->size);
+
+ for (uint32_t i = 0; i < port->size && !is_error; i++) {
+ sql_value *param =
+ (sql_value *)((struct Mem *)port->mem + i);
+ switch (sql_value_type(param)) {
+ case MP_INT: {
+ sql_int64 val = sql_value_int64(param);
+ if (val < 0)
+ mpstream_encode_int(&stream, val);
+ else
+ mpstream_encode_uint(&stream, val);
+ break;
+ }
+ case MP_DOUBLE: {
+ mpstream_encode_double(&stream,
+ sql_value_double(param));
+ break;
+ }
+ case MP_STR: {
+ mpstream_encode_str(&stream,
+ (const char *) sql_value_text(param));
+ break;
+ }
+ case MP_BIN: {
+ mpstream_encode_binl(&stream, sql_value_bytes(param));
+ mpstream_memcpy(&stream, sql_value_blob(param),
+ sql_value_bytes(param));
+ break;
+ }
+ case MP_NIL: {
+ mpstream_encode_nil(&stream);
+ break;
+ }
+ case MP_BOOL: {
+ mpstream_encode_bool(&stream, sql_value_boolean(param));
+ break;
+ }
+ default:
+ unreachable();
+ }
+ }
+ mpstream_flush(&stream);
+ *size = region_used(region) - region_svp;
+ if (is_error)
+ goto error;
+ const char *ret = (char *)region_join(region, *size);
+ if (ret == NULL)
+ goto error;
+ return ret;
+error:
+ diag_set(OutOfMemory, *size, "region", "ret");
+ return NULL;
+}
+
static const struct port_vtab port_vdbemem_vtab = {
.dump_msgpack = NULL,
.dump_msgpack_16 = NULL,
- .dump_lua = NULL,
+ .dump_lua = port_vdbemem_dump_lua,
.dump_plain = NULL,
- .get_msgpack = NULL,
+ .get_msgpack = port_vdbemem_get_msgpack,
.get_vdbemem = port_vdbemem_get_vdbemem,
.get_context = port_vdbemem_get_context,
.destroy = NULL,
};
+
+struct sql_value *
+port_lua_get_vdbemem(struct port *base, uint32_t *size)
+{
+ struct port_lua *port = (struct port_lua *) base;
+ struct lua_State *L = port->L;
+ int argc = lua_gettop(L);
+ *size = argc;
+ struct region *region = &fiber()->gc;
+ size_t region_svp = region_used(region);
+ struct Mem *val = vdbemem_alloc_on_region(argc);
+ if (val == NULL)
+ return NULL;
+ for (int i = 0; i < argc; i++) {
+ switch(lua_type(L, -1 - i)) {
+ case LUA_TBOOLEAN:
+ mem_set_bool(&val[i], lua_toboolean(L, -1 - i));
+ break;
+ case LUA_TNUMBER:
+ sqlVdbeMemSetDouble(&val[i], lua_tonumber(L, -1 - i));
+ break;
+ case LUA_TSTRING:
+ if (sqlVdbeMemSetStr(&val[i], lua_tostring(L, -1 - i), -1,
+ 1, SQL_TRANSIENT) != 0)
+ goto error;
+ break;
+ case LUA_TNIL:
+ sqlVdbeMemSetNull(&val[i]);
+ break;
+ default:
+ diag_set(ClientError, ER_SQL_EXECUTE,
+ "Unsupported type passed from Lua");
+ goto error;
+ }
+ }
+ return (struct sql_value *)val;
+error:
+ for (int i = 0; i < argc; i++)
+ sqlVdbeMemRelease(&val[i]);
+ region_truncate(region, region_svp);
+ return NULL;
+}
+
+struct sql_value *
+port_tuple_get_vdbemem(struct port *base, uint32_t *size)
+{
+ struct port_tuple *port = (struct port_tuple *)base;
+ *size = port->size;
+ struct region *region = &fiber()->gc;
+ size_t region_svp = region_used(region);
+ struct Mem *val = vdbemem_alloc_on_region(port->size);
+ if (val == NULL)
+ return NULL;
+ int i = 0;
+ struct port_tuple_entry *pe;
+ for (pe = port->first; pe != NULL; pe = pe->next) {
+ const char *data = tuple_data(pe->tuple);
+ if (mp_typeof(*data) != MP_ARRAY ||
+ mp_decode_array(&data) != 1) {
+ diag_set(ClientError, ER_SQL_EXECUTE,
+ "Unsupported type passed from C");
+ goto error;
+ }
+ uint32_t len;
+ const char *str;
+ switch (mp_typeof(*data)) {
+ case MP_BOOL:
+ mem_set_bool(&val[i], mp_decode_bool(&data));
+ break;
+ case MP_FLOAT:
+ sqlVdbeMemSetDouble(&val[i], mp_decode_float(&data));
+ break;
+ case MP_DOUBLE:
+ sqlVdbeMemSetDouble(&val[i], mp_decode_double(&data));
+ break;
+ case MP_INT:
+ sqlVdbeMemSetInt64(val, mp_decode_int(&data));
+ break;
+ case MP_UINT:
+ sqlVdbeMemSetInt64(val, mp_decode_uint(&data));
+ break;
+ case MP_STR:
+ str = mp_decode_str(&data, &len);
+ if (sqlVdbeMemSetStr(&val[i], str, len,
+ 1, SQL_TRANSIENT) != 0)
+ goto error;
+ break;
+ case MP_NIL:
+ sqlVdbeMemSetNull(val);
+ break;
+ default:
+ diag_set(ClientError, ER_SQL_EXECUTE,
+ "Unsupported type passed from C");
+ goto error;
+ }
+ i++;
+ }
+ return (struct sql_value *) val;
+error:
+ for (int i = 0; i < port->size; i++)
+ sqlVdbeMemRelease(&val[i]);
+ region_truncate(region, region_svp);
+ return NULL;
+}
+
/*
* Implementation of the non-aggregate min() and max() functions
*/
@@ -714,19 +923,6 @@ sql_builtin_ICU##case_type(struct func *func, struct port *args, \
ICU_CASE_CONVERT(Lower);
ICU_CASE_CONVERT(Upper);
-
-/*
- * Some functions like COALESCE() and IFNULL() are implemented
- * as VDBE code so that unused argument values do not have to be
- * computed.
- * However, we still need some kind of function implementation for
- * this routines in the function table. The sql_builtin_noop macro
- * provides this. sql_builtin_noop will never be called so it
- * doesn't matter what the implementation is. We might as well
- * use the "version()" function as a substitute.
- */
-#define sql_builtin_noop sql_builtin_version
-
/*
* Implementation of random(). Return a random integer.
*/
@@ -2028,142 +2224,301 @@ sql_builtin_group_concat_finalize(struct func *func, struct port *args,
}
int
-sql_is_like_func(struct sql *db, struct Expr *expr, int *is_like_ci)
+sql_is_like_func(struct Expr *expr, int *is_like_ci)
{
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, strlen(expr->u.zToken), 2);
assert(func != NULL);
- if ((func->funcFlags & SQL_FUNC_LIKE) == 0)
+ if ((func->def->sql_flags & SQL_FUNC_LIKE) == 0)
return 0;
*is_like_ci =
(current_session()->sql_flags & LIKE_CASE_SENS_FLAG) == 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
- */
-void
-sqlRegisterBuiltinFunctions(void)
+static struct func_vtab func_sql_builtin_vtab;
+
+struct func *
+sql_func_by_signature(const char *name, uint32_t name_len, int argc)
{
- /*
- * 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_COLL(trim, 1, 3, 0, sql_builtin_trim_one_arg),
- FUNCTION_COLL(trim, 2, 3, 0, sql_builtin_trim_two_args),
- FUNCTION_COLL(trim, 3, 3, 0, sql_builtin_trim_three_args),
- FUNCTION(min, -1, 0, 1, sql_builtin_minmax, FIELD_TYPE_SCALAR),
- FUNCTION(min, 0, 0, 1, 0, FIELD_TYPE_SCALAR),
- AGGREGATE2(min, 1, 0, 1, sql_builtin_minmax_step,
- sql_builtin_minmax_finalize, SQL_FUNC_MINMAX,
- FIELD_TYPE_SCALAR),
- FUNCTION(max, -1, 1, 1, sql_builtin_minmax, FIELD_TYPE_SCALAR),
- FUNCTION(max, 0, 1, 1, 0, FIELD_TYPE_SCALAR),
- AGGREGATE2(max, 1, 1, 1, sql_builtin_minmax_step,
- sql_builtin_minmax_finalize, SQL_FUNC_MINMAX,
- FIELD_TYPE_SCALAR),
- FUNCTION2(typeof, 1, 0, 0, sql_builtin_typeof, SQL_FUNC_TYPEOF,
- FIELD_TYPE_STRING),
- FUNCTION2(length, 1, 0, 0, sql_builtin_length, SQL_FUNC_LENGTH,
- FIELD_TYPE_INTEGER),
- FUNCTION(position, 2, 0, 1, sql_builtin_position,
- FIELD_TYPE_INTEGER),
- FUNCTION(printf, -1, 0, 0, sql_builtin_printf,
- FIELD_TYPE_STRING),
- FUNCTION(unicode, 1, 0, 0, sql_builtin_unicode,
- FIELD_TYPE_STRING),
- FUNCTION(char, -1, 0, 0, sql_builtin_char, FIELD_TYPE_STRING),
- FUNCTION(abs, 1, 0, 0, sql_builtin_abs, FIELD_TYPE_NUMBER),
- FUNCTION(round, 1, 0, 0, sql_builtin_round, FIELD_TYPE_INTEGER),
- FUNCTION(round, 2, 0, 0, sql_builtin_round, FIELD_TYPE_INTEGER),
- FUNCTION_COLL(upper, 1, 0, 1, sql_builtin_ICUUpper),
- FUNCTION_COLL(lower, 1, 0, 1, sql_builtin_ICULower),
- FUNCTION(hex, 1, 0, 0, sql_builtin_hex, FIELD_TYPE_STRING),
- FUNCTION2(ifnull, 2, 0, 0, sql_builtin_noop, SQL_FUNC_COALESCE,
- FIELD_TYPE_INTEGER),
- VFUNCTION(random, 0, 0, 0, sql_builtin_random,
- FIELD_TYPE_INTEGER),
- VFUNCTION(randomblob, 1, 0, 0, sql_builtin_random_blob,
- FIELD_TYPE_SCALAR),
- FUNCTION(nullif, 2, 0, 1, sql_builtin_nullif,
- FIELD_TYPE_SCALAR),
- FUNCTION(version, 0, 0, 0, sql_builtin_version,
- FIELD_TYPE_STRING),
- FUNCTION(quote, 1, 0, 0, sql_builtin_quote, FIELD_TYPE_STRING),
- VFUNCTION(row_count, 0, 0, 0, sql_builtin_row_count,
- FIELD_TYPE_INTEGER),
- FUNCTION_COLL(replace, 3, 0, 0, sql_builtin_replace),
- FUNCTION(zeroblob, 1, 0, 0, sql_builtin_zeroblob,
- FIELD_TYPE_SCALAR),
- FUNCTION_COLL(substr, 2, 0, 0, sql_builtin_substr),
- FUNCTION_COLL(substr, 3, 0, 0, sql_builtin_substr),
- AGGREGATE(sum, 1, 0, 0, sql_builtin_sum_step,
- sql_builtin_sum_finalize, FIELD_TYPE_NUMBER),
- AGGREGATE(total, 1, 0, 0, sql_builtin_sum_step,
- sql_builtin_total_finalize, FIELD_TYPE_NUMBER),
- AGGREGATE(avg, 1, 0, 0, sql_builtin_sum_step,
- sql_builtin_avg_finalize, FIELD_TYPE_NUMBER),
- AGGREGATE2(count, 0, 0, 0, sql_builtin_count_step,
- sql_builtin_count_finalize, SQL_FUNC_COUNT,
- FIELD_TYPE_INTEGER),
- AGGREGATE(count, 1, 0, 0, sql_builtin_count_step,
- sql_builtin_count_finalize, FIELD_TYPE_INTEGER),
- AGGREGATE(group_concat, 1, 0, 0, sql_builtin_group_concat_step,
- sql_builtin_group_concat_finalize, FIELD_TYPE_STRING),
- AGGREGATE(group_concat, 2, 0, 0, sql_builtin_group_concat_step,
- sql_builtin_group_concat_finalize, FIELD_TYPE_STRING),
- FUNCTION2(like, 2, 1, 0, sql_builtin_like, SQL_FUNC_LIKE,
- FIELD_TYPE_INTEGER),
- FUNCTION2(like, 3, 1, 0, sql_builtin_like, SQL_FUNC_LIKE,
- FIELD_TYPE_INTEGER),
- FUNCTION(coalesce, 1, 0, 0, 0, FIELD_TYPE_SCALAR),
- FUNCTION(coalesce, 0, 0, 0, 0, FIELD_TYPE_SCALAR),
- FUNCTION2(coalesce, -1, 0, 0, sql_builtin_noop,
- 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");
+ struct func *ret = NULL;
+ struct func *main = func_by_name(name, name_len);
+ if (main == NULL || argc == -2)
+ return main;
+ if (main->def->param_count == argc) {
+ ret = main;
+ goto end;
+ }
+ ret = main->def->param_count < 0 ? main : NULL;
+ struct func *func;
+ rlist_foreach_entry(func, &main->signature, signature) {
+ /** Exact match. */
+ if (func->def->param_count == argc) {
+ ret = func;
+ goto end;
}
+ /** Multiparam function. */
+ if (func->def->param_count < 0)
+ ret = func;
}
-#endif
+end:
+ return ret != NULL && ret->def->exports.sql ? ret : NULL;
}
-struct func_sql_builtin {
- /** Function object base class. */
- struct func base;
+static int
+sql_builtin_stub(struct func *func, struct port *args, struct port *ret)
+{
+ (void) func; (void) args; (void) ret;
+ struct Mem *val = vdbemem_alloc_on_region(1);
+ if (val == NULL)
+ return -1;
+ diag_set(ClientError, ER_SQL_EXECUTE, "function is not implemented");
+ return -1;
+}
+
+struct sql_builtin_entry {
+ int param_count;
+ enum field_type returns;
+ uint16_t sql_flags;
+ int (*call)(struct func *func, struct port *args, struct port *ret);
+ int (*finalize)(struct func *func, struct port *args, struct port *ret);
+ void *user_data;
+ enum func_aggregate aggregate;
+ bool is_deterministic;
+ bool is_available;
};
-static struct func_vtab func_sql_builtin_vtab;
+#define BUILTIN_FUNCTION_ENTRY(param_count, returns, flags, call,\
+ user_data, is_deterministic, \
+ is_available) \
+ {param_count, returns, flags, call, NULL, user_data, \
+ FUNC_AGGREGATE_NONE, is_deterministic, is_available}
+
+#define BUILTIN_AGGREGATE_ENTRY(param_count, returns, flags, call,\
+ finalize, user_data, is_available)\
+ {param_count, returns, flags, call, finalize, user_data, \
+ FUNC_AGGREGATE_GROUP, false, is_available}
+
+#define BUILTIN_STUB_ENTRY() \
+ {-1, FIELD_TYPE_ANY, 0, sql_builtin_stub, NULL, \
+ NULL, FUNC_AGGREGATE_NONE, false, false}
+
+/**
+ * A sequence of SQL builtins definitions in
+ * lexicographic order.
+ */
+static struct {
+ const char *name;
+ int entries;
+ struct sql_builtin_entry entry[5];
+} sql_builtins[] = {
+ {"ABS", 1, {
+ BUILTIN_FUNCTION_ENTRY(1, FIELD_TYPE_NUMBER, 0,
+ sql_builtin_abs, NULL, true, true),
+ }},
+ {"AVG", 1, {
+ BUILTIN_AGGREGATE_ENTRY(1, FIELD_TYPE_NUMBER, 0,
+ sql_builtin_sum_step,
+ sql_builtin_avg_finalize, NULL, true),
+ }},
+ {"CEIL", 1, {BUILTIN_STUB_ENTRY()}},
+ {"CEILING", 1, {BUILTIN_STUB_ENTRY()}},
+ {"CHAR", 1, {
+ BUILTIN_FUNCTION_ENTRY(-1, FIELD_TYPE_STRING, 0,
+ sql_builtin_char, NULL, true, true),
+ }},
+ {"CHARACTER_LENGTH", 1, {BUILTIN_STUB_ENTRY()}},
+ {"CHAR_LENGTH", 1, {BUILTIN_STUB_ENTRY()}},
+ {"COALESCE", 3, {
+ BUILTIN_FUNCTION_ENTRY(1, FIELD_TYPE_SCALAR, 0,
+ sql_builtin_stub, NULL, true, false),
+ BUILTIN_FUNCTION_ENTRY(0, FIELD_TYPE_SCALAR, 0,
+ sql_builtin_stub, NULL, true, false),
+ BUILTIN_FUNCTION_ENTRY(-1, FIELD_TYPE_SCALAR, SQL_FUNC_COALESCE,
+ sql_builtin_stub, NULL, true, true),
+ }},
+ {"COUNT", 2, {
+ BUILTIN_AGGREGATE_ENTRY(0, FIELD_TYPE_INTEGER,
+ SQL_FUNC_COUNT,
+ sql_builtin_count_step,
+ sql_builtin_count_finalize, NULL, true),
+ BUILTIN_AGGREGATE_ENTRY(1, FIELD_TYPE_INTEGER, 0,
+ sql_builtin_count_step,
+ sql_builtin_count_finalize, NULL, true),
+ }},
+ {"CURRENT_DATE", 1, {BUILTIN_STUB_ENTRY()}},
+ {"CURRENT_TIME", 1, {BUILTIN_STUB_ENTRY()}},
+ {"CURRENT_TIMESTAMP", 1, {BUILTIN_STUB_ENTRY()}},
+ {"DATE", 1, {BUILTIN_STUB_ENTRY()}},
+ {"DATETIME", 1, {BUILTIN_STUB_ENTRY()}},
+ {"EVERY", 1, {BUILTIN_STUB_ENTRY()}},
+ {"EXISTS", 1, {BUILTIN_STUB_ENTRY()}},
+ {"EXP", 1, {BUILTIN_STUB_ENTRY()}},
+ {"EXTRACT", 1, {BUILTIN_STUB_ENTRY()}},
+ {"FLOOR", 1, {BUILTIN_STUB_ENTRY()}},
+ {"GROUP_CONCAT", 2, {
+ BUILTIN_AGGREGATE_ENTRY(1, FIELD_TYPE_STRING, 0,
+ sql_builtin_group_concat_step,
+ sql_builtin_group_concat_finalize, NULL, true),
+ BUILTIN_AGGREGATE_ENTRY(2, FIELD_TYPE_STRING, 1,
+ sql_builtin_group_concat_step,
+ sql_builtin_group_concat_finalize, NULL, true),
+ }},
+ {"HEX", 1, {
+ BUILTIN_FUNCTION_ENTRY(1, FIELD_TYPE_STRING, 0,
+ sql_builtin_hex, NULL, true, true),
+ }},
+ {"IFNULL", 1, {
+ BUILTIN_FUNCTION_ENTRY(2, FIELD_TYPE_INTEGER, SQL_FUNC_COALESCE,
+ sql_builtin_stub, NULL, true, true),
+ }},
+ {"JULIANDAY", 1, {BUILTIN_STUB_ENTRY()}},
+ {"LENGTH", 1, {
+ BUILTIN_FUNCTION_ENTRY(1, FIELD_TYPE_INTEGER, SQL_FUNC_LENGTH,
+ sql_builtin_length, NULL, true, true),
+ }},
+ {"LIKE", 2, {
+ BUILTIN_FUNCTION_ENTRY(2, FIELD_TYPE_INTEGER, SQL_FUNC_LIKE,
+ sql_builtin_like, SQL_INT_TO_PTR(1),
+ true, true),
+ BUILTIN_FUNCTION_ENTRY(3, FIELD_TYPE_INTEGER, SQL_FUNC_LIKE,
+ sql_builtin_like, SQL_INT_TO_PTR(1),
+ true, true),
+ }},
+ {"LN", 1, {BUILTIN_STUB_ENTRY()}},
+ {"LOWER", 1, {
+ BUILTIN_FUNCTION_ENTRY(1, FIELD_TYPE_STRING,
+ SQL_FUNC_DERIVEDCOLL | SQL_FUNC_NEEDCOLL,
+ sql_builtin_ICULower, NULL, true, true),
+ }},
+ {"MAX", 3, {
+ BUILTIN_FUNCTION_ENTRY(-1, FIELD_TYPE_SCALAR, SQL_FUNC_NEEDCOLL,
+ sql_builtin_minmax, SQL_INT_TO_PTR(1),
+ true, true),
+ BUILTIN_FUNCTION_ENTRY(0, FIELD_TYPE_SCALAR, 0,
+ sql_builtin_stub, SQL_INT_TO_PTR(1),
+ true, false),
+ BUILTIN_AGGREGATE_ENTRY(1, FIELD_TYPE_SCALAR,
+ SQL_FUNC_NEEDCOLL | SQL_FUNC_MINMAX,
+ sql_builtin_minmax_step,
+ sql_builtin_minmax_finalize,
+ SQL_INT_TO_PTR(1), true),
+ }},
+ {"MIN", 3, {
+ BUILTIN_FUNCTION_ENTRY(-1, FIELD_TYPE_SCALAR, SQL_FUNC_NEEDCOLL,
+ sql_builtin_minmax, SQL_INT_TO_PTR(0),
+ true, true),
+ BUILTIN_FUNCTION_ENTRY(0, FIELD_TYPE_SCALAR, 0,
+ sql_builtin_stub, SQL_INT_TO_PTR(0),
+ true, false),
+ BUILTIN_AGGREGATE_ENTRY(1, FIELD_TYPE_SCALAR,
+ SQL_FUNC_NEEDCOLL | SQL_FUNC_MINMAX,
+ sql_builtin_minmax_step,
+ sql_builtin_minmax_finalize,
+ SQL_INT_TO_PTR(0), true),
+ }},
+ {"MOD", 1, {BUILTIN_STUB_ENTRY()}},
+ {"NULLIF", 1, {
+ BUILTIN_FUNCTION_ENTRY(2, FIELD_TYPE_SCALAR, SQL_FUNC_NEEDCOLL,
+ sql_builtin_nullif, NULL, true, true),
+ }},
+ {"OCTET_LENGTH", 1, {BUILTIN_STUB_ENTRY()}},
+ {"POSITION", 1, {
+ BUILTIN_FUNCTION_ENTRY(2, FIELD_TYPE_INTEGER, SQL_FUNC_NEEDCOLL,
+ sql_builtin_position, NULL, true, true),
+ }},
+ {"POWER", 1, {BUILTIN_STUB_ENTRY()}},
+ {"PRINTF", 1, {
+ BUILTIN_FUNCTION_ENTRY(-1, FIELD_TYPE_STRING, 0,
+ sql_builtin_printf, NULL, true, true),
+ }},
+ {"QUOTE", 1, {
+ BUILTIN_FUNCTION_ENTRY(1, FIELD_TYPE_STRING, 0,
+ sql_builtin_quote, NULL, true, true),
+ }},
+ {"RANDOM", 1, {
+ BUILTIN_FUNCTION_ENTRY(0, FIELD_TYPE_INTEGER, 0,
+ sql_builtin_random, NULL, false, true),
+ }},
+ {"RANDOMBLOB", 1, {
+ BUILTIN_FUNCTION_ENTRY(1, FIELD_TYPE_SCALAR, 0,
+ sql_builtin_random_blob, NULL, false, true),
+ }},
+ {"REPLACE", 1, {
+ BUILTIN_FUNCTION_ENTRY(3, FIELD_TYPE_STRING, SQL_FUNC_DERIVEDCOLL,
+ sql_builtin_replace, NULL, true, true),
+ }},
+ {"ROUND", 2, {
+ BUILTIN_FUNCTION_ENTRY(1, FIELD_TYPE_INTEGER, 0,
+ sql_builtin_round, NULL, true, true),
+ BUILTIN_FUNCTION_ENTRY(2, FIELD_TYPE_INTEGER, 0,
+ sql_builtin_round, NULL, true, true),
+ }},
+ {"ROW_COUNT", 1, {
+ BUILTIN_FUNCTION_ENTRY(0, FIELD_TYPE_INTEGER, 0,
+ sql_builtin_row_count, NULL, true, true),
+ }},
+ {"SOME", 1, {BUILTIN_STUB_ENTRY()}},
+ {"SQRT", 1, {BUILTIN_STUB_ENTRY()}},
+ {"STRFTIME", 1, {BUILTIN_STUB_ENTRY()}},
+ {"SUBSTR", 2, {
+ BUILTIN_FUNCTION_ENTRY(2, FIELD_TYPE_STRING, SQL_FUNC_DERIVEDCOLL,
+ sql_builtin_substr, NULL, true, true),
+ BUILTIN_FUNCTION_ENTRY(3, FIELD_TYPE_STRING, SQL_FUNC_DERIVEDCOLL,
+ sql_builtin_substr, NULL, true, true),
+ }},
+ {"SUM", 1, {
+ BUILTIN_AGGREGATE_ENTRY(1, FIELD_TYPE_NUMBER, 0,
+ sql_builtin_sum_step,
+ sql_builtin_sum_finalize, NULL, true),
+ }},
+ {"TIME", 1, {BUILTIN_STUB_ENTRY()}},
+ {"TOTAL", 1, {
+ BUILTIN_AGGREGATE_ENTRY(1, FIELD_TYPE_NUMBER, 0,
+ sql_builtin_sum_step,
+ sql_builtin_total_finalize, NULL, true),
+ }},
+ {"TRIM", 3, {
+ BUILTIN_FUNCTION_ENTRY(1, FIELD_TYPE_STRING, SQL_FUNC_DERIVEDCOLL,
+ sql_builtin_trim_one_arg, NULL,
+ true, true),
+ BUILTIN_FUNCTION_ENTRY(2, FIELD_TYPE_STRING, SQL_FUNC_DERIVEDCOLL,
+ sql_builtin_trim_two_args, NULL,
+ true, true),
+ BUILTIN_FUNCTION_ENTRY(3, FIELD_TYPE_STRING, SQL_FUNC_DERIVEDCOLL,
+ sql_builtin_trim_three_args, NULL,
+ true, true),
+ }},
+ {"TYPEOF", 1, {
+ BUILTIN_FUNCTION_ENTRY(1, FIELD_TYPE_STRING, SQL_FUNC_TYPEOF,
+ sql_builtin_typeof, NULL, true, true),
+ }},
+ {"UNICODE", 1, {
+ BUILTIN_FUNCTION_ENTRY(1, FIELD_TYPE_STRING, 0,
+ sql_builtin_unicode, NULL, true, true),
+ }},
+ {"UPPER", 1, {
+ BUILTIN_FUNCTION_ENTRY(1, FIELD_TYPE_STRING,
+ SQL_FUNC_DERIVEDCOLL | SQL_FUNC_NEEDCOLL,
+ sql_builtin_ICUUpper, NULL, true, true),
+ }},
+ {"VERSION", 1, {
+ BUILTIN_FUNCTION_ENTRY(0, FIELD_TYPE_STRING, 0,
+ sql_builtin_version, NULL, true, true),
+ }},
+ {"ZEROBLOB", 1, {
+ BUILTIN_FUNCTION_ENTRY(1, FIELD_TYPE_SCALAR, 0,
+ sql_builtin_zeroblob, NULL, true, true),
+ }},
+ {"_sql_record", 1, {BUILTIN_STUB_ENTRY()}},
+ {"_sql_stat_get", 1, {BUILTIN_STUB_ENTRY()}},
+ {"_sql_stat_init", 1, {BUILTIN_STUB_ENTRY()}},
+ {"_sql_stat_push", 1, {BUILTIN_STUB_ENTRY()}},
+};
struct func *
func_sql_builtin_new(struct func_def *def)
@@ -2175,26 +2530,88 @@ func_sql_builtin_new(struct func_def *def)
"with SQL language");
return NULL;
}
- struct func_sql_builtin *func =
- (struct func_sql_builtin *) malloc(sizeof(*func));
- if (func == NULL) {
- diag_set(OutOfMemory, sizeof(*func), "malloc", "func");
+ /** 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;
+ }
+ if (idx == -1) {
+ diag_set(ClientError, ER_CREATE_FUNCTION, def->name,
+ "unknown sql builtin name");
+ return NULL;
+ }
+ int func_cnt = sql_builtins[idx].entries;
+ struct func_sql_builtin *funcs =
+ (struct func_sql_builtin *) malloc(sizeof(*funcs) * func_cnt);
+ if (funcs == NULL) {
+ diag_set(OutOfMemory, sizeof(*funcs) * func_cnt,
+ "malloc", "func");
return NULL;
}
+ int func_idx = 0;
+ for (; func_idx < func_cnt; func_idx++) {
+ struct func_def *curr_def;
+ if (func_idx == 0) {
+ curr_def = def;
+ rlist_create(&funcs[func_idx].base.signature);
+ } else {
+ curr_def = func_def_dup(def);
+ if (curr_def == NULL)
+ goto error;
+ rlist_add(&funcs[0].base.signature,
+ &funcs[func_idx].base.signature);
+ }
+ curr_def->is_deterministic =
+ sql_builtins[idx].entry[func_idx].is_deterministic;
+ curr_def->sql_flags =
+ sql_builtins[idx].entry[func_idx].sql_flags;
+ curr_def->param_count =
+ sql_builtins[idx].entry[func_idx].param_count;
+ curr_def->returns =
+ sql_builtins[idx].entry[func_idx].returns;
+ curr_def->aggregate =
+ sql_builtins[idx].entry[func_idx].aggregate;
+ curr_def->exports.sql =
+ sql_builtins[idx].entry[func_idx].is_available;
+ funcs[func_idx].base.def = curr_def;
+ funcs[func_idx].user_data =
+ sql_builtins[idx].entry[func_idx].user_data;
+ funcs[func_idx].finalize =
+ sql_builtins[idx].entry[func_idx].finalize;
+ funcs[func_idx].base.vtab = func_sql_builtin_vtab;
+ funcs[func_idx].base.vtab.call =
+ sql_builtins[idx].entry[func_idx].call;
+ }
/** Don't export SQL builtins in Lua for now. */
def->exports.lua = false;
- func->base.vtab = func_sql_builtin_vtab;
- return &func->base;
+ return &funcs[0].base;
+error:
+ for (int i = 1; i < func_idx; i++)
+ free(funcs[func_idx].base.def);
+ free(funcs);
+ return NULL;
}
static void
func_sql_builtin_destroy(struct func *base)
{
assert(base != NULL && base->def->language == FUNC_LANGUAGE_SQL_BUILTIN);
+ struct func *func, *tmp;
+ rlist_foreach_entry_safe(func, &base->signature, signature, tmp)
+ free(func->def);
free(base);
}
static struct func_vtab func_sql_builtin_vtab = {
- .call = NULL,
+ .call = sql_builtin_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 baaf7dcc9..16505ac7f 100644
--- a/src/box/sql/main.c
+++ b/src/box/sql/main.c
@@ -127,9 +127,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;
@@ -177,25 +174,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
@@ -214,118 +192,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(struct sql * db, const char *zFunctionName, enum field_type type,
- int nArg, int flags, void *pUserData,
- int (*xSFunc) (struct func *func, struct port *args,
- struct port *ret),
- int (*xStep) (struct func *func, struct port *args,
- struct port *ret),
- int (*xFinal) (struct func *func, struct port *args,
- struct port *ret),
- struct 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(struct sql * db, const char *zFunc, enum field_type type,
- int nArg, int flags, void *p,
- int (*xSFunc) (struct func *func, struct port *args,
- struct port *ret),
- int (*xStep) (struct func *func, struct port *args,
- struct port *ret),
- int (*xFinal) (struct func *func, struct port *args,
- struct port *ret),
- void (*xDestroy) (void *ctx))
-{
- 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 7c81b1980..7c67d5d56 100644
--- a/src/box/sql/resolve.c
+++ b/src/box/sql/resolve.c
@@ -38,6 +38,8 @@
#include "sqlInt.h"
#include <stdlib.h>
#include <string.h>
+#include "box/func.h"
+#include "box/field_def.h"
/*
* Walk the expression tree pExpr and increase the aggregate function
@@ -576,36 +578,31 @@ resolveExprStep(Walker * pWalker, Expr * pExpr)
int no_such_func = 0; /* True if no such function exists */
int wrong_num_args = 0; /* True if wrong 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) {
+ const char *name = pExpr->u.zToken;
+ uint32_t name_len = strlen(name);
+ struct func *func =
+ sql_func_by_signature(name, name_len, n);
+ if (func == 0) {
+ func = sql_func_by_signature(name, name_len, -2);
+ if (func == NULL) {
no_such_func = 1;
} else {
wrong_num_args = 1;
}
} else {
- is_agg = pDef->xFinalize != 0;
- pExpr->type = pDef->ret_type;
- if (pDef->
- funcFlags & (SQL_FUNC_CONSTANT |
- SQL_FUNC_SLOCHNG)) {
+ is_agg = func->def->language ==
+ FUNC_LANGUAGE_SQL_BUILTIN &&
+ func->def->aggregate ==
+ FUNC_AGGREGATE_GROUP;
+ pExpr->type = func->def->returns;
+ if (func->def->is_deterministic) {
/* 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) {
+ } else {
/* Date/time functions that use 'now', and other functions
* that might change over time cannot be used
* in an index.
@@ -614,22 +611,21 @@ resolveExprStep(Walker * pWalker, Expr * pExpr)
}
}
if (is_agg && (pNC->ncFlags & NC_AllowAgg) == 0) {
- const char *err =
- tt_sprintf("misuse of aggregate "\
- "function %.*s()", nId, zId);
- diag_set(ClientError, ER_SQL_PARSER_GENERIC, err);
+ diag_set(ClientError, ER_SQL_PARSER_GENERIC,
+ tt_sprintf("misuse of aggregate "
+ "function %s()", name));
pParse->is_aborted = true;
pNC->nErr++;
is_agg = 0;
} else if (no_such_func && pParse->db->init.busy == 0) {
- diag_set(ClientError, ER_NO_SUCH_FUNCTION, zId);
+ diag_set(ClientError, ER_NO_SUCH_FUNCTION,
+ name);
pParse->is_aborted = true;
pNC->nErr++;
} else if (wrong_num_args) {
- const char *err = "wrong number of arguments "\
- "to function %.*s()";
diag_set(ClientError, ER_SQL_PARSER_GENERIC,
- tt_sprintf(err, nId, zId));
+ tt_sprintf("wrong number of arguments "
+ "to function %s()", name));
pParse->is_aborted = true;
pNC->nErr++;
}
@@ -648,7 +644,7 @@ resolveExprStep(Walker * pWalker, Expr * pExpr)
pExpr->op2++;
pNC2 = pNC2->pNext;
}
- assert(pDef != 0);
+ assert(func != NULL);
if (pNC2) {
assert(SQL_FUNC_MINMAX ==
NC_MinMaxAgg);
@@ -656,8 +652,7 @@ resolveExprStep(Walker * pWalker, Expr * pExpr)
funcFlags &
SQL_FUNC_MINMAX) != 0);
pNC2->ncFlags |=
- NC_HasAgg | (pDef->
- funcFlags &
+ NC_HasAgg | (func->def->sql_flags &
SQL_FUNC_MINMAX);
}
diff --git a/src/box/sql/select.c b/src/box/sql/select.c
index 7c8da251e..70b39ff30 100644
--- a/src/box/sql/select.c
+++ b/src/box/sql/select.c
@@ -4374,7 +4374,7 @@ is_simple_count(struct Select *select, struct AggInfo *agg_info)
return NULL;
if (NEVER(agg_info->nFunc == 0))
return NULL;
- if ((agg_info->aFunc[0].pFunc->funcFlags & SQL_FUNC_COUNT) == 0)
+ if ((agg_info->aFunc[0].func->def->sql_flags & SQL_FUNC_COUNT) == 0)
return NULL;
if (expr->flags & EP_Distinct)
return NULL;
@@ -5269,7 +5269,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);
}
}
@@ -5310,11 +5310,11 @@ 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 ((pF->func->def->sql_flags & SQL_FUNC_NEEDCOLL) != 0) {
struct coll *coll = NULL;
struct ExprList_item *pItem;
int j;
- assert(pList != 0); /* pList!=0 if pF->pFunc has NEEDCOLL */
+ assert(pList != 0);
bool unused;
uint32_t id;
for (j = 0, pItem = pList->a; coll == NULL && j < nArg;
@@ -5329,7 +5329,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/sqlInt.h b/src/box/sql/sqlInt.h
index ce15500e5..3c24126fb 100644
--- a/src/box/sql/sqlInt.h
+++ b/src/box/sql/sqlInt.h
@@ -520,19 +520,6 @@ sql_initialize(void);
#define SQL_TRACE_ROW 0x04
#define SQL_TRACE_CLOSE 0x08
-#define SQL_DETERMINISTIC 0x800
-
-int
-sql_create_function_v2(struct sql * db, const char *zFunc, enum field_type type,
- int nArg, int flags, void *p,
- int (*xSFunc) (struct func *func, struct port *args,
- struct port *ret),
- int (*xStep) (struct func *func, struct port *args,
- struct port *ret),
- int (*xFinal) (struct func *func, struct port *args,
- struct port *ret),
- void (*xDestroy) (void *ctx));
-
#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() */
@@ -983,9 +970,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 NameContext NameContext;
@@ -1030,18 +1014,6 @@ typedef int VList;
*/
#define SQL_N_LIMIT (SQL_LIMIT_TRIGGER_DEPTH+1)
-/*
- * 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.
*/
@@ -1152,62 +1124,14 @@ 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 */
- int (*xSFunc) (struct func *func, struct port *args, struct port *ret);
- int (*xFinalize) (struct func *func, struct port *args, struct port *ret);
- 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.
+ * Possible values for FuncDef.flags. Note that the _LENGTH and
+ * _TYPEOF values must correspond to OPFLAG_LENGTHARG and
+ * OPFLAG_TYPEOFARG.
*
* 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 */
@@ -1229,11 +1153,7 @@ struct FuncDestructor {
* argument.
*/
#define SQL_FUNC_DERIVEDCOLL 0x0400
-#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
- */
/*
* Trim side mask components. TRIM_LEADING means to trim left side
@@ -1246,72 +1166,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.
- *
- * DFUNCTION(zName, nArg, 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, 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 DFUNCTION(zName, nArg, iArg, bNC, xFunc, type) \
- {nArg, SQL_FUNC_SLOCHNG|(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 STR_FUNCTION(zName, nArg, pArg, bNC, xFunc) \
- {nArg, SQL_FUNC_SLOCHNG|(bNC*SQL_FUNC_NEEDCOLL), \
- pArg, 0, xFunc, 0, #zName, {SQL_AFF_STRING, {0}}}
-#define LIKEFUNC(zName, nArg, arg, flags, type) \
- {nArg, 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}
-
/*
* All current savepoints are stored in a linked list starting at
* sql.pSavepoint. The first element in the list is the most recently
@@ -1504,7 +1358,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 */
/**
@@ -1654,7 +1509,8 @@ struct Expr {
#define EP_Static 0x008000 /* Held in memory not obtained from malloc() */
#define EP_MemToken 0x010000 /* Need to sqlDbFree() Expr.zToken */
#define EP_NoReduce 0x020000 /* Cannot EXPRDUP_REDUCE this Expr */
-#define EP_ConstFunc 0x080000 /* A sql_FUNC_CONSTANT or _SLOCHNG function */
+/** A deterministic function. */
+#define EP_ConstFunc 0x080000
#define EP_CanBeNull 0x100000 /* Can be null despite NOT NULL constraint */
#define EP_Subquery 0x200000 /* Tree contains a TK_SELECT operator */
#define EP_Alias 0x400000 /* Is an alias for a result set column */
@@ -3462,10 +3318,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.
@@ -4037,7 +3889,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;
/**
@@ -4218,18 +4069,7 @@ sql_key_info_to_key_def(struct sql_key_info *key_info);
* @retval 1 if LIKE optimization can be used, 0 otherwise.
*/
int
-sql_is_like_func(struct sql *db, struct Expr *expr, int *is_like_ci);
-
-int
-sqlCreateFunc(struct sql * db, const char *zFunctionName, enum field_type type,
- int nArg, int flags, void *pUserData,
- int (*xSFunc) (struct func *func, struct port *args,
- struct port *ret),
- int (*xStep) (struct func *func, struct port *args,
- struct port *ret),
- int (*xFinal) (struct func *func, struct port *args,
- struct port *ret),
- struct FuncDestructor * pDestructor);
+sql_is_like_func(struct Expr *expr, int *is_like_ci);
/** Set OOM error flag. */
static inline void
diff --git a/src/box/sql/vdbe.c b/src/box/sql/vdbe.c
index 2972126e3..c4f72a51b 100644
--- a/src/box/sql/vdbe.c
+++ b/src/box/sql/vdbe.c
@@ -1723,14 +1723,14 @@ case OP_Function0: {
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));
assert(pOp->p3<pOp->p2 || pOp->p3>=pOp->p2+n);
pCtx = sqlDbMallocRawNN(db, sizeof(*pCtx));
if (pCtx==0) goto no_mem;
- pCtx->pFunc = pOp->p4.pFunc;
+ pCtx->func = pOp->p4.func;
pCtx->iOp = (int)(pOp - aOp);
pCtx->pVdbe = p;
pOp->p4type = P4_FUNCCTX;
@@ -1766,7 +1766,7 @@ case OP_Function: {
}
#endif
MemSetTypeFlag(pOut, MEM_Null);
- rc = (*pCtx->pFunc->xSFunc)(NULL, &args, &ret);
+ rc = func_call(pCtx->func, &args, &ret);
if (rc != 0)
goto abort_due_to_error;
uint32_t size;
@@ -4972,15 +4972,18 @@ 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));
assert(pOp->p3<pOp->p2 || pOp->p3>=pOp->p2+n);
+ assert(pOp->p4.func != NULL &&
+ pOp->p4.func->def->language == FUNC_LANGUAGE_SQL_BUILTIN &&
+ pOp->p4.func->def->aggregate == FUNC_AGGREGATE_GROUP);
pCtx = sqlDbMallocRawNN(db, sizeof(*pCtx));
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;
pOp->p4type = P4_FUNCCTX;
@@ -5014,7 +5017,7 @@ case OP_AggStep: {
sqlVdbeMemInit(&t, db, MEM_Null);
pOut = &t;
pCtx->skipFlag = 0;
- rc = (*pCtx->pFunc->xSFunc)(NULL, &args, &ret);
+ rc = func_call(pCtx->func, &args, &ret);
region_truncate(region, region_svp);
if (rc != 0)
goto abort_due_to_error;
@@ -5045,7 +5048,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_finilize(pMem, pOp->p4.func) != 0)
goto abort_due_to_error;
UPDATE_MAX_BLOBSIZE(pMem);
if (sqlVdbeMemTooBig(pMem)) {
diff --git a/src/box/sql/vdbe.h b/src/box/sql/vdbe.h
index 4f94643cb..31a0a39d7 100644
--- a/src/box/sql/vdbe.h
+++ b/src/box/sql/vdbe.h
@@ -72,7 +72,8 @@ struct VdbeOp {
char *z; /* Pointer to data for string (char array) types */
i64 *pI64; /* Used when p4type is P4_INT64 */
double *pReal; /* Used when p4type is P4_REAL */
- FuncDef *pFunc; /* Used when p4type is P4_FUNCDEF */
+ /* 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 +123,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 function object. */
+#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 4c8363a22..0d4acb0eb 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
*/
@@ -167,7 +169,8 @@ 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 */
+ /** 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. */
@@ -291,7 +294,8 @@ mem_apply_numeric_type(struct Mem *record);
* (Mem) which are only defined there.
*/
struct sql_context {
- FuncDef *pFunc; /* Pointer to function information */
+ /** Pointer to function object. */
+ struct func *func;
Mem *pMem; /* Memory cell used to store aggregate context */
Vdbe *pVdbe; /* The VM that owns this context */
int iOp; /* Instruction number of OP_Function */
@@ -472,7 +476,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_finilize(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/sql/vdbeapi.c b/src/box/sql/vdbeapi.c
index d014e8258..c59b740a7 100644
--- a/src/box/sql/vdbeapi.c
+++ b/src/box/sql/vdbeapi.c
@@ -370,8 +370,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 &&
+ p->func->def->language == FUNC_LANGUAGE_SQL_BUILTIN);
+ return ((struct func_sql_builtin *)p->func)->user_data;
}
/*
@@ -416,7 +417,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);
}
@@ -432,7 +433,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 baeeb4624..2c6f5b279 100644
--- a/src/box/sql/vdbeaux.c
+++ b/src/box/sql/vdbeaux.c
@@ -39,6 +39,8 @@
#include "box/schema.h"
#include "box/tuple_format.h"
#include "box/txn.h"
+#include "box/func.h"
+#include "box/func_def.h"
#include "msgpuck/msgpuck.h"
#include "sqlInt.h"
#include "vdbeInt.h"
@@ -650,24 +652,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);
}
@@ -690,10 +679,6 @@ 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;
@@ -1150,15 +1135,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.pCtx->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 7bd5cdd71..b785739a6 100644
--- a/src/box/sql/vdbemem.c
+++ b/src/box/sql/vdbemem.c
@@ -39,6 +39,8 @@
#include "sqlInt.h"
#include "vdbeInt.h"
#include "tarantoolInt.h"
+#include "box/func.h"
+#include "box/func_def.h"
#include "box/schema.h"
#include "box/tuple.h"
#include "box/port.h"
@@ -306,40 +308,30 @@ sqlVdbeMemStringify(Mem * pMem, u8 bForce)
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_finilize(struct Mem *mem, struct func *func)
{
int rc = 0;
- if (ALWAYS(pFunc && pFunc->xFinalize)) {
- assert((pMem->flags & MEM_Null) != 0 || pFunc == pMem->u.pDef);
- struct sql_context ctx;
- memset(&ctx, 0, sizeof(ctx));
- ctx.pMem = pMem;
- ctx.pFunc = pFunc;
- struct region *region = &fiber()->gc;
- size_t region_svp = region_used(region);
- struct port args, ret;
- port_vdbemem_create(&args, NULL, 0, &ctx);
- rc = pFunc->xFinalize(NULL, &args, &ret);
- assert((pMem->flags & MEM_Dyn) == 0);
- if (pMem->szMalloc > 0)
- sqlDbFree(pMem->db, pMem->zMalloc);
- uint32_t size;
- struct Mem *mem = (struct Mem *)port_get_vdbemem(&ret, &size);
- if (mem != NULL) {
- *pMem = mem[0];
- } else {
- rc = -1;
- }
- region_truncate(region, region_svp);
- }
+ assert(func != NULL && func->def->aggregate == FUNC_AGGREGATE_GROUP);
+ struct sql_context ctx;
+ memset(&ctx, 0, sizeof(ctx));
+ ctx.pMem = mem;
+ ctx.func = func;
+ struct region *region = &fiber()->gc;
+ size_t region_svp = region_used(region);
+ struct port args, ret;
+ port_vdbemem_create(&args, NULL, 0, &ctx);
+ rc = ((struct func_sql_builtin *)func)->finalize(func, &args, &ret);
+ assert((mem->flags & MEM_Dyn) == 0);
+ if (mem->szMalloc > 0)
+ sqlDbFree(mem->db, mem->zMalloc);
+ uint32_t size;
+ struct Mem *val = (struct Mem *)port_get_vdbemem(&ret, &size);
+ if (val != NULL)
+ *mem = val[0];
+ else
+ rc = -1;
+ region_truncate(region, region_svp);
return rc;
}
@@ -357,7 +349,7 @@ vdbeMemClearExternAndSetNull(Mem * p)
{
assert(VdbeMemDynamic(p));
if (p->flags & MEM_Agg) {
- sqlVdbeMemFinalize(p, p->u.pDef);
+ sql_vdbemem_finilize(p, p->u.func);
assert((p->flags & MEM_Agg) == 0);
testcase(p->flags & MEM_Dyn);
}
@@ -1222,8 +1214,8 @@ valueNew(sql * db, struct ValueNewStat4Ctx *p)
* to be a scalar SQL function. If
*
* * all function arguments are SQL literals,
- * * one of the SQL_FUNC_CONSTANT or _SLOCHNG function flags is set, and
- * * the SQL_FUNC_NEEDCOLL function flag is not set,
+ * * the function is deterministic and the SQL_FUNC_NEEDCOLL
+ * flag is not set,
*
* then this routine attempts to invoke the SQL function. Assuming no
* error occurs, output parameter (*ppVal) is set to point to a value
@@ -1247,7 +1239,6 @@ valueFromFunction(sql * db, /* The database connection */
{
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 */
@@ -1258,13 +1249,11 @@ 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, strlen(p->u.zToken), nVal);
+ assert(func != NULL);
+ if ((func->def->sql_flags & SQL_FUNC_NEEDCOLL) != 0)
return 0;
- }
struct region *region = &fiber()->gc;
size_t region_svp = region_used(region);
@@ -1295,7 +1284,7 @@ valueFromFunction(sql * db, /* The database connection */
struct port args, ret;
port_vdbemem_create(&args, (struct sql_value *)apVal, nVal, NULL);
- if ((*pFunc->xSFunc)(NULL, &args, &ret) != 0) {
+ if (func_call(func, &args, &ret) != 0) {
rc = -1;
goto value_from_function_out;
}
diff --git a/src/box/sql/whereexpr.c b/src/box/sql/whereexpr.c
index befe4ce73..5736a4a0b 100644
--- a/src/box/sql/whereexpr.c
+++ b/src/box/sql/whereexpr.c
@@ -259,9 +259,9 @@ like_optimization_is_valid(Parse *pParse, Expr *pExpr, Expr **ppPrefix,
/* Result code to return. */
int rc;
- if (!sql_is_like_func(db, pExpr, pnoCase)) {
+ if (!sql_is_like_func(pExpr, pnoCase))
return 0;
- }
+
pList = pExpr->x.pList;
pLeft = pList->a[1].pExpr;
/* Value might be numeric */
diff --git a/src/lib/coll/coll.c b/src/lib/coll/coll.c
index fa27bf34e..275b1cfd7 100644
--- a/src/lib/coll/coll.c
+++ b/src/lib/coll/coll.c
@@ -36,6 +36,7 @@
#include <unicode/ucol.h>
#include <unicode/ucnv.h>
#include <unicode/ucasemap.h>
+#include <unicode/ucnv.h>
#include "tt_static.h"
struct UCaseMap *icu_ucase_default_map = NULL;
diff --git a/test/box/function1.result b/test/box/function1.result
index a8d017653..820860410 100644
--- a/test/box/function1.result
+++ b/test/box/function1.result
@@ -365,6 +365,125 @@ test_run:cmd("setopt delimiter ''");
c:close()
---
...
+--
+-- gh-2233: Invoke Lua functions created outside SQL.
+--
+box.schema.func.create("function1.divide", {language = 'C', returns = 'number', is_deterministic = true, exports = {'LUA'}})
+---
+...
+box.execute('SELECT "function1.divide"()')
+---
+- error: wrong number of arguments to function function1.divide()
+...
+box.func["function1.divide"]:drop()
+---
+...
+box.schema.func.create("function1.divide", {language = 'C', returns = 'number', is_deterministic = true, exports = {'LUA', 'SQL'}})
+---
+...
+box.execute('SELECT "function1.divide"()')
+---
+- error: invalid argument
+...
+box.execute('SELECT "function1.divide"(6)')
+---
+- error: wrong number of arguments to function function1.divide()
+...
+box.execute('SELECT "function1.divide"(6, 3)')
+---
+- error: wrong number of arguments to function function1.divide()
+...
+box.func["function1.divide"]:drop()
+---
+...
+box.schema.func.create("function1.divide", {language = 'C', returns = 'number', param_list = {'number', 'number'}, is_deterministic = true, exports = {'LUA', 'SQL'}})
+---
+...
+box.execute('SELECT "function1.divide"()')
+---
+- error: wrong number of arguments to function function1.divide()
+...
+box.execute('SELECT "function1.divide"(6)')
+---
+- error: wrong number of arguments to function function1.divide()
+...
+box.execute('SELECT "function1.divide"(6, 3, 3)')
+---
+- error: wrong number of arguments to function function1.divide()
+...
+box.execute('SELECT "function1.divide"(6, 3)')
+---
+- metadata:
+ - name: '"function1.divide"(6, 3)'
+ type: number
+ rows:
+ - [2]
+...
+box.execute('SELECT "function1.divide"(5, 2)')
+---
+- metadata:
+ - name: '"function1.divide"(5, 2)'
+ type: number
+ rows:
+ - [2.5]
+...
+box.func["function1.divide"]:drop()
+---
+...
+function SUMMARIZE(a, b) return a + b end
+---
+...
+box.schema.func.create("SUMMARIZE", {language = 'LUA', returns = 'number', is_deterministic = true, param_list = {'number', 'number'}, exports = {'LUA', 'SQL'}})
+---
+...
+box.execute('SELECT summarize(1, 2)')
+---
+- metadata:
+ - name: summarize(1, 2)
+ type: number
+ rows:
+ - [3]
+...
+box.func.SUMMARIZE:drop()
+---
+...
+box.schema.func.create("SUMMARIZE", {language = 'LUA', returns = 'number', body = 'function (a, b) return a + b end', is_deterministic = true, param_list = {'number', 'number'}, exports = {'LUA', 'SQL'}})
+---
+...
+box.execute('SELECT summarize(1, 2)')
+---
+- metadata:
+ - name: summarize(1, 2)
+ type: number
+ rows:
+ - [3]
+...
+box.func.SUMMARIZE:drop()
+---
+...
+--
+-- gh-4113: Valid method to use Lua from SQL
+--
+box.execute('SELECT lua(\'return 1 + 1\')')
+---
+- metadata:
+ - name: lua('return 1 + 1')
+ type: any
+ rows:
+ - [2]
+...
+box.execute('SELECT lua(\'return box.cfg\')')
+---
+- error: 'Failed to execute SQL statement: Unsupported type passed from Lua'
+...
+box.execute('SELECT lua(\'return box.cfg.memtx_memory\')')
+---
+- metadata:
+ - name: lua('return box.cfg.memtx_memory')
+ type: any
+ rows:
+ - [107374182]
+...
-- Test registered functions interface.
function divide(a, b) return a / b end
---
diff --git a/test/box/function1.test.lua b/test/box/function1.test.lua
index a6d750f2b..c88fae9ba 100644
--- a/test/box/function1.test.lua
+++ b/test/box/function1.test.lua
@@ -128,6 +128,41 @@ identifier.run_test(
test_run:cmd("setopt delimiter ''");
c:close()
+--
+-- gh-2233: Invoke Lua functions created outside SQL.
+--
+box.schema.func.create("function1.divide", {language = 'C', returns = 'number', is_deterministic = true, exports = {'LUA'}})
+box.execute('SELECT "function1.divide"()')
+box.func["function1.divide"]:drop()
+box.schema.func.create("function1.divide", {language = 'C', returns = 'number', is_deterministic = true, exports = {'LUA', 'SQL'}})
+box.execute('SELECT "function1.divide"()')
+box.execute('SELECT "function1.divide"(6)')
+box.execute('SELECT "function1.divide"(6, 3)')
+box.func["function1.divide"]:drop()
+box.schema.func.create("function1.divide", {language = 'C', returns = 'number', param_list = {'number', 'number'}, is_deterministic = true, exports = {'LUA', 'SQL'}})
+box.execute('SELECT "function1.divide"()')
+box.execute('SELECT "function1.divide"(6)')
+box.execute('SELECT "function1.divide"(6, 3, 3)')
+box.execute('SELECT "function1.divide"(6, 3)')
+box.execute('SELECT "function1.divide"(5, 2)')
+box.func["function1.divide"]:drop()
+
+function SUMMARIZE(a, b) return a + b end
+box.schema.func.create("SUMMARIZE", {language = 'LUA', returns = 'number', is_deterministic = true, param_list = {'number', 'number'}, exports = {'LUA', 'SQL'}})
+box.execute('SELECT summarize(1, 2)')
+box.func.SUMMARIZE:drop()
+
+box.schema.func.create("SUMMARIZE", {language = 'LUA', returns = 'number', body = 'function (a, b) return a + b end', is_deterministic = true, param_list = {'number', 'number'}, exports = {'LUA', 'SQL'}})
+box.execute('SELECT summarize(1, 2)')
+box.func.SUMMARIZE:drop()
+
+--
+-- gh-4113: Valid method to use Lua from SQL
+--
+box.execute('SELECT lua(\'return 1 + 1\')')
+box.execute('SELECT lua(\'return box.cfg\')')
+box.execute('SELECT lua(\'return box.cfg.memtx_memory\')')
+
-- Test registered functions interface.
function divide(a, b) return a / b end
box.schema.func.create("divide", {comment = 'Divide two values'})
diff --git a/test/sql-tap/alias.test.lua b/test/sql-tap/alias.test.lua
index f9e6fc9fa..75391b305 100755
--- a/test/sql-tap/alias.test.lua
+++ b/test/sql-tap/alias.test.lua
@@ -25,14 +25,13 @@ test:plan(9)
--
counter = 0
-sequence = function()
- counter = counter + 1
- return counter
-end
-- Function is declared as deterministic deliberately.
-- Otherwise it would be called as much as it occurs in a query.
-box.internal.sql_create_function("sequence", "INT", sequence, 0, true)
+box.schema.func.create('SEQUENCE', {language = 'Lua', is_deterministic = true,
+ returns = 'unsigned',
+ body = 'function() counter = counter + 1 return counter end',
+ exports = {'LUA', 'SQL'}})
test:do_test(
"alias-1.1",
@@ -220,6 +219,6 @@ test:do_test(
-- -- </alias-3.1>
-- })
-
+box.func.SEQUENCE:drop()
test:finish_test()
diff --git a/test/sql-tap/check.test.lua b/test/sql-tap/check.test.lua
index 98f906cf4..d83aafe2c 100755
--- a/test/sql-tap/check.test.lua
+++ b/test/sql-tap/check.test.lua
@@ -679,10 +679,12 @@ test:do_execsql_test(
-- cannot be tested).
--
--reset_db()
-local function myfunc(x)
- return x < 10
-end
-box.internal.sql_create_function("myfunc", "INT", myfunc)
+
+box.schema.func.create('MYFUNC', {language = 'Lua',
+ is_deterministic = true,
+ body = 'function(x) return x < 10 end',
+ returns = 'boolean', param_list = {'number'},
+ exports = {'LUA', 'SQL'}})
test:do_execsql_test(
7.1,
@@ -807,5 +809,8 @@ test:do_catchsql_test(
-- </9.3>
})
+
+box.func.MYFUNC:drop()
+
test:finish_test()
diff --git a/test/sql-tap/func5.test.lua b/test/sql-tap/func5.test.lua
index f3706e631..4134d4036 100755
--- a/test/sql-tap/func5.test.lua
+++ b/test/sql-tap/func5.test.lua
@@ -71,13 +71,25 @@ test:do_execsql_test(
global_counter = 0
-counter = function(str)
- global_counter = global_counter + 1
- return global_counter
-end
-
-box.internal.sql_create_function("counter1", "INT", counter, -1, false)
-box.internal.sql_create_function("counter2", "INT", counter, -1, true)
+box.schema.func.create('COUNTER1', {language = 'Lua', is_deterministic = false,
+ param_list = {'any'}, returns = 'integer',
+ exports = {'SQL', 'LUA'},
+ body = [[
+ function(str)
+ global_counter = global_counter + 1
+ return global_counter
+ end
+ ]]})
+
+box.schema.func.create('COUNTER2', {language = 'Lua', is_deterministic = true,
+ param_list = {'any'}, returns = 'integer',
+ exports = {'SQL', 'LUA'},
+ body = [[
+ function(str)
+ global_counter = global_counter + 1
+ return global_counter
+ end
+ ]]})
test:do_execsql_test(
"func5-2.2",
@@ -229,4 +241,7 @@ test:do_catchsql_test(
}
)
+box.func.COUNTER1:drop()
+box.func.COUNTER2:drop()
+
test:finish_test()
diff --git a/test/sql-tap/lua_sql.test.lua b/test/sql-tap/lua_sql.test.lua
index b0913e7f4..6451a2cf5 100755
--- a/test/sql-tap/lua_sql.test.lua
+++ b/test/sql-tap/lua_sql.test.lua
@@ -1,19 +1,16 @@
#!/usr/bin/env tarantool
test = require("sqltester")
NULL = require('msgpack').NULL
-test:plan(24)
-
-local function func1(a)
- return a
-end
-local function allways_2(a)
- return 2
-end
+test:plan(22)
test:do_test(
"lua_sql-1.0",
function ()
- box.internal.sql_create_function("func1", "INT", allways_2)
+ box.schema.func.create('FUNC1', {language = 'Lua',
+ is_deterministic = true,
+ body = 'function(a) return 2 end',
+ param_list = {'any'}, returns = 'integer',
+ exports = {'LUA', 'SQL'}})
return test:execsql("select func1(1)")
end,
{2})
@@ -22,34 +19,16 @@ test:do_test(
test:do_test(
"lua_sql-1.1",
function ()
- box.internal.sql_create_function("func1", "INT", func1)
+ box.func.FUNC1:drop()
+ box.schema.func.create('FUNC1', {language = 'Lua',
+ is_deterministic = true,
+ body = 'function(a) return a end',
+ param_list = {'scalar'}, returns = 'integer',
+ exports = {'LUA', 'SQL'}})
return test:execsql("select func1(1)")
end,
{1})
--- try to loose memory
-test:do_test(
- "lua_sql-1.2",
- function ()
- for i = 1, 1000000, 1 do
- box.internal.sql_create_function("func1", "INT", func1)
- end
- return test:execsql("select func1(1)")
- end,
- {1})
-
--- check sql polymorphism
-test:do_test(
- "lua_sql-1.3",
- function ()
- box.internal.sql_create_function("allways_2", "INT", allways_2, 1) -- specify 1 arg
- box.internal.sql_create_function("allways_2", "INT", func1)
- box.internal.sql_create_function("allways_2", "INT", func1, 2)
- box.internal.sql_create_function("allways_2", "INT", func1, 3)
- return test:execsql("select allways_2(1)")
- end,
- {2})
-
test:do_catchsql_test(
"lua_sql-1.0",
"select func3(1)",
@@ -72,7 +51,7 @@ for _, val in ipairs({
{result})
end
-local from_sql_to_lua = {
+from_sql_to_lua = {
[1] = {1, 1},
[2] = {"1", 1},
[3] = {"1.5", 1.5},
@@ -81,14 +60,19 @@ local from_sql_to_lua = {
[6] = {"x'0500'", "\u{0005}\u{0000}"},
[7] = {"123123123123123", 123123123123123LL},
}
-local json = require("json")
-local function check_from_sql_to_lua(i, arg)
- if from_sql_to_lua[i][2] == arg then
- return 1
- end
- return 0
-end
-box.internal.sql_create_function("check_from_sql_to_lua", "INT", check_from_sql_to_lua)
+
+box.schema.func.create('CHECK_FROM_SQL_TO_LUA', {language = 'Lua',
+ is_deterministic = true,
+ body = [[
+ function(i, arg)
+ if from_sql_to_lua[i][2] == arg then
+ return 1
+ end
+ return 0
+ end
+ ]],
+ param_list = {'integer', 'scalar'}, returns = 'integer',
+ exports = {'LUA', 'SQL'}})
-- check for different types
for i = 1, #from_sql_to_lua, 1 do
@@ -98,17 +82,23 @@ for i = 1, #from_sql_to_lua, 1 do
{1})
end
-local from_lua_to_sql = {
+from_lua_to_sql = {
[1] = {1, 1},
[2] = {"1.5", 1.5},
[3] = {"'1'", "1"},
[4] = {"true", true},
[5] = {"false", false},
}
-local function check_from_lua_to_sql(i)
- return from_lua_to_sql[i][2]
-end
-box.internal.sql_create_function("check_from_lua_to_sql", "BLOB", check_from_lua_to_sql)
+
+box.schema.func.create('CHECK_FROM_LUA_TO_SQL', {language = 'Lua',
+ is_deterministic = true,
+ body = [[
+ function(i)
+ return from_lua_to_sql[i][2]
+ end
+ ]],
+ param_list = {'integer'}, returns = 'scalar',
+ exports = {'LUA', 'SQL'}})
-- check for different types
for i = 1, #from_lua_to_sql, 1 do
@@ -118,14 +108,20 @@ for i = 1, #from_lua_to_sql, 1 do
{1})
end
-local from_lua_to_sql_bad = {
+from_lua_to_sql_bad = {
[1] = NULL,
[2] = 12LL, -- it is possible to support this type
}
-local function check_from_lua_to_sql_bad(i)
- return from_lua_to_sql_bad[i]
-end
-box.internal.sql_create_function("check_from_lua_to_sql_bad", "BLOB", check_from_lua_to_sql_bad)
+
+box.schema.func.create('CHECK_FROM_LUA_TO_SQL_BAD', {language = 'Lua',
+ is_deterministic = true,
+ body = [[
+ function(i)
+ return from_lua_to_sql_bad[i]
+ end
+ ]],
+ param_list = {'integer'}, returns = 'scalar',
+ exports = {'LUA', 'SQL'}})
for i = 1, #from_lua_to_sql_bad, 1 do
test:do_catchsql_test(
@@ -134,16 +130,26 @@ for i = 1, #from_lua_to_sql_bad, 1 do
{1, "/Unsupported/"})
end
-local function allways_error()
- error("my_error123")
- return 1
-end
-box.internal.sql_create_function("allways_error", "INT", allways_error)
+box.schema.func.create('ALLWAYS_ERROR', {language = 'Lua',
+ is_deterministic = true,
+ body = [[
+ function()
+ error("my_error123")
+ return 1
+ end
+ ]],
+ param_list = {}, returns = 'integer',
+ exports = {'LUA', 'SQL'}})
test:do_catchsql_test(
"lua_sql-2.6",
"select allways_error()",
{1, "/my_error123/"})
+box.func.FUNC1:drop()
+box.func.CHECK_FROM_SQL_TO_LUA:drop()
+box.func.CHECK_FROM_LUA_TO_SQL:drop()
+box.func.CHECK_FROM_LUA_TO_SQL_BAD:drop()
+box.func.ALLWAYS_ERROR:drop()
test:finish_test()
diff --git a/test/sql-tap/subquery.test.lua b/test/sql-tap/subquery.test.lua
index 7a3e270dc..743b7141e 100755
--- a/test/sql-tap/subquery.test.lua
+++ b/test/sql-tap/subquery.test.lua
@@ -710,17 +710,20 @@ test:do_execsql_test(
-- for a matching column name did not cause an otherwise static subquery
-- to become a dynamic (correlated) subquery.
--
-local callcnt = 0
+callcnt = 0
test:do_test(
"subquery-5.1",
function()
- local function callcntproc(n)
- callcnt = callcnt + 1
- return n
- end
-
- callcnt = 0
- box.internal.sql_create_function("callcnt", "INT", callcntproc)
+ box.schema.func.create('CALLCNT', {language = 'Lua',
+ is_deterministic = true,
+ param_list = {'integer'}, returns = 'integer',
+ exports = {'LUA', 'SQL'},
+ body = [[
+ function(n)
+ callcnt = callcnt + 1
+ return n
+ end
+ ]]})
return test:execsql [[
CREATE TABLE t4(x TEXT,y INT PRIMARY KEY);
INSERT INTO t4 VALUES('one',1);
@@ -791,6 +794,8 @@ test:do_test(
return callcnt
end, 1)
+box.func.CALLCNT:drop()
+
--############ was disable until we get #2652 fixed
-- Ticket #2652. Allow aggregate functions of outer queries inside
-- a non-aggregate subquery.
diff --git a/test/sql-tap/trigger9.test.lua b/test/sql-tap/trigger9.test.lua
index e7e170b3d..64dcaff33 100755
--- a/test/sql-tap/trigger9.test.lua
+++ b/test/sql-tap/trigger9.test.lua
@@ -46,7 +46,10 @@ local function has_rowdata(sql)
-- X(41, "X!cmd", [=[["expr","[lsearch [execsql \"explain $sql\"] RowData]>=0"]]=])
end
-box.internal.sql_create_function('randstr', 'TEXT', test.randstr, 1)
+box.schema.func.create('RANDSTR', {language = 'Lua',
+ body = 'function(n) return test.randstr(n) end',
+ param_list = {'integer'}, returns = 'string',
+ exports = {'LUA', 'SQL'}})
-- MUST_WORK_TEST
test:do_execsql_test(
@@ -450,5 +453,6 @@ test:do_execsql_test(
-- </4.3>
})
+box.func.RANDSTR:drop()
test:finish_test()
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();
]], {
-- <where2-2.5>
- "/random/"
+ "/RANDOM/"
-- </where2-2.5>
})
@@ -254,7 +254,7 @@ test:do_execsql_test(
EXPLAIN SELECT * FROM x1, x2 WHERE x=1 ORDER BY abs(5);
]], {
-- <where2-2.6>
- "~/abs/"
+ "~/ABS/"
-- </where2-2.6>
})
diff --git a/test/sql/errinj.result b/test/sql/errinj.result
index 8846e5ee8..b4b44dda4 100644
--- a/test/sql/errinj.result
+++ b/test/sql/errinj.result
@@ -439,31 +439,6 @@ errinj.set("ERRINJ_WAL_DELAY", false)
- ok
...
--
--- gh-3931: Store regular identifiers in case-normal form
---
-errinj = box.error.injection
----
-...
-errinj.set("ERRINJ_SQL_NAME_NORMALIZATION", true)
----
-- ok
-...
-box.execute("CREATE TABLE hello (id INT primary key,x INT,y INT);")
----
-- error: Failed to allocate 6 bytes in sqlDbMallocRawNN for res
-...
-dummy_f = function(int) return 1 end
----
-...
-box.internal.sql_create_function("counter1", "INT", dummy_f, -1, false)
----
-- error: Failed to allocate 9 bytes in region_alloc for res
-...
-errinj.set("ERRINJ_SQL_NAME_NORMALIZATION", false)
----
-- ok
-...
---
-- Tests which are aimed at verifying work of commit/rollback
-- triggers on _ck_constraint space.
--
diff --git a/test/sql/errinj.test.lua b/test/sql/errinj.test.lua
index 48b80a443..1250bf34b 100644
--- a/test/sql/errinj.test.lua
+++ b/test/sql/errinj.test.lua
@@ -140,16 +140,6 @@ box.execute("UPDATE t SET id = 2;")
-- Finish drop space.
errinj.set("ERRINJ_WAL_DELAY", false)
---
--- gh-3931: Store regular identifiers in case-normal form
---
-errinj = box.error.injection
-errinj.set("ERRINJ_SQL_NAME_NORMALIZATION", true)
-box.execute("CREATE TABLE hello (id INT primary key,x INT,y INT);")
-dummy_f = function(int) return 1 end
-box.internal.sql_create_function("counter1", "INT", dummy_f, -1, false)
-errinj.set("ERRINJ_SQL_NAME_NORMALIZATION", false)
-
--
-- Tests which are aimed at verifying work of commit/rollback
-- triggers on _ck_constraint space.
diff --git a/test/sql/func-recreate.result b/test/sql/func-recreate.result
index 73fb03cc4..da6d6e770 100644
--- a/test/sql/func-recreate.result
+++ b/test/sql/func-recreate.result
@@ -12,7 +12,15 @@ box.execute('pragma sql_default_engine=\''..engine..'\'')
fiber = require('fiber')
---
...
-box.internal.sql_create_function('WAITFOR', 'INT', function (n) fiber.sleep(n) return n end)
+test_run:cmd("setopt delimiter ';'")
+---
+- true
+...
+box.schema.func.create('WAITFOR', {language = 'Lua',
+ body = 'function (n) fiber.sleep(n) return n end',
+ param_list = {'integer'}, returns = 'integer',
+ exports = {'LUA', 'SQL'}})
+test_run:cmd("setopt delimiter ''");
---
...
ch = fiber.channel(1)
@@ -24,10 +32,19 @@ _ = fiber.create(function () ch:put(box.execute('select WAITFOR(0.2)')) end)
fiber.sleep(0.1)
---
...
-box.internal.sql_create_function('WAITFOR', 'INT', function (n) require('fiber').sleep(n) return n end)
+box.func.WAITFOR:drop()
+---
+...
+test_run:cmd("setopt delimiter ';'")
+---
+- true
+...
+box.schema.func.create('WAITFOR', {language = 'Lua',
+ body = 'function (n) fiber.sleep(n) return n end',
+ param_list = {'integer'}, returns = 'integer',
+ exports = {'LUA', 'SQL'}})
+test_run:cmd("setopt delimiter ''");
---
-- error: 'Failed to create function ''WAITFOR'': unable to create function due to
- active statements'
...
ch:get()
---
@@ -37,6 +54,20 @@ ch:get()
rows:
- [0.2]
...
-box.internal.sql_create_function('WAITFOR', 'INT', function (n) require('fiber').sleep(n) return n end)
+box.func.WAITFOR:drop()
+---
+...
+test_run:cmd("setopt delimiter ';'")
+---
+- true
+...
+box.schema.func.create('WAITFOR', {language = 'Lua',
+ body = 'function (n) fiber.sleep(n) return n end',
+ param_list = {'integer'}, returns = 'integer',
+ exports = {'LUA', 'SQL'}})
+test_run:cmd("setopt delimiter ''");
+---
+...
+box.func.WAITFOR:drop()
---
...
diff --git a/test/sql/func-recreate.test.lua b/test/sql/func-recreate.test.lua
index 753e9ca4d..1b6e870f3 100644
--- a/test/sql/func-recreate.test.lua
+++ b/test/sql/func-recreate.test.lua
@@ -4,14 +4,36 @@ box.execute('pragma sql_default_engine=\''..engine..'\'')
-- Check errors during function create process
fiber = require('fiber')
-box.internal.sql_create_function('WAITFOR', 'INT', function (n) fiber.sleep(n) return n end)
+
+test_run:cmd("setopt delimiter ';'")
+box.schema.func.create('WAITFOR', {language = 'Lua',
+ body = 'function (n) fiber.sleep(n) return n end',
+ param_list = {'integer'}, returns = 'integer',
+ exports = {'LUA', 'SQL'}})
+test_run:cmd("setopt delimiter ''");
ch = fiber.channel(1)
_ = fiber.create(function () ch:put(box.execute('select WAITFOR(0.2)')) end)
fiber.sleep(0.1)
-box.internal.sql_create_function('WAITFOR', 'INT', function (n) require('fiber').sleep(n) return n end)
+box.func.WAITFOR:drop()
+
+test_run:cmd("setopt delimiter ';'")
+box.schema.func.create('WAITFOR', {language = 'Lua',
+ body = 'function (n) fiber.sleep(n) return n end',
+ param_list = {'integer'}, returns = 'integer',
+ exports = {'LUA', 'SQL'}})
+test_run:cmd("setopt delimiter ''");
+
ch:get()
-box.internal.sql_create_function('WAITFOR', 'INT', function (n) require('fiber').sleep(n) return n end)
+box.func.WAITFOR:drop()
+
+test_run:cmd("setopt delimiter ';'")
+box.schema.func.create('WAITFOR', {language = 'Lua',
+ body = 'function (n) fiber.sleep(n) return n end',
+ param_list = {'integer'}, returns = 'integer',
+ exports = {'LUA', 'SQL'}})
+test_run:cmd("setopt delimiter ''");
+box.func.WAITFOR:drop()
--
2.21.0
More information about the Tarantool-patches
mailing list