From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from localhost (localhost [127.0.0.1]) by turing.freelists.org (Avenir Technologies Mail Multiplex) with ESMTP id 3190D27683 for ; Sun, 15 Jul 2018 16:44:58 -0400 (EDT) Received: from turing.freelists.org ([127.0.0.1]) by localhost (turing.freelists.org [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id VhkAmxpZ4dcb for ; Sun, 15 Jul 2018 16:44:58 -0400 (EDT) Received: from smtp44.i.mail.ru (smtp44.i.mail.ru [94.100.177.104]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by turing.freelists.org (Avenir Technologies Mail Multiplex) with ESMTPS id 9643027679 for ; Sun, 15 Jul 2018 16:44:57 -0400 (EDT) From: Georgy Kirichenko Subject: [tarantool-patches] [PATCH 3/5] Annotate a sql function with affinity Date: Sun, 15 Jul 2018 23:44:48 +0300 Message-Id: <29017adb5cbc233d1fe7ef60d2bd94fe3f97da95.1531686753.git.georgy@tarantool.org> In-Reply-To: References: Sender: tarantool-patches-bounce@freelists.org Errors-to: tarantool-patches-bounce@freelists.org Reply-To: tarantool-patches@freelists.org List-help: List-unsubscribe: List-software: Ecartis version 1.0.0 List-Id: tarantool-patches List-subscribe: List-owner: List-post: List-archive: To: tarantool-patches@freelists.org Cc: Georgy Kirichenko Any sql function should a have declared affinity. Now return value is not checked against a declared type. It is safe to declare a function with undefined type with a "BLOB" type. --- src/box/lua/lua_sql.c | 44 ++++++--- src/box/sql/analyze.c | 9 +- src/box/sql/date.c | 16 ++-- src/box/sql/func.c | 122 ++++++++++++------------ src/box/sql/main.c | 5 +- src/box/sql/sqliteInt.h | 35 +++---- src/box/sql/vdbemem.c | 2 +- test/sql-tap/alias.test.lua | 7 +- test/sql-tap/analyze5.test.lua | 1 + test/sql-tap/analyze9.test.lua | 12 +-- test/sql-tap/analyzeF.test.lua | 6 +- test/sql-tap/check.test.lua | 2 +- test/sql-tap/date.test.lua | 2 +- test/sql-tap/e_expr.test.lua | 26 ++--- test/sql-tap/func5.test.lua | 4 +- test/sql-tap/gh-3350-skip-scan.test.lua | 4 +- test/sql-tap/lua_sql.test.lua | 22 ++--- test/sql-tap/subquery.test.lua | 2 +- test/sql-tap/trigger9.test.lua | 2 +- 19 files changed, 173 insertions(+), 150 deletions(-) diff --git a/src/box/lua/lua_sql.c b/src/box/lua/lua_sql.c index 9d78679fb..f39e580a4 100644 --- a/src/box/lua/lua_sql.c +++ b/src/box/lua/lua_sql.c @@ -151,18 +151,20 @@ int lbox_sql_create_function(struct lua_State *L) char *normalized_name; struct lua_sql_func_info *func_info; int rc; + char type = '\0'; + const char *type_arg; sqlite3 *db = sql_get(); /** * Check args. Three types are possible: - * sql_create_function("func_name", func) - * sql_create_function("func_name", func, func_arg_num) - * sql_create_function("func_name", func, func_arg_num, is_deterministic) + * sql_create_function("func_name", "type", func) + * sql_create_function("func_name", "type", func, func_arg_num) + * sql_create_function("func_name", "type", func, func_arg_num, is_deterministic) */ - if ( !(argc == 2 && lua_isstring(L, 1) && lua_isfunction(L, 2)) && - !(argc == 3 && lua_isstring(L, 1) && lua_isfunction(L, 2) && - lua_isnumber(L, 3)) && - !(argc == 4 && lua_isstring(L, 1) && lua_isfunction(L, 2) && - lua_isnumber(L, 3) && lua_isboolean(L, 4))) { + 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))) { luaL_error(L, "Invalid arguments"); return 0; } @@ -170,15 +172,29 @@ int lbox_sql_create_function(struct lua_State *L) luaL_error(L, "Please call box.cfg{} first"); return 0; } - if (argc == 3) { - func_arg_num = (int) lua_tonumber(L, 3); + type_arg = lua_tostring(L, 2); + if (!strcmp(type_arg, "INT")) { + type = AFFINITY_INTEGER; + } else if (!strcmp(type_arg, "TEXT")) { + type = AFFINITY_TEXT; + } else if (!strcmp(type_arg, "FLOAT")) { + type = AFFINITY_REAL; + } else if (!strcmp(type_arg, "NUM")) { + type = AFFINITY_NUMERIC; + } else if (!strcmp(type_arg, "BLOB")) { + type = AFFINITY_BLOB; + } else { + luaL_error(L, "Unknown type"); + } + if (argc == 4) { + func_arg_num = (int) lua_tonumber(L, 4); // func should be on top of the stack because of luaL_ref api lua_pop(L, 1); } - if (argc == 4) { - if(lua_toboolean(L, 4)) + if (argc == 5) { + if(lua_toboolean(L, 5)) is_deterministic = SQLITE_DETERMINISTIC; - func_arg_num = (int) lua_tonumber(L, 3); + func_arg_num = (int) lua_tonumber(L, 4); lua_pop(L, 2); } name = lua_tostring(L, 1); @@ -188,7 +204,7 @@ int lbox_sql_create_function(struct lua_State *L) sqlite3NormalizeName(normalized_name); func_info = (struct lua_sql_func_info*)malloc(sizeof(struct lua_sql_func_info)); func_info->func_ref = luaL_ref(L, LUA_REGISTRYINDEX); - rc = sqlite3_create_function_v2(db, normalized_name, func_arg_num, + rc = sqlite3_create_function_v2(db, normalized_name, type, func_arg_num, is_deterministic, func_info, lua_sql_call, NULL, NULL, lua_sql_destroy); diff --git a/src/box/sql/analyze.c b/src/box/sql/analyze.c index 5f73f026e..fb44b69f6 100644 --- a/src/box/sql/analyze.c +++ b/src/box/sql/analyze.c @@ -399,7 +399,8 @@ static const FuncDef statInitFuncdef = { statInit, /* xSFunc */ 0, /* xFinalize */ "stat_init", /* zName */ - {0} + {0}, + 0 }; /* @@ -654,7 +655,8 @@ static const FuncDef statPushFuncdef = { statPush, /* xSFunc */ 0, /* xFinalize */ "stat_push", /* zName */ - {0} + {0}, + 0 }; #define STAT_GET_STAT1 0 /* "stat" column of stat1 table */ @@ -781,7 +783,8 @@ static const FuncDef statGetFuncdef = { statGet, /* xSFunc */ 0, /* xFinalize */ "stat_get", /* zName */ - {0} + {0}, + 0 }; static void diff --git a/src/box/sql/date.c b/src/box/sql/date.c index 9566cc386..8a3588355 100644 --- a/src/box/sql/date.c +++ b/src/box/sql/date.c @@ -1306,14 +1306,14 @@ sqlite3RegisterDateTimeFunctions(void) { static FuncDef aDateTimeFuncs[] = { #ifndef SQLITE_OMIT_DATETIME_FUNCS - DFUNCTION(julianday, -1, 0, 0, juliandayFunc), - DFUNCTION(date, -1, 0, 0, dateFunc), - DFUNCTION(time, -1, 0, 0, timeFunc), - DFUNCTION(datetime, -1, 0, 0, datetimeFunc), - DFUNCTION(strftime, -1, 0, 0, strftimeFunc), - DFUNCTION(current_time, 0, 0, 0, ctimeFunc), - DFUNCTION(current_timestamp, 0, 0, 0, ctimestampFunc), - DFUNCTION(current_date, 0, 0, 0, cdateFunc), + DFUNCTION(julianday, -1, 0, 0, juliandayFunc, AFFINITY_REAL), + DFUNCTION(date, -1, 0, 0, dateFunc, AFFINITY_REAL), + DFUNCTION(time, -1, 0, 0, timeFunc, AFFINITY_REAL), + DFUNCTION(datetime, -1, 0, 0, datetimeFunc, AFFINITY_REAL), + DFUNCTION(strftime, -1, 0, 0, strftimeFunc, AFFINITY_REAL), + DFUNCTION(current_time, 0, 0, 0, ctimeFunc, AFFINITY_REAL), + DFUNCTION(current_timestamp, 0, 0, 0, ctimestampFunc, AFFINITY_REAL), + DFUNCTION(current_date, 0, 0, 0, cdateFunc, AFFINITY_REAL), #else STR_FUNCTION(current_time, 0, "%H:%M:%S", 0, currentTimeFunc), STR_FUNCTION(current_date, 0, "%Y-%m-%d", 0, currentTimeFunc), diff --git a/src/box/sql/func.c b/src/box/sql/func.c index e211de114..fb9d47f1a 100644 --- a/src/box/sql/func.c +++ b/src/box/sql/func.c @@ -1668,7 +1668,7 @@ groupConcatFinalize(sqlite3_context * context) * a new one that always throws a run-time error. */ static inline int -sqlite3_overload_function(sqlite3 * db, const char *zName, int nArg) +sqlite3_overload_function(sqlite3 * db, const char *zName, char type, int nArg) { int rc = SQLITE_OK; @@ -1678,7 +1678,7 @@ sqlite3_overload_function(sqlite3 * db, const char *zName, int nArg) } #endif if (sqlite3FindFunction(db, zName, nArg, 0) == 0) { - rc = sqlite3CreateFunc(db, zName, nArg, 0, 0, sqlite3InvalidFunction, 0, 0, 0); + rc = sqlite3CreateFunc(db, zName, type, nArg, 0, 0, sqlite3InvalidFunction, 0, 0, 0); } rc = sqlite3ApiExit(db, rc); return rc; @@ -1692,7 +1692,7 @@ sqlite3_overload_function(sqlite3 * db, const char *zName, int nArg) void sqlite3RegisterPerConnectionBuiltinFunctions(sqlite3 * db) { - int rc = sqlite3_overload_function(db, "MATCH", 2); + int rc = sqlite3_overload_function(db, "MATCH", '\0', 2); assert(rc == SQLITE_NOMEM || rc == SQLITE_OK); if (rc == SQLITE_NOMEM) { sqlite3OomFault(db); @@ -1726,9 +1726,9 @@ sqlite3RegisterLikeFunctions(sqlite3 * db, int caseSensitive) } else { pInfo = (struct compareInfo *)&likeInfoNorm; } - sqlite3CreateFunc(db, "LIKE", 2, 0, pInfo, likeFunc, 0, 0, 0); - sqlite3CreateFunc(db, "LIKE", 3, 0, pInfo, likeFunc, 0, 0, 0); - sqlite3CreateFunc(db, "GLOB", 2, 0, (struct compareInfo *)&globInfo, likeFunc, 0, 0, 0); + sqlite3CreateFunc(db, "LIKE", AFFINITY_INTEGER, 2, 0, pInfo, likeFunc, 0, 0, 0); + sqlite3CreateFunc(db, "LIKE", AFFINITY_INTEGER, 3, 0, pInfo, likeFunc, 0, 0, 0); + sqlite3CreateFunc(db, "GLOB", AFFINITY_INTEGER, 2, 0, (struct compareInfo *)&globInfo, likeFunc, 0, 0, 0); setLikeOptFlag(db, "GLOB", SQLITE_FUNC_LIKE | SQLITE_FUNC_CASE); setLikeOptFlag(db, "LIKE", caseSensitive ? (SQLITE_FUNC_LIKE | SQLITE_FUNC_CASE) : @@ -1807,77 +1807,77 @@ sqlite3RegisterBuiltinFunctions(void) #ifdef SQLITE_SOUNDEX FUNCTION(soundex, 1, 0, 0, soundexFunc), #endif - FUNCTION2(unlikely, 1, 0, 0, noopFunc, SQLITE_FUNC_UNLIKELY), - FUNCTION2(likelihood, 2, 0, 0, noopFunc, SQLITE_FUNC_UNLIKELY), - FUNCTION2(likely, 1, 0, 0, noopFunc, SQLITE_FUNC_UNLIKELY), - FUNCTION(ltrim, 1, 1, 0, trimFunc), - FUNCTION(ltrim, 2, 1, 0, trimFunc), - FUNCTION(rtrim, 1, 2, 0, trimFunc), - FUNCTION(rtrim, 2, 2, 0, trimFunc), - FUNCTION(trim, 1, 3, 0, trimFunc), - FUNCTION(trim, 2, 3, 0, trimFunc), - FUNCTION(min, -1, 0, 1, minmaxFunc), - FUNCTION(min, 0, 0, 1, 0), + FUNCTION2(unlikely, 1, 0, 0, noopFunc, SQLITE_FUNC_UNLIKELY, AFFINITY_INTEGER), + FUNCTION2(likelihood, 2, 0, 0, noopFunc, SQLITE_FUNC_UNLIKELY, AFFINITY_INTEGER), + FUNCTION2(likely, 1, 0, 0, noopFunc, SQLITE_FUNC_UNLIKELY, AFFINITY_INTEGER), + FUNCTION(ltrim, 1, 1, 0, trimFunc, AFFINITY_TEXT), + FUNCTION(ltrim, 2, 1, 0, trimFunc, AFFINITY_TEXT), + FUNCTION(rtrim, 1, 2, 0, trimFunc, AFFINITY_TEXT), + FUNCTION(rtrim, 2, 2, 0, trimFunc, AFFINITY_TEXT), + FUNCTION(trim, 1, 3, 0, trimFunc, AFFINITY_TEXT), + FUNCTION(trim, 2, 3, 0, trimFunc, AFFINITY_TEXT), + FUNCTION(min, -1, 0, 1, minmaxFunc, 0), + FUNCTION(min, 0, 0, 1, 0, 0), AGGREGATE2(min, 1, 0, 1, minmaxStep, minMaxFinalize, - SQLITE_FUNC_MINMAX), - FUNCTION(max, -1, 1, 1, minmaxFunc), - FUNCTION(max, 0, 1, 1, 0), + SQLITE_FUNC_MINMAX, 0), + FUNCTION(max, -1, 1, 1, minmaxFunc, 0), + FUNCTION(max, 0, 1, 1, 0, 0), AGGREGATE2(max, 1, 1, 1, minmaxStep, minMaxFinalize, - SQLITE_FUNC_MINMAX), - FUNCTION2(typeof, 1, 0, 0, typeofFunc, SQLITE_FUNC_TYPEOF), - FUNCTION2(length, 1, 0, 0, lengthFunc, SQLITE_FUNC_LENGTH), - FUNCTION(instr, 2, 0, 0, instrFunc), - FUNCTION(printf, -1, 0, 0, printfFunc), - FUNCTION(unicode, 1, 0, 0, unicodeFunc), - FUNCTION(char, -1, 0, 0, charFunc), - FUNCTION(abs, 1, 0, 0, absFunc), + SQLITE_FUNC_MINMAX, 0), + FUNCTION2(typeof, 1, 0, 0, typeofFunc, SQLITE_FUNC_TYPEOF, AFFINITY_TEXT), + FUNCTION2(length, 1, 0, 0, lengthFunc, SQLITE_FUNC_LENGTH, AFFINITY_INTEGER), + FUNCTION(instr, 2, 0, 0, instrFunc, AFFINITY_INTEGER), + FUNCTION(printf, -1, 0, 0, printfFunc, AFFINITY_TEXT), + FUNCTION(unicode, 1, 0, 0, unicodeFunc, AFFINITY_TEXT), + FUNCTION(char, -1, 0, 0, charFunc, AFFINITY_TEXT), + FUNCTION(abs, 1, 0, 0, absFunc, AFFINITY_REAL), #ifndef SQLITE_OMIT_FLOATING_POINT - FUNCTION(round, 1, 0, 0, roundFunc), - FUNCTION(round, 2, 0, 0, roundFunc), + FUNCTION(round, 1, 0, 0, roundFunc, AFFINITY_INTEGER), + FUNCTION(round, 2, 0, 0, roundFunc, AFFINITY_INTEGER), #endif - FUNCTION(upper, 1, 0, 0, UpperICUFunc), - FUNCTION(lower, 1, 0, 0, LowerICUFunc), - FUNCTION(hex, 1, 0, 0, hexFunc), - FUNCTION2(ifnull, 2, 0, 0, noopFunc, SQLITE_FUNC_COALESCE), - VFUNCTION(random, 0, 0, 0, randomFunc), - VFUNCTION(randomblob, 1, 0, 0, randomBlob), - FUNCTION(nullif, 2, 0, 1, nullifFunc), - FUNCTION(version, 0, 0, 0, sql_func_version), - FUNCTION(quote, 1, 0, 0, quoteFunc), - VFUNCTION(changes, 0, 0, 0, changes), - VFUNCTION(total_changes, 0, 0, 0, total_changes), - FUNCTION(replace, 3, 0, 0, replaceFunc), - FUNCTION(zeroblob, 1, 0, 0, zeroblobFunc), - FUNCTION(substr, 2, 0, 0, substrFunc), - FUNCTION(substr, 3, 0, 0, substrFunc), - AGGREGATE(sum, 1, 0, 0, sumStep, sumFinalize), - AGGREGATE(total, 1, 0, 0, sumStep, totalFinalize), - AGGREGATE(avg, 1, 0, 0, sumStep, avgFinalize), + FUNCTION(upper, 1, 0, 0, UpperICUFunc, AFFINITY_TEXT), + FUNCTION(lower, 1, 0, 0, LowerICUFunc, AFFINITY_TEXT), + FUNCTION(hex, 1, 0, 0, hexFunc, AFFINITY_TEXT), + FUNCTION2(ifnull, 2, 0, 0, noopFunc, SQLITE_FUNC_COALESCE, AFFINITY_INTEGER), + VFUNCTION(random, 0, 0, 0, randomFunc, AFFINITY_REAL), + VFUNCTION(randomblob, 1, 0, 0, randomBlob, AFFINITY_BLOB), + FUNCTION(nullif, 2, 0, 1, nullifFunc, 0), + FUNCTION(version, 0, 0, 0, sql_func_version, AFFINITY_TEXT), + FUNCTION(quote, 1, 0, 0, quoteFunc, AFFINITY_TEXT), + VFUNCTION(changes, 0, 0, 0, changes, AFFINITY_INTEGER), + VFUNCTION(total_changes, 0, 0, 0, total_changes, AFFINITY_INTEGER), + FUNCTION(replace, 3, 0, 0, replaceFunc, AFFINITY_TEXT), + FUNCTION(zeroblob, 1, 0, 0, zeroblobFunc, AFFINITY_TEXT), + FUNCTION(substr, 2, 0, 0, substrFunc, AFFINITY_TEXT), + FUNCTION(substr, 3, 0, 0, substrFunc, AFFINITY_TEXT), + AGGREGATE(sum, 1, 0, 0, sumStep, sumFinalize, 0), + AGGREGATE(total, 1, 0, 0, sumStep, totalFinalize, 0), + AGGREGATE(avg, 1, 0, 0, sumStep, avgFinalize, 0), AGGREGATE2(count, 0, 0, 0, countStep, countFinalize, - SQLITE_FUNC_COUNT), - AGGREGATE(count, 1, 0, 0, countStep, countFinalize), + SQLITE_FUNC_COUNT, AFFINITY_INTEGER), + AGGREGATE(count, 1, 0, 0, countStep, countFinalize, AFFINITY_INTEGER), AGGREGATE(group_concat, 1, 0, 0, groupConcatStep, - groupConcatFinalize), + groupConcatFinalize, AFFINITY_TEXT), AGGREGATE(group_concat, 2, 0, 0, groupConcatStep, - groupConcatFinalize), + groupConcatFinalize, AFFINITY_TEXT), LIKEFUNC(glob, 2, &globInfo, - SQLITE_FUNC_LIKE | SQLITE_FUNC_CASE), + SQLITE_FUNC_LIKE | SQLITE_FUNC_CASE, AFFINITY_INTEGER), #ifdef SQLITE_CASE_SENSITIVE_LIKE LIKEFUNC(like, 2, &likeInfoAlt, - SQLITE_FUNC_LIKE | SQLITE_FUNC_CASE), + SQLITE_FUNC_LIKE | SQLITE_FUNC_CASE, AFFINITY_INTEGER), LIKEFUNC(like, 3, &likeInfoAlt, - SQLITE_FUNC_LIKE | SQLITE_FUNC_CASE), + SQLITE_FUNC_LIKE | SQLITE_FUNC_CASE, AFFINITY_INTEGER), #else - LIKEFUNC(like, 2, &likeInfoNorm, SQLITE_FUNC_LIKE), - LIKEFUNC(like, 3, &likeInfoNorm, SQLITE_FUNC_LIKE), + LIKEFUNC(like, 2, &likeInfoNorm, SQLITE_FUNC_LIKE, AFFINITY_INTEGER), + LIKEFUNC(like, 3, &likeInfoNorm, SQLITE_FUNC_LIKE, AFFINITY_INTEGER), #endif #ifdef SQLITE_ENABLE_UNKNOWN_SQL_FUNCTION - FUNCTION(unknown, -1, 0, 0, unknownFunc), + FUNCTION(unknown, -1, 0, 0, unknownFunc, 0), #endif - FUNCTION(coalesce, 1, 0, 0, 0), - FUNCTION(coalesce, 0, 0, 0, 0), - FUNCTION2(coalesce, -1, 0, 0, noopFunc, SQLITE_FUNC_COALESCE), + FUNCTION(coalesce, 1, 0, 0, 0, 0), + FUNCTION(coalesce, 0, 0, 0, 0, 0), + FUNCTION2(coalesce, -1, 0, 0, noopFunc, SQLITE_FUNC_COALESCE, 0), }; sqlite3AnalyzeFunctions(); sqlite3RegisterDateTimeFunctions(); diff --git a/src/box/sql/main.c b/src/box/sql/main.c index 00dc7a631..45bbff5ee 100644 --- a/src/box/sql/main.c +++ b/src/box/sql/main.c @@ -1117,6 +1117,7 @@ sqlite3_interrupt(sqlite3 * db) int sqlite3CreateFunc(sqlite3 * db, const char *zFunctionName, + enum affinity_type type, int nArg, int flags, void *pUserData, @@ -1179,12 +1180,14 @@ sqlite3CreateFunc(sqlite3 * db, p->xFinalize = xFinal; p->pUserData = pUserData; p->nArg = (u16) nArg; + p->affinity = type; return SQLITE_OK; } int sqlite3_create_function_v2(sqlite3 * db, const char *zFunc, + enum affinity_type type, int nArg, int flags, void *p, @@ -1215,7 +1218,7 @@ sqlite3_create_function_v2(sqlite3 * db, pArg->xDestroy = xDestroy; pArg->pUserData = p; } - rc = sqlite3CreateFunc(db, zFunc, nArg, flags, p, xSFunc, xStep, xFinal, + rc = sqlite3CreateFunc(db, zFunc, type, nArg, flags, p, xSFunc, xStep, xFinal, pArg); if (pArg && pArg->nRef == 0) { assert(rc != SQLITE_OK); diff --git a/src/box/sql/sqliteInt.h b/src/box/sql/sqliteInt.h index 2b3d6a35d..803887746 100644 --- a/src/box/sql/sqliteInt.h +++ b/src/box/sql/sqliteInt.h @@ -792,6 +792,7 @@ sqlite3_memory_used(void); int sqlite3_create_function_v2(sqlite3 * db, const char *zFunctionName, + enum affinity_type type, int nArg, int flags, void *pApp, @@ -1745,6 +1746,7 @@ struct FuncDef { FuncDef *pHash; /* Next with a different name but the same hash */ FuncDestructor *pDestructor; /* Reference counted destructor function */ } u; + enum affinity_type affinity; /* Return type. */ }; /* @@ -1827,30 +1829,30 @@ struct FuncDestructor { * FuncDef.flags variable is set to the value passed as the flags * parameter. */ -#define FUNCTION(zName, nArg, iArg, bNC, xFunc) \ +#define FUNCTION(zName, nArg, iArg, bNC, xFunc, type) \ {nArg, SQLITE_FUNC_CONSTANT|(bNC*SQLITE_FUNC_NEEDCOLL), \ - SQLITE_INT_TO_PTR(iArg), 0, xFunc, 0, #zName, {0} } -#define VFUNCTION(zName, nArg, iArg, bNC, xFunc) \ + SQLITE_INT_TO_PTR(iArg), 0, xFunc, 0, #zName, {0}, type } +#define VFUNCTION(zName, nArg, iArg, bNC, xFunc, type) \ {nArg, (bNC*SQLITE_FUNC_NEEDCOLL), \ - SQLITE_INT_TO_PTR(iArg), 0, xFunc, 0, #zName, {0} } -#define DFUNCTION(zName, nArg, iArg, bNC, xFunc) \ + SQLITE_INT_TO_PTR(iArg), 0, xFunc, 0, #zName, {0}, type } +#define DFUNCTION(zName, nArg, iArg, bNC, xFunc, type) \ {nArg, SQLITE_FUNC_SLOCHNG|(bNC*SQLITE_FUNC_NEEDCOLL), \ - SQLITE_INT_TO_PTR(iArg), 0, xFunc, 0, #zName, {0} } -#define FUNCTION2(zName, nArg, iArg, bNC, xFunc, extraFlags) \ + SQLITE_INT_TO_PTR(iArg), 0, xFunc, 0, #zName, {0}, type } +#define FUNCTION2(zName, nArg, iArg, bNC, xFunc, extraFlags, type) \ {nArg,SQLITE_FUNC_CONSTANT|(bNC*SQLITE_FUNC_NEEDCOLL)|extraFlags,\ - SQLITE_INT_TO_PTR(iArg), 0, xFunc, 0, #zName, {0} } + SQLITE_INT_TO_PTR(iArg), 0, xFunc, 0, #zName, {0}, type } #define STR_FUNCTION(zName, nArg, pArg, bNC, xFunc) \ {nArg, SQLITE_FUNC_SLOCHNG|(bNC*SQLITE_FUNC_NEEDCOLL), \ - pArg, 0, xFunc, 0, #zName, } -#define LIKEFUNC(zName, nArg, arg, flags) \ + pArg, 0, xFunc, 0, #zName, {SQLITE_AFF_STRING, {0}}} +#define LIKEFUNC(zName, nArg, arg, flags, type) \ {nArg, SQLITE_FUNC_CONSTANT|flags, \ - (void *)arg, 0, likeFunc, 0, #zName, {0} } -#define AGGREGATE(zName, nArg, arg, nc, xStep, xFinal) \ + (void *)arg, 0, likeFunc, 0, #zName, {0}, type } +#define AGGREGATE(zName, nArg, arg, nc, xStep, xFinal, type) \ {nArg, (nc*SQLITE_FUNC_NEEDCOLL), \ - SQLITE_INT_TO_PTR(arg), 0, xStep,xFinal,#zName, {0}} -#define AGGREGATE2(zName, nArg, arg, nc, xStep, xFinal, extraFlags) \ + SQLITE_INT_TO_PTR(arg), 0, xStep,xFinal,#zName, {0}, type} +#define AGGREGATE2(zName, nArg, arg, nc, xStep, xFinal, extraFlags, type) \ {nArg, (nc*SQLITE_FUNC_NEEDCOLL)|extraFlags, \ - SQLITE_INT_TO_PTR(arg), 0, xStep,xFinal,#zName, {0}} + SQLITE_INT_TO_PTR(arg), 0, xStep,xFinal,#zName, {0}, type} /* * All current savepoints are stored in a linked list starting at @@ -4562,7 +4564,8 @@ void sqlite3RegisterLikeFunctions(sqlite3 *, int); int sqlite3IsLikeFunction(sqlite3 *, Expr *, int *, char *); void sqlite3SchemaClear(sqlite3 *); Schema *sqlite3SchemaCreate(sqlite3 *); -int sqlite3CreateFunc(sqlite3 *, const char *, int, int, void *, +int sqlite3CreateFunc(sqlite3 *, const char *, enum affinity_type, + int, int, void *, void (*)(sqlite3_context *, int, sqlite3_value **), void (*)(sqlite3_context *, int, sqlite3_value **), void (*)(sqlite3_context *), diff --git a/src/box/sql/vdbemem.c b/src/box/sql/vdbemem.c index 2ce90747d..ed437b05d 100644 --- a/src/box/sql/vdbemem.c +++ b/src/box/sql/vdbemem.c @@ -1421,7 +1421,7 @@ void sqlite3AnalyzeFunctions(void) { static FuncDef aAnalyzeTableFuncs[] = { - FUNCTION(sqlite_record, 1, 0, 0, recordFunc), + FUNCTION(sqlite_record, 1, 0, 0, recordFunc, 0), }; sqlite3InsertBuiltinFuncs(aAnalyzeTableFuncs, ArraySize(aAnalyzeTableFuncs)); diff --git a/test/sql-tap/alias.test.lua b/test/sql-tap/alias.test.lua index 4321413cb..af73718c9 100755 --- a/test/sql-tap/alias.test.lua +++ b/test/sql-tap/alias.test.lua @@ -25,14 +25,11 @@ test:plan(9) -- counter = 0 -sequence = function() - counter = counter + 1 - return counter -end +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", sequence, 0, true) +box.internal.sql_create_function("sequence", "INT", sequence, 0, true) test:do_test( "alias-1.1", diff --git a/test/sql-tap/analyze5.test.lua b/test/sql-tap/analyze5.test.lua index d68bd3c5b..67229e78f 100755 --- a/test/sql-tap/analyze5.test.lua +++ b/test/sql-tap/analyze5.test.lua @@ -46,6 +46,7 @@ test:do_test( function() box.internal.sql_create_function( "msgpack_decode", + "BLOB", function(txt) -- MsgPack, must contain single-element array w/ string return require('msgpack').decode(txt)[1] diff --git a/test/sql-tap/analyze9.test.lua b/test/sql-tap/analyze9.test.lua index 28a6c20f3..dd46cc840 100755 --- a/test/sql-tap/analyze9.test.lua +++ b/test/sql-tap/analyze9.test.lua @@ -61,7 +61,7 @@ msgpack_decode_sample = function(txt) return decoded_str end -box.internal.sql_create_function("msgpack_decode_sample", msgpack_decode_sample) +box.internal.sql_create_function("msgpack_decode_sample", "TEXT", msgpack_decode_sample) test:do_execsql_test( 1.2, @@ -135,7 +135,7 @@ lindex = function(str, pos) return string.sub(str, pos+1, pos+1) end -box.internal.sql_create_function("lindex", lindex) +box.internal.sql_create_function("lindex", "TEXT", lindex) -- Analogue of function from tcl lrange = function(str, first, last) @@ -154,7 +154,7 @@ lrange = function(str, first, last) return res_tokens end -box.internal.sql_create_function("lrange", lrange) +box.internal.sql_create_function("lrange", "TEXT", lrange) generate_tens = function(n) tens = {} @@ -242,7 +242,7 @@ insert_filler_rows_n = function(iStart, nCopy, nVal) end end -box.internal.sql_create_function("insert_filler_rows_n", insert_filler_rows_n) +box.internal.sql_create_function("insert_filler_rows_n", "INT", insert_filler_rows_n) test:do_test( 4.1, @@ -1210,7 +1210,7 @@ r = function() return math.random(1, 15) end -box.internal.sql_create_function("r", r) +box.internal.sql_create_function("r", "NUM", r) test:do_test( 20.1, @@ -1346,7 +1346,7 @@ int_to_char = function(i) return ret end -box.internal.sql_create_function("int_to_char", int_to_char) +box.internal.sql_create_function("int_to_char", "TEXT", int_to_char) -- These tests are commented until query planer will be stable. --test:do_execsql_test( diff --git a/test/sql-tap/analyzeF.test.lua b/test/sql-tap/analyzeF.test.lua index 10cb574bd..0f67881af 100755 --- a/test/sql-tap/analyzeF.test.lua +++ b/test/sql-tap/analyzeF.test.lua @@ -22,7 +22,7 @@ local function isqrt(i) return math.floor(math.sqrt(i)) end -box.internal.sql_create_function("isqrt", isqrt) +box.internal.sql_create_function("isqrt", "FLOAT", isqrt) test:do_execsql_test( 1.0, @@ -109,9 +109,9 @@ local function det19() end -box.internal.sql_create_function("det4", det4) +box.internal.sql_create_function("det4", "NUM", det4) -box.internal.sql_create_function("det19", det19) +box.internal.sql_create_function("det19", "NUM", det19) where_clause_x = {"x = det4() AND y = det19()"} where_clauses_y = {"x = det19() AND y = det4()"} diff --git a/test/sql-tap/check.test.lua b/test/sql-tap/check.test.lua index f0b745ea6..5908d3327 100755 --- a/test/sql-tap/check.test.lua +++ b/test/sql-tap/check.test.lua @@ -721,7 +721,7 @@ test:do_execsql_test( local function myfunc(x) return x < 10 end -box.internal.sql_create_function("myfunc", myfunc) +box.internal.sql_create_function("myfunc", "INT", myfunc) test:do_execsql_test( 7.1, diff --git a/test/sql-tap/date.test.lua b/test/sql-tap/date.test.lua index 5c22bf606..624437641 100755 --- a/test/sql-tap/date.test.lua +++ b/test/sql-tap/date.test.lua @@ -451,7 +451,7 @@ local function sleeper() -- after 100 ms os.execute("sleep 0.1") end -box.internal.sql_create_function("sleeper", sleeper) +box.internal.sql_create_function("sleeper", "INT", sleeper) -- db("func", "sleeper", "sleeper") test:do_test( "date-15.1", diff --git a/test/sql-tap/e_expr.test.lua b/test/sql-tap/e_expr.test.lua index d617f0909..ab0acecb6 100755 --- a/test/sql-tap/e_expr.test.lua +++ b/test/sql-tap/e_expr.test.lua @@ -43,8 +43,8 @@ end local function regexfunc(a, b) return (a == b) end -box.internal.sql_create_function("MATCH", matchfunc) -box.internal.sql_create_function("REGEXP", regexfunc) +box.internal.sql_create_function("MATCH", "INT", matchfunc) +box.internal.sql_create_function("REGEXP", "INT", regexfunc) -- Set up three global variables: -- @@ -633,7 +633,7 @@ end local function reverse_collate(zLeft, zRight) return reverse_str(zLeft) > reverse_str(zRight) end -box.internal.sql_create_function("REVERSE", reverse_collate) +box.internal.sql_create_function("REVERSE", "INT", reverse_collate) --db("collate", "reverse", "reverse_collate") -- EVIDENCE-OF: R-59577-33471 The COLLATE operator is a unary postfix -- operator that assigns a collating sequence to an expression. @@ -1257,9 +1257,9 @@ local function glob(args) return 1 end -box.internal.sql_create_function("GLOB", glob) -box.internal.sql_create_function("MATCH", glob) -box.internal.sql_create_function("REGEXP", glob) +box.internal.sql_create_function("GLOB", "INT", glob) +box.internal.sql_create_function("MATCH", "INT", glob) +box.internal.sql_create_function("REGEXP", "INT", glob) local test_cases12 ={ {1, 123}, {2, 123.4e05}, @@ -1418,7 +1418,7 @@ local function func_x() xcount = xcount + 1 return x end -box.internal.sql_create_function("X", func_x) +box.internal.sql_create_function("X", "INT", func_x) local test_cases13 = { {1, 10, "x() >= 5 AND x() <= 15", 1, 2}, {2, 10, "x() BETWEEN 5 AND 15", 1, 1}, @@ -2106,7 +2106,7 @@ function likefunc(...) return 1 end -box.internal.sql_create_function("LIKE", likefunc) +box.internal.sql_create_function("LIKE", "INT", likefunc) --db("func", "like", "-argcount", 2, "likefunc") --db("func", "like", "-argcount", 3, "likefunc") test:do_execsql_test( @@ -2410,7 +2410,7 @@ local function globfunc(...) end return 1 end -box.internal.sql_create_function("GLOB", globfunc, 2) +box.internal.sql_create_function("GLOB", "INT", globfunc, 2) --db("func", "glob", "-argcount", 2, "globfunc") test:do_execsql_test( @@ -2478,7 +2478,7 @@ local function regexpfunc(...) end return 1 end -box.internal.sql_create_function("REGEXP", regexpfunc, 2) +box.internal.sql_create_function("REGEXP", "INT", regexpfunc, 2) --db("func", "regexp", "-argcount", 2, "regexpfunc") test:do_execsql_test( @@ -2561,7 +2561,7 @@ local function matchfunc(...) end return 1 end -box.internal.sql_create_function("MATCH", matchfunc, 2) +box.internal.sql_create_function("MATCH", "INT", matchfunc, 2) test:do_execsql_test( "e_expr-19.2.1", @@ -2639,7 +2639,7 @@ local function var(nm) local result = loadstring("return "..nm)() return result end -box.internal.sql_create_function("VAR", var) +box.internal.sql_create_function("VAR", "BLOB", var) --db("func", "var", "var") -- EVIDENCE-OF: R-30638-59954 In a CASE without a base expression, each -- WHEN expression is evaluated and the result treated as a boolean, @@ -3050,7 +3050,7 @@ local function ceval(x) evalcount = evalcount + 1 return x end -box.internal.sql_create_function("CEVAL", ceval) +box.internal.sql_create_function("CEVAL", "BLOB", ceval) evalcount = 0 test:do_execsql_test( "e_expr-26.1.1", diff --git a/test/sql-tap/func5.test.lua b/test/sql-tap/func5.test.lua index c8aa4f135..493b50552 100755 --- a/test/sql-tap/func5.test.lua +++ b/test/sql-tap/func5.test.lua @@ -76,8 +76,8 @@ counter = function(str) return global_counter end -box.internal.sql_create_function("counter1", counter, -1, false) -box.internal.sql_create_function("counter2", counter, -1, true) +box.internal.sql_create_function("counter1", "INT", counter, -1, false) +box.internal.sql_create_function("counter2", "INT", counter, -1, true) test:do_execsql_test( "func5-2.2", diff --git a/test/sql-tap/gh-3350-skip-scan.test.lua b/test/sql-tap/gh-3350-skip-scan.test.lua index 301b04a13..4cecfe081 100755 --- a/test/sql-tap/gh-3350-skip-scan.test.lua +++ b/test/sql-tap/gh-3350-skip-scan.test.lua @@ -19,8 +19,8 @@ local function int_to_char(i) return res end -box.internal.sql_create_function("lindex", lindex) -box.internal.sql_create_function("int_to_char", int_to_char) +box.internal.sql_create_function("lindex", "TEXT", lindex) +box.internal.sql_create_function("int_to_char", "TEXT", int_to_char) test:do_execsql_test( "skip-scan-1.1", diff --git a/test/sql-tap/lua_sql.test.lua b/test/sql-tap/lua_sql.test.lua index 394922a2f..68bb24ff2 100755 --- a/test/sql-tap/lua_sql.test.lua +++ b/test/sql-tap/lua_sql.test.lua @@ -13,7 +13,7 @@ end test:do_test( "lua_sql-1.0", function () - box.internal.sql_create_function("func1", allways_2) + box.internal.sql_create_function("func1", "INT", allways_2) return test:execsql("select func1(1)") end, {2}) @@ -22,7 +22,7 @@ test:do_test( test:do_test( "lua_sql-1.1", function () - box.internal.sql_create_function("func1", func1) + box.internal.sql_create_function("func1", "INT", func1) return test:execsql("select func1(1)") end, {1}) @@ -32,7 +32,7 @@ test:do_test( "lua_sql-1.2", function () for i = 1, 1000000, 1 do - box.internal.sql_create_function("func1", func1) + box.internal.sql_create_function("func1", "INT", func1) end return test:execsql("select func1(1)") end, @@ -42,10 +42,10 @@ test:do_test( test:do_test( "lua_sql-1.3", function () - box.internal.sql_create_function("allways_2", allways_2, 1) -- specify 1 arg - box.internal.sql_create_function("allways_2", func1) - box.internal.sql_create_function("allways_2", func1, 2) - box.internal.sql_create_function("allways_2", func1, 3) + 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}) @@ -88,7 +88,7 @@ local function check_from_sql_to_lua(i, arg) end return 0 end -box.internal.sql_create_function("check_from_sql_to_lua", check_from_sql_to_lua) +box.internal.sql_create_function("check_from_sql_to_lua", "INT", check_from_sql_to_lua) -- check for different types for i = 1, #from_sql_to_lua, 1 do @@ -108,7 +108,7 @@ local from_lua_to_sql = { 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", check_from_lua_to_sql) +box.internal.sql_create_function("check_from_lua_to_sql", "BLOB", check_from_lua_to_sql) -- check for different types for i = 1, #from_lua_to_sql, 1 do @@ -125,7 +125,7 @@ local from_lua_to_sql_bad = { 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", check_from_lua_to_sql_bad) +box.internal.sql_create_function("check_from_lua_to_sql_bad", "BLOB", check_from_lua_to_sql_bad) for i = 1, #from_lua_to_sql_bad, 1 do test:do_catchsql_test( @@ -138,7 +138,7 @@ local function allways_error() error("my_error123") return 1 end -box.internal.sql_create_function("allways_error", allways_error) +box.internal.sql_create_function("allways_error", "INT", allways_error) test:do_catchsql_test( "lua_sql-2.6", diff --git a/test/sql-tap/subquery.test.lua b/test/sql-tap/subquery.test.lua index 119f60268..fbaac7a02 100755 --- a/test/sql-tap/subquery.test.lua +++ b/test/sql-tap/subquery.test.lua @@ -720,7 +720,7 @@ test:do_test( end callcnt = 0 - box.internal.sql_create_function("callcnt", callcntproc) + box.internal.sql_create_function("callcnt", "INT", callcntproc) return test:execsql [[ CREATE TABLE t4(x,y PRIMARY KEY); INSERT INTO t4 VALUES('one',1); diff --git a/test/sql-tap/trigger9.test.lua b/test/sql-tap/trigger9.test.lua index 840d184bf..f854fb873 100755 --- a/test/sql-tap/trigger9.test.lua +++ b/test/sql-tap/trigger9.test.lua @@ -46,7 +46,7 @@ local function has_rowdata(sql) -- X(41, "X!cmd", [=[["expr","[lsearch [execsql \"explain $sql\"] RowData]>=0"]]=]) end -box.internal.sql_create_function('randstr', test.randstr, 1) +box.internal.sql_create_function('randstr', 'TEXT', test.randstr, 1) -- MUST_WORK_TEST test:do_execsql_test( -- 2.18.0