[tarantool-patches] [PATCH 3/5] Annotate a sql function with affinity

  • From: Georgy Kirichenko <georgy@xxxxxxxxxxxxx>
  • To: tarantool-patches@xxxxxxxxxxxxx
  • Date: Sun, 15 Jul 2018 23:44:48 +0300

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


Other related posts:

  • » [tarantool-patches] [PATCH 3/5] Annotate a sql function with affinity - Georgy Kirichenko