[tarantool-patches] [PATCH v2 5/8] sql: introduce a signature_mask for functions

Kirill Shcherbatov kshcherbatov at tarantool.org
Thu Aug 8 17:50:49 MSK 2019


This patch replaces nArgs field with signature_mask bitmask that
allows to use an only hash table entry for all builtin functions
overloads.

The code refactoring is not a goal of this patch: the most of
affected code would be removed in following patches. The role of
this patch itself is to introduce such mechanism (signature_mask)
in Tarantool's SQL.

Needed for #2200, #4113, #2233
---
 src/box/sql/sqlInt.h   | 70 ++++++++++++++++++++------------
 src/box/sql/callback.c | 22 +++-------
 src/box/sql/func.c     | 91 ++++++++++++++++++------------------------
 src/box/sql/main.c     |  4 +-
 src/box/sql/vdbeaux.c  |  6 ++-
 5 files changed, 94 insertions(+), 99 deletions(-)

diff --git a/src/box/sql/sqlInt.h b/src/box/sql/sqlInt.h
index 941c87420..114ac0e4b 100644
--- a/src/box/sql/sqlInt.h
+++ b/src/box/sql/sqlInt.h
@@ -1254,7 +1254,20 @@ struct type_def {
  * field is used by per-connection app-def functions.
  */
 struct FuncDef {
-	i8 nArg;		/* Number of arguments.  -1 means unlimited */
+	/**
+	 * A bitmask representing all supported function
+	 * overloads. The function supports argc == n iff this
+	 * bitmask has bit n set 1. In particular case, a bitmask
+	 * (~0) means this function works with any possible
+	 * argument.
+	 *
+	 * The count of arguments for function is limited with
+	 * (CHAR_BITS*sizeof(uint64_t) - 1). When the highest bit
+	 * of the mask is set, this means that greater values
+	 * are supported. E.g. greatest function works correctly
+	 * with any number of input arguments.
+	 */
+	uint64_t signature_mask;
 	u16 funcFlags;		/* Some combination of sql_FUNC_* */
 	void *pUserData;	/* User data parameter */
 	FuncDef *pNext;		/* Next function with same name */
@@ -1342,9 +1355,9 @@ enum trim_side_mask {
  * The following three macros, FUNCTION(), LIKEFUNC() and AGGREGATE() are
  * used to create the initializers for the FuncDef structures.
  *
- *   FUNCTION(zName, nArg, iArg, bNC, xFunc)
+ *   FUNCTION(zName, mask, iArg, bNC, xFunc)
  *     Used to create a scalar function definition of a function zName
- *     implemented by C function xFunc that accepts nArg arguments. The
+ *     implemented by C function xFunc that accepts mask arguments. The
  *     value passed as iArg is cast to a (void*) and made available
  *     as the user-data (sql_user_data()) for the function. If
  *     argument bNC is true, then the sql_FUNC_NEEDCOLL flag is set.
@@ -1354,56 +1367,61 @@ enum trim_side_mask {
  *     STRING which collation should be derived from first
  *     argument (trim, substr etc).
  *
- *   VFUNCTION(zName, nArg, iArg, bNC, xFunc)
+ *   VFUNCTION(zName, mask, iArg, bNC, xFunc)
  *     Like FUNCTION except it omits the sql_FUNC_CONSTANT flag.
  *
- *   DFUNCTION(zName, nArg, iArg, bNC, xFunc)
+ *   DFUNCTION(zName, mask, iArg, bNC, xFunc)
  *     Like FUNCTION except it omits the sql_FUNC_CONSTANT flag and
  *     adds the sql_FUNC_SLOCHNG flag.  Used for date & time functions,
  *     but not during a single query.
  *
- *   AGGREGATE(zName, nArg, iArg, bNC, xStep, xFinal)
+ *   AGGREGATE(zName, mask, iArg, bNC, xStep, xFinal)
  *     Used to create an aggregate function definition implemented by
  *     the C functions xStep and xFinal. The first four parameters
  *     are interpreted in the same way as the first 4 parameters to
  *     FUNCTION().
  *
- *   LIKEFUNC(zName, nArg, pArg, flags)
+ *   LIKEFUNC(zName, mask, pArg, flags)
  *     Used to create a scalar function definition of a function zName
- *     that accepts nArg arguments and is implemented by a call to C
+ *     that accepts mask arguments and is implemented by a call to C
  *     function likeFunc. Argument pArg is cast to a (void *) and made
  *     available as the function user-data (sql_user_data()). The
  *     FuncDef.flags variable is set to the value passed as the flags
  *     parameter.
  */
-#define FUNCTION(zName, nArg, iArg, bNC, xFunc, type) \
-  {nArg, SQL_FUNC_CONSTANT|(bNC*SQL_FUNC_NEEDCOLL), \
+#define FUNCTION(zName, mask, iArg, bNC, xFunc, type) \
+  {mask, SQL_FUNC_CONSTANT|(bNC*SQL_FUNC_NEEDCOLL), \
    SQL_INT_TO_PTR(iArg), 0, xFunc, 0, #zName, {0}, type}
-#define FUNCTION_COLL(zName, nArg, iArg, bNC, xFunc) \
-  {nArg, SQL_FUNC_CONSTANT|SQL_FUNC_DERIVEDCOLL|(bNC*SQL_FUNC_NEEDCOLL), \
+#define FUNCTION_COLL(zName, mask, iArg, bNC, xFunc) \
+  {mask, SQL_FUNC_CONSTANT|SQL_FUNC_DERIVEDCOLL|(bNC*SQL_FUNC_NEEDCOLL), \
    SQL_INT_TO_PTR(iArg), 0, xFunc, 0, #zName, {0}, FIELD_TYPE_STRING}
-#define VFUNCTION(zName, nArg, iArg, bNC, xFunc, type) \
-  {nArg, (bNC*SQL_FUNC_NEEDCOLL), \
+#define VFUNCTION(zName, mask, iArg, bNC, xFunc, type) \
+  {mask, (bNC*SQL_FUNC_NEEDCOLL), \
    SQL_INT_TO_PTR(iArg), 0, xFunc, 0, #zName, {0}, type}
-#define DFUNCTION(zName, nArg, iArg, bNC, xFunc, type) \
-  {nArg, SQL_FUNC_SLOCHNG|(bNC*SQL_FUNC_NEEDCOLL), \
+#define DFUNCTION(zName, mask, iArg, bNC, xFunc, type) \
+  {mask, SQL_FUNC_SLOCHNG|(bNC*SQL_FUNC_NEEDCOLL), \
    SQL_INT_TO_PTR(iArg), 0, xFunc, 0, #zName, {0}, type}
-#define FUNCTION2(zName, nArg, iArg, bNC, xFunc, extraFlags, type) \
-  {nArg,SQL_FUNC_CONSTANT|(bNC*SQL_FUNC_NEEDCOLL)|extraFlags,\
+#define FUNCTION2(zName, mask, iArg, bNC, xFunc, extraFlags, type) \
+  {mask,SQL_FUNC_CONSTANT|(bNC*SQL_FUNC_NEEDCOLL)|extraFlags,\
    SQL_INT_TO_PTR(iArg), 0, xFunc, 0, #zName, {0}, type}
-#define STR_FUNCTION(zName, nArg, pArg, bNC, xFunc) \
-  {nArg, SQL_FUNC_SLOCHNG|(bNC*SQL_FUNC_NEEDCOLL), \
+#define STR_FUNCTION(zName, mask, pArg, bNC, xFunc) \
+  {mask, SQL_FUNC_SLOCHNG|(bNC*SQL_FUNC_NEEDCOLL), \
    pArg, 0, xFunc, 0, #zName, {SQL_AFF_STRING, {0}}}
-#define LIKEFUNC(zName, nArg, arg, flags, type) \
-  {nArg, SQL_FUNC_NEEDCOLL|SQL_FUNC_CONSTANT|flags, \
+#define LIKEFUNC(zName, mask, arg, flags, type) \
+  {mask, SQL_FUNC_NEEDCOLL|SQL_FUNC_CONSTANT|flags, \
    (void *)(SQL_INT_TO_PTR(arg)), 0, likeFunc, 0, #zName, {0}, type}
-#define AGGREGATE(zName, nArg, arg, nc, xStep, xFinal, type) \
-  {nArg, (nc*SQL_FUNC_NEEDCOLL), \
+#define AGGREGATE(zName, mask, arg, nc, xStep, xFinal, type) \
+  {mask, (nc*SQL_FUNC_NEEDCOLL), \
    SQL_INT_TO_PTR(arg), 0, xStep,xFinal,#zName, {0}, type}
-#define AGGREGATE2(zName, nArg, arg, nc, xStep, xFinal, extraFlags, type) \
-  {nArg, (nc*SQL_FUNC_NEEDCOLL)|extraFlags, \
+#define AGGREGATE2(zName, mask, arg, nc, xStep, xFinal, extraFlags, type) \
+  {mask, (nc*SQL_FUNC_NEEDCOLL)|extraFlags, \
    SQL_INT_TO_PTR(arg), 0, xStep,xFinal,#zName, {0}, type}
 
+#define ARGC_MASK_FULL (~0ULL)
+#define ARGC_MASK(a) (a < 0 ? ARGC_MASK_FULL : (1ULL << a))
+#define ARGC_MASK2(a, b) (ARGC_MASK(a) | ARGC_MASK(b))
+#define ARGC_MASK3(a, b, c) (ARGC_MASK2(a, b) | ARGC_MASK(c))
+
 /*
  * All current savepoints are stored in a linked list starting at
  * sql.pSavepoint. The first element in the list is the most recently
diff --git a/src/box/sql/callback.c b/src/box/sql/callback.c
index 6c272de71..737c24d98 100644
--- a/src/box/sql/callback.c
+++ b/src/box/sql/callback.c
@@ -36,6 +36,7 @@
  */
 
 #include "box/coll_id_cache.h"
+#include "box/column_mask.h"
 #include "sqlInt.h"
 #include "box/session.h"
 
@@ -91,26 +92,13 @@ matchQuality(FuncDef * p,	/* The function we are evaluating for match quality */
 	     int nArg		/* Desired number of arguments.  (-1)==any */
     )
 {
-	int match;
-
 	/* nArg of -2 is a special case */
 	if (nArg == (-2))
-		return (p->xSFunc == 0) ? 0 : FUNC_PERFECT_MATCH;
-
+		return FUNC_PERFECT_MATCH;
 	/* Wrong number of arguments means "no match" */
-	if (p->nArg != nArg && p->nArg >= 0)
+	if (!column_mask_fieldno_is_set(p->signature_mask, (uint32_t)nArg))
 		return 0;
-
-	/* Give a better score to a function with a specific number of arguments
-	 * than to function that accepts any number of arguments.
-	 */
-	if (p->nArg == nArg) {
-		match = 4;
-	} else {
-		match = 1;
-	}
-
-	return match;
+	return p->signature_mask == ARGC_MASK(nArg) ? FUNC_PERFECT_MATCH : 1;
 }
 
 /*
@@ -240,7 +228,7 @@ sqlFindFunction(sql * db,	/* An open database */
 	     sqlDbMallocZero(db, sizeof(*pBest) + nName + 1)) != 0) {
 		FuncDef *pOther;
 		pBest->zName = (const char *)&pBest[1];
-		pBest->nArg = (u16) nArg;
+		pBest->signature_mask = ARGC_MASK(nArg);
 		pBest->funcFlags = 0;
 		memcpy((char *)&pBest[1], zName, nName + 1);
 		pOther =
diff --git a/src/box/sql/func.c b/src/box/sql/func.c
index e011fd958..8e07ce892 100644
--- a/src/box/sql/func.c
+++ b/src/box/sql/func.c
@@ -1859,73 +1859,60 @@ sqlRegisterBuiltinFunctions(void)
 	 * For peak efficiency, put the most frequently used function last.
 	 */
 	static FuncDef aBuiltinFunc[] = {
-		FUNCTION(soundex, 1, 0, 0, soundexFunc, FIELD_TYPE_STRING),
-		FUNCTION2(unlikely, 1, 0, 0, noopFunc, SQL_FUNC_UNLIKELY,
+		FUNCTION(soundex, ARGC_MASK(1), 0, 0, soundexFunc, FIELD_TYPE_STRING),
+		FUNCTION2(unlikely, ARGC_MASK(1), 0, 0, noopFunc, SQL_FUNC_UNLIKELY,
 			  FIELD_TYPE_BOOLEAN),
-		FUNCTION2(likelihood, 2, 0, 0, noopFunc, SQL_FUNC_UNLIKELY,
+		FUNCTION2(likelihood, ARGC_MASK(2), 0, 0, noopFunc, SQL_FUNC_UNLIKELY,
 			  FIELD_TYPE_BOOLEAN),
-		FUNCTION2(likely, 1, 0, 0, noopFunc, SQL_FUNC_UNLIKELY,
+		FUNCTION2(likely, ARGC_MASK(1), 0, 0, noopFunc, SQL_FUNC_UNLIKELY,
 			  FIELD_TYPE_BOOLEAN),
-		FUNCTION_COLL(trim, 1, 3, 0, trim_func),
-		FUNCTION_COLL(trim, 2, 3, 0, trim_func),
-		FUNCTION_COLL(trim, 3, 3, 0, trim_func),
-		FUNCTION(least, -1, 0, 1, minmaxFunc, FIELD_TYPE_SCALAR),
-		AGGREGATE2(min, 1, 0, 1, minmaxStep, minMaxFinalize,
+		FUNCTION_COLL(trim, ARGC_MASK3(1, 2, 3), 3, 0, trim_func),
+		FUNCTION(least, ARGC_MASK_FULL, 0, 1, minmaxFunc, FIELD_TYPE_SCALAR),
+		AGGREGATE2(min, ARGC_MASK(1), 0, 1, minmaxStep, minMaxFinalize,
 			   SQL_FUNC_MINMAX, FIELD_TYPE_SCALAR),
-		FUNCTION(greatest, -1, 1, 1, minmaxFunc, FIELD_TYPE_SCALAR),
-		AGGREGATE2(max, 1, 1, 1, minmaxStep, minMaxFinalize,
+		FUNCTION(greatest, ARGC_MASK_FULL, 1, 1, minmaxFunc, FIELD_TYPE_SCALAR),
+		AGGREGATE2(max, ARGC_MASK(1), 1, 1, minmaxStep, minMaxFinalize,
 			   SQL_FUNC_MINMAX, FIELD_TYPE_SCALAR),
-		FUNCTION2(typeof, 1, 0, 0, typeofFunc, SQL_FUNC_TYPEOF,
+		FUNCTION2(typeof, ARGC_MASK(1), 0, 0, typeofFunc, SQL_FUNC_TYPEOF,
 			  FIELD_TYPE_STRING),
-		FUNCTION2(length, 1, 0, 0, lengthFunc, SQL_FUNC_LENGTH,
+		FUNCTION2(length, ARGC_MASK(1), 0, 0, lengthFunc, SQL_FUNC_LENGTH,
 			  FIELD_TYPE_INTEGER),
-		FUNCTION(char_length, 1, 0, 0, lengthFunc, FIELD_TYPE_INTEGER),
-		FUNCTION(character_length, 1, 0, 0, lengthFunc,
+		FUNCTION(char_length, ARGC_MASK(1), 0, 0, lengthFunc, FIELD_TYPE_INTEGER),
+		FUNCTION(character_length, ARGC_MASK(1), 0, 0, lengthFunc,
 			 FIELD_TYPE_INTEGER),
-		FUNCTION(position, 2, 0, 1, position_func, FIELD_TYPE_INTEGER),
-		FUNCTION(printf, -1, 0, 0, printfFunc, FIELD_TYPE_STRING),
-		FUNCTION(unicode, 1, 0, 0, unicodeFunc, FIELD_TYPE_STRING),
-		FUNCTION(char, -1, 0, 0, charFunc, FIELD_TYPE_STRING),
-		FUNCTION(abs, 1, 0, 0, absFunc, FIELD_TYPE_NUMBER),
-		FUNCTION(round, 1, 0, 0, roundFunc, FIELD_TYPE_INTEGER),
-		FUNCTION(round, 2, 0, 0, roundFunc, FIELD_TYPE_INTEGER),
-		FUNCTION_COLL(upper, 1, 0, 1, UpperICUFunc),
-		FUNCTION_COLL(lower, 1, 0, 1, LowerICUFunc),
-		FUNCTION(hex, 1, 0, 0, hexFunc, FIELD_TYPE_STRING),
-		FUNCTION2(ifnull, 2, 0, 0, noopFunc, SQL_FUNC_COALESCE,
+		FUNCTION(position, ARGC_MASK(2), 0, 1, position_func, FIELD_TYPE_INTEGER),
+		FUNCTION(printf, ARGC_MASK_FULL, 0, 0, printfFunc, FIELD_TYPE_STRING),
+		FUNCTION(unicode, ARGC_MASK(1), 0, 0, unicodeFunc, FIELD_TYPE_STRING),
+		FUNCTION(char, ARGC_MASK_FULL, 0, 0, charFunc, FIELD_TYPE_STRING),
+		FUNCTION(abs, ARGC_MASK(1), 0, 0, absFunc, FIELD_TYPE_NUMBER),
+		FUNCTION(round, ARGC_MASK2(1, 2), 0, 0, roundFunc, FIELD_TYPE_INTEGER),
+		FUNCTION_COLL(upper, ARGC_MASK(1), 0, 1, UpperICUFunc),
+		FUNCTION_COLL(lower, ARGC_MASK(1), 0, 1, LowerICUFunc),
+		FUNCTION(hex, ARGC_MASK(1), 0, 0, hexFunc, FIELD_TYPE_STRING),
+		FUNCTION2(ifnull, ARGC_MASK(2), 0, 0, noopFunc, SQL_FUNC_COALESCE,
 			  FIELD_TYPE_INTEGER),
-		VFUNCTION(random, 0, 0, 0, randomFunc, FIELD_TYPE_INTEGER),
-		VFUNCTION(randomblob, 1, 0, 0, randomBlob, FIELD_TYPE_VARBINARY),
-		FUNCTION(nullif, 2, 0, 1, nullifFunc, FIELD_TYPE_SCALAR),
-		FUNCTION(version, 0, 0, 0, sql_func_version, FIELD_TYPE_STRING),
-		FUNCTION(quote, 1, 0, 0, quoteFunc, FIELD_TYPE_STRING),
-		VFUNCTION(row_count, 0, 0, 0, sql_row_count, FIELD_TYPE_INTEGER),
-		FUNCTION_COLL(replace, 3, 0, 0, replaceFunc),
-		FUNCTION(zeroblob, 1, 0, 0, zeroblobFunc, FIELD_TYPE_VARBINARY),
-		FUNCTION_COLL(substr, 2, 0, 0, substrFunc),
-		FUNCTION_COLL(substr, 3, 0, 0, substrFunc),
-		AGGREGATE(sum, 1, 0, 0, sum_step, sumFinalize,
+		VFUNCTION(random, ARGC_MASK(0), 0, 0, randomFunc, FIELD_TYPE_INTEGER),
+		VFUNCTION(randomblob, ARGC_MASK(1), 0, 0, randomBlob, FIELD_TYPE_VARBINARY),
+		FUNCTION(nullif, ARGC_MASK(2), 0, 1, nullifFunc, FIELD_TYPE_SCALAR),
+		FUNCTION(version, ARGC_MASK(0), 0, 0, sql_func_version, FIELD_TYPE_STRING),
+		FUNCTION(quote, ARGC_MASK(1), 0, 0, quoteFunc, FIELD_TYPE_STRING),
+		VFUNCTION(row_count, ARGC_MASK(0), 0, 0, sql_row_count, FIELD_TYPE_INTEGER),
+		FUNCTION_COLL(replace, ARGC_MASK(3), 0, 0, replaceFunc),
+		FUNCTION(zeroblob, ARGC_MASK(1), 0, 0, zeroblobFunc, FIELD_TYPE_VARBINARY),
+		FUNCTION_COLL(substr, ARGC_MASK2(2, 3), 0, 0, substrFunc),
+		AGGREGATE(sum, ARGC_MASK(1), 0, 0, sum_step, sumFinalize,
 			  FIELD_TYPE_NUMBER),
-		AGGREGATE(total, 1, 0, 0, sum_step, totalFinalize,
+		AGGREGATE(total, ARGC_MASK(1), 0, 0, sum_step, totalFinalize,
 			  FIELD_TYPE_NUMBER),
-		AGGREGATE(avg, 1, 0, 0, sum_step, avgFinalize,
+		AGGREGATE(avg, ARGC_MASK(1), 0, 0, sum_step, avgFinalize,
 			  FIELD_TYPE_NUMBER),
-		AGGREGATE(count, 0, 0, 0, countStep, countFinalize,
+		AGGREGATE(count, ARGC_MASK2(0, 1), 0, 0, countStep, countFinalize,
 			  FIELD_TYPE_INTEGER),
-		AGGREGATE(count, 1, 0, 0, countStep, countFinalize,
-			  FIELD_TYPE_INTEGER),
-		AGGREGATE(group_concat, 1, 0, 0, groupConcatStep,
-			  groupConcatFinalize, FIELD_TYPE_STRING),
-		AGGREGATE(group_concat, 2, 0, 0, groupConcatStep,
+		AGGREGATE(group_concat, ARGC_MASK2(1, 2), 0, 0, groupConcatStep,
 			  groupConcatFinalize, FIELD_TYPE_STRING),
-
-		LIKEFUNC(like, 2, 1, SQL_FUNC_LIKE,
-			 FIELD_TYPE_INTEGER),
-		LIKEFUNC(like, 3, 1, SQL_FUNC_LIKE,
+		LIKEFUNC(like, ARGC_MASK2(2, 3), 1, SQL_FUNC_LIKE,
 			 FIELD_TYPE_INTEGER),
-		FUNCTION(coalesce, 1, 0, 0, 0, FIELD_TYPE_SCALAR),
-		FUNCTION(coalesce, 0, 0, 0, 0, FIELD_TYPE_SCALAR),
-		FUNCTION2(coalesce, -1, 0, 0, noopFunc, SQL_FUNC_COALESCE,
+		FUNCTION2(coalesce, ARGC_MASK_FULL & ~ARGC_MASK2(0, 1), 0, 0, noopFunc, SQL_FUNC_COALESCE,
 			  FIELD_TYPE_SCALAR),
 	};
 	sql_register_analyze_builtins();
diff --git a/src/box/sql/main.c b/src/box/sql/main.c
index 748dc1b8e..eb6e4a7db 100644
--- a/src/box/sql/main.c
+++ b/src/box/sql/main.c
@@ -321,7 +321,7 @@ sqlCreateFunc(sql * db,
 	 * operation to continue but invalidate all precompiled statements.
 	 */
 	p = sqlFindFunction(db, zFunctionName, nArg, 0);
-	if (p && p->nArg == nArg) {
+	if (p != NULL && (p->signature_mask & nArg) != 0) {
 		if (db->nVdbeActive) {
 			diag_set(ClientError, ER_CREATE_FUNCTION, zFunctionName,
 				 "unable to create function due to active "\
@@ -351,7 +351,7 @@ sqlCreateFunc(sql * db,
 	p->xSFunc = xSFunc ? xSFunc : xStep;
 	p->xFinalize = xFinal;
 	p->pUserData = pUserData;
-	p->nArg = (u16) nArg;
+	p->signature_mask = ARGC_MASK(nArg);
 	p->ret_type = type;
 	return 0;
 }
diff --git a/src/box/sql/vdbeaux.c b/src/box/sql/vdbeaux.c
index e7accc745..d32404580 100644
--- a/src/box/sql/vdbeaux.c
+++ b/src/box/sql/vdbeaux.c
@@ -1151,13 +1151,15 @@ displayP4(Op * pOp, char *zTemp, int nTemp)
 		}
 	case P4_FUNCDEF:{
 			FuncDef *pDef = pOp->p4.pFunc;
-			sqlXPrintf(&x, "%s(%d)", pDef->zName, pDef->nArg);
+			sqlXPrintf(&x, "%s(%d)", pDef->zName,
+				   pDef->signature_mask);
 			break;
 		}
 #if defined(SQL_DEBUG) || defined(VDBE_PROFILE)
 	case P4_FUNCCTX:{
 			FuncDef *pDef = pOp->p4.pCtx->pFunc;
-			sqlXPrintf(&x, "%s(%d)", pDef->zName, pDef->nArg);
+			sqlXPrintf(&x, "%s(%d)", pDef->zName,
+				   pDef->signature_mask);
 			break;
 		}
 #endif
-- 
2.22.0





More information about the Tarantool-patches mailing list