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

Georgy Kirichenko georgy at tarantool.org
Sun Jul 15 23:44:48 MSK 2018


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





More information about the Tarantool-patches mailing list