[Tarantool-patches] [PATCH v4 3/5] sql: check number of arguments in sql_func_by_signature()

imeevma at tarantool.org imeevma at tarantool.org
Mon Jul 13 08:33:08 MSK 2020


After this patch, the number of function arguments will always be
checked in the sql_func_by_signature() function. This was not the
case for some of the built-in functions.

Part of #4159
---
 src/box/sql/expr.c                          |  18 +-
 src/box/sql/func.c                          | 208 +++--
 src/box/sql/sqlInt.h                        |  13 +
 test/sql-tap/func.test.lua                  |   8 +-
 test/sql-tap/func2.test.lua                 |  18 +-
 test/sql-tap/func5.test.lua                 |   6 +-
 test/sql-tap/select1.test.lua               |   4 +-
 test/sql/collation.result                   |   2 +-
 test/sql/gh-4159-function-argumens.result   | 821 ++++++++++++++++++++
 test/sql/gh-4159-function-argumens.test.sql | 193 +++++
 10 files changed, 1212 insertions(+), 79 deletions(-)
 create mode 100644 test/sql/gh-4159-function-argumens.result
 create mode 100644 test/sql/gh-4159-function-argumens.test.sql

diff --git a/src/box/sql/expr.c b/src/box/sql/expr.c
index d0620b98c..7aee240a3 100644
--- a/src/box/sql/expr.c
+++ b/src/box/sql/expr.c
@@ -3987,14 +3987,7 @@ sqlExprCodeTarget(Parse * pParse, Expr * pExpr, int target)
 			 */
 			if (sql_func_flag_is_set(func, SQL_FUNC_COALESCE)) {
 				int endCoalesce = sqlVdbeMakeLabel(v);
-				if (nFarg < 2) {
-					diag_set(ClientError,
-						 ER_FUNC_WRONG_ARG_COUNT,
-						 func->def->name,
-						 "at least two", nFarg);
-					pParse->is_aborted = true;
-					break;
-				}
+				assert(nFarg >= 2);
 				sqlExprCode(pParse, pFarg->a[0].pExpr,
 						target);
 				for (i = 1; i < nFarg; i++) {
@@ -4017,14 +4010,7 @@ sqlExprCodeTarget(Parse * pParse, Expr * pExpr, int target)
 			 * of the first argument.
 			 */
 			if (sql_func_flag_is_set(func, SQL_FUNC_UNLIKELY)) {
-				if (nFarg < 1) {
-					diag_set(ClientError,
-						 ER_FUNC_WRONG_ARG_COUNT,
-						 func->def->name,
-						 "at least one", nFarg);
-					pParse->is_aborted = true;
-					break;
-				}
+				assert(nFarg == 1 || nFarg == 2);
 				return sqlExprCodeTarget(pParse,
 							     pFarg->a[0].pExpr,
 							     target);
diff --git a/src/box/sql/func.c b/src/box/sql/func.c
index 4bbe4d4b7..9d4c26081 100644
--- a/src/box/sql/func.c
+++ b/src/box/sql/func.c
@@ -383,17 +383,12 @@ error:
 static void
 minmaxFunc(sql_context * context, int argc, sql_value ** argv)
 {
+	assert(argc > 1);
 	int i;
 	int iBest;
 	struct coll *pColl;
 	struct func *func = context->func;
 	int mask = sql_func_flag_is_set(func, SQL_FUNC_MAX) ? -1 : 0;
-	if (argc < 2) {
-		diag_set(ClientError, ER_FUNC_WRONG_ARG_COUNT,
-		mask ? "GREATEST" : "LEAST", "at least two", argc);
-		context->is_aborted = true;
-		return;
-	}
 	pColl = sqlGetFuncCollSeq(context);
 	assert(mask == -1 || mask == 0);
 	iBest = 0;
@@ -722,6 +717,7 @@ printfFunc(sql_context * context, int argc, sql_value ** argv)
 static void
 substrFunc(sql_context * context, int argc, sql_value ** argv)
 {
+	assert(argc == 2 || argc == 3);
 	const unsigned char *z;
 	const unsigned char *z2;
 	int len;
@@ -729,12 +725,6 @@ substrFunc(sql_context * context, int argc, sql_value ** argv)
 	i64 p1, p2;
 	int negP2 = 0;
 
-	if (argc != 2 && argc != 3) {
-		diag_set(ClientError, ER_FUNC_WRONG_ARG_COUNT, "SUBSTR",
-			 "1 or 2", argc);
-		context->is_aborted = true;
-		return;
-	}
 	if (sql_value_is_null(argv[1])
 	    || (argc == 3 && sql_value_is_null(argv[2]))
 	    ) {
@@ -828,14 +818,9 @@ substrFunc(sql_context * context, int argc, sql_value ** argv)
 static void
 roundFunc(sql_context * context, int argc, sql_value ** argv)
 {
+	assert(argc == 1 || argc == 2);
 	int n = 0;
 	double r;
-	if (argc != 1 && argc != 2) {
-		diag_set(ClientError, ER_FUNC_WRONG_ARG_COUNT, "ROUND",
-			 "1 or 2", argc);
-		context->is_aborted = true;
-		return;
-	}
 	if (argc == 2) {
 		if (sql_value_is_null(argv[1]))
 			return;
@@ -1226,14 +1211,9 @@ sql_utf8_pattern_compare(const char *pattern,
 static void
 likeFunc(sql_context *context, int argc, sql_value **argv)
 {
+	assert(argc == 2 || argc == 3);
 	u32 escape = SQL_END_OF_STRING;
 	int nPat;
-	if (argc != 2 && argc != 3) {
-		diag_set(ClientError, ER_FUNC_WRONG_ARG_COUNT,
-			 "LIKE", "2 or 3", argc);
-		context->is_aborted = true;
-		return;
-	}
 	sql *db = sql_context_db_handle(context);
 	int rhs_type = sql_value_type(argv[0]);
 	int lhs_type = sql_value_type(argv[1]);
@@ -1838,6 +1818,7 @@ trim_func_three_args(struct sql_context *context, sql_value *arg1,
 static void
 trim_func(struct sql_context *context, int argc, sql_value **argv)
 {
+	assert(argc == 1 || argc == 2 || argc == 3);
 	switch (argc) {
 	case 1:
 		trim_func_one_arg(context, argv[0]);
@@ -1849,9 +1830,7 @@ trim_func(struct sql_context *context, int argc, sql_value **argv)
 		trim_func_three_args(context, argv[0], argv[1], argv[2]);
 		break;
 	default:
-		diag_set(ClientError, ER_FUNC_WRONG_ARG_COUNT, "TRIM",
-			 "1 or 2 or 3", argc);
-		context->is_aborted = true;
+		unreachable();
 	}
 }
 
@@ -2030,13 +2009,8 @@ struct CountCtx {
 static void
 countStep(sql_context * context, int argc, sql_value ** argv)
 {
+	assert(argc == 0 || argc == 1);
 	CountCtx *p;
-	if (argc != 0 && argc != 1) {
-		diag_set(ClientError, ER_FUNC_WRONG_ARG_COUNT,
-			 "COUNT", "0 or 1", argc);
-		context->is_aborted = true;
-		return;
-	}
 	p = sql_aggregate_context(context, sizeof(*p));
 	if ((argc == 0 || ! sql_value_is_null(argv[0])) && p) {
 		p->n++;
@@ -2111,16 +2085,11 @@ minMaxFinalize(sql_context * context)
 static void
 groupConcatStep(sql_context * context, int argc, sql_value ** argv)
 {
+	assert(argc == 1 || argc == 2);
 	const char *zVal;
 	StrAccum *pAccum;
 	const char *zSep;
 	int nVal, nSep;
-	if (argc != 1 && argc != 2) {
-		diag_set(ClientError, ER_FUNC_WRONG_ARG_COUNT,
-			 "GROUP_CONCAT", "1 or 2", argc);
-		context->is_aborted = true;
-		return;
-	}
 	if (sql_value_is_null(argv[0]))
 		return;
 	pAccum =
@@ -2195,11 +2164,28 @@ sql_func_by_signature(const char *name, int argc)
 				     name));
 		return NULL;
 	}
-	int param_count = base->def->param_count;
-	if (param_count != -1 && param_count != argc) {
-		const char *err = tt_sprintf("%d", param_count);
-		diag_set(ClientError, ER_FUNC_WRONG_ARG_COUNT,
-			 base->def->name, err, argc);
+	if (base->def->language != FUNC_LANGUAGE_SQL_BUILTIN) {
+		int param_count = base->def->param_count;
+		if (param_count != -1 && param_count != argc) {
+			const char *err = tt_sprintf("%d", param_count);
+			diag_set(ClientError, ER_FUNC_WRONG_ARG_COUNT,
+				 base->def->name, err, argc);
+			return NULL;
+		}
+		return base;
+	}
+	struct func_sql_builtin *func = (struct func_sql_builtin *)base;
+	uint32_t arg_c = (uint32_t)argc;
+	if (func->args.min_count > arg_c || func->args.max_count < arg_c) {
+		const char *err;
+		uint32_t min = func->args.min_count;
+		uint32_t max = func->args.max_count;
+		if (min != max)
+			err = tt_sprintf("from %d to %d", min, max);
+		else
+			err = tt_sprintf("%d", min);
+		diag_set(ClientError, ER_FUNC_WRONG_ARG_COUNT, base->def->name,
+			 err, argc);
 		return NULL;
 	}
 	return base;
@@ -2242,12 +2228,16 @@ static struct {
 	/** Members below are related to struct func_def. */
 	bool is_deterministic;
 	int param_count;
+	uint32_t min_count;
+	uint32_t max_count;
 	enum field_type returns;
 	enum func_aggregate aggregate;
 	bool export_to_sql;
 } sql_builtins[] = {
 	{.name = "ABS",
 	 .param_count = 1,
+	 .min_count = 1,
+	 .max_count = 1,
 	 .returns = FIELD_TYPE_NUMBER,
 	 .aggregate = FUNC_AGGREGATE_NONE,
 	 .is_deterministic = true,
@@ -2258,6 +2248,8 @@ static struct {
 	}, {
 	 .name = "AVG",
 	 .param_count = 1,
+	 .min_count = 1,
+	 .max_count = 1,
 	 .returns = FIELD_TYPE_NUMBER,
 	 .is_deterministic = false,
 	 .aggregate = FUNC_AGGREGATE_GROUP,
@@ -2270,6 +2262,8 @@ static struct {
 	 .call = sql_builtin_stub,
 	 .export_to_sql = false,
 	 .param_count = -1,
+	 .min_count = 0,
+	 .max_count = 0,
 	 .returns = FIELD_TYPE_ANY,
 	 .aggregate = FUNC_AGGREGATE_NONE,
 	 .is_deterministic = false,
@@ -2280,6 +2274,8 @@ static struct {
 	 .call = sql_builtin_stub,
 	 .export_to_sql = false,
 	 .param_count = -1,
+	 .min_count = 0,
+	 .max_count = 0,
 	 .returns = FIELD_TYPE_ANY,
 	 .aggregate = FUNC_AGGREGATE_NONE,
 	 .is_deterministic = false,
@@ -2288,6 +2284,8 @@ static struct {
 	}, {
 	 .name = "CHAR",
 	 .param_count = -1,
+	 .min_count = 0,
+	 .max_count = SQL_MAX_FUNCTION_ARG,
 	 .returns = FIELD_TYPE_STRING,
 	 .is_deterministic = true,
 	 .aggregate = FUNC_AGGREGATE_NONE,
@@ -2298,6 +2296,8 @@ static struct {
 	 }, {
 	 .name = "CHARACTER_LENGTH",
 	 .param_count = 1,
+	 .min_count = 1,
+	 .max_count = 1,
 	 .returns = FIELD_TYPE_INTEGER,
 	 .aggregate = FUNC_AGGREGATE_NONE,
 	 .is_deterministic = true,
@@ -2308,6 +2308,8 @@ static struct {
 	}, {
 	 .name = "CHAR_LENGTH",
 	 .param_count = 1,
+	 .min_count = 1,
+	 .max_count = 1,
 	 .returns = FIELD_TYPE_INTEGER,
 	 .aggregate = FUNC_AGGREGATE_NONE,
 	 .is_deterministic = true,
@@ -2318,6 +2320,8 @@ static struct {
 	}, {
 	 .name = "COALESCE",
 	 .param_count = -1,
+	 .min_count = 2,
+	 .max_count = SQL_MAX_FUNCTION_ARG,
 	 .returns = FIELD_TYPE_SCALAR,
 	 .aggregate = FUNC_AGGREGATE_NONE,
 	 .is_deterministic = true,
@@ -2328,6 +2332,8 @@ static struct {
 	}, {
 	 .name = "COUNT",
 	 .param_count = -1,
+	 .min_count = 0,
+	 .max_count = 1,
 	 .returns = FIELD_TYPE_INTEGER,
 	 .aggregate = FUNC_AGGREGATE_GROUP,
 	 .is_deterministic = false,
@@ -2340,6 +2346,8 @@ static struct {
 	 .call = sql_builtin_stub,
 	 .export_to_sql = false,
 	 .param_count = -1,
+	 .min_count = 0,
+	 .max_count = 0,
 	 .returns = FIELD_TYPE_ANY,
 	 .aggregate = FUNC_AGGREGATE_NONE,
 	 .is_deterministic = false,
@@ -2350,6 +2358,8 @@ static struct {
 	 .call = sql_builtin_stub,
 	 .export_to_sql = false,
 	 .param_count = -1,
+	 .min_count = 0,
+	 .max_count = 0,
 	 .returns = FIELD_TYPE_ANY,
 	 .aggregate = FUNC_AGGREGATE_NONE,
 	 .is_deterministic = false,
@@ -2360,6 +2370,8 @@ static struct {
 	 .call = sql_builtin_stub,
 	 .export_to_sql = false,
 	 .param_count = -1,
+	 .min_count = 0,
+	 .max_count = 0,
 	 .returns = FIELD_TYPE_ANY,
 	 .aggregate = FUNC_AGGREGATE_NONE,
 	 .is_deterministic = false,
@@ -2370,6 +2382,8 @@ static struct {
 	 .call = sql_builtin_stub,
 	 .export_to_sql = false,
 	 .param_count = -1,
+	 .min_count = 0,
+	 .max_count = 0,
 	 .returns = FIELD_TYPE_ANY,
 	 .aggregate = FUNC_AGGREGATE_NONE,
 	 .is_deterministic = false,
@@ -2380,6 +2394,8 @@ static struct {
 	 .call = sql_builtin_stub,
 	 .export_to_sql = false,
 	 .param_count = -1,
+	 .min_count = 0,
+	 .max_count = 0,
 	 .returns = FIELD_TYPE_ANY,
 	 .aggregate = FUNC_AGGREGATE_NONE,
 	 .is_deterministic = false,
@@ -2390,6 +2406,8 @@ static struct {
 	 .call = sql_builtin_stub,
 	 .export_to_sql = false,
 	 .param_count = -1,
+	 .min_count = 0,
+	 .max_count = 0,
 	 .returns = FIELD_TYPE_ANY,
 	 .aggregate = FUNC_AGGREGATE_NONE,
 	 .is_deterministic = false,
@@ -2400,6 +2418,8 @@ static struct {
 	 .call = sql_builtin_stub,
 	 .export_to_sql = false,
 	 .param_count = -1,
+	 .min_count = 0,
+	 .max_count = 0,
 	 .returns = FIELD_TYPE_ANY,
 	 .aggregate = FUNC_AGGREGATE_NONE,
 	 .is_deterministic = false,
@@ -2410,6 +2430,8 @@ static struct {
 	 .call = sql_builtin_stub,
 	 .export_to_sql = false,
 	 .param_count = -1,
+	 .min_count = 0,
+	 .max_count = 0,
 	 .returns = FIELD_TYPE_ANY,
 	 .aggregate = FUNC_AGGREGATE_NONE,
 	 .is_deterministic = false,
@@ -2420,6 +2442,8 @@ static struct {
 	 .call = sql_builtin_stub,
 	 .export_to_sql = false,
 	 .param_count = -1,
+	 .min_count = 0,
+	 .max_count = 0,
 	 .returns = FIELD_TYPE_ANY,
 	 .aggregate = FUNC_AGGREGATE_NONE,
 	 .is_deterministic = false,
@@ -2430,6 +2454,8 @@ static struct {
 	 .call = sql_builtin_stub,
 	 .export_to_sql = false,
 	 .param_count = -1,
+	 .min_count = 0,
+	 .max_count = 0,
 	 .returns = FIELD_TYPE_ANY,
 	 .aggregate = FUNC_AGGREGATE_NONE,
 	 .is_deterministic = false,
@@ -2440,6 +2466,8 @@ static struct {
 	 .call = sql_builtin_stub,
 	 .export_to_sql = false,
 	 .param_count = -1,
+	 .min_count = 0,
+	 .max_count = 0,
 	 .returns = FIELD_TYPE_ANY,
 	 .aggregate = FUNC_AGGREGATE_NONE,
 	 .is_deterministic = false,
@@ -2448,6 +2476,8 @@ static struct {
 	}, {
 	 .name = "GREATEST",
 	 .param_count = -1,
+	 .min_count = 2,
+	 .max_count = SQL_MAX_FUNCTION_ARG,
 	 .returns = FIELD_TYPE_SCALAR,
 	 .aggregate = FUNC_AGGREGATE_NONE,
 	 .is_deterministic = true,
@@ -2458,6 +2488,8 @@ static struct {
 	}, {
 	 .name = "GROUP_CONCAT",
 	 .param_count = -1,
+	 .min_count = 1,
+	 .max_count = 2,
 	 .returns = FIELD_TYPE_STRING,
 	 .aggregate = FUNC_AGGREGATE_GROUP,
 	 .is_deterministic = false,
@@ -2468,6 +2500,8 @@ static struct {
 	}, {
 	 .name = "HEX",
 	 .param_count = 1,
+	 .min_count = 1,
+	 .max_count = 1,
 	 .returns = FIELD_TYPE_STRING,
 	 .aggregate = FUNC_AGGREGATE_NONE,
 	 .is_deterministic = true,
@@ -2478,6 +2512,8 @@ static struct {
 	}, {
 	 .name = "IFNULL",
 	 .param_count = 2,
+	 .min_count = 2,
+	 .max_count = 2,
 	 .returns = FIELD_TYPE_INTEGER,
 	 .aggregate = FUNC_AGGREGATE_NONE,
 	 .is_deterministic = true,
@@ -2490,6 +2526,8 @@ static struct {
 	 .call = sql_builtin_stub,
 	 .export_to_sql = false,
 	 .param_count = -1,
+	 .min_count = 0,
+	 .max_count = 0,
 	 .returns = FIELD_TYPE_ANY,
 	 .aggregate = FUNC_AGGREGATE_NONE,
 	 .is_deterministic = false,
@@ -2498,6 +2536,8 @@ static struct {
 	}, {
 	 .name = "LEAST",
 	 .param_count = -1,
+	 .min_count = 2,
+	 .max_count = SQL_MAX_FUNCTION_ARG,
 	 .returns = FIELD_TYPE_SCALAR,
 	 .aggregate = FUNC_AGGREGATE_NONE,
 	 .is_deterministic = true,
@@ -2508,6 +2548,8 @@ static struct {
 	}, {
 	 .name = "LENGTH",
 	 .param_count = 1,
+	 .min_count = 1,
+	 .max_count = 1,
 	 .returns = FIELD_TYPE_INTEGER,
 	 .aggregate = FUNC_AGGREGATE_NONE,
 	 .is_deterministic = true,
@@ -2520,6 +2562,8 @@ static struct {
 	 .call = sql_builtin_stub,
 	 .export_to_sql = false,
 	 .param_count = -1,
+	 .min_count = 0,
+	 .max_count = 0,
 	 .returns = FIELD_TYPE_ANY,
 	 .aggregate = FUNC_AGGREGATE_NONE,
 	 .is_deterministic = false,
@@ -2528,6 +2572,8 @@ static struct {
 	}, {
 	 .name = "LIKE",
 	 .param_count = -1,
+	 .min_count = 2,
+	 .max_count = 3,
 	 .returns = FIELD_TYPE_INTEGER,
 	 .aggregate = FUNC_AGGREGATE_NONE,
 	 .is_deterministic = true,
@@ -2538,6 +2584,8 @@ static struct {
 	}, {
 	 .name = "LIKELIHOOD",
 	 .param_count = 2,
+	 .min_count = 2,
+	 .max_count = 2,
 	 .returns = FIELD_TYPE_BOOLEAN,
 	 .aggregate = FUNC_AGGREGATE_NONE,
 	 .is_deterministic = true,
@@ -2548,6 +2596,8 @@ static struct {
 	}, {
 	 .name = "LIKELY",
 	 .param_count = 1,
+	 .min_count = 1,
+	 .max_count = 1,
 	 .returns = FIELD_TYPE_BOOLEAN,
 	 .aggregate = FUNC_AGGREGATE_NONE,
 	 .is_deterministic = true,
@@ -2560,6 +2610,8 @@ static struct {
 	 .call = sql_builtin_stub,
 	 .export_to_sql = false,
 	 .param_count = -1,
+	 .min_count = 0,
+	 .max_count = 0,
 	 .returns = FIELD_TYPE_ANY,
 	 .aggregate = FUNC_AGGREGATE_NONE,
 	 .is_deterministic = false,
@@ -2568,6 +2620,8 @@ static struct {
 	}, {
 	 .name = "LOWER",
 	 .param_count = 1,
+	 .min_count = 1,
+	 .max_count = 1,
 	 .returns = FIELD_TYPE_STRING,
 	 .aggregate = FUNC_AGGREGATE_NONE,
 	 .is_deterministic = true,
@@ -2578,6 +2632,8 @@ static struct {
 	}, {
 	 .name = "MAX",
 	 .param_count = 1,
+	 .min_count = 1,
+	 .max_count = 1,
 	 .returns = FIELD_TYPE_SCALAR,
 	 .aggregate = FUNC_AGGREGATE_GROUP,
 	 .is_deterministic = false,
@@ -2588,6 +2644,8 @@ static struct {
 	}, {
 	 .name = "MIN",
 	 .param_count = 1,
+	 .min_count = 1,
+	 .max_count = 1,
 	 .returns = FIELD_TYPE_SCALAR,
 	 .aggregate = FUNC_AGGREGATE_GROUP,
 	 .is_deterministic = false,
@@ -2600,6 +2658,8 @@ static struct {
 	 .call = sql_builtin_stub,
 	 .export_to_sql = false,
 	 .param_count = -1,
+	 .min_count = 0,
+	 .max_count = 0,
 	 .returns = FIELD_TYPE_ANY,
 	 .aggregate = FUNC_AGGREGATE_NONE,
 	 .is_deterministic = false,
@@ -2608,6 +2668,8 @@ static struct {
 	}, {
 	 .name = "NULLIF",
 	 .param_count = 2,
+	 .min_count = 2,
+	 .max_count = 2,
 	 .returns = FIELD_TYPE_SCALAR,
 	 .aggregate = FUNC_AGGREGATE_NONE,
 	 .is_deterministic = true,
@@ -2620,6 +2682,8 @@ static struct {
 	 .call = sql_builtin_stub,
 	 .export_to_sql = false,
 	 .param_count = -1,
+	 .min_count = 0,
+	 .max_count = 0,
 	 .returns = FIELD_TYPE_ANY,
 	 .aggregate = FUNC_AGGREGATE_NONE,
 	 .is_deterministic = false,
@@ -2628,6 +2692,8 @@ static struct {
 	}, {
 	 .name = "POSITION",
 	 .param_count = 2,
+	 .min_count = 2,
+	 .max_count = 2,
 	 .returns = FIELD_TYPE_INTEGER,
 	 .aggregate = FUNC_AGGREGATE_NONE,
 	 .is_deterministic = true,
@@ -2640,6 +2706,8 @@ static struct {
 	 .call = sql_builtin_stub,
 	 .export_to_sql = false,
 	 .param_count = -1,
+	 .min_count = 0,
+	 .max_count = 0,
 	 .returns = FIELD_TYPE_ANY,
 	 .aggregate = FUNC_AGGREGATE_NONE,
 	 .is_deterministic = false,
@@ -2648,6 +2716,8 @@ static struct {
 	}, {
 	 .name = "PRINTF",
 	 .param_count = -1,
+	 .min_count = 0,
+	 .max_count = SQL_MAX_FUNCTION_ARG,
 	 .returns = FIELD_TYPE_STRING,
 	 .aggregate = FUNC_AGGREGATE_NONE,
 	 .is_deterministic = true,
@@ -2658,6 +2728,8 @@ static struct {
 	}, {
 	 .name = "QUOTE",
 	 .param_count = 1,
+	 .min_count = 1,
+	 .max_count = 1,
 	 .returns = FIELD_TYPE_STRING,
 	 .aggregate = FUNC_AGGREGATE_NONE,
 	 .is_deterministic = true,
@@ -2668,6 +2740,8 @@ static struct {
 	}, {
 	 .name = "RANDOM",
 	 .param_count = 0,
+	 .min_count = 0,
+	 .max_count = 0,
 	 .returns = FIELD_TYPE_INTEGER,
 	 .aggregate = FUNC_AGGREGATE_NONE,
 	 .is_deterministic = false,
@@ -2678,6 +2752,8 @@ static struct {
 	}, {
 	 .name = "RANDOMBLOB",
 	 .param_count = 1,
+	 .min_count = 1,
+	 .max_count = 1,
 	 .returns = FIELD_TYPE_VARBINARY,
 	 .aggregate = FUNC_AGGREGATE_NONE,
 	 .is_deterministic = false,
@@ -2688,6 +2764,8 @@ static struct {
 	}, {
 	 .name = "REPLACE",
 	 .param_count = 3,
+	 .min_count = 3,
+	 .max_count = 3,
 	 .returns = FIELD_TYPE_STRING,
 	 .aggregate = FUNC_AGGREGATE_NONE,
 	 .is_deterministic = true,
@@ -2698,6 +2776,8 @@ static struct {
 	}, {
 	 .name = "ROUND",
 	 .param_count = -1,
+	 .min_count = 1,
+	 .max_count = 2,
 	 .returns = FIELD_TYPE_INTEGER,
 	 .aggregate = FUNC_AGGREGATE_NONE,
 	 .is_deterministic = true,
@@ -2708,6 +2788,8 @@ static struct {
 	}, {
 	 .name = "ROW_COUNT",
 	 .param_count = 0,
+	 .min_count = 0,
+	 .max_count = 0,
 	 .returns = FIELD_TYPE_INTEGER,
 	 .aggregate = FUNC_AGGREGATE_NONE,
 	 .is_deterministic = true,
@@ -2720,6 +2802,8 @@ static struct {
 	 .call = sql_builtin_stub,
 	 .export_to_sql = false,
 	 .param_count = -1,
+	 .min_count = 0,
+	 .max_count = 0,
 	 .returns = FIELD_TYPE_ANY,
 	 .aggregate = FUNC_AGGREGATE_NONE,
 	 .is_deterministic = false,
@@ -2728,6 +2812,8 @@ static struct {
 	}, {
 	 .name = "SOUNDEX",
 	 .param_count = 1,
+	 .min_count = 1,
+	 .max_count = 1,
 	 .returns = FIELD_TYPE_STRING,
 	 .aggregate = FUNC_AGGREGATE_NONE,
 	 .is_deterministic = true,
@@ -2740,6 +2826,8 @@ static struct {
 	 .call = sql_builtin_stub,
 	 .export_to_sql = false,
 	 .param_count = -1,
+	 .min_count = 0,
+	 .max_count = 0,
 	 .returns = FIELD_TYPE_ANY,
 	 .aggregate = FUNC_AGGREGATE_NONE,
 	 .is_deterministic = false,
@@ -2750,6 +2838,8 @@ static struct {
 	 .call = sql_builtin_stub,
 	 .export_to_sql = false,
 	 .param_count = -1,
+	 .min_count = 0,
+	 .max_count = 0,
 	 .returns = FIELD_TYPE_ANY,
 	 .aggregate = FUNC_AGGREGATE_NONE,
 	 .is_deterministic = false,
@@ -2758,6 +2848,8 @@ static struct {
 	}, {
 	 .name = "SUBSTR",
 	 .param_count = -1,
+	 .min_count = 2,
+	 .max_count = 3,
 	 .returns = FIELD_TYPE_STRING,
 	 .aggregate = FUNC_AGGREGATE_NONE,
 	 .is_deterministic = true,
@@ -2768,6 +2860,8 @@ static struct {
 	}, {
 	 .name = "SUM",
 	 .param_count = 1,
+	 .min_count = 1,
+	 .max_count = 1,
 	 .returns = FIELD_TYPE_NUMBER,
 	 .aggregate = FUNC_AGGREGATE_GROUP,
 	 .is_deterministic = false,
@@ -2780,6 +2874,8 @@ static struct {
 	 .call = sql_builtin_stub,
 	 .export_to_sql = false,
 	 .param_count = -1,
+	 .min_count = 0,
+	 .max_count = 0,
 	 .returns = FIELD_TYPE_ANY,
 	 .aggregate = FUNC_AGGREGATE_NONE,
 	 .is_deterministic = false,
@@ -2788,6 +2884,8 @@ static struct {
 	}, {
 	 .name = "TOTAL",
 	 .param_count = 1,
+	 .min_count = 1,
+	 .max_count = 1,
 	 .returns = FIELD_TYPE_NUMBER,
 	 .aggregate = FUNC_AGGREGATE_GROUP,
 	 .is_deterministic = false,
@@ -2798,6 +2896,8 @@ static struct {
 	}, {
 	 .name = "TRIM",
 	 .param_count = -1,
+	 .min_count = 1,
+	 .max_count = 3,
 	 .returns = FIELD_TYPE_STRING,
 	 .aggregate = FUNC_AGGREGATE_NONE,
 	 .is_deterministic = true,
@@ -2808,6 +2908,8 @@ static struct {
 	}, {
 	 .name = "TYPEOF",
 	 .param_count = 1,
+	 .min_count = 1,
+	 .max_count = 1,
 	 .returns = FIELD_TYPE_STRING,
 	 .aggregate = FUNC_AGGREGATE_NONE,
 	 .is_deterministic = true,
@@ -2818,6 +2920,8 @@ static struct {
 	}, {
 	 .name = "UNICODE",
 	 .param_count = 1,
+	 .min_count = 1,
+	 .max_count = 1,
 	 .returns = FIELD_TYPE_STRING,
 	 .aggregate = FUNC_AGGREGATE_NONE,
 	 .is_deterministic = true,
@@ -2828,6 +2932,8 @@ static struct {
 	}, {
 	 .name = "UNLIKELY",
 	 .param_count = 1,
+	 .min_count = 1,
+	 .max_count = 1,
 	 .returns = FIELD_TYPE_BOOLEAN,
 	 .aggregate = FUNC_AGGREGATE_NONE,
 	 .is_deterministic = true,
@@ -2838,6 +2944,8 @@ static struct {
 	}, {
 	 .name = "UPPER",
 	 .param_count = 1,
+	 .min_count = 1,
+	 .max_count = 1,
 	 .returns = FIELD_TYPE_STRING,
 	 .aggregate = FUNC_AGGREGATE_NONE,
 	 .is_deterministic = true,
@@ -2848,6 +2956,8 @@ static struct {
 	}, {
 	 .name = "VERSION",
 	 .param_count = 0,
+	 .min_count = 0,
+	 .max_count = 0,
 	 .returns = FIELD_TYPE_STRING,
 	 .aggregate = FUNC_AGGREGATE_NONE,
 	 .is_deterministic = true,
@@ -2858,6 +2968,8 @@ static struct {
 	}, {
 	 .name = "ZEROBLOB",
 	 .param_count = 1,
+	 .min_count = 1,
+	 .max_count = 1,
 	 .returns = FIELD_TYPE_VARBINARY,
 	 .aggregate = FUNC_AGGREGATE_NONE,
 	 .is_deterministic = true,
@@ -2870,6 +2982,8 @@ static struct {
 	 .call = sql_builtin_stub,
 	 .export_to_sql = false,
 	 .param_count = -1,
+	 .min_count = 0,
+	 .max_count = 0,
 	 .returns = FIELD_TYPE_ANY,
 	 .aggregate = FUNC_AGGREGATE_NONE,
 	 .is_deterministic = false,
@@ -2880,6 +2994,8 @@ static struct {
 	 .call = sql_builtin_stub,
 	 .export_to_sql = false,
 	 .param_count = -1,
+	 .min_count = 0,
+	 .max_count = 0,
 	 .returns = FIELD_TYPE_ANY,
 	 .aggregate = FUNC_AGGREGATE_NONE,
 	 .is_deterministic = false,
@@ -2890,6 +3006,8 @@ static struct {
 	 .call = sql_builtin_stub,
 	 .export_to_sql = false,
 	 .param_count = -1,
+	 .min_count = 0,
+	 .max_count = 0,
 	 .returns = FIELD_TYPE_ANY,
 	 .aggregate = FUNC_AGGREGATE_NONE,
 	 .is_deterministic = false,
@@ -2941,6 +3059,8 @@ func_sql_builtin_new(struct func_def *def)
 	func->flags = sql_builtins[idx].flags;
 	func->call = sql_builtins[idx].call;
 	func->finalize = sql_builtins[idx].finalize;
+	func->args.min_count = sql_builtins[idx].min_count;
+	func->args.max_count = sql_builtins[idx].max_count;
 	def->param_count = sql_builtins[idx].param_count;
 	def->is_deterministic = sql_builtins[idx].is_deterministic;
 	def->returns = sql_builtins[idx].returns;
diff --git a/src/box/sql/sqlInt.h b/src/box/sql/sqlInt.h
index 58a65acc1..6af9d7473 100644
--- a/src/box/sql/sqlInt.h
+++ b/src/box/sql/sqlInt.h
@@ -4397,11 +4397,24 @@ Expr *sqlExprForVectorField(Parse *, Expr *, int);
  */
 extern int sqlSubProgramsRemaining;
 
+/**
+ * A structure that contains additional information about
+ * arguments to built-in SQL functions.
+ */
+struct sql_builtin_func_args {
+	/** Min number of arguments. */
+	uint32_t min_count;
+	/** Max number of arguments. */
+	uint32_t max_count;
+};
+
 struct func_sql_builtin {
 	/** Function object base class. */
 	struct func base;
 	/** A bitmask of SQL flags. */
 	uint16_t flags;
+	/** Information about arguments to built-in functions. */
+	struct sql_builtin_func_args args;
 	/**
 	 * A VDBE-memory-compatible call method.
 	 * SQL built-ins don't use func base class "call"
diff --git a/test/sql-tap/func.test.lua b/test/sql-tap/func.test.lua
index 3c088920f..1d3ef9e2a 100755
--- a/test/sql-tap/func.test.lua
+++ b/test/sql-tap/func.test.lua
@@ -428,7 +428,7 @@ test:do_catchsql_test(
         SELECT round(a,b,c) FROM t1
     ]], {
         -- <func-4.5>
-        1, "Wrong number of arguments is passed to ROUND(): expected 1 or 2, got 3"
+        1, "Wrong number of arguments is passed to ROUND(): expected from 1 to 2, got 3"
         -- </func-4.5>
     })
 
@@ -488,7 +488,7 @@ test:do_catchsql_test(
         SELECT round() FROM t1 ORDER BY a
     ]], {
         -- <func-4.11>
-        1, "Wrong number of arguments is passed to ROUND(): expected 1 or 2, got 0"
+        1, "Wrong number of arguments is passed to ROUND(): expected from 1 to 2, got 0"
         -- </func-4.11>
     })
 
@@ -2540,7 +2540,7 @@ test:do_catchsql_test(
         SELECT coalesce()
     ]], {
         -- <func-27.1>
-        1, "Wrong number of arguments is passed to COALESCE(): expected at least two, got 0"
+        1, "Wrong number of arguments is passed to COALESCE(): expected from 2 to 127, got 0"
         -- </func-27.1>
     })
 
@@ -2550,7 +2550,7 @@ test:do_catchsql_test(
         SELECT coalesce(1)
     ]], {
         -- <func-27.2>
-        1, "Wrong number of arguments is passed to COALESCE(): expected at least two, got 1"
+        1, "Wrong number of arguments is passed to COALESCE(): expected from 2 to 127, got 1"
         -- </func-27.2>
     })
 
diff --git a/test/sql-tap/func2.test.lua b/test/sql-tap/func2.test.lua
index c9bd3665f..4bfee7150 100755
--- a/test/sql-tap/func2.test.lua
+++ b/test/sql-tap/func2.test.lua
@@ -50,7 +50,7 @@ test:do_catchsql_test(
         SELECT SUBSTR()
     ]], {
         -- <func2-1.2.1>
-        1, "Wrong number of arguments is passed to SUBSTR(): expected 1 or 2, got 0"
+        1, "Wrong number of arguments is passed to SUBSTR(): expected from 2 to 3, got 0"
         -- </func2-1.2.1>
     })
 
@@ -60,7 +60,7 @@ test:do_catchsql_test(
         SELECT SUBSTR('Supercalifragilisticexpialidocious')
     ]], {
         -- <func2-1.2.2>
-        1, "Wrong number of arguments is passed to SUBSTR(): expected 1 or 2, got 1"
+        1, "Wrong number of arguments is passed to SUBSTR(): expected from 2 to 3, got 1"
         -- </func2-1.2.2>
     })
 
@@ -70,7 +70,7 @@ test:do_catchsql_test(
         SELECT SUBSTR('Supercalifragilisticexpialidocious', 1,1,1)
     ]], {
         -- <func2-1.2.3>
-        1, "Wrong number of arguments is passed to SUBSTR(): expected 1 or 2, got 4"
+        1, "Wrong number of arguments is passed to SUBSTR(): expected from 2 to 3, got 4"
         -- </func2-1.2.3>
     })
 
@@ -673,7 +673,7 @@ if ("ሴ" ~= "u1234")
             SELECT SUBSTR()
         ]], {
             -- <func2-2.1.2>
-            1, "Wrong number of arguments is passed to SUBSTR(): expected 1 or 2, got 0"
+            1, "Wrong number of arguments is passed to SUBSTR(): expected from 2 to 3, got 0"
             -- </func2-2.1.2>
         })
 
@@ -683,7 +683,7 @@ if ("ሴ" ~= "u1234")
             SELECT SUBSTR('hiሴho')
         ]], {
             -- <func2-2.1.3>
-            1, "Wrong number of arguments is passed to SUBSTR(): expected 1 or 2, got 1"
+            1, "Wrong number of arguments is passed to SUBSTR(): expected from 2 to 3, got 1"
             -- </func2-2.1.3>
         })
 
@@ -693,7 +693,7 @@ if ("ሴ" ~= "u1234")
             SELECT SUBSTR('hiሴho', 1,1,1)
         ]], {
             -- <func2-2.1.4>
-            1, "Wrong number of arguments is passed to SUBSTR(): expected 1 or 2, got 4"
+            1, "Wrong number of arguments is passed to SUBSTR(): expected from 2 to 3, got 4"
             -- </func2-2.1.4>
         })
 
@@ -1038,7 +1038,7 @@ test:do_catchsql_test(
         SELECT SUBSTR()
     ]], {
         -- <func2-3.1.2>
-        1, "Wrong number of arguments is passed to SUBSTR(): expected 1 or 2, got 0"
+        1, "Wrong number of arguments is passed to SUBSTR(): expected from 2 to 3, got 0"
         -- </func2-3.1.2>
     })
 
@@ -1048,7 +1048,7 @@ test:do_catchsql_test(
         SELECT SUBSTR(x'1234')
     ]], {
         -- <func2-3.1.3>
-        1, "Wrong number of arguments is passed to SUBSTR(): expected 1 or 2, got 1"
+        1, "Wrong number of arguments is passed to SUBSTR(): expected from 2 to 3, got 1"
         -- </func2-3.1.3>
     })
 
@@ -1058,7 +1058,7 @@ test:do_catchsql_test(
         SELECT SUBSTR(x'1234', 1,1,1)
     ]], {
         -- <func2-3.1.4>
-        1, "Wrong number of arguments is passed to SUBSTR(): expected 1 or 2, got 4"
+        1, "Wrong number of arguments is passed to SUBSTR(): expected from 2 to 3, got 4"
         -- </func2-3.1.4>
     })
 
diff --git a/test/sql-tap/func5.test.lua b/test/sql-tap/func5.test.lua
index 8329e1735..08c40fd02 100755
--- a/test/sql-tap/func5.test.lua
+++ b/test/sql-tap/func5.test.lua
@@ -276,19 +276,19 @@ test:do_catchsql_test(
     "func-5-5.1",
     [[
         SELECT LEAST(false);
-    ]], { 1, "Wrong number of arguments is passed to LEAST(): expected at least two, got 1" } )
+    ]], { 1, "Wrong number of arguments is passed to LEAST(): expected from 2 to 127, got 1" } )
 
 test:do_catchsql_test(
     "func-5-5.2",
     [[
         SELECT GREATEST('abc');
-    ]], { 1, "Wrong number of arguments is passed to GREATEST(): expected at least two, got 1" } )
+    ]], { 1, "Wrong number of arguments is passed to GREATEST(): expected from 2 to 127, got 1" } )
 
 test:do_catchsql_test(
     "func-5-5.3",
     [[
         SELECT LEAST();
-    ]], { 1, "Wrong number of arguments is passed to LEAST(): expected at least two, got 0" } )
+    ]], { 1, "Wrong number of arguments is passed to LEAST(): expected from 2 to 127, got 0" } )
 
 box.func.COUNTER1:drop()
 box.func.COUNTER2:drop()
diff --git a/test/sql-tap/select1.test.lua b/test/sql-tap/select1.test.lua
index fbebfab37..6248abb65 100755
--- a/test/sql-tap/select1.test.lua
+++ b/test/sql-tap/select1.test.lua
@@ -250,7 +250,7 @@ test:do_catchsql_test(
         SELECT count(f1,f2) FROM test1
     ]], {
         -- <select1-2.1>
-        1, "Wrong number of arguments is passed to COUNT(): expected 0 or 1, got 2"
+        1, "Wrong number of arguments is passed to COUNT(): expected from 0 to 1, got 2"
         -- </select1-2.1>
     })
 
@@ -697,7 +697,7 @@ test:do_catchsql_test(
         SELECT f1 FROM test1 WHERE count(f1,f2)!=11
     ]], {
         -- <select1-3.9>
-        1, "misuse of aggregate function COUNT()"
+        1, "Wrong number of arguments is passed to COUNT(): expected from 0 to 1, got 2"
         -- </select1-3.9>
     })
 
diff --git a/test/sql/collation.result b/test/sql/collation.result
index 4e4c27ef0..9c22c7191 100644
--- a/test/sql/collation.result
+++ b/test/sql/collation.result
@@ -298,7 +298,7 @@ box.execute("SELECT * FROM t WHERE a COLLATE \"binary\" = c COLLATE \"unicode\";
 box.execute("SELECT * FROM t WHERE a COLLATE \"binary\" = substr();")
 ---
 - null
-- 'Wrong number of arguments is passed to SUBSTR(): expected 1 or 2, got 0'
+- 'Wrong number of arguments is passed to SUBSTR(): expected from 2 to 3, got 0'
 ...
 -- Compound queries perform implicit comparisons between values.
 -- Hence, rules for collations compatibilities are the same.
diff --git a/test/sql/gh-4159-function-argumens.result b/test/sql/gh-4159-function-argumens.result
new file mode 100644
index 000000000..48bd550a4
--- /dev/null
+++ b/test/sql/gh-4159-function-argumens.result
@@ -0,0 +1,821 @@
+-- test-run result file version 2
+-- Function abs().
+SELECT abs();
+ | ---
+ | - null
+ | - 'Wrong number of arguments is passed to ABS(): expected 1, got 0'
+ | ...
+SELECT abs(1);
+ | ---
+ | - metadata:
+ |   - name: abs(1)
+ |     type: number
+ |   rows:
+ |   - [1]
+ | ...
+SELECT abs(1, 2);
+ | ---
+ | - null
+ | - 'Wrong number of arguments is passed to ABS(): expected 1, got 2'
+ | ...
+
+-- Function char().
+SELECT char();
+ | ---
+ | - metadata:
+ |   - name: char()
+ |     type: string
+ |   rows:
+ |   - ['']
+ | ...
+SELECT char(1);
+ | ---
+ | - metadata:
+ |   - name: char(1)
+ |     type: string
+ |   rows:
+ |   - ["\x01"]
+ | ...
+SELECT char(1, 2);
+ | ---
+ | - metadata:
+ |   - name: char(1, 2)
+ |     type: string
+ |   rows:
+ |   - ["\x01\x02"]
+ | ...
+
+-- Function character_length().
+SELECT character_length();
+ | ---
+ | - null
+ | - 'Wrong number of arguments is passed to CHARACTER_LENGTH(): expected 1, got 0'
+ | ...
+SELECT character_length('1');
+ | ---
+ | - metadata:
+ |   - name: character_length('1')
+ |     type: integer
+ |   rows:
+ |   - [1]
+ | ...
+SELECT character_length('1', '2');
+ | ---
+ | - null
+ | - 'Wrong number of arguments is passed to CHARACTER_LENGTH(): expected 1, got 2'
+ | ...
+
+-- Function char_length().
+SELECT char_length();
+ | ---
+ | - null
+ | - 'Wrong number of arguments is passed to CHAR_LENGTH(): expected 1, got 0'
+ | ...
+SELECT char_length('1');
+ | ---
+ | - metadata:
+ |   - name: char_length('1')
+ |     type: integer
+ |   rows:
+ |   - [1]
+ | ...
+SELECT char_length('1', '2');
+ | ---
+ | - null
+ | - 'Wrong number of arguments is passed to CHAR_LENGTH(): expected 1, got 2'
+ | ...
+
+-- Function coalesce().
+SELECT coalesce();
+ | ---
+ | - null
+ | - 'Wrong number of arguments is passed to COALESCE(): expected from 2 to 127, got
+ |   0'
+ | ...
+SELECT coalesce('1');
+ | ---
+ | - null
+ | - 'Wrong number of arguments is passed to COALESCE(): expected from 2 to 127, got
+ |   1'
+ | ...
+SELECT coalesce('1', '2');
+ | ---
+ | - metadata:
+ |   - name: coalesce('1', '2')
+ |     type: scalar
+ |   rows:
+ |   - ['1']
+ | ...
+SELECT coalesce('1', '2', '3');
+ | ---
+ | - metadata:
+ |   - name: coalesce('1', '2', '3')
+ |     type: scalar
+ |   rows:
+ |   - ['1']
+ | ...
+
+-- Function greatest().
+SELECT greatest();
+ | ---
+ | - null
+ | - 'Wrong number of arguments is passed to GREATEST(): expected from 2 to 127, got
+ |   0'
+ | ...
+SELECT greatest('1');
+ | ---
+ | - null
+ | - 'Wrong number of arguments is passed to GREATEST(): expected from 2 to 127, got
+ |   1'
+ | ...
+SELECT greatest('1', '2');
+ | ---
+ | - metadata:
+ |   - name: greatest('1', '2')
+ |     type: scalar
+ |   rows:
+ |   - ['2']
+ | ...
+
+-- Function hex().
+SELECT hex();
+ | ---
+ | - null
+ | - 'Wrong number of arguments is passed to HEX(): expected 1, got 0'
+ | ...
+SELECT hex(X'33');
+ | ---
+ | - metadata:
+ |   - name: hex(X'33')
+ |     type: string
+ |   rows:
+ |   - ['33']
+ | ...
+SELECT hex(X'33', X'33');
+ | ---
+ | - null
+ | - 'Wrong number of arguments is passed to HEX(): expected 1, got 2'
+ | ...
+
+-- Function ifnull
+SELECT ifnull();
+ | ---
+ | - null
+ | - 'Wrong number of arguments is passed to IFNULL(): expected 2, got 0'
+ | ...
+SELECT ifnull(1);
+ | ---
+ | - null
+ | - 'Wrong number of arguments is passed to IFNULL(): expected 2, got 1'
+ | ...
+SELECT ifnull(1, 2);
+ | ---
+ | - metadata:
+ |   - name: ifnull(1, 2)
+ |     type: integer
+ |   rows:
+ |   - [1]
+ | ...
+SELECT ifnull(1, 2, 3);
+ | ---
+ | - null
+ | - 'Wrong number of arguments is passed to IFNULL(): expected 2, got 3'
+ | ...
+
+-- Function least().
+SELECT least();
+ | ---
+ | - null
+ | - 'Wrong number of arguments is passed to LEAST(): expected from 2 to 127, got 0'
+ | ...
+SELECT least('1');
+ | ---
+ | - null
+ | - 'Wrong number of arguments is passed to LEAST(): expected from 2 to 127, got 1'
+ | ...
+SELECT least('1', '2');
+ | ---
+ | - metadata:
+ |   - name: least('1', '2')
+ |     type: scalar
+ |   rows:
+ |   - ['1']
+ | ...
+
+-- Function length().
+SELECT length();
+ | ---
+ | - null
+ | - 'Wrong number of arguments is passed to LENGTH(): expected 1, got 0'
+ | ...
+SELECT length('1');
+ | ---
+ | - metadata:
+ |   - name: length('1')
+ |     type: integer
+ |   rows:
+ |   - [1]
+ | ...
+SELECT length('1', '2');
+ | ---
+ | - null
+ | - 'Wrong number of arguments is passed to LENGTH(): expected 1, got 2'
+ | ...
+
+-- Function likelihood
+SELECT likelihood();
+ | ---
+ | - null
+ | - 'Wrong number of arguments is passed to LIKELIHOOD(): expected 2, got 0'
+ | ...
+SELECT likelihood(1);
+ | ---
+ | - null
+ | - 'Wrong number of arguments is passed to LIKELIHOOD(): expected 2, got 1'
+ | ...
+SELECT likelihood(1, 0.5);
+ | ---
+ | - metadata:
+ |   - name: likelihood(1, 0.5)
+ |     type: integer
+ |   rows:
+ |   - [1]
+ | ...
+SELECT likelihood(1, 0.5, 3);
+ | ---
+ | - null
+ | - 'Wrong number of arguments is passed to LIKELIHOOD(): expected 2, got 3'
+ | ...
+
+-- Function likely
+SELECT likely();
+ | ---
+ | - null
+ | - 'Wrong number of arguments is passed to LIKELY(): expected 1, got 0'
+ | ...
+SELECT likely(1);
+ | ---
+ | - metadata:
+ |   - name: likely(1)
+ |     type: integer
+ |   rows:
+ |   - [1]
+ | ...
+SELECT likely(1, 2);
+ | ---
+ | - null
+ | - 'Wrong number of arguments is passed to LIKELY(): expected 1, got 2'
+ | ...
+
+-- Function lower
+SELECT lower();
+ | ---
+ | - null
+ | - 'Wrong number of arguments is passed to LOWER(): expected 1, got 0'
+ | ...
+SELECT lower('a');
+ | ---
+ | - metadata:
+ |   - name: lower('a')
+ |     type: string
+ |   rows:
+ |   - ['a']
+ | ...
+SELECT lower('a', 2);
+ | ---
+ | - null
+ | - 'Wrong number of arguments is passed to LOWER(): expected 1, got 2'
+ | ...
+
+-- Function nullif
+SELECT nullif();
+ | ---
+ | - null
+ | - 'Wrong number of arguments is passed to NULLIF(): expected 2, got 0'
+ | ...
+SELECT nullif(1);
+ | ---
+ | - null
+ | - 'Wrong number of arguments is passed to NULLIF(): expected 2, got 1'
+ | ...
+SELECT nullif(1, 2);
+ | ---
+ | - metadata:
+ |   - name: nullif(1, 2)
+ |     type: scalar
+ |   rows:
+ |   - [1]
+ | ...
+SELECT nullif(1, 2, 3);
+ | ---
+ | - null
+ | - 'Wrong number of arguments is passed to NULLIF(): expected 2, got 3'
+ | ...
+
+-- Function position
+SELECT position();
+ | ---
+ | - null
+ | - 'Wrong number of arguments is passed to POSITION(): expected 2, got 0'
+ | ...
+SELECT position('12345');
+ | ---
+ | - null
+ | - 'Wrong number of arguments is passed to POSITION(): expected 2, got 1'
+ | ...
+SELECT position('12345', '2');
+ | ---
+ | - metadata:
+ |   - name: position('12345', '2')
+ |     type: integer
+ |   rows:
+ |   - [0]
+ | ...
+SELECT position('12345', '2', 3);
+ | ---
+ | - null
+ | - 'Wrong number of arguments is passed to POSITION(): expected 2, got 3'
+ | ...
+
+-- Function printf
+SELECT printf();
+ | ---
+ | - metadata:
+ |   - name: printf()
+ |     type: string
+ |   rows:
+ |   - [null]
+ | ...
+SELECT printf('1');
+ | ---
+ | - metadata:
+ |   - name: printf('1')
+ |     type: string
+ |   rows:
+ |   - ['1']
+ | ...
+SELECT printf('1', 2);
+ | ---
+ | - metadata:
+ |   - name: printf('1', 2)
+ |     type: string
+ |   rows:
+ |   - ['1']
+ | ...
+SELECT printf('1', 2, 3);
+ | ---
+ | - metadata:
+ |   - name: printf('1', 2, 3)
+ |     type: string
+ |   rows:
+ |   - ['1']
+ | ...
+
+-- Function quote
+SELECT quote();
+ | ---
+ | - null
+ | - 'Wrong number of arguments is passed to QUOTE(): expected 1, got 0'
+ | ...
+SELECT quote('1');
+ | ---
+ | - metadata:
+ |   - name: quote('1')
+ |     type: string
+ |   rows:
+ |   - ['''1''']
+ | ...
+SELECT quote('1', 2);
+ | ---
+ | - null
+ | - 'Wrong number of arguments is passed to QUOTE(): expected 1, got 2'
+ | ...
+
+-- Function random
+SELECT typeof(random());
+ | ---
+ | - metadata:
+ |   - name: typeof(random())
+ |     type: string
+ |   rows:
+ |   - ['integer']
+ | ...
+SELECT typeof(random(1));
+ | ---
+ | - null
+ | - 'Wrong number of arguments is passed to RANDOM(): expected 0, got 1'
+ | ...
+
+-- Function randomblob
+SELECT typeof(randomblob());
+ | ---
+ | - null
+ | - 'Wrong number of arguments is passed to RANDOMBLOB(): expected 1, got 0'
+ | ...
+SELECT typeof(randomblob(1));
+ | ---
+ | - metadata:
+ |   - name: typeof(randomblob(1))
+ |     type: string
+ |   rows:
+ |   - ['varbinary']
+ | ...
+SELECT typeof(randomblob(1, 2));
+ | ---
+ | - null
+ | - 'Wrong number of arguments is passed to RANDOMBLOB(): expected 1, got 2'
+ | ...
+
+-- Function replace
+SELECT replace();
+ | ---
+ | - null
+ | - 'Wrong number of arguments is passed to REPLACE(): expected 3, got 0'
+ | ...
+SELECT replace('12345');
+ | ---
+ | - null
+ | - 'Wrong number of arguments is passed to REPLACE(): expected 3, got 1'
+ | ...
+SELECT replace('12345', '2');
+ | ---
+ | - null
+ | - 'Wrong number of arguments is passed to REPLACE(): expected 3, got 2'
+ | ...
+SELECT replace('12345', '2', '3');
+ | ---
+ | - metadata:
+ |   - name: replace('12345', '2', '3')
+ |     type: string
+ |   rows:
+ |   - ['13345']
+ | ...
+SELECT replace('12345', '2', '3', 4);
+ | ---
+ | - null
+ | - 'Wrong number of arguments is passed to REPLACE(): expected 3, got 4'
+ | ...
+
+-- Function round
+SELECT round();
+ | ---
+ | - null
+ | - 'Wrong number of arguments is passed to ROUND(): expected from 1 to 2, got 0'
+ | ...
+SELECT round(1.1245);
+ | ---
+ | - metadata:
+ |   - name: round(1.1245)
+ |     type: integer
+ |   rows:
+ |   - [1]
+ | ...
+SELECT round(1.1245, 2);
+ | ---
+ | - metadata:
+ |   - name: round(1.1245, 2)
+ |     type: integer
+ |   rows:
+ |   - [1.12]
+ | ...
+SELECT round(1.1245, 2, 3);
+ | ---
+ | - null
+ | - 'Wrong number of arguments is passed to ROUND(): expected from 1 to 2, got 3'
+ | ...
+
+-- Function row_count
+SELECT row_count();
+ | ---
+ | - metadata:
+ |   - name: row_count()
+ |     type: integer
+ |   rows:
+ |   - [0]
+ | ...
+SELECT row_count(1);
+ | ---
+ | - null
+ | - 'Wrong number of arguments is passed to ROW_COUNT(): expected 0, got 1'
+ | ...
+
+-- Function soundex
+SELECT soundex();
+ | ---
+ | - null
+ | - 'Wrong number of arguments is passed to SOUNDEX(): expected 1, got 0'
+ | ...
+SELECT soundex(1);
+ | ---
+ | - metadata:
+ |   - name: soundex(1)
+ |     type: string
+ |   rows:
+ |   - ['?000']
+ | ...
+SELECT soundex(1, 2);
+ | ---
+ | - null
+ | - 'Wrong number of arguments is passed to SOUNDEX(): expected 1, got 2'
+ | ...
+
+-- Function substr
+SELECT substr();
+ | ---
+ | - null
+ | - 'Wrong number of arguments is passed to SUBSTR(): expected from 2 to 3, got 0'
+ | ...
+SELECT substr('12345');
+ | ---
+ | - null
+ | - 'Wrong number of arguments is passed to SUBSTR(): expected from 2 to 3, got 1'
+ | ...
+SELECT substr('12345', 2);
+ | ---
+ | - metadata:
+ |   - name: substr('12345', 2)
+ |     type: string
+ |   rows:
+ |   - ['2345']
+ | ...
+SELECT substr('12345', 2, 3);
+ | ---
+ | - metadata:
+ |   - name: substr('12345', 2, 3)
+ |     type: string
+ |   rows:
+ |   - ['234']
+ | ...
+SELECT substr('12345', 2, 3, 4);
+ | ---
+ | - null
+ | - 'Wrong number of arguments is passed to SUBSTR(): expected from 2 to 3, got 4'
+ | ...
+
+-- Function typeof
+SELECT typeof();
+ | ---
+ | - null
+ | - 'Wrong number of arguments is passed to TYPEOF(): expected 1, got 0'
+ | ...
+SELECT typeof(1);
+ | ---
+ | - metadata:
+ |   - name: typeof(1)
+ |     type: string
+ |   rows:
+ |   - ['integer']
+ | ...
+SELECT typeof(1, 2);
+ | ---
+ | - null
+ | - 'Wrong number of arguments is passed to TYPEOF(): expected 1, got 2'
+ | ...
+
+-- Function unicode
+SELECT unicode();
+ | ---
+ | - null
+ | - 'Wrong number of arguments is passed to UNICODE(): expected 1, got 0'
+ | ...
+SELECT unicode('1');
+ | ---
+ | - metadata:
+ |   - name: unicode('1')
+ |     type: string
+ |   rows:
+ |   - [49]
+ | ...
+SELECT unicode('1', '2');
+ | ---
+ | - null
+ | - 'Wrong number of arguments is passed to UNICODE(): expected 1, got 2'
+ | ...
+
+-- Function unlikely
+SELECT unlikely();
+ | ---
+ | - null
+ | - 'Wrong number of arguments is passed to UNLIKELY(): expected 1, got 0'
+ | ...
+SELECT unlikely(1);
+ | ---
+ | - metadata:
+ |   - name: unlikely(1)
+ |     type: integer
+ |   rows:
+ |   - [1]
+ | ...
+SELECT unlikely(1, 2);
+ | ---
+ | - null
+ | - 'Wrong number of arguments is passed to UNLIKELY(): expected 1, got 2'
+ | ...
+
+-- Function upper
+SELECT upper();
+ | ---
+ | - null
+ | - 'Wrong number of arguments is passed to UPPER(): expected 1, got 0'
+ | ...
+SELECT upper('a');
+ | ---
+ | - metadata:
+ |   - name: upper('a')
+ |     type: string
+ |   rows:
+ |   - ['A']
+ | ...
+SELECT upper('a', 2);
+ | ---
+ | - null
+ | - 'Wrong number of arguments is passed to UPPER(): expected 1, got 2'
+ | ...
+
+-- Function version
+SELECT typeof(version());
+ | ---
+ | - metadata:
+ |   - name: typeof(version())
+ |     type: string
+ |   rows:
+ |   - ['string']
+ | ...
+SELECT typeof(version(1));
+ | ---
+ | - null
+ | - 'Wrong number of arguments is passed to VERSION(): expected 0, got 1'
+ | ...
+
+-- Function zeroblob
+SELECT zeroblob();
+ | ---
+ | - null
+ | - 'Wrong number of arguments is passed to ZEROBLOB(): expected 1, got 0'
+ | ...
+SELECT zeroblob(1);
+ | ---
+ | - metadata:
+ |   - name: zeroblob(1)
+ |     type: varbinary
+ |   rows:
+ |   - ["\0"]
+ | ...
+SELECT zeroblob(1, 2);
+ | ---
+ | - null
+ | - 'Wrong number of arguments is passed to ZEROBLOB(): expected 1, got 2'
+ | ...
+
+-- Function avg
+SELECT avg() FROM (values(1), (2), (3));
+ | ---
+ | - null
+ | - 'Wrong number of arguments is passed to AVG(): expected 1, got 0'
+ | ...
+SELECT avg("_auto_field_") FROM (values(1), (2), (3));
+ | ---
+ | - metadata:
+ |   - name: avg("_auto_field_")
+ |     type: number
+ |   rows:
+ |   - [2]
+ | ...
+SELECT avg("_auto_field_", 2) FROM (values(1), (2), (3));
+ | ---
+ | - null
+ | - 'Wrong number of arguments is passed to AVG(): expected 1, got 2'
+ | ...
+
+-- Function count
+SELECT count() FROM (values(1), (2), (3));
+ | ---
+ | - metadata:
+ |   - name: count()
+ |     type: integer
+ |   rows:
+ |   - [3]
+ | ...
+SELECT count("_auto_field_") FROM (values(1), (2), (3));
+ | ---
+ | - metadata:
+ |   - name: count("_auto_field_")
+ |     type: integer
+ |   rows:
+ |   - [3]
+ | ...
+SELECT count("_auto_field_", 2) FROM (values(1), (2), (3));
+ | ---
+ | - null
+ | - 'Wrong number of arguments is passed to COUNT(): expected from 0 to 1, got 2'
+ | ...
+
+-- Function group_concat
+SELECT group_concat() FROM (values('1'), ('2'), ('3'));
+ | ---
+ | - null
+ | - 'Wrong number of arguments is passed to GROUP_CONCAT(): expected from 1 to 2, got
+ |   0'
+ | ...
+SELECT group_concat("_auto_field_") FROM (values('1'), ('2'), ('3'));
+ | ---
+ | - metadata:
+ |   - name: group_concat("_auto_field_")
+ |     type: string
+ |   rows:
+ |   - ['1,2,3']
+ | ...
+SELECT group_concat("_auto_field_", '2') FROM (values('1'), ('2'), ('3'));
+ | ---
+ | - metadata:
+ |   - name: group_concat("_auto_field_", '2')
+ |     type: string
+ |   rows:
+ |   - ['12223']
+ | ...
+SELECT group_concat("_auto_field_", '2', '3') FROM (values('1'), ('2'), ('3'));
+ | ---
+ | - null
+ | - 'Wrong number of arguments is passed to GROUP_CONCAT(): expected from 1 to 2, got
+ |   3'
+ | ...
+
+-- Function max
+SELECT max() FROM (values(1), (2), (3));
+ | ---
+ | - null
+ | - 'Wrong number of arguments is passed to MAX(): expected 1, got 0'
+ | ...
+SELECT max("_auto_field_") FROM (values(1), (2), (3));
+ | ---
+ | - metadata:
+ |   - name: max("_auto_field_")
+ |     type: scalar
+ |   rows:
+ |   - [3]
+ | ...
+SELECT max("_auto_field_", 2) FROM (values(1), (2), (3));
+ | ---
+ | - null
+ | - 'Wrong number of arguments is passed to MAX(): expected 1, got 2'
+ | ...
+
+-- Function min
+SELECT min() FROM (values(1), (2), (3));
+ | ---
+ | - null
+ | - 'Wrong number of arguments is passed to MIN(): expected 1, got 0'
+ | ...
+SELECT min("_auto_field_") FROM (values(1), (2), (3));
+ | ---
+ | - metadata:
+ |   - name: min("_auto_field_")
+ |     type: scalar
+ |   rows:
+ |   - [1]
+ | ...
+SELECT min("_auto_field_", 2) FROM (values(1), (2), (3));
+ | ---
+ | - null
+ | - 'Wrong number of arguments is passed to MIN(): expected 1, got 2'
+ | ...
+
+-- Function sum
+SELECT sum() FROM (values(1), (2), (3));
+ | ---
+ | - null
+ | - 'Wrong number of arguments is passed to SUM(): expected 1, got 0'
+ | ...
+SELECT sum("_auto_field_") FROM (values(1), (2), (3));
+ | ---
+ | - metadata:
+ |   - name: sum("_auto_field_")
+ |     type: number
+ |   rows:
+ |   - [6]
+ | ...
+SELECT sum("_auto_field_", 2) FROM (values(1), (2), (3));
+ | ---
+ | - null
+ | - 'Wrong number of arguments is passed to SUM(): expected 1, got 2'
+ | ...
+
+-- Function total
+SELECT total() FROM (values(1), (2), (3));
+ | ---
+ | - null
+ | - 'Wrong number of arguments is passed to TOTAL(): expected 1, got 0'
+ | ...
+SELECT total("_auto_field_") FROM (values(1), (2), (3));
+ | ---
+ | - metadata:
+ |   - name: total("_auto_field_")
+ |     type: number
+ |   rows:
+ |   - [6]
+ | ...
+SELECT total("_auto_field_", 2) FROM (values(1), (2), (3));
+ | ---
+ | - null
+ | - 'Wrong number of arguments is passed to TOTAL(): expected 1, got 2'
+ | ...
diff --git a/test/sql/gh-4159-function-argumens.test.sql b/test/sql/gh-4159-function-argumens.test.sql
new file mode 100644
index 000000000..7578768cb
--- /dev/null
+++ b/test/sql/gh-4159-function-argumens.test.sql
@@ -0,0 +1,193 @@
+-- Function abs().
+SELECT abs();
+SELECT abs(1);
+SELECT abs(1, 2);
+
+-- Function char().
+SELECT char();
+SELECT char(1);
+SELECT char(1, 2);
+
+-- Function character_length().
+SELECT character_length();
+SELECT character_length('1');
+SELECT character_length('1', '2');
+
+-- Function char_length().
+SELECT char_length();
+SELECT char_length('1');
+SELECT char_length('1', '2');
+
+-- Function coalesce().
+SELECT coalesce();
+SELECT coalesce('1');
+SELECT coalesce('1', '2');
+SELECT coalesce('1', '2', '3');
+
+-- Function greatest().
+SELECT greatest();
+SELECT greatest('1');
+SELECT greatest('1', '2');
+
+-- Function hex().
+SELECT hex();
+SELECT hex(X'33');
+SELECT hex(X'33', X'33');
+
+-- Function ifnull
+SELECT ifnull();
+SELECT ifnull(1);
+SELECT ifnull(1, 2);
+SELECT ifnull(1, 2, 3);
+
+-- Function least().
+SELECT least();
+SELECT least('1');
+SELECT least('1', '2');
+
+-- Function length().
+SELECT length();
+SELECT length('1');
+SELECT length('1', '2');
+
+-- Function likelihood
+SELECT likelihood();
+SELECT likelihood(1);
+SELECT likelihood(1, 0.5);
+SELECT likelihood(1, 0.5, 3);
+
+-- Function likely
+SELECT likely();
+SELECT likely(1);
+SELECT likely(1, 2);
+
+-- Function lower
+SELECT lower();
+SELECT lower('a');
+SELECT lower('a', 2);
+
+-- Function nullif
+SELECT nullif();
+SELECT nullif(1);
+SELECT nullif(1, 2);
+SELECT nullif(1, 2, 3);
+
+-- Function position
+SELECT position();
+SELECT position('12345');
+SELECT position('12345', '2');
+SELECT position('12345', '2', 3);
+
+-- Function printf
+SELECT printf();
+SELECT printf('1');
+SELECT printf('1', 2);
+SELECT printf('1', 2, 3);
+
+-- Function quote
+SELECT quote();
+SELECT quote('1');
+SELECT quote('1', 2);
+
+-- Function random
+SELECT typeof(random());
+SELECT typeof(random(1));
+
+-- Function randomblob
+SELECT typeof(randomblob());
+SELECT typeof(randomblob(1));
+SELECT typeof(randomblob(1, 2));
+
+-- Function replace
+SELECT replace();
+SELECT replace('12345');
+SELECT replace('12345', '2');
+SELECT replace('12345', '2', '3');
+SELECT replace('12345', '2', '3', 4);
+
+-- Function round
+SELECT round();
+SELECT round(1.1245);
+SELECT round(1.1245, 2);
+SELECT round(1.1245, 2, 3);
+
+-- Function row_count
+SELECT row_count();
+SELECT row_count(1);
+
+-- Function soundex
+SELECT soundex();
+SELECT soundex(1);
+SELECT soundex(1, 2);
+
+-- Function substr
+SELECT substr();
+SELECT substr('12345');
+SELECT substr('12345', 2);
+SELECT substr('12345', 2, 3);
+SELECT substr('12345', 2, 3, 4);
+
+-- Function typeof
+SELECT typeof();
+SELECT typeof(1);
+SELECT typeof(1, 2);
+
+-- Function unicode
+SELECT unicode();
+SELECT unicode('1');
+SELECT unicode('1', '2');
+
+-- Function unlikely
+SELECT unlikely();
+SELECT unlikely(1);
+SELECT unlikely(1, 2);
+
+-- Function upper
+SELECT upper();
+SELECT upper('a');
+SELECT upper('a', 2);
+
+-- Function version
+SELECT typeof(version());
+SELECT typeof(version(1));
+
+-- Function zeroblob
+SELECT zeroblob();
+SELECT zeroblob(1);
+SELECT zeroblob(1, 2);
+
+-- Function avg
+SELECT avg() FROM (values(1), (2), (3));
+SELECT avg("_auto_field_") FROM (values(1), (2), (3));
+SELECT avg("_auto_field_", 2) FROM (values(1), (2), (3));
+
+-- Function count
+SELECT count() FROM (values(1), (2), (3));
+SELECT count("_auto_field_") FROM (values(1), (2), (3));
+SELECT count("_auto_field_", 2) FROM (values(1), (2), (3));
+
+-- Function group_concat
+SELECT group_concat() FROM (values('1'), ('2'), ('3'));
+SELECT group_concat("_auto_field_") FROM (values('1'), ('2'), ('3'));
+SELECT group_concat("_auto_field_", '2') FROM (values('1'), ('2'), ('3'));
+SELECT group_concat("_auto_field_", '2', '3') FROM (values('1'), ('2'), ('3'));
+
+-- Function max
+SELECT max() FROM (values(1), (2), (3));
+SELECT max("_auto_field_") FROM (values(1), (2), (3));
+SELECT max("_auto_field_", 2) FROM (values(1), (2), (3));
+
+-- Function min
+SELECT min() FROM (values(1), (2), (3));
+SELECT min("_auto_field_") FROM (values(1), (2), (3));
+SELECT min("_auto_field_", 2) FROM (values(1), (2), (3));
+
+-- Function sum
+SELECT sum() FROM (values(1), (2), (3));
+SELECT sum("_auto_field_") FROM (values(1), (2), (3));
+SELECT sum("_auto_field_", 2) FROM (values(1), (2), (3));
+
+-- Function total
+SELECT total() FROM (values(1), (2), (3));
+SELECT total("_auto_field_") FROM (values(1), (2), (3));
+SELECT total("_auto_field_", 2) FROM (values(1), (2), (3));
-- 
2.25.1



More information about the Tarantool-patches mailing list