Tarantool development patches archive
 help / color / mirror / Atom feed
* [Tarantool-patches] [PATCH v1 0/7] sql: properly check arguments types of built-in functions
@ 2020-08-12 15:15 imeevma
  2020-08-12 15:15 ` [Tarantool-patches] [PATCH v1 1/7] box: add has_vararg option for functions imeevma
                   ` (6 more replies)
  0 siblings, 7 replies; 8+ messages in thread
From: imeevma @ 2020-08-12 15:15 UTC (permalink / raw)
  To: v.shpilevoy; +Cc: tarantool-patches

This patch-set makes SQL to use the ApplyType opcode to validate the argument
types of built-in functions.

https://github.com/tarantool/tarantool/issues/4159
https://github.com/tarantool/tarantool/tree/imeevma/gh-4159-rework-sql-builtins

@ChangeLog
 - Built-in function argument types are now properly checked (gh-4159).

Mergen Imeev (7):
  box: add has_vararg option for functions
  sql: do not return UNSIGNED in built-in functions
  sql: move built-in function definitions in _func
  box: add param_list to 'struct func'
  sql: check built-in functions argument types
  sql: VARBINARY and STRING in built-in functions
  sql: refactor sql/func.c

 src/box/alter.cc               |   12 +-
 src/box/bootstrap.snap         |  Bin 5976 -> 6291 bytes
 src/box/func.c                 |    1 +
 src/box/func_def.c             |    5 +
 src/box/func_def.h             |    8 +
 src/box/lua/call.c             |    2 +
 src/box/lua/upgrade.lua        |  143 +++
 src/box/sql/expr.c             |    4 +
 src/box/sql/func.c             |  894 +++----------------
 src/box/sql/resolve.c          |    2 +-
 src/box/sql/select.c           |   26 +
 src/box/sql/sqlInt.h           |   14 +
 src/box/sql/vdbeapi.c          |    2 +-
 test/box-py/bootstrap.result   |    2 +-
 test/sql-tap/cse.test.lua      |    4 +-
 test/sql-tap/func.test.lua     |   36 +-
 test/sql-tap/orderby1.test.lua |    2 +-
 test/sql-tap/position.test.lua |    6 +-
 test/sql/boolean.result        |   32 +-
 test/sql/checks.result         |    8 -
 test/sql/checks.test.lua       |    2 -
 test/sql/types.result          | 1489 +++++++++++++++++++++++++++++++-
 test/sql/types.test.lua        |  250 ++++++
 23 files changed, 2092 insertions(+), 852 deletions(-)

-- 
2.25.1

^ permalink raw reply	[flat|nested] 8+ messages in thread

* [Tarantool-patches] [PATCH v1 1/7] box: add has_vararg option for functions
  2020-08-12 15:15 [Tarantool-patches] [PATCH v1 0/7] sql: properly check arguments types of built-in functions imeevma
@ 2020-08-12 15:15 ` imeevma
  2020-08-12 15:15 ` [Tarantool-patches] [PATCH v1 2/7] sql: do not return UNSIGNED in built-in functions imeevma
                   ` (5 subsequent siblings)
  6 siblings, 0 replies; 8+ messages in thread
From: imeevma @ 2020-08-12 15:15 UTC (permalink / raw)
  To: v.shpilevoy; +Cc: tarantool-patches

The has_vararg option allows us to work with functions with a variable number of
arguments. This is required for built-in SQL functions.

Suppose this option is TRUE for a built-in SQL function. Then:
1) If param_list is empty, all arguments can be of any type.
2) If the length of param_list is not less than the number of the given
arguments, the types of the given arguments must be compatible with the
corresponding types described in param_list.
3) If the length of param_list is less than the number of given arguments, the
rest of the arguments must be compatible with the last type in param_list.

Part of #4159
---
 src/box/func_def.c | 5 +++++
 src/box/func_def.h | 6 ++++++
 2 files changed, 11 insertions(+)

diff --git a/src/box/func_def.c b/src/box/func_def.c
index 11d2bdb84..be12ce970 100644
--- a/src/box/func_def.c
+++ b/src/box/func_def.c
@@ -40,10 +40,13 @@ const char *func_aggregate_strs[] = {"none", "group"};
 
 const struct func_opts func_opts_default = {
 	/* .is_multikey = */ false,
+	/* .has_vararg = */ false,
 };
 
 const struct opt_def func_opts_reg[] = {
 	OPT_DEF("is_multikey", OPT_BOOL, struct func_opts, is_multikey),
+	OPT_DEF("has_vararg", OPT_BOOL, struct func_opts, has_vararg),
+	OPT_END,
 };
 
 int
@@ -51,6 +54,8 @@ func_opts_cmp(struct func_opts *o1, struct func_opts *o2)
 {
 	if (o1->is_multikey != o2->is_multikey)
 		return o1->is_multikey - o2->is_multikey;
+	if (o1->has_vararg != o2->has_vararg)
+		return o1->has_vararg - o2->has_vararg;
 	return 0;
 }
 
diff --git a/src/box/func_def.h b/src/box/func_def.h
index d99d89190..89d5a404a 100644
--- a/src/box/func_def.h
+++ b/src/box/func_def.h
@@ -68,6 +68,12 @@ struct func_opts {
 	 * packed in array.
 	 */
 	bool is_multikey;
+	/**
+	 * TRUE if the function can have a variable number of arguments.
+	 *
+	 * Currently only used in built-in SQL functions.
+	 */
+	bool has_vararg;
 };
 
 extern const struct func_opts func_opts_default;
-- 
2.25.1

^ permalink raw reply	[flat|nested] 8+ messages in thread

* [Tarantool-patches] [PATCH v1 2/7] sql: do not return UNSIGNED in built-in functions
  2020-08-12 15:15 [Tarantool-patches] [PATCH v1 0/7] sql: properly check arguments types of built-in functions imeevma
  2020-08-12 15:15 ` [Tarantool-patches] [PATCH v1 1/7] box: add has_vararg option for functions imeevma
@ 2020-08-12 15:15 ` imeevma
  2020-08-12 15:15 ` [Tarantool-patches] [PATCH v1 3/7] sql: move built-in function definitions in _func imeevma
                   ` (4 subsequent siblings)
  6 siblings, 0 replies; 8+ messages in thread
From: imeevma @ 2020-08-12 15:15 UTC (permalink / raw)
  To: v.shpilevoy; +Cc: tarantool-patches

This patch forces functions to return INTEGER instead of UNSIGNED.

Part of #4159
---
 src/box/sql/vdbeapi.c   |  2 +-
 test/sql/types.result   | 12 ++++++++++++
 test/sql/types.test.lua |  6 ++++++
 3 files changed, 19 insertions(+), 1 deletion(-)

diff --git a/src/box/sql/vdbeapi.c b/src/box/sql/vdbeapi.c
index 7c59ef83f..d1eeaf114 100644
--- a/src/box/sql/vdbeapi.c
+++ b/src/box/sql/vdbeapi.c
@@ -328,7 +328,7 @@ sql_result_double(sql_context * pCtx, double rVal)
 void
 sql_result_uint(sql_context *ctx, uint64_t u_val)
 {
-	mem_set_u64(ctx->pOut, u_val);
+	mem_set_int(ctx->pOut, u_val, false);
 }
 
 void
diff --git a/test/sql/types.result b/test/sql/types.result
index 442245186..95f7713e8 100644
--- a/test/sql/types.result
+++ b/test/sql/types.result
@@ -2795,3 +2795,15 @@ box.execute([[DROP TABLE ts;]])
 ---
 - row_count: 1
 ...
+--
+-- gh-4159: Make sure that functions returns values of type INTEGER
+-- instead of values of type UNSIGNED.
+--
+box.execute([[SELECT typeof(length('abc'));]])
+---
+- metadata:
+  - name: COLUMN_1
+    type: string
+  rows:
+  - ['integer']
+...
diff --git a/test/sql/types.test.lua b/test/sql/types.test.lua
index 0270d9f8a..fff0057bd 100644
--- a/test/sql/types.test.lua
+++ b/test/sql/types.test.lua
@@ -623,3 +623,9 @@ box.execute([[DROP TABLE tb;]])
 box.execute([[DROP TABLE tt;]])
 box.execute([[DROP TABLE tv;]])
 box.execute([[DROP TABLE ts;]])
+
+--
+-- gh-4159: Make sure that functions returns values of type INTEGER
+-- instead of values of type UNSIGNED.
+--
+box.execute([[SELECT typeof(length('abc'));]])
-- 
2.25.1

^ permalink raw reply	[flat|nested] 8+ messages in thread

* [Tarantool-patches] [PATCH v1 3/7] sql: move built-in function definitions in _func
  2020-08-12 15:15 [Tarantool-patches] [PATCH v1 0/7] sql: properly check arguments types of built-in functions imeevma
  2020-08-12 15:15 ` [Tarantool-patches] [PATCH v1 1/7] box: add has_vararg option for functions imeevma
  2020-08-12 15:15 ` [Tarantool-patches] [PATCH v1 2/7] sql: do not return UNSIGNED in built-in functions imeevma
@ 2020-08-12 15:15 ` imeevma
  2020-08-12 15:15 ` [Tarantool-patches] [PATCH v1 4/7] box: add param_list to 'struct func' imeevma
                   ` (3 subsequent siblings)
  6 siblings, 0 replies; 8+ messages in thread
From: imeevma @ 2020-08-12 15:15 UTC (permalink / raw)
  To: v.shpilevoy; +Cc: tarantool-patches

This patch moves SQL built-in function definitions to _func. This helps
create an unified way to check the types of arguments. It also allows
users to see these definitions.

Part of #4159
---
 src/box/bootstrap.snap       | Bin 5976 -> 6291 bytes
 src/box/lua/upgrade.lua      | 143 +++++++++++++++++++++++++++++++++++
 src/box/sql/func.c           |   7 +-
 src/box/sql/resolve.c        |   2 +-
 test/box-py/bootstrap.result |   2 +-
 test/sql/types.result        |   4 +-
 6 files changed, 148 insertions(+), 10 deletions(-)

diff --git a/src/box/bootstrap.snap b/src/box/bootstrap.snap
index 8bd4f7ce24216a8bcced6aa97c20c83eb7a02c77..9163165b13b27771414243650051e900b3af8def 100644
GIT binary patch
literal 6291
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
Jf<M&|t?j=z`0@Y%

literal 5976
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
G5UuU4uRFp3

diff --git a/src/box/lua/upgrade.lua b/src/box/lua/upgrade.lua
index add791cd7..54fc82dc6 100644
--- a/src/box/lua/upgrade.lua
+++ b/src/box/lua/upgrade.lua
@@ -971,6 +971,148 @@ local function upgrade_to_2_3_1()
     create_session_settings_space()
 end
 
+--------------------------------------------------------------------------------
+-- Tarantool 2.5.2
+--------------------------------------------------------------------------------
+
+local function update_sql_builtin_functions()
+    local _func = box.space[box.schema.FUNC_ID]
+    local updates
+
+    _func:run_triggers(false)
+    updates = {{'=', 'param_list', {'number'}}, {'=', 'returns', 'number'},
+               {'=', 'is_deterministic', true}, {'=', 'exports', {'SQL'}}}
+    _func.index[2]:update('ABS', updates)
+
+    updates = {{'=', 'param_list', {'number'}}, {'=', 'returns', 'number'},
+               {'=', 'aggregate', 'group'}, {'=', 'exports', {'SQL'}}}
+    _func.index[2]:update('AVG', updates)
+    _func.index[2]:update('SUM', updates)
+    _func.index[2]:update('TOTAL', updates)
+
+    updates = {{'=', 'param_list', {'unsigned'}}, {'=', 'returns', 'string'},
+               {'=', 'is_deterministic', true}, {'=', 'exports', {'SQL'}},
+               {'=', 'opts', {has_vararg = true}}}
+    _func.index[2]:update('CHAR', updates)
+
+    updates = {{'=', 'param_list', {'scalar'}}, {'=', 'returns', 'integer'},
+               {'=', 'is_deterministic', true}, {'=', 'exports', {'SQL'}}}
+    _func.index[2]:update('CHARACTER_LENGTH', updates)
+    _func.index[2]:update('CHAR_LENGTH', updates)
+    _func.index[2]:update('LENGTH', updates)
+
+    updates = {{'=', 'param_list', {'scalar'}}, {'=', 'returns', 'scalar'},
+               {'=', 'is_deterministic', true}, {'=', 'exports', {'SQL'}},
+               {'=', 'opts', {has_vararg = true}}}
+    _func.index[2]:update('COALESCE', updates)
+
+    updates = {{'=', 'param_list', {'scalar'}}, {'=', 'returns', 'integer'},
+               {'=', 'aggregate', 'group'}, {'=', 'exports', {'SQL'}},
+               {'=', 'opts', {has_vararg = true}}}
+    _func.index[2]:update('COUNT', updates)
+
+    updates = {{'=', 'param_list', {'scalar'}}, {'=', 'returns', 'scalar'},
+               {'=', 'is_deterministic', true}, {'=', 'exports', {'SQL'}},
+               {'=', 'opts', {has_vararg = true}}}
+    _func.index[2]:update('GREATEST', updates)
+    _func.index[2]:update('LEAST', updates)
+
+    updates = {{'=', 'param_list', {'scalar', 'scalar'}},
+               {'=', 'returns', 'string'}, {'=', 'aggregate', 'group'},
+               {'=', 'exports', {'SQL'}}, {'=', 'opts', {has_vararg = true}}}
+    _func.index[2]:update('GROUP_CONCAT', updates)
+
+    updates = {{'=', 'param_list', {'scalar'}}, {'=', 'returns', 'string'},
+               {'=', 'is_deterministic', true}, {'=', 'exports', {'SQL'}}}
+    _func.index[2]:update('HEX', updates)
+
+    updates = {{'=', 'param_list', {'scalar', 'scalar'}},
+               {'=', 'returns', 'scalar'}, {'=', 'is_deterministic', true},
+               {'=', 'exports', {'SQL'}}}
+    _func.index[2]:update('IFNULL', updates)
+    _func.index[2]:update('NULLIF', updates)
+
+    updates = {{'=', 'param_list', {'scalar', 'scalar', 'scalar'}},
+               {'=', 'returns', 'boolean'}, {'=', 'is_deterministic', true},
+               {'=', 'exports', {'SQL'}}, {'=', 'opts', {has_vararg = true}}}
+    _func.index[2]:update('LIKE', updates)
+
+    updates = {{'=', 'param_list', {'scalar', 'double'}},
+               {'=', 'returns', 'scalar'}, {'=', 'is_deterministic', true},
+               {'=', 'exports', {'SQL'}}}
+    _func.index[2]:update('LIKELIHOOD', updates)
+
+    updates = {{'=', 'param_list', {'scalar'}}, {'=', 'returns', 'scalar'},
+               {'=', 'is_deterministic', true}, {'=', 'exports', {'SQL'}}}
+    _func.index[2]:update('LIKELY', updates)
+    _func.index[2]:update('UNLIKELY', updates)
+
+    updates = {{'=', 'param_list', {'scalar'}}, {'=', 'returns', 'string'},
+               {'=', 'is_deterministic', true}, {'=', 'exports', {'SQL'}}}
+    _func.index[2]:update('LOWER', updates)
+    _func.index[2]:update('QUOTE', updates)
+    _func.index[2]:update('SOUNDEX', updates)
+    _func.index[2]:update('TYPEOF', updates)
+    _func.index[2]:update('UPPER', updates)
+    _func.index[2]:update('UNICODE', updates)
+
+    updates = {{'=', 'param_list', {'scalar'}}, {'=', 'returns', 'scalar'},
+               {'=', 'aggregate', 'group'}, {'=', 'exports', {'SQL'}}}
+    _func.index[2]:update('MAX', updates)
+    _func.index[2]:update('MIN', updates)
+
+    updates = {{'=', 'param_list', {'scalar', 'scalar'}},
+               {'=', 'returns', 'integer'}, {'=', 'is_deterministic', true},
+               {'=', 'exports', {'SQL'}}}
+    _func.index[2]:update('POSITION', updates)
+
+    updates = {{'=', 'param_list', {'scalar'}}, {'=', 'returns', 'string'},
+               {'=', 'is_deterministic', true}, {'=', 'exports', {'SQL'}},
+               {'=', 'opts', {has_vararg = true}}}
+    _func.index[2]:update('PRINTF', updates)
+    _func.index[2]:update('TRIM', updates)
+
+    updates = {{'=', 'returns', 'integer'}, {'=', 'exports', {'SQL'}}}
+    _func.index[2]:update('RANDOM', updates)
+
+    updates = {{'=', 'returns', 'integer'}, {'=', 'is_deterministic', true},
+               {'=', 'exports', {'SQL'}}}
+    _func.index[2]:update('ROW_COUNT', updates)
+
+    updates = {{'=', 'param_list', {'unsigned'}},
+               {'=', 'returns', 'varbinary'}, {'=', 'exports', {'SQL'}}}
+    _func.index[2]:update('RANDOMBLOB', updates)
+
+    updates = {{'=', 'param_list', {'unsigned'}},
+               {'=', 'returns', 'varbinary'}, {'=', 'is_deterministic', true},
+               {'=', 'exports', {'SQL'}}}
+    _func.index[2]:update('ZEROBLOB', updates)
+
+    updates = {{'=', 'param_list', {'scalar', 'scalar', 'scalar'}},
+               {'=', 'returns', 'string'}, {'=', 'is_deterministic', true},
+               {'=', 'exports', {'SQL'}}}
+    _func.index[2]:update('REPLACE', updates)
+
+    updates = {{'=', 'param_list', {'double', 'unsigned'}},
+               {'=', 'returns', 'double'}, {'=', 'is_deterministic', true},
+               {'=', 'exports', {'SQL'}}, {'=', 'opts', {has_vararg = true}}}
+    _func.index[2]:update('ROUND', updates)
+
+    updates = {{'=', 'param_list', {'scalar', 'integer', 'integer'}},
+               {'=', 'returns', 'string'}, {'=', 'is_deterministic', true},
+               {'=', 'exports', {'SQL'}}, {'=', 'opts', {has_vararg = true}}}
+    _func.index[2]:update('SUBSTR', updates)
+
+    updates = {{'=', 'returns', 'string'}, {'=', 'is_deterministic', true},
+               {'=', 'exports', {'SQL'}}}
+    _func.index[2]:update('VERSION', updates)
+    _func:run_triggers(true)
+end
+
+local function upgrade_to_2_5_2()
+    update_sql_builtin_functions()
+end
+
 --------------------------------------------------------------------------------
 
 local function get_version()
@@ -1007,6 +1149,7 @@ local function upgrade(options)
         {version = mkversion(2, 2, 1), func = upgrade_to_2_2_1, auto = true},
         {version = mkversion(2, 3, 0), func = upgrade_to_2_3_0, auto = true},
         {version = mkversion(2, 3, 1), func = upgrade_to_2_3_1, auto = true},
+        {version = mkversion(2, 5, 2), func = upgrade_to_2_5_2, auto = true},
     }
 
     for _, handler in ipairs(handlers) do
diff --git a/src/box/sql/func.c b/src/box/sql/func.c
index 487cdafe1..df15d303a 100644
--- a/src/box/sql/func.c
+++ b/src/box/sql/func.c
@@ -2188,7 +2188,7 @@ sql_func_by_signature(const char *name, int argc)
 	if (base == NULL || !base->def->exports.sql)
 		return NULL;
 
-	if (base->def->param_count != -1 && base->def->param_count != argc)
+	if (!base->def->opts.has_vararg && base->def->param_count != argc)
 		return NULL;
 	return base;
 }
@@ -2929,11 +2929,6 @@ 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;
-	def->param_count = sql_builtins[idx].param_count;
-	def->is_deterministic = sql_builtins[idx].is_deterministic;
-	def->returns = sql_builtins[idx].returns;
-	def->aggregate = sql_builtins[idx].aggregate;
-	def->exports.sql = sql_builtins[idx].export_to_sql;
 	return &func->base;
 }
 
diff --git a/src/box/sql/resolve.c b/src/box/sql/resolve.c
index 6f625dc18..5238555c3 100644
--- a/src/box/sql/resolve.c
+++ b/src/box/sql/resolve.c
@@ -614,7 +614,7 @@ resolveExprStep(Walker * pWalker, Expr * pExpr)
 				pNC->nErr++;
 				return WRC_Abort;
 			}
-			if (func->def->param_count != -1 &&
+			if (!func->def->opts.has_vararg &&
 			    func->def->param_count != n) {
 				uint32_t argc = func->def->param_count;
 				const char *err = tt_sprintf("%d", argc);
diff --git a/test/box-py/bootstrap.result b/test/box-py/bootstrap.result
index 0876e77a6..c56f07cef 100644
--- a/test/box-py/bootstrap.result
+++ b/test/box-py/bootstrap.result
@@ -4,7 +4,7 @@ box.internal.bootstrap()
 box.space._schema:select{}
 ---
 - - ['max_id', 511]
-  - ['version', 2, 3, 1]
+  - ['version', 2, 5, 2]
 ...
 box.space._cluster:select{}
 ---
diff --git a/test/sql/types.result b/test/sql/types.result
index 95f7713e8..2498f3a48 100644
--- a/test/sql/types.result
+++ b/test/sql/types.result
@@ -231,7 +231,7 @@ box.execute("SELECT s LIKE NULL FROM t1;")
 ---
 - metadata:
   - name: COLUMN_1
-    type: integer
+    type: boolean
   rows:
   - [null]
 ...
@@ -257,7 +257,7 @@ box.execute("SELECT NULL LIKE s FROM t1;")
 ---
 - metadata:
   - name: COLUMN_1
-    type: integer
+    type: boolean
   rows:
   - [null]
 ...
-- 
2.25.1

^ permalink raw reply	[flat|nested] 8+ messages in thread

* [Tarantool-patches] [PATCH v1 4/7] box: add param_list to 'struct func'
  2020-08-12 15:15 [Tarantool-patches] [PATCH v1 0/7] sql: properly check arguments types of built-in functions imeevma
                   ` (2 preceding siblings ...)
  2020-08-12 15:15 ` [Tarantool-patches] [PATCH v1 3/7] sql: move built-in function definitions in _func imeevma
@ 2020-08-12 15:15 ` imeevma
  2020-08-12 15:15 ` [Tarantool-patches] [PATCH v1 5/7] sql: check built-in functions argument types imeevma
                   ` (2 subsequent siblings)
  6 siblings, 0 replies; 8+ messages in thread
From: imeevma @ 2020-08-12 15:15 UTC (permalink / raw)
  To: v.shpilevoy; +Cc: tarantool-patches

This is needed to create an uniform way to check the types of arguments
of SQL built-in functions.

Part of #4159
---
 src/box/alter.cc   | 12 ++++++++++--
 src/box/func.c     |  1 +
 src/box/func_def.h |  2 ++
 src/box/lua/call.c |  2 ++
 src/box/sql/func.c |  1 +
 5 files changed, 16 insertions(+), 2 deletions(-)

diff --git a/src/box/alter.cc b/src/box/alter.cc
index ba96d9c62..0914a7615 100644
--- a/src/box/alter.cc
+++ b/src/box/alter.cc
@@ -3293,7 +3293,11 @@ func_def_new_from_tuple(struct tuple *tuple)
 		diag_set(OutOfMemory, def_sz, "malloc", "def");
 		return NULL;
 	}
-	auto def_guard = make_scoped_guard([=] { free(def); });
+	def->param_list = NULL;
+	auto def_guard = make_scoped_guard([=] {
+		free(def->param_list);
+		free(def);
+	});
 	if (func_def_get_ids_from_tuple(tuple, &def->fid, &def->uid) != 0)
 		return NULL;
 	if (def->fid > BOX_FUNCTION_MAX) {
@@ -3403,6 +3407,8 @@ func_def_new_from_tuple(struct tuple *tuple)
 		if (param_list == NULL)
 			return NULL;
 		uint32_t argc = mp_decode_array(&param_list);
+		uint32_t size = sizeof(enum field_type) * argc;
+		def->param_list = (enum field_type *)malloc(size);
 		for (uint32_t i = 0; i < argc; i++) {
 			 if (mp_typeof(*param_list) != MP_STR) {
 				diag_set(ClientError, ER_FIELD_TYPE,
@@ -3412,7 +3418,8 @@ func_def_new_from_tuple(struct tuple *tuple)
 			}
 			uint32_t len;
 			const char *str = mp_decode_str(&param_list, &len);
-			if (STRN2ENUM(field_type, str, len) == field_type_MAX) {
+			def->param_list[i] = STRN2ENUM(field_type, str, len);
+			if (def->param_list[i] == field_type_MAX) {
 				diag_set(ClientError, ER_CREATE_FUNCTION,
 					  def->name, "invalid argument type");
 				return NULL;
@@ -3433,6 +3440,7 @@ func_def_new_from_tuple(struct tuple *tuple)
 		/* By default export to Lua, but not other frontends. */
 		def->exports.lua = true;
 		def->param_count = 0;
+		assert(def->param_list == NULL);
 	}
 	if (func_def_check(def) != 0)
 		return NULL;
diff --git a/src/box/func.c b/src/box/func.c
index 8087c953f..1d20f8872 100644
--- a/src/box/func.c
+++ b/src/box/func.c
@@ -510,6 +510,7 @@ func_c_destroy(struct func *base)
 {
 	assert(base->vtab == &func_c_vtab);
 	assert(base != NULL && base->def->language == FUNC_LANGUAGE_C);
+	free(base->def->param_list);
 	struct func_c *func = (struct func_c *) base;
 	func_c_unload(func);
 	TRASH(base);
diff --git a/src/box/func_def.h b/src/box/func_def.h
index 89d5a404a..8b86167af 100644
--- a/src/box/func_def.h
+++ b/src/box/func_def.h
@@ -117,6 +117,8 @@ struct func_def {
 	bool is_sandboxed;
 	/** The count of function's input arguments. */
 	int param_count;
+	/** List of input arguments to the function. */
+	enum field_type *param_list;
 	/** The type of the value returned by function. */
 	enum field_type returns;
 	/** Function aggregate option. */
diff --git a/src/box/lua/call.c b/src/box/lua/call.c
index 0315e720c..1dc4589dd 100644
--- a/src/box/lua/call.c
+++ b/src/box/lua/call.c
@@ -783,6 +783,7 @@ func_lua_destroy(struct func *func)
 {
 	assert(func != NULL && func->def->language == FUNC_LANGUAGE_LUA);
 	assert(func->vtab == &func_lua_vtab);
+	free(func->def->param_list);
 	TRASH(func);
 	free(func);
 }
@@ -812,6 +813,7 @@ func_persistent_lua_destroy(struct func *base)
 	assert(base != NULL && base->def->language == FUNC_LANGUAGE_LUA &&
 	       base->def->body != NULL);
 	assert(base->vtab == &func_persistent_lua_vtab);
+	free(base->def->param_list);
 	struct func_lua *func = (struct func_lua *) base;
 	func_persistent_lua_unload(func);
 	free(func);
diff --git a/src/box/sql/func.c b/src/box/sql/func.c
index df15d303a..91755380d 100644
--- a/src/box/sql/func.c
+++ b/src/box/sql/func.c
@@ -2937,6 +2937,7 @@ func_sql_builtin_destroy(struct func *func)
 {
 	assert(func->vtab == &func_sql_builtin_vtab);
 	assert(func->def->language == FUNC_LANGUAGE_SQL_BUILTIN);
+	free(func->def->param_list);
 	free(func);
 }
 
-- 
2.25.1

^ permalink raw reply	[flat|nested] 8+ messages in thread

* [Tarantool-patches] [PATCH v1 5/7] sql: check built-in functions argument types
  2020-08-12 15:15 [Tarantool-patches] [PATCH v1 0/7] sql: properly check arguments types of built-in functions imeevma
                   ` (3 preceding siblings ...)
  2020-08-12 15:15 ` [Tarantool-patches] [PATCH v1 4/7] box: add param_list to 'struct func' imeevma
@ 2020-08-12 15:15 ` imeevma
  2020-08-12 15:15 ` [Tarantool-patches] [PATCH v1 6/7] sql: VARBINARY and STRING in built-in functions imeevma
  2020-08-12 15:15 ` [Tarantool-patches] [PATCH v1 7/7] sql: refactor sql/func.c imeevma
  6 siblings, 0 replies; 8+ messages in thread
From: imeevma @ 2020-08-12 15:15 UTC (permalink / raw)
  To: v.shpilevoy; +Cc: tarantool-patches

This patch creates a uniform way to check the argument types of SQL
built-in functions. Prior to this patch, argument types were checked
inside functions. They are now checked in most cases in the ApplyType
opcode.

The only case where there is additional validation in a function is when
a function can take either STRING or VARBINARY as an argument. In this
case, the definition says that the function accepts a SCALAR, but in
fact only STRING or VARBINARY should be accepted. This case will be
completely fixed in the next patches of the patch set.

Part of #4159
---
 src/box/sql/expr.c         |  4 ++++
 src/box/sql/select.c       | 26 ++++++++++++++++++++++++++
 src/box/sql/sqlInt.h       | 14 ++++++++++++++
 test/sql-tap/func.test.lua | 22 +++++++++++-----------
 test/sql/boolean.result    |  2 +-
 test/sql/checks.result     |  8 --------
 test/sql/checks.test.lua   |  2 --
 test/sql/types.result      |  2 +-
 8 files changed, 57 insertions(+), 23 deletions(-)

diff --git a/src/box/sql/expr.c b/src/box/sql/expr.c
index bc2182446..e2c8e1385 100644
--- a/src/box/sql/expr.c
+++ b/src/box/sql/expr.c
@@ -4120,6 +4120,10 @@ sqlExprCodeTarget(Parse * pParse, Expr * pExpr, int target)
 			} else {
 				r1 = 0;
 			}
+			if (func->def->language == FUNC_LANGUAGE_SQL_BUILTIN) {
+				sql_emit_func_arg_type_check(v, func, r1,
+							     nFarg);
+			}
 			if (sql_func_flag_is_set(func, SQL_FUNC_NEEDCOLL)) {
 				sqlVdbeAddOp4(v, OP_CollSeq, 0, 0, 0,
 						  (char *)coll, P4_COLLSEQ);
diff --git a/src/box/sql/select.c b/src/box/sql/select.c
index b0554a172..49f01eb0d 100644
--- a/src/box/sql/select.c
+++ b/src/box/sql/select.c
@@ -124,6 +124,31 @@ clearSelect(sql * db, Select * p, int bFree)
 	}
 }
 
+void
+sql_emit_func_arg_type_check(struct Vdbe *vdbe, struct func *func, int reg,
+			     uint32_t argc)
+{
+	if (argc == 0 || func->def->param_list == NULL)
+		return;
+	assert(func->def->param_count > 0);
+	uint32_t len = (uint32_t)func->def->param_count;
+	assert(len > 0);
+	size_t size = (argc + 1) * sizeof(enum field_type);
+	enum field_type *types = sqlDbMallocZero(sql_get(), size);
+	if (argc <= len) {
+		for (uint32_t i = 0; i < argc; ++i)
+			types[i] = func->def->param_list[i];
+	} else {
+		for (uint32_t i = 0; i < len; ++i)
+			types[i] = func->def->param_list[i];
+		for (uint32_t i = len; i < argc; ++i)
+			types[i] = func->def->param_list[len - 1];
+	}
+	types[argc] = field_type_MAX;
+	sqlVdbeAddOp4(vdbe, OP_ApplyType, reg, argc, 0, (char *)types,
+		      P4_DYNAMIC);
+}
+
 /*
  * Initialize a SelectDest structure.
  */
@@ -5420,6 +5445,7 @@ updateAccumulator(Parse * pParse, AggInfo * pAggInfo)
 			vdbe_insert_distinct(pParse, pF->iDistinct, pF->reg_eph,
 					     addrNext, 1, regAgg);
 		}
+		sql_emit_func_arg_type_check(v, pF->func, regAgg, nArg);
 		if (sql_func_flag_is_set(pF->func, SQL_FUNC_NEEDCOLL)) {
 			struct coll *coll = NULL;
 			struct ExprList_item *pItem;
diff --git a/src/box/sql/sqlInt.h b/src/box/sql/sqlInt.h
index adf90d824..d02614140 100644
--- a/src/box/sql/sqlInt.h
+++ b/src/box/sql/sqlInt.h
@@ -3883,6 +3883,20 @@ sql_index_type_str(struct sql *db, const struct index_def *idx_def);
 void
 sql_emit_table_types(struct Vdbe *v, struct space_def *def, int reg);
 
+/**
+ * Code an OP_ApplyType opcode that try to cast implicitly types
+ * for given range of register starting from @a reg. These values
+ * then will be used as arguments of a function.
+ *
+ * @param vdbe VDBE.
+ * @param func Definition of the function.
+ * @param reg Register where types will be placed.
+ * @param argc Number of arguments.
+ */
+void
+sql_emit_func_arg_type_check(struct Vdbe *vdbe, struct func *func,
+			     int reg, uint32_t argc);
+
 enum field_type
 sql_type_result(enum field_type lhs, enum field_type rhs);
 
diff --git a/test/sql-tap/func.test.lua b/test/sql-tap/func.test.lua
index 3c088920f..82cf350ea 100755
--- a/test/sql-tap/func.test.lua
+++ b/test/sql-tap/func.test.lua
@@ -412,13 +412,13 @@ test:do_execsql_test(
         -- </func-4.4.1>
     })
 
-test:do_execsql_test(
+test:do_catchsql_test(
     "func-4.4.2",
     [[
         SELECT abs(t1) FROM tbl1
     ]], {
         -- <func-4.4.2>
-        0.0, 0.0, 0.0, 0.0, 0.0
+        1, "Type mismatch: can not convert this to number"
         -- </func-4.4.2>
     })
 
@@ -502,13 +502,13 @@ test:do_execsql_test(
         -- </func-4.12>
     })
 
-test:do_execsql_test(
+test:do_catchsql_test(
     "func-4.13",
     [[
         SELECT round(t1,2) FROM tbl1
     ]], {
         -- <func-4.13>
-        0.0, 0.0, 0.0, 0.0, 0.0
+        1, "Type mismatch: can not convert this to double"
         -- </func-4.13>
     })
 
@@ -893,7 +893,7 @@ test:do_execsql_test(
 test:do_execsql_test(
     "func-8.5",
     [[
-        SELECT sum(x) FROM (SELECT '9223372036' || '854775807' AS x
+        SELECT sum(x) FROM (SELECT CAST('9223372036' || '854775807' AS INTEGER) AS x
                             UNION ALL SELECT -9223372036854775807)
     ]], {
         -- <func-8.5>
@@ -904,7 +904,7 @@ test:do_execsql_test(
 test:do_execsql_test(
     "func-8.6",
     [[
-        SELECT typeof(sum(x)) FROM (SELECT '9223372036' || '854775807' AS x
+        SELECT typeof(sum(x)) FROM (SELECT CAST('9223372036' || '854775807' AS INTEGER) AS x
                             UNION ALL SELECT -9223372036854775807)
     ]], {
         -- <func-8.6>
@@ -915,7 +915,7 @@ test:do_execsql_test(
 test:do_execsql_test(
     "func-8.7",
     [[
-        SELECT typeof(sum(x)) FROM (SELECT '9223372036' || '854775808' AS x
+        SELECT typeof(sum(x)) FROM (SELECT CAST('9223372036' || '854775808' AS INTEGER) AS x
                             UNION ALL SELECT -9223372036854775807)
     ]], {
         -- <func-8.7>
@@ -926,7 +926,7 @@ test:do_execsql_test(
 test:do_execsql_test(
     "func-8.8",
     [[
-        SELECT sum(x)>0.0 FROM (SELECT '9223372036' || '854775808' AS x
+        SELECT sum(x)>0.0 FROM (SELECT CAST('9223372036' || '854775808' AS INTEGER) AS x
                             UNION ALL SELECT -9223372036850000000)
     ]], {
         -- <func-8.8>
@@ -985,7 +985,7 @@ test:do_execsql_test(
 test:do_execsql_test(
     "func-9.5",
     [[
-        SELECT length(randomblob(32)), length(randomblob(-5)),
+        SELECT length(randomblob(32)), length(randomblob(0)),
                length(randomblob(2000))
     ]], {
         -- <func-9.5>
@@ -2918,7 +2918,7 @@ test:do_catchsql_test(
         SELECT ROUND(X'FF')
     ]], {
         -- <func-76.1>
-        1, "Type mismatch: can not convert varbinary to numeric"
+        1, "Type mismatch: can not convert varbinary to double"
         -- </func-76.1>
     })
 
@@ -2928,7 +2928,7 @@ test:do_catchsql_test(
         SELECT RANDOMBLOB(X'FF')
     ]], {
         -- <func-76.2>
-        1, "Type mismatch: can not convert varbinary to numeric"
+        1, "Type mismatch: can not convert varbinary to unsigned"
         -- </func-76.2>
     })
 
diff --git a/test/sql/boolean.result b/test/sql/boolean.result
index 51ec5820b..d366aca7d 100644
--- a/test/sql/boolean.result
+++ b/test/sql/boolean.result
@@ -276,7 +276,7 @@ SELECT is_boolean('true');
 SELECT abs(a) FROM t0;
  | ---
  | - null
- | - 'Inconsistent types: expected number got boolean'
+ | - 'Type mismatch: can not convert FALSE to number'
  | ...
 SELECT lower(a) FROM t0;
  | ---
diff --git a/test/sql/checks.result b/test/sql/checks.result
index 7b18e5d6b..3c942fb23 100644
--- a/test/sql/checks.result
+++ b/test/sql/checks.result
@@ -519,14 +519,6 @@ s:insert({1, 'string'})
 ---
 - error: 'Check constraint failed ''complex2'': typeof(coalesce(z,0))==''integer'''
 ...
-s:insert({1, {map=true}})
----
-- error: 'Check constraint failed ''complex2'': typeof(coalesce(z,0))==''integer'''
-...
-s:insert({1, {'a', 'r','r','a','y'}})
----
-- error: 'Check constraint failed ''complex2'': typeof(coalesce(z,0))==''integer'''
-...
 s:insert({1, 3.14})
 ---
 - error: 'Check constraint failed ''complex2'': typeof(coalesce(z,0))==''integer'''
diff --git a/test/sql/checks.test.lua b/test/sql/checks.test.lua
index 301f8ea69..b55abe955 100644
--- a/test/sql/checks.test.lua
+++ b/test/sql/checks.test.lua
@@ -173,8 +173,6 @@ s:format({{name='X', type='integer'}, {name='Z', type='any'}})
 _ = s:create_index('pk', {parts = {1, 'integer'}})
 _ = box.space._ck_constraint:insert({s.id, 'complex2', false, 'SQL', 'typeof(coalesce(z,0))==\'integer\'', true})
 s:insert({1, 'string'})
-s:insert({1, {map=true}})
-s:insert({1, {'a', 'r','r','a','y'}})
 s:insert({1, 3.14})
 s:insert({1, 666})
 s:drop()
diff --git a/test/sql/types.result b/test/sql/types.result
index 2498f3a48..82a82117b 100644
--- a/test/sql/types.result
+++ b/test/sql/types.result
@@ -1322,7 +1322,7 @@ box.execute("SELECT upper(v) FROM t;")
 box.execute("SELECT abs(v) FROM t;")
 ---
 - null
-- 'Inconsistent types: expected number got varbinary'
+- 'Type mismatch: can not convert varbinary to number'
 ...
 box.execute("SELECT typeof(v) FROM t;")
 ---
-- 
2.25.1

^ permalink raw reply	[flat|nested] 8+ messages in thread

* [Tarantool-patches] [PATCH v1 6/7] sql: VARBINARY and STRING in built-in functions
  2020-08-12 15:15 [Tarantool-patches] [PATCH v1 0/7] sql: properly check arguments types of built-in functions imeevma
                   ` (4 preceding siblings ...)
  2020-08-12 15:15 ` [Tarantool-patches] [PATCH v1 5/7] sql: check built-in functions argument types imeevma
@ 2020-08-12 15:15 ` imeevma
  2020-08-12 15:15 ` [Tarantool-patches] [PATCH v1 7/7] sql: refactor sql/func.c imeevma
  6 siblings, 0 replies; 8+ messages in thread
From: imeevma @ 2020-08-12 15:15 UTC (permalink / raw)
  To: v.shpilevoy; +Cc: tarantool-patches

This patch forces SQL built-in functions to accept VARBINARY arguments
and treat them as STRING arguments if they were given in place of STRING
arguments.

Closes #4159
---
 src/box/sql/func.c             |   89 +-
 test/sql-tap/cse.test.lua      |    4 +-
 test/sql-tap/func.test.lua     |   14 +-
 test/sql-tap/orderby1.test.lua |    2 +-
 test/sql-tap/position.test.lua |    6 +-
 test/sql/boolean.result        |   30 +-
 test/sql/types.result          | 1471 +++++++++++++++++++++++++++++++-
 test/sql/types.test.lua        |  244 ++++++
 8 files changed, 1758 insertions(+), 102 deletions(-)

diff --git a/src/box/sql/func.c b/src/box/sql/func.c
index 91755380d..694bd0e83 100644
--- a/src/box/sql/func.c
+++ b/src/box/sql/func.c
@@ -463,34 +463,23 @@ typeofFunc(sql_context * context, int NotUsed, sql_value ** argv)
 static void
 lengthFunc(sql_context * context, int argc, sql_value ** argv)
 {
-	int len;
-
 	assert(argc == 1);
 	UNUSED_PARAMETER(argc);
-	switch (sql_value_type(argv[0])) {
-	case MP_BIN:
-	case MP_ARRAY:
-	case MP_MAP:
-	case MP_INT:
-	case MP_UINT:
-	case MP_BOOL:
-	case MP_DOUBLE:{
-			sql_result_uint(context, sql_value_bytes(argv[0]));
-			break;
-		}
-	case MP_STR:{
-			const unsigned char *z = sql_value_text(argv[0]);
-			if (z == 0)
-				return;
-			len = sql_utf8_char_count(z, sql_value_bytes(argv[0]));
-			sql_result_uint(context, len);
-			break;
-		}
-	default:{
-			sql_result_null(context);
-			break;
-		}
+	if (sql_value_type(argv[0]) == MP_NIL)
+		return sql_result_null(context);
+	if (sql_value_type(argv[0]) == MP_BIN)
+		return sql_result_uint(context, sql_value_bytes(argv[0]));
+	if (sql_value_type(argv[0]) != MP_STR) {
+		diag_set(ClientError, ER_SQL_TYPE_MISMATCH,
+			 sql_value_to_diag_str(argv[0]), "string or varbinary");
+		context->is_aborted = true;
+		return;
 	}
+	const unsigned char *str = sql_value_text(argv[0]);
+	if (str == NULL)
+		return sql_result_null(context);
+	int len = sql_utf8_char_count(str, sql_value_bytes(argv[0]));
+	return sql_result_uint(context, len);
 }
 
 /*
@@ -576,9 +565,9 @@ position_func(struct sql_context *context, int argc, struct Mem **argv)
 	if (haystack_type != MP_STR && haystack_type != MP_BIN)
 		inconsistent_type_arg = haystack;
 	if (inconsistent_type_arg != NULL) {
-		diag_set(ClientError, ER_INCONSISTENT_TYPES,
-			 "text or varbinary",
-			 mem_type_to_str(inconsistent_type_arg));
+		diag_set(ClientError, ER_SQL_TYPE_MISMATCH,
+			 sql_value_to_diag_str(inconsistent_type_arg),
+			 "string or varbinary");
 		context->is_aborted = true;
 		return;
 	}
@@ -748,13 +737,20 @@ substrFunc(sql_context * context, int argc, sql_value ** argv)
 		if (z == 0)
 			return;
 		assert(len == sql_value_bytes(argv[0]));
-	} else {
+	} else if (p0type == MP_NIL) {
+		return sql_result_null(context);
+	} else if (p0type == MP_STR) {
 		z = sql_value_text(argv[0]);
 		if (z == 0)
 			return;
 		len = 0;
 		if (p1 < 0)
 			len = sql_utf8_char_count(z, sql_value_bytes(argv[0]));
+	} else {
+		diag_set(ClientError, ER_SQL_TYPE_MISMATCH,
+			 sql_value_to_diag_str(argv[0]), "string or varbinary");
+		context->is_aborted = true;
+		return;
 	}
 	if (argc == 3) {
 		p2 = sql_value_int(argv[2]);
@@ -906,10 +902,15 @@ case_type##ICUFunc(sql_context *context, int argc, sql_value **argv)   \
 	const char *z2;                                                        \
 	int n;                                                                 \
 	UNUSED_PARAMETER(argc);                                                \
-	int arg_type = sql_value_type(argv[0]);                                \
-	if (mp_type_is_bloblike(arg_type)) {                                   \
-		diag_set(ClientError, ER_INCONSISTENT_TYPES, "text",           \
-			 "varbinary");                                         \
+	enum mp_type type = sql_value_type(argv[0]);                           \
+	if (type == MP_NIL)                                                    \
+		return sql_result_null(context);                               \
+	if (type == MP_BIN)                                                    \
+		return sql_result_value(context, argv[0]);                     \
+	if (type != MP_STR) {                                                  \
+		diag_set(ClientError, ER_SQL_TYPE_MISMATCH,                    \
+			 sql_value_to_diag_str(argv[0]),                       \
+			 "string or varbinary");                               \
 		context->is_aborted = true;                                    \
 		return;                                                        \
 	}                                                                      \
@@ -1452,6 +1453,13 @@ quoteFunc(sql_context * context, int argc, sql_value ** argv)
 static void
 unicodeFunc(sql_context * context, int argc, sql_value ** argv)
 {
+	enum mp_type type = sql_value_type(argv[0]);
+	if (type != MP_NIL && type != MP_STR && type != MP_BIN) {
+		diag_set(ClientError, ER_SQL_TYPE_MISMATCH,
+			 sql_value_to_diag_str(argv[0]), "string or varbinary");
+		context->is_aborted = true;
+		return;
+	}
 	const unsigned char *z = sql_value_text(argv[0]);
 	(void)argc;
 	if (z && z[0])
@@ -1568,6 +1576,17 @@ replaceFunc(sql_context * context, int argc, sql_value ** argv)
 	int loopLimit;		/* Last zStr[] that might match zPattern[] */
 	int i, j;		/* Loop counters */
 
+	for (int i = 0; i < 3; ++i) {
+		enum mp_type type = sql_value_type(argv[i]);
+		if (type != MP_NIL && type != MP_STR && type != MP_BIN) {
+			diag_set(ClientError, ER_SQL_TYPE_MISMATCH,
+				 sql_value_to_diag_str(argv[i]),
+				 "string or varbinary");
+			context->is_aborted = true;
+			return;
+		}
+	}
+
 	assert(argc == 3);
 	UNUSED_PARAMETER(argc);
 	zStr = sql_value_text(argv[0]);
@@ -1880,9 +1899,9 @@ soundexFunc(sql_context * context, int argc, sql_value ** argv)
 	};
 	assert(argc == 1);
 	enum mp_type mp_type = sql_value_type(argv[0]);
-	if (mp_type_is_bloblike(mp_type)) {
+	if (mp_type != MP_NIL && mp_type != MP_STR && mp_type != MP_BIN) {
 		diag_set(ClientError, ER_SQL_TYPE_MISMATCH,
-			 sql_value_to_diag_str(argv[0]), "text");
+			 sql_value_to_diag_str(argv[0]), "string or varbinary");
 		context->is_aborted = true;
 		return;
 	}
diff --git a/test/sql-tap/cse.test.lua b/test/sql-tap/cse.test.lua
index 341b6de01..cfb642cfe 100755
--- a/test/sql-tap/cse.test.lua
+++ b/test/sql-tap/cse.test.lua
@@ -198,7 +198,7 @@ test:do_execsql_test(
 test:do_execsql_test(
     "cse-1.13",
     [[
-        SELECT upper(b), typeof(b), b FROM t1
+        SELECT upper(CAST(b AS STRING)), typeof(b), b FROM t1
     ]], {
         -- <cse-1.13>
         "11", "integer", 11, "21", "integer", 21
@@ -208,7 +208,7 @@ test:do_execsql_test(
 test:do_execsql_test(
     "cse-1.14",
     [[
-        SELECT b, typeof(b), upper(b), typeof(b), b FROM t1
+        SELECT b, typeof(b), upper(CAST(b AS STRING)), typeof(b), b FROM t1
     ]], {
         -- <cse-1.14>
         11, "integer", "11", "integer", 11, 21, "integer", "21", "integer", 21
diff --git a/test/sql-tap/func.test.lua b/test/sql-tap/func.test.lua
index 82cf350ea..29d6e422c 100755
--- a/test/sql-tap/func.test.lua
+++ b/test/sql-tap/func.test.lua
@@ -95,7 +95,7 @@ test:do_execsql_test(
 test:do_execsql_test(
     "func-1.4",
     [[
-        SELECT coalesce(length(a),-1) FROM t2
+        SELECT coalesce(length(CAST(a AS STRING)),-1) FROM t2
     ]], {
         -- <func-1.4>
         1, -1, 3, -1, 5
@@ -197,7 +197,7 @@ test:do_execsql_test(
 test:do_execsql_test(
     "func-2.9",
     [[
-        SELECT substr(a,1,1) FROM t2
+        SELECT substr(CAST(a AS STRING),1,1) FROM t2
     ]], {
         -- <func-2.9>
         "1", "", "3", "", "6"
@@ -207,7 +207,7 @@ test:do_execsql_test(
 test:do_execsql_test(
     "func-2.10",
     [[
-        SELECT substr(a,2,2) FROM t2
+        SELECT substr(CAST(a AS STRING),2,2) FROM t2
     ]], {
         -- <func-2.10>
         "", "", "45", "", "78"
@@ -763,7 +763,7 @@ test:do_execsql_test(
 test:do_execsql_test(
     "func-5.3",
     [[
-        SELECT upper(a), lower(a) FROM t2
+        SELECT upper(CAST(a AS STRING)), lower(CAST(a AS STRING)) FROM t2
     ]], {
         -- <func-5.3>
         "1","1","","","345","345","","","67890","67890"
@@ -797,7 +797,7 @@ test:do_execsql_test(
 test:do_execsql_test(
     "func-6.2",
     [[
-        SELECT coalesce(upper(a),'nil') FROM t2
+        SELECT coalesce(upper(CAST(a AS STRING)),'nil') FROM t2
     ]], {
         -- <func-6.2>
         "1","nil","345","nil","67890"
@@ -2932,13 +2932,13 @@ test:do_catchsql_test(
         -- </func-76.2>
     })
 
-test:do_catchsql_test(
+test:do_execsql_test(
     "func-76.3",
     [[
         SELECT SOUNDEX(X'FF')
     ]], {
         -- <func-76.3>
-        1, "Type mismatch: can not convert varbinary to text"
+        "?000"
         -- </func-76.3>
     })
 
diff --git a/test/sql-tap/orderby1.test.lua b/test/sql-tap/orderby1.test.lua
index 51e8d301f..95a8de487 100755
--- a/test/sql-tap/orderby1.test.lua
+++ b/test/sql-tap/orderby1.test.lua
@@ -735,7 +735,7 @@ test:do_execsql_test(
         SELECT (
           SELECT 'hardware' FROM ( 
             SELECT 'software' ORDER BY 'firmware' ASC, 'sportswear' DESC
-          ) GROUP BY 1 HAVING length(b) <> 0
+          ) GROUP BY 1 HAVING length(CAST(b AS STRING)) <> 0
         )
         FROM abc;
     ]], {
diff --git a/test/sql-tap/position.test.lua b/test/sql-tap/position.test.lua
index e0455abc9..a84e11cd0 100755
--- a/test/sql-tap/position.test.lua
+++ b/test/sql-tap/position.test.lua
@@ -228,7 +228,7 @@ test:do_test(
         return test:catchsql "SELECT position(34, 12345);"
     end, {
         -- <position-1.23>
-        1, "Inconsistent types: expected text or varbinary got unsigned"
+        1, "Type mismatch: can not convert 12345 to string or varbinary"
         -- </position-1.23>
     })
 
@@ -238,7 +238,7 @@ test:do_test(
         return test:catchsql "SELECT position(34, 123456.78);"
     end, {
         -- <position-1.24>
-        1, "Inconsistent types: expected text or varbinary got real"
+        1, "Type mismatch: can not convert 123456.78 to string or varbinary"
         -- </position-1.24>
     })
 
@@ -248,7 +248,7 @@ test:do_test(
         return test:catchsql "SELECT position(x'3334', 123456.78);"
     end, {
         -- <position-1.25>
-        1, "Inconsistent types: expected text or varbinary got real"
+        1, "Type mismatch: can not convert 123456.78 to string or varbinary"
         -- </position-1.25>
     })
 
diff --git a/test/sql/boolean.result b/test/sql/boolean.result
index d366aca7d..743183f7d 100644
--- a/test/sql/boolean.result
+++ b/test/sql/boolean.result
@@ -280,25 +280,13 @@ SELECT abs(a) FROM t0;
  | ...
 SELECT lower(a) FROM t0;
  | ---
- | - metadata:
- |   - name: COLUMN_1
- |     type: string
- |   rows:
- |   - ['false']
- |   - ['true']
- |   - [null]
- |   - [null]
+ | - null
+ | - 'Type mismatch: can not convert FALSE to string or varbinary'
  | ...
 SELECT upper(a) FROM t0;
  | ---
- | - metadata:
- |   - name: COLUMN_1
- |     type: string
- |   rows:
- |   - ['FALSE']
- |   - ['TRUE']
- |   - [null]
- |   - [null]
+ | - null
+ | - 'Type mismatch: can not convert FALSE to string or varbinary'
  | ...
 SELECT quote(a) FROM t0;
  | ---
@@ -314,14 +302,8 @@ SELECT quote(a) FROM t0;
 -- gh-4462: LENGTH didn't take BOOLEAN arguments.
 SELECT length(a) FROM t0;
  | ---
- | - metadata:
- |   - name: COLUMN_1
- |     type: integer
- |   rows:
- |   - [5]
- |   - [4]
- |   - [null]
- |   - [null]
+ | - null
+ | - 'Type mismatch: can not convert FALSE to string or varbinary'
  | ...
 SELECT typeof(a) FROM t0;
  | ---
diff --git a/test/sql/types.result b/test/sql/types.result
index 82a82117b..c3eb4e27d 100644
--- a/test/sql/types.result
+++ b/test/sql/types.result
@@ -830,19 +830,13 @@ box.execute("DELETE FROM t WHERE i < 18446744073709551613;")
 ...
 box.execute("SELECT lower(i) FROM t;")
 ---
-- metadata:
-  - name: COLUMN_1
-    type: string
-  rows:
-  - ['18446744073709551613']
+- null
+- 'Type mismatch: can not convert 18446744073709551613 to string or varbinary'
 ...
 box.execute("SELECT upper(i) FROM t;")
 ---
-- metadata:
-  - name: COLUMN_1
-    type: string
-  rows:
-  - ['18446744073709551613']
+- null
+- 'Type mismatch: can not convert 18446744073709551613 to string or varbinary'
 ...
 box.execute("SELECT abs(i) FROM t;")
 ---
@@ -1311,13 +1305,19 @@ box.execute("SELECT group_concat(v) FROM t;")
 ...
 box.execute("SELECT lower(v) FROM t;")
 ---
-- null
-- 'Inconsistent types: expected text got varbinary'
+- metadata:
+  - name: COLUMN_1
+    type: string
+  rows:
+  - ['abc']
 ...
 box.execute("SELECT upper(v) FROM t;")
 ---
-- null
-- 'Inconsistent types: expected text got varbinary'
+- metadata:
+  - name: COLUMN_1
+    type: string
+  rows:
+  - ['abc']
 ...
 box.execute("SELECT abs(v) FROM t;")
 ---
@@ -1879,25 +1879,13 @@ box.execute("SELECT group_concat(d) FROM t;")
 ...
 box.execute("SELECT lower(d) FROM t;")
 ---
-- metadata:
-  - name: COLUMN_1
-    type: string
-  rows:
-  - ['10.0']
-  - ['-2.0']
-  - ['3.3']
-  - ['1.8e+19']
+- null
+- 'Type mismatch: can not convert 10.0 to string or varbinary'
 ...
 box.execute("SELECT upper(d) FROM t;")
 ---
-- metadata:
-  - name: COLUMN_1
-    type: string
-  rows:
-  - ['10.0']
-  - ['-2.0']
-  - ['3.3']
-  - ['1.8E+19']
+- null
+- 'Type mismatch: can not convert 10.0 to string or varbinary'
 ...
 box.execute("SELECT abs(d) FROM t;")
 ---
@@ -2807,3 +2795,1426 @@ box.execute([[SELECT typeof(length('abc'));]])
   rows:
   - ['integer']
 ...
+-- Make sure the function argument types are checked.
+box.execute([[SELECT abs(-1);]])
+---
+- metadata:
+  - name: COLUMN_1
+    type: number
+  rows:
+  - [1]
+...
+box.execute([[SELECT abs(1);]])
+---
+- metadata:
+  - name: COLUMN_1
+    type: number
+  rows:
+  - [1]
+...
+box.execute([[SELECT abs(1.5);]])
+---
+- metadata:
+  - name: COLUMN_1
+    type: number
+  rows:
+  - [1.5]
+...
+box.execute([[SELECT abs(true);]])
+---
+- null
+- 'Type mismatch: can not convert TRUE to number'
+...
+box.execute([[SELECT abs('a');]])
+---
+- null
+- 'Type mismatch: can not convert a to number'
+...
+box.execute([[SELECT abs(X'33');]])
+---
+- null
+- 'Type mismatch: can not convert varbinary to number'
+...
+box.execute([[SELECT char(-1);]])
+---
+- null
+- 'Type mismatch: can not convert -1 to unsigned'
+...
+box.execute([[SELECT char(1);]])
+---
+- metadata:
+  - name: COLUMN_1
+    type: string
+  rows:
+  - ["\x01"]
+...
+box.execute([[SELECT char(1.5);]])
+---
+- metadata:
+  - name: COLUMN_1
+    type: string
+  rows:
+  - ["\x01"]
+...
+box.execute([[SELECT char(true);]])
+---
+- null
+- 'Type mismatch: can not convert TRUE to unsigned'
+...
+box.execute([[SELECT char('a');]])
+---
+- null
+- 'Type mismatch: can not convert a to unsigned'
+...
+box.execute([[SELECT char(X'33');]])
+---
+- null
+- 'Type mismatch: can not convert varbinary to unsigned'
+...
+box.execute([[SELECT character_length(-1);]])
+---
+- null
+- 'Type mismatch: can not convert -1 to string or varbinary'
+...
+box.execute([[SELECT character_length(1);]])
+---
+- null
+- 'Type mismatch: can not convert 1 to string or varbinary'
+...
+box.execute([[SELECT character_length(1.5);]])
+---
+- null
+- 'Type mismatch: can not convert 1.5 to string or varbinary'
+...
+box.execute([[SELECT character_length(true);]])
+---
+- null
+- 'Type mismatch: can not convert TRUE to string or varbinary'
+...
+box.execute([[SELECT character_length('a');]])
+---
+- metadata:
+  - name: COLUMN_1
+    type: integer
+  rows:
+  - [1]
+...
+box.execute([[SELECT character_length(X'33');]])
+---
+- metadata:
+  - name: COLUMN_1
+    type: integer
+  rows:
+  - [1]
+...
+box.execute([[SELECT char_length(-1);]])
+---
+- null
+- 'Type mismatch: can not convert -1 to string or varbinary'
+...
+box.execute([[SELECT char_length(1);]])
+---
+- null
+- 'Type mismatch: can not convert 1 to string or varbinary'
+...
+box.execute([[SELECT char_length(1.5);]])
+---
+- null
+- 'Type mismatch: can not convert 1.5 to string or varbinary'
+...
+box.execute([[SELECT char_length(true);]])
+---
+- null
+- 'Type mismatch: can not convert TRUE to string or varbinary'
+...
+box.execute([[SELECT char_length('a');]])
+---
+- metadata:
+  - name: COLUMN_1
+    type: integer
+  rows:
+  - [1]
+...
+box.execute([[SELECT char_length(X'33');]])
+---
+- metadata:
+  - name: COLUMN_1
+    type: integer
+  rows:
+  - [1]
+...
+box.execute([[SELECT coalesce(-1, -1);]])
+---
+- metadata:
+  - name: COLUMN_1
+    type: scalar
+  rows:
+  - [-1]
+...
+box.execute([[SELECT coalesce(1, 1);]])
+---
+- metadata:
+  - name: COLUMN_1
+    type: scalar
+  rows:
+  - [1]
+...
+box.execute([[SELECT coalesce(1.5, 1.5);]])
+---
+- metadata:
+  - name: COLUMN_1
+    type: scalar
+  rows:
+  - [1.5]
+...
+box.execute([[SELECT coalesce(true, true);]])
+---
+- metadata:
+  - name: COLUMN_1
+    type: scalar
+  rows:
+  - [true]
+...
+box.execute([[SELECT coalesce('a', 'a');]])
+---
+- metadata:
+  - name: COLUMN_1
+    type: scalar
+  rows:
+  - ['a']
+...
+box.execute([[SELECT coalesce(X'33', X'33');]])
+---
+- metadata:
+  - name: COLUMN_1
+    type: scalar
+  rows:
+  - ['3']
+...
+box.execute([[SELECT greatest(-1, -1);]])
+---
+- metadata:
+  - name: COLUMN_1
+    type: scalar
+  rows:
+  - [-1]
+...
+box.execute([[SELECT greatest(1, 1);]])
+---
+- metadata:
+  - name: COLUMN_1
+    type: scalar
+  rows:
+  - [1]
+...
+box.execute([[SELECT greatest(1.5, 1.5);]])
+---
+- metadata:
+  - name: COLUMN_1
+    type: scalar
+  rows:
+  - [1.5]
+...
+box.execute([[SELECT greatest(true, true);]])
+---
+- metadata:
+  - name: COLUMN_1
+    type: scalar
+  rows:
+  - [true]
+...
+box.execute([[SELECT greatest('a', 'a');]])
+---
+- metadata:
+  - name: COLUMN_1
+    type: scalar
+  rows:
+  - ['a']
+...
+box.execute([[SELECT greatest(X'33', X'33');]])
+---
+- metadata:
+  - name: COLUMN_1
+    type: scalar
+  rows:
+  - ['3']
+...
+box.execute([[SELECT hex(-1);]])
+---
+- metadata:
+  - name: COLUMN_1
+    type: string
+  rows:
+  - ['2D31']
+...
+box.execute([[SELECT hex(1);]])
+---
+- metadata:
+  - name: COLUMN_1
+    type: string
+  rows:
+  - ['31']
+...
+box.execute([[SELECT hex(1.5);]])
+---
+- metadata:
+  - name: COLUMN_1
+    type: string
+  rows:
+  - ['312E35']
+...
+box.execute([[SELECT hex(true);]])
+---
+- metadata:
+  - name: COLUMN_1
+    type: string
+  rows:
+  - ['54525545']
+...
+box.execute([[SELECT hex('a');]])
+---
+- metadata:
+  - name: COLUMN_1
+    type: string
+  rows:
+  - ['61']
+...
+box.execute([[SELECT hex(X'33');]])
+---
+- metadata:
+  - name: COLUMN_1
+    type: string
+  rows:
+  - ['33']
+...
+box.execute([[SELECT ifnull(-1, -1);]])
+---
+- metadata:
+  - name: COLUMN_1
+    type: scalar
+  rows:
+  - [-1]
+...
+box.execute([[SELECT ifnull(1, 1);]])
+---
+- metadata:
+  - name: COLUMN_1
+    type: scalar
+  rows:
+  - [1]
+...
+box.execute([[SELECT ifnull(1.5, 1);]])
+---
+- metadata:
+  - name: COLUMN_1
+    type: scalar
+  rows:
+  - [1.5]
+...
+box.execute([[SELECT ifnull(true, true);]])
+---
+- metadata:
+  - name: COLUMN_1
+    type: scalar
+  rows:
+  - [true]
+...
+box.execute([[SELECT ifnull('a', 'a');]])
+---
+- metadata:
+  - name: COLUMN_1
+    type: scalar
+  rows:
+  - ['a']
+...
+box.execute([[SELECT ifnull(X'33', X'33');]])
+---
+- metadata:
+  - name: COLUMN_1
+    type: scalar
+  rows:
+  - ['3']
+...
+box.execute([[SELECT least(-1, -1);]])
+---
+- metadata:
+  - name: COLUMN_1
+    type: scalar
+  rows:
+  - [-1]
+...
+box.execute([[SELECT least(1, 1);]])
+---
+- metadata:
+  - name: COLUMN_1
+    type: scalar
+  rows:
+  - [1]
+...
+box.execute([[SELECT least(1.5, 1.5);]])
+---
+- metadata:
+  - name: COLUMN_1
+    type: scalar
+  rows:
+  - [1.5]
+...
+box.execute([[SELECT least(true, true);]])
+---
+- metadata:
+  - name: COLUMN_1
+    type: scalar
+  rows:
+  - [true]
+...
+box.execute([[SELECT least('a', 'a');]])
+---
+- metadata:
+  - name: COLUMN_1
+    type: scalar
+  rows:
+  - ['a']
+...
+box.execute([[SELECT least(X'33', X'33');]])
+---
+- metadata:
+  - name: COLUMN_1
+    type: scalar
+  rows:
+  - ['3']
+...
+box.execute([[SELECT length(-1);]])
+---
+- null
+- 'Type mismatch: can not convert -1 to string or varbinary'
+...
+box.execute([[SELECT length(1);]])
+---
+- null
+- 'Type mismatch: can not convert 1 to string or varbinary'
+...
+box.execute([[SELECT length(1.5);]])
+---
+- null
+- 'Type mismatch: can not convert 1.5 to string or varbinary'
+...
+box.execute([[SELECT length(true);]])
+---
+- null
+- 'Type mismatch: can not convert TRUE to string or varbinary'
+...
+box.execute([[SELECT length('a');]])
+---
+- metadata:
+  - name: COLUMN_1
+    type: integer
+  rows:
+  - [1]
+...
+box.execute([[SELECT length(X'33');]])
+---
+- metadata:
+  - name: COLUMN_1
+    type: integer
+  rows:
+  - [1]
+...
+box.execute([[SELECT likelihood(-1, -1);]])
+---
+- null
+- Illegal parameters, second argument to likelihood() must be a constant between 0.0
+  and 1.0
+...
+box.execute([[SELECT likelihood(1, 1);]])
+---
+- null
+- Illegal parameters, second argument to likelihood() must be a constant between 0.0
+  and 1.0
+...
+box.execute([[SELECT likelihood(1.5, 1.5);]])
+---
+- null
+- Illegal parameters, second argument to likelihood() must be a constant between 0.0
+  and 1.0
+...
+box.execute([[SELECT likelihood(true, true);]])
+---
+- null
+- Illegal parameters, second argument to likelihood() must be a constant between 0.0
+  and 1.0
+...
+box.execute([[SELECT likelihood('a', 'a');]])
+---
+- null
+- Illegal parameters, second argument to likelihood() must be a constant between 0.0
+  and 1.0
+...
+box.execute([[SELECT likelihood(X'33', X'33');]])
+---
+- null
+- Illegal parameters, second argument to likelihood() must be a constant between 0.0
+  and 1.0
+...
+box.execute([[SELECT likely(-1);]])
+---
+- metadata:
+  - name: COLUMN_1
+    type: integer
+  rows:
+  - [-1]
+...
+box.execute([[SELECT likely(1);]])
+---
+- metadata:
+  - name: COLUMN_1
+    type: integer
+  rows:
+  - [1]
+...
+box.execute([[SELECT likely(1.5);]])
+---
+- metadata:
+  - name: COLUMN_1
+    type: double
+  rows:
+  - [1.5]
+...
+box.execute([[SELECT likely(true);]])
+---
+- metadata:
+  - name: COLUMN_1
+    type: boolean
+  rows:
+  - [true]
+...
+box.execute([[SELECT likely('a');]])
+---
+- metadata:
+  - name: COLUMN_1
+    type: string
+  rows:
+  - ['a']
+...
+box.execute([[SELECT likely(X'33');]])
+---
+- metadata:
+  - name: COLUMN_1
+    type: varbinary
+  rows:
+  - ['3']
+...
+box.execute([[SELECT lower(-1);]])
+---
+- null
+- 'Type mismatch: can not convert -1 to string or varbinary'
+...
+box.execute([[SELECT lower(1);]])
+---
+- null
+- 'Type mismatch: can not convert 1 to string or varbinary'
+...
+box.execute([[SELECT lower(1.5);]])
+---
+- null
+- 'Type mismatch: can not convert 1.5 to string or varbinary'
+...
+box.execute([[SELECT lower(true);]])
+---
+- null
+- 'Type mismatch: can not convert TRUE to string or varbinary'
+...
+box.execute([[SELECT lower('a');]])
+---
+- metadata:
+  - name: COLUMN_1
+    type: string
+  rows:
+  - ['a']
+...
+box.execute([[SELECT lower(X'33');]])
+---
+- metadata:
+  - name: COLUMN_1
+    type: string
+  rows:
+  - ['3']
+...
+box.execute([[SELECT nullif(-1, -1);]])
+---
+- metadata:
+  - name: COLUMN_1
+    type: scalar
+  rows:
+  - [null]
+...
+box.execute([[SELECT nullif(1, 1);]])
+---
+- metadata:
+  - name: COLUMN_1
+    type: scalar
+  rows:
+  - [null]
+...
+box.execute([[SELECT nullif(1.5, 1.5);]])
+---
+- metadata:
+  - name: COLUMN_1
+    type: scalar
+  rows:
+  - [null]
+...
+box.execute([[SELECT nullif(true, true);]])
+---
+- metadata:
+  - name: COLUMN_1
+    type: scalar
+  rows:
+  - [null]
+...
+box.execute([[SELECT nullif('a', 'a');]])
+---
+- metadata:
+  - name: COLUMN_1
+    type: scalar
+  rows:
+  - [null]
+...
+box.execute([[SELECT nullif(X'33', X'33');]])
+---
+- metadata:
+  - name: COLUMN_1
+    type: scalar
+  rows:
+  - [null]
+...
+box.execute([[SELECT position(-1, -1);]])
+---
+- null
+- 'Type mismatch: can not convert -1 to string or varbinary'
+...
+box.execute([[SELECT position(1, 1);]])
+---
+- null
+- 'Type mismatch: can not convert 1 to string or varbinary'
+...
+box.execute([[SELECT position(1.5, 1.5);]])
+---
+- null
+- 'Type mismatch: can not convert 1.5 to string or varbinary'
+...
+box.execute([[SELECT position(true, true);]])
+---
+- null
+- 'Type mismatch: can not convert TRUE to string or varbinary'
+...
+box.execute([[SELECT position('a', 'a');]])
+---
+- metadata:
+  - name: COLUMN_1
+    type: integer
+  rows:
+  - [1]
+...
+box.execute([[SELECT position(X'33', X'33');]])
+---
+- metadata:
+  - name: COLUMN_1
+    type: integer
+  rows:
+  - [1]
+...
+box.execute([[SELECT printf(-1);]])
+---
+- metadata:
+  - name: COLUMN_1
+    type: string
+  rows:
+  - ['-1']
+...
+box.execute([[SELECT printf(1);]])
+---
+- metadata:
+  - name: COLUMN_1
+    type: string
+  rows:
+  - ['1']
+...
+box.execute([[SELECT printf(1.5);]])
+---
+- metadata:
+  - name: COLUMN_1
+    type: string
+  rows:
+  - ['1.5']
+...
+box.execute([[SELECT printf(true);]])
+---
+- metadata:
+  - name: COLUMN_1
+    type: string
+  rows:
+  - ['TRUE']
+...
+box.execute([[SELECT printf('a');]])
+---
+- metadata:
+  - name: COLUMN_1
+    type: string
+  rows:
+  - ['a']
+...
+box.execute([[SELECT printf(X'33');]])
+---
+- metadata:
+  - name: COLUMN_1
+    type: string
+  rows:
+  - ['3']
+...
+box.execute([[SELECT quote(-1);]])
+---
+- metadata:
+  - name: COLUMN_1
+    type: string
+  rows:
+  - [-1]
+...
+box.execute([[SELECT quote(1);]])
+---
+- metadata:
+  - name: COLUMN_1
+    type: string
+  rows:
+  - [1]
+...
+box.execute([[SELECT quote(1.5);]])
+---
+- metadata:
+  - name: COLUMN_1
+    type: string
+  rows:
+  - ['1.5']
+...
+box.execute([[SELECT quote(true);]])
+---
+- metadata:
+  - name: COLUMN_1
+    type: string
+  rows:
+  - ['TRUE']
+...
+box.execute([[SELECT quote('a');]])
+---
+- metadata:
+  - name: COLUMN_1
+    type: string
+  rows:
+  - ['''a''']
+...
+box.execute([[SELECT quote(X'33');]])
+---
+- metadata:
+  - name: COLUMN_1
+    type: string
+  rows:
+  - ['X''33''']
+...
+box.execute([[SELECT randomblob(-1);]])
+---
+- null
+- 'Type mismatch: can not convert -1 to unsigned'
+...
+box.execute([[SELECT randomblob(0);]])
+---
+- metadata:
+  - name: COLUMN_1
+    type: varbinary
+  rows:
+  - [null]
+...
+box.execute([[SELECT randomblob(0.5);]])
+---
+- metadata:
+  - name: COLUMN_1
+    type: varbinary
+  rows:
+  - [null]
+...
+box.execute([[SELECT randomblob(true);]])
+---
+- null
+- 'Type mismatch: can not convert TRUE to unsigned'
+...
+box.execute([[SELECT randomblob('a');]])
+---
+- null
+- 'Type mismatch: can not convert a to unsigned'
+...
+box.execute([[SELECT randomblob(X'33');]])
+---
+- null
+- 'Type mismatch: can not convert varbinary to unsigned'
+...
+box.execute([[SELECT replace(-1, -1, -1);]])
+---
+- null
+- 'Type mismatch: can not convert -1 to string or varbinary'
+...
+box.execute([[SELECT replace(1, 1, 1);]])
+---
+- null
+- 'Type mismatch: can not convert 1 to string or varbinary'
+...
+box.execute([[SELECT replace(1.5, 1.5, 1.5);]])
+---
+- null
+- 'Type mismatch: can not convert 1.5 to string or varbinary'
+...
+box.execute([[SELECT replace(true, true, true);]])
+---
+- null
+- 'Type mismatch: can not convert TRUE to string or varbinary'
+...
+box.execute([[SELECT replace('a', 'a', 'a');]])
+---
+- metadata:
+  - name: COLUMN_1
+    type: string
+  rows:
+  - ['a']
+...
+box.execute([[SELECT replace(X'33', X'33', X'33');]])
+---
+- metadata:
+  - name: COLUMN_1
+    type: string
+  rows:
+  - ['3']
+...
+box.execute([[SELECT round(-1, -1);]])
+---
+- null
+- 'Type mismatch: can not convert -1 to unsigned'
+...
+box.execute([[SELECT round(1, 1);]])
+---
+- metadata:
+  - name: COLUMN_1
+    type: double
+  rows:
+  - [1]
+...
+box.execute([[SELECT round(1.5, 1.5);]])
+---
+- metadata:
+  - name: COLUMN_1
+    type: double
+  rows:
+  - [1.5]
+...
+box.execute([[SELECT round(true, true);]])
+---
+- null
+- 'Type mismatch: can not convert TRUE to double'
+...
+box.execute([[SELECT round('a', 'a');]])
+---
+- null
+- 'Type mismatch: can not convert a to double'
+...
+box.execute([[SELECT round(X'33', X'33');]])
+---
+- null
+- 'Type mismatch: can not convert varbinary to double'
+...
+box.execute([[SELECT soundex(-1);]])
+---
+- null
+- 'Type mismatch: can not convert -1 to string or varbinary'
+...
+box.execute([[SELECT soundex(1);]])
+---
+- null
+- 'Type mismatch: can not convert 1 to string or varbinary'
+...
+box.execute([[SELECT soundex(1.5);]])
+---
+- null
+- 'Type mismatch: can not convert 1.5 to string or varbinary'
+...
+box.execute([[SELECT soundex(true);]])
+---
+- null
+- 'Type mismatch: can not convert TRUE to string or varbinary'
+...
+box.execute([[SELECT soundex('a');]])
+---
+- metadata:
+  - name: COLUMN_1
+    type: string
+  rows:
+  - ['A000']
+...
+box.execute([[SELECT soundex(X'33');]])
+---
+- metadata:
+  - name: COLUMN_1
+    type: string
+  rows:
+  - ['?000']
+...
+box.execute([[SELECT substr(-1, -1, -1);]])
+---
+- null
+- 'Type mismatch: can not convert -1 to string or varbinary'
+...
+box.execute([[SELECT substr(1, 1, 1);]])
+---
+- null
+- 'Type mismatch: can not convert 1 to string or varbinary'
+...
+box.execute([[SELECT substr(1.5, 1.5, 1.5);]])
+---
+- null
+- 'Type mismatch: can not convert 1.5 to string or varbinary'
+...
+box.execute([[SELECT substr(true, true, true);]])
+---
+- null
+- 'Type mismatch: can not convert TRUE to integer'
+...
+box.execute([[SELECT substr('a', 'a', 'a');]])
+---
+- null
+- 'Type mismatch: can not convert a to integer'
+...
+box.execute([[SELECT substr(X'33', X'33', X'33');]])
+---
+- null
+- 'Type mismatch: can not convert varbinary to integer'
+...
+box.execute([[SELECT typeof(-1);]])
+---
+- metadata:
+  - name: COLUMN_1
+    type: string
+  rows:
+  - ['integer']
+...
+box.execute([[SELECT typeof(1);]])
+---
+- metadata:
+  - name: COLUMN_1
+    type: string
+  rows:
+  - ['integer']
+...
+box.execute([[SELECT typeof(1.5);]])
+---
+- metadata:
+  - name: COLUMN_1
+    type: string
+  rows:
+  - ['double']
+...
+box.execute([[SELECT typeof(true);]])
+---
+- metadata:
+  - name: COLUMN_1
+    type: string
+  rows:
+  - ['boolean']
+...
+box.execute([[SELECT typeof('a');]])
+---
+- metadata:
+  - name: COLUMN_1
+    type: string
+  rows:
+  - ['string']
+...
+box.execute([[SELECT typeof(X'33');]])
+---
+- metadata:
+  - name: COLUMN_1
+    type: string
+  rows:
+  - ['varbinary']
+...
+box.execute([[SELECT unicode(-1);]])
+---
+- null
+- 'Type mismatch: can not convert -1 to string or varbinary'
+...
+box.execute([[SELECT unicode(1);]])
+---
+- null
+- 'Type mismatch: can not convert 1 to string or varbinary'
+...
+box.execute([[SELECT unicode(1.5);]])
+---
+- null
+- 'Type mismatch: can not convert 1.5 to string or varbinary'
+...
+box.execute([[SELECT unicode(true);]])
+---
+- null
+- 'Type mismatch: can not convert TRUE to string or varbinary'
+...
+box.execute([[SELECT unicode('a');]])
+---
+- metadata:
+  - name: COLUMN_1
+    type: string
+  rows:
+  - [97]
+...
+box.execute([[SELECT unicode(X'33');]])
+---
+- metadata:
+  - name: COLUMN_1
+    type: string
+  rows:
+  - [51]
+...
+box.execute([[SELECT unlikely(-1);]])
+---
+- metadata:
+  - name: COLUMN_1
+    type: integer
+  rows:
+  - [-1]
+...
+box.execute([[SELECT unlikely(1);]])
+---
+- metadata:
+  - name: COLUMN_1
+    type: integer
+  rows:
+  - [1]
+...
+box.execute([[SELECT unlikely(1.5);]])
+---
+- metadata:
+  - name: COLUMN_1
+    type: double
+  rows:
+  - [1.5]
+...
+box.execute([[SELECT unlikely(true);]])
+---
+- metadata:
+  - name: COLUMN_1
+    type: boolean
+  rows:
+  - [true]
+...
+box.execute([[SELECT unlikely('a');]])
+---
+- metadata:
+  - name: COLUMN_1
+    type: string
+  rows:
+  - ['a']
+...
+box.execute([[SELECT unlikely(X'33');]])
+---
+- metadata:
+  - name: COLUMN_1
+    type: varbinary
+  rows:
+  - ['3']
+...
+box.execute([[SELECT upper(-1);]])
+---
+- null
+- 'Type mismatch: can not convert -1 to string or varbinary'
+...
+box.execute([[SELECT upper(1);]])
+---
+- null
+- 'Type mismatch: can not convert 1 to string or varbinary'
+...
+box.execute([[SELECT upper(1.5);]])
+---
+- null
+- 'Type mismatch: can not convert 1.5 to string or varbinary'
+...
+box.execute([[SELECT upper(true);]])
+---
+- null
+- 'Type mismatch: can not convert TRUE to string or varbinary'
+...
+box.execute([[SELECT upper('a');]])
+---
+- metadata:
+  - name: COLUMN_1
+    type: string
+  rows:
+  - ['A']
+...
+box.execute([[SELECT upper(X'33');]])
+---
+- metadata:
+  - name: COLUMN_1
+    type: string
+  rows:
+  - ['3']
+...
+box.execute([[SELECT zeroblob(-1);]])
+---
+- null
+- 'Type mismatch: can not convert -1 to unsigned'
+...
+box.execute([[SELECT zeroblob(1);]])
+---
+- metadata:
+  - name: COLUMN_1
+    type: varbinary
+  rows:
+  - ["\0"]
+...
+box.execute([[SELECT zeroblob(1.5);]])
+---
+- metadata:
+  - name: COLUMN_1
+    type: varbinary
+  rows:
+  - ["\0"]
+...
+box.execute([[SELECT zeroblob(true);]])
+---
+- null
+- 'Type mismatch: can not convert TRUE to unsigned'
+...
+box.execute([[SELECT zeroblob('a');]])
+---
+- null
+- 'Type mismatch: can not convert a to unsigned'
+...
+box.execute([[SELECT zeroblob(X'33');]])
+---
+- null
+- 'Type mismatch: can not convert varbinary to unsigned'
+...
+box.execute([[CREATE TABLE t (i INTEGER PRIMARY KEY, u UNSIGNED, d DOUBLE, b BOOLEAN, s STRING, v VARBINARY);]])
+---
+- row_count: 1
+...
+box.execute([[INSERT INTO t VALUES (-1, 1, 1.5, true, 'a', X'33');]])
+---
+- row_count: 1
+...
+box.execute([[SELECT avg(i) FROM t;]])
+---
+- metadata:
+  - name: COLUMN_1
+    type: number
+  rows:
+  - [-1]
+...
+box.execute([[SELECT avg(u) FROM t;]])
+---
+- metadata:
+  - name: COLUMN_1
+    type: number
+  rows:
+  - [1]
+...
+box.execute([[SELECT avg(d) FROM t;]])
+---
+- metadata:
+  - name: COLUMN_1
+    type: number
+  rows:
+  - [1.5]
+...
+box.execute([[SELECT avg(b) FROM t;]])
+---
+- null
+- 'Type mismatch: can not convert TRUE to number'
+...
+box.execute([[SELECT avg(s) FROM t;]])
+---
+- null
+- 'Type mismatch: can not convert a to number'
+...
+box.execute([[SELECT avg(v) FROM t;]])
+---
+- null
+- 'Type mismatch: can not convert varbinary to number'
+...
+box.execute([[SELECT count(i) FROM t;]])
+---
+- metadata:
+  - name: COLUMN_1
+    type: integer
+  rows:
+  - [1]
+...
+box.execute([[SELECT count(u) FROM t;]])
+---
+- metadata:
+  - name: COLUMN_1
+    type: integer
+  rows:
+  - [1]
+...
+box.execute([[SELECT count(d) FROM t;]])
+---
+- metadata:
+  - name: COLUMN_1
+    type: integer
+  rows:
+  - [1]
+...
+box.execute([[SELECT count(b) FROM t;]])
+---
+- metadata:
+  - name: COLUMN_1
+    type: integer
+  rows:
+  - [1]
+...
+box.execute([[SELECT count(s) FROM t;]])
+---
+- metadata:
+  - name: COLUMN_1
+    type: integer
+  rows:
+  - [1]
+...
+box.execute([[SELECT count(v) FROM t;]])
+---
+- metadata:
+  - name: COLUMN_1
+    type: integer
+  rows:
+  - [1]
+...
+box.execute([[SELECT group_concat(i) FROM t;]])
+---
+- metadata:
+  - name: COLUMN_1
+    type: string
+  rows:
+  - ['-1']
+...
+box.execute([[SELECT group_concat(u) FROM t;]])
+---
+- metadata:
+  - name: COLUMN_1
+    type: string
+  rows:
+  - ['1']
+...
+box.execute([[SELECT group_concat(d) FROM t;]])
+---
+- metadata:
+  - name: COLUMN_1
+    type: string
+  rows:
+  - ['1.5']
+...
+box.execute([[SELECT group_concat(b) FROM t;]])
+---
+- metadata:
+  - name: COLUMN_1
+    type: string
+  rows:
+  - ['TRUE']
+...
+box.execute([[SELECT group_concat(s) FROM t;]])
+---
+- metadata:
+  - name: COLUMN_1
+    type: string
+  rows:
+  - ['a']
+...
+box.execute([[SELECT group_concat(v) FROM t;]])
+---
+- metadata:
+  - name: COLUMN_1
+    type: string
+  rows:
+  - ['3']
+...
+box.execute([[SELECT max(i) FROM t;]])
+---
+- metadata:
+  - name: COLUMN_1
+    type: scalar
+  rows:
+  - [-1]
+...
+box.execute([[SELECT max(u) FROM t;]])
+---
+- metadata:
+  - name: COLUMN_1
+    type: scalar
+  rows:
+  - [1]
+...
+box.execute([[SELECT max(d) FROM t;]])
+---
+- metadata:
+  - name: COLUMN_1
+    type: scalar
+  rows:
+  - [1.5]
+...
+box.execute([[SELECT max(b) FROM t;]])
+---
+- metadata:
+  - name: COLUMN_1
+    type: scalar
+  rows:
+  - [true]
+...
+box.execute([[SELECT max(s) FROM t;]])
+---
+- metadata:
+  - name: COLUMN_1
+    type: scalar
+  rows:
+  - ['a']
+...
+box.execute([[SELECT max(v) FROM t;]])
+---
+- metadata:
+  - name: COLUMN_1
+    type: scalar
+  rows:
+  - ['3']
+...
+box.execute([[SELECT min(i) FROM t;]])
+---
+- metadata:
+  - name: COLUMN_1
+    type: scalar
+  rows:
+  - [-1]
+...
+box.execute([[SELECT min(u) FROM t;]])
+---
+- metadata:
+  - name: COLUMN_1
+    type: scalar
+  rows:
+  - [1]
+...
+box.execute([[SELECT min(d) FROM t;]])
+---
+- metadata:
+  - name: COLUMN_1
+    type: scalar
+  rows:
+  - [1.5]
+...
+box.execute([[SELECT min(b) FROM t;]])
+---
+- metadata:
+  - name: COLUMN_1
+    type: scalar
+  rows:
+  - [true]
+...
+box.execute([[SELECT min(s) FROM t;]])
+---
+- metadata:
+  - name: COLUMN_1
+    type: scalar
+  rows:
+  - ['a']
+...
+box.execute([[SELECT min(v) FROM t;]])
+---
+- metadata:
+  - name: COLUMN_1
+    type: scalar
+  rows:
+  - ['3']
+...
+box.execute([[SELECT sum(i) FROM t;]])
+---
+- metadata:
+  - name: COLUMN_1
+    type: number
+  rows:
+  - [-1]
+...
+box.execute([[SELECT sum(u) FROM t;]])
+---
+- metadata:
+  - name: COLUMN_1
+    type: number
+  rows:
+  - [1]
+...
+box.execute([[SELECT sum(d) FROM t;]])
+---
+- metadata:
+  - name: COLUMN_1
+    type: number
+  rows:
+  - [1.5]
+...
+box.execute([[SELECT sum(b) FROM t;]])
+---
+- null
+- 'Type mismatch: can not convert TRUE to number'
+...
+box.execute([[SELECT sum(s) FROM t;]])
+---
+- null
+- 'Type mismatch: can not convert a to number'
+...
+box.execute([[SELECT sum(v) FROM t;]])
+---
+- null
+- 'Type mismatch: can not convert varbinary to number'
+...
+box.execute([[SELECT total(i) FROM t;]])
+---
+- metadata:
+  - name: COLUMN_1
+    type: number
+  rows:
+  - [-1]
+...
+box.execute([[SELECT total(u) FROM t;]])
+---
+- metadata:
+  - name: COLUMN_1
+    type: number
+  rows:
+  - [1]
+...
+box.execute([[SELECT total(d) FROM t;]])
+---
+- metadata:
+  - name: COLUMN_1
+    type: number
+  rows:
+  - [1.5]
+...
+box.execute([[SELECT total(b) FROM t;]])
+---
+- null
+- 'Type mismatch: can not convert TRUE to number'
+...
+box.execute([[SELECT total(s) FROM t;]])
+---
+- null
+- 'Type mismatch: can not convert a to number'
+...
+box.execute([[SELECT total(v) FROM t;]])
+---
+- null
+- 'Type mismatch: can not convert varbinary to number'
+...
+box.execute([[DROP TABLE t;]])
+---
+- row_count: 1
+...
diff --git a/test/sql/types.test.lua b/test/sql/types.test.lua
index fff0057bd..ac9128548 100644
--- a/test/sql/types.test.lua
+++ b/test/sql/types.test.lua
@@ -629,3 +629,247 @@ box.execute([[DROP TABLE ts;]])
 -- instead of values of type UNSIGNED.
 --
 box.execute([[SELECT typeof(length('abc'));]])
+
+-- Make sure the function argument types are checked.
+box.execute([[SELECT abs(-1);]])
+box.execute([[SELECT abs(1);]])
+box.execute([[SELECT abs(1.5);]])
+box.execute([[SELECT abs(true);]])
+box.execute([[SELECT abs('a');]])
+box.execute([[SELECT abs(X'33');]])
+
+box.execute([[SELECT char(-1);]])
+box.execute([[SELECT char(1);]])
+box.execute([[SELECT char(1.5);]])
+box.execute([[SELECT char(true);]])
+box.execute([[SELECT char('a');]])
+box.execute([[SELECT char(X'33');]])
+
+box.execute([[SELECT character_length(-1);]])
+box.execute([[SELECT character_length(1);]])
+box.execute([[SELECT character_length(1.5);]])
+box.execute([[SELECT character_length(true);]])
+box.execute([[SELECT character_length('a');]])
+box.execute([[SELECT character_length(X'33');]])
+
+box.execute([[SELECT char_length(-1);]])
+box.execute([[SELECT char_length(1);]])
+box.execute([[SELECT char_length(1.5);]])
+box.execute([[SELECT char_length(true);]])
+box.execute([[SELECT char_length('a');]])
+box.execute([[SELECT char_length(X'33');]])
+
+box.execute([[SELECT coalesce(-1, -1);]])
+box.execute([[SELECT coalesce(1, 1);]])
+box.execute([[SELECT coalesce(1.5, 1.5);]])
+box.execute([[SELECT coalesce(true, true);]])
+box.execute([[SELECT coalesce('a', 'a');]])
+box.execute([[SELECT coalesce(X'33', X'33');]])
+
+box.execute([[SELECT greatest(-1, -1);]])
+box.execute([[SELECT greatest(1, 1);]])
+box.execute([[SELECT greatest(1.5, 1.5);]])
+box.execute([[SELECT greatest(true, true);]])
+box.execute([[SELECT greatest('a', 'a');]])
+box.execute([[SELECT greatest(X'33', X'33');]])
+
+box.execute([[SELECT hex(-1);]])
+box.execute([[SELECT hex(1);]])
+box.execute([[SELECT hex(1.5);]])
+box.execute([[SELECT hex(true);]])
+box.execute([[SELECT hex('a');]])
+box.execute([[SELECT hex(X'33');]])
+
+box.execute([[SELECT ifnull(-1, -1);]])
+box.execute([[SELECT ifnull(1, 1);]])
+box.execute([[SELECT ifnull(1.5, 1);]])
+box.execute([[SELECT ifnull(true, true);]])
+box.execute([[SELECT ifnull('a', 'a');]])
+box.execute([[SELECT ifnull(X'33', X'33');]])
+
+box.execute([[SELECT least(-1, -1);]])
+box.execute([[SELECT least(1, 1);]])
+box.execute([[SELECT least(1.5, 1.5);]])
+box.execute([[SELECT least(true, true);]])
+box.execute([[SELECT least('a', 'a');]])
+box.execute([[SELECT least(X'33', X'33');]])
+
+box.execute([[SELECT length(-1);]])
+box.execute([[SELECT length(1);]])
+box.execute([[SELECT length(1.5);]])
+box.execute([[SELECT length(true);]])
+box.execute([[SELECT length('a');]])
+box.execute([[SELECT length(X'33');]])
+
+box.execute([[SELECT likelihood(-1, -1);]])
+box.execute([[SELECT likelihood(1, 1);]])
+box.execute([[SELECT likelihood(1.5, 1.5);]])
+box.execute([[SELECT likelihood(true, true);]])
+box.execute([[SELECT likelihood('a', 'a');]])
+box.execute([[SELECT likelihood(X'33', X'33');]])
+
+box.execute([[SELECT likely(-1);]])
+box.execute([[SELECT likely(1);]])
+box.execute([[SELECT likely(1.5);]])
+box.execute([[SELECT likely(true);]])
+box.execute([[SELECT likely('a');]])
+box.execute([[SELECT likely(X'33');]])
+
+box.execute([[SELECT lower(-1);]])
+box.execute([[SELECT lower(1);]])
+box.execute([[SELECT lower(1.5);]])
+box.execute([[SELECT lower(true);]])
+box.execute([[SELECT lower('a');]])
+box.execute([[SELECT lower(X'33');]])
+
+box.execute([[SELECT nullif(-1, -1);]])
+box.execute([[SELECT nullif(1, 1);]])
+box.execute([[SELECT nullif(1.5, 1.5);]])
+box.execute([[SELECT nullif(true, true);]])
+box.execute([[SELECT nullif('a', 'a');]])
+box.execute([[SELECT nullif(X'33', X'33');]])
+
+box.execute([[SELECT position(-1, -1);]])
+box.execute([[SELECT position(1, 1);]])
+box.execute([[SELECT position(1.5, 1.5);]])
+box.execute([[SELECT position(true, true);]])
+box.execute([[SELECT position('a', 'a');]])
+box.execute([[SELECT position(X'33', X'33');]])
+
+box.execute([[SELECT printf(-1);]])
+box.execute([[SELECT printf(1);]])
+box.execute([[SELECT printf(1.5);]])
+box.execute([[SELECT printf(true);]])
+box.execute([[SELECT printf('a');]])
+box.execute([[SELECT printf(X'33');]])
+
+box.execute([[SELECT quote(-1);]])
+box.execute([[SELECT quote(1);]])
+box.execute([[SELECT quote(1.5);]])
+box.execute([[SELECT quote(true);]])
+box.execute([[SELECT quote('a');]])
+box.execute([[SELECT quote(X'33');]])
+
+box.execute([[SELECT randomblob(-1);]])
+box.execute([[SELECT randomblob(0);]])
+box.execute([[SELECT randomblob(0.5);]])
+box.execute([[SELECT randomblob(true);]])
+box.execute([[SELECT randomblob('a');]])
+box.execute([[SELECT randomblob(X'33');]])
+
+box.execute([[SELECT replace(-1, -1, -1);]])
+box.execute([[SELECT replace(1, 1, 1);]])
+box.execute([[SELECT replace(1.5, 1.5, 1.5);]])
+box.execute([[SELECT replace(true, true, true);]])
+box.execute([[SELECT replace('a', 'a', 'a');]])
+box.execute([[SELECT replace(X'33', X'33', X'33');]])
+
+box.execute([[SELECT round(-1, -1);]])
+box.execute([[SELECT round(1, 1);]])
+box.execute([[SELECT round(1.5, 1.5);]])
+box.execute([[SELECT round(true, true);]])
+box.execute([[SELECT round('a', 'a');]])
+box.execute([[SELECT round(X'33', X'33');]])
+
+box.execute([[SELECT soundex(-1);]])
+box.execute([[SELECT soundex(1);]])
+box.execute([[SELECT soundex(1.5);]])
+box.execute([[SELECT soundex(true);]])
+box.execute([[SELECT soundex('a');]])
+box.execute([[SELECT soundex(X'33');]])
+
+box.execute([[SELECT substr(-1, -1, -1);]])
+box.execute([[SELECT substr(1, 1, 1);]])
+box.execute([[SELECT substr(1.5, 1.5, 1.5);]])
+box.execute([[SELECT substr(true, true, true);]])
+box.execute([[SELECT substr('a', 'a', 'a');]])
+box.execute([[SELECT substr(X'33', X'33', X'33');]])
+
+box.execute([[SELECT typeof(-1);]])
+box.execute([[SELECT typeof(1);]])
+box.execute([[SELECT typeof(1.5);]])
+box.execute([[SELECT typeof(true);]])
+box.execute([[SELECT typeof('a');]])
+box.execute([[SELECT typeof(X'33');]])
+
+box.execute([[SELECT unicode(-1);]])
+box.execute([[SELECT unicode(1);]])
+box.execute([[SELECT unicode(1.5);]])
+box.execute([[SELECT unicode(true);]])
+box.execute([[SELECT unicode('a');]])
+box.execute([[SELECT unicode(X'33');]])
+
+box.execute([[SELECT unlikely(-1);]])
+box.execute([[SELECT unlikely(1);]])
+box.execute([[SELECT unlikely(1.5);]])
+box.execute([[SELECT unlikely(true);]])
+box.execute([[SELECT unlikely('a');]])
+box.execute([[SELECT unlikely(X'33');]])
+
+box.execute([[SELECT upper(-1);]])
+box.execute([[SELECT upper(1);]])
+box.execute([[SELECT upper(1.5);]])
+box.execute([[SELECT upper(true);]])
+box.execute([[SELECT upper('a');]])
+box.execute([[SELECT upper(X'33');]])
+
+box.execute([[SELECT zeroblob(-1);]])
+box.execute([[SELECT zeroblob(1);]])
+box.execute([[SELECT zeroblob(1.5);]])
+box.execute([[SELECT zeroblob(true);]])
+box.execute([[SELECT zeroblob('a');]])
+box.execute([[SELECT zeroblob(X'33');]])
+
+box.execute([[CREATE TABLE t (i INTEGER PRIMARY KEY, u UNSIGNED, d DOUBLE, b BOOLEAN, s STRING, v VARBINARY);]])
+box.execute([[INSERT INTO t VALUES (-1, 1, 1.5, true, 'a', X'33');]])
+
+box.execute([[SELECT avg(i) FROM t;]])
+box.execute([[SELECT avg(u) FROM t;]])
+box.execute([[SELECT avg(d) FROM t;]])
+box.execute([[SELECT avg(b) FROM t;]])
+box.execute([[SELECT avg(s) FROM t;]])
+box.execute([[SELECT avg(v) FROM t;]])
+
+box.execute([[SELECT count(i) FROM t;]])
+box.execute([[SELECT count(u) FROM t;]])
+box.execute([[SELECT count(d) FROM t;]])
+box.execute([[SELECT count(b) FROM t;]])
+box.execute([[SELECT count(s) FROM t;]])
+box.execute([[SELECT count(v) FROM t;]])
+
+box.execute([[SELECT group_concat(i) FROM t;]])
+box.execute([[SELECT group_concat(u) FROM t;]])
+box.execute([[SELECT group_concat(d) FROM t;]])
+box.execute([[SELECT group_concat(b) FROM t;]])
+box.execute([[SELECT group_concat(s) FROM t;]])
+box.execute([[SELECT group_concat(v) FROM t;]])
+
+box.execute([[SELECT max(i) FROM t;]])
+box.execute([[SELECT max(u) FROM t;]])
+box.execute([[SELECT max(d) FROM t;]])
+box.execute([[SELECT max(b) FROM t;]])
+box.execute([[SELECT max(s) FROM t;]])
+box.execute([[SELECT max(v) FROM t;]])
+
+box.execute([[SELECT min(i) FROM t;]])
+box.execute([[SELECT min(u) FROM t;]])
+box.execute([[SELECT min(d) FROM t;]])
+box.execute([[SELECT min(b) FROM t;]])
+box.execute([[SELECT min(s) FROM t;]])
+box.execute([[SELECT min(v) FROM t;]])
+
+box.execute([[SELECT sum(i) FROM t;]])
+box.execute([[SELECT sum(u) FROM t;]])
+box.execute([[SELECT sum(d) FROM t;]])
+box.execute([[SELECT sum(b) FROM t;]])
+box.execute([[SELECT sum(s) FROM t;]])
+box.execute([[SELECT sum(v) FROM t;]])
+
+box.execute([[SELECT total(i) FROM t;]])
+box.execute([[SELECT total(u) FROM t;]])
+box.execute([[SELECT total(d) FROM t;]])
+box.execute([[SELECT total(b) FROM t;]])
+box.execute([[SELECT total(s) FROM t;]])
+box.execute([[SELECT total(v) FROM t;]])
+
+box.execute([[DROP TABLE t;]])
-- 
2.25.1

^ permalink raw reply	[flat|nested] 8+ messages in thread

* [Tarantool-patches] [PATCH v1 7/7] sql: refactor sql/func.c
  2020-08-12 15:15 [Tarantool-patches] [PATCH v1 0/7] sql: properly check arguments types of built-in functions imeevma
                   ` (5 preceding siblings ...)
  2020-08-12 15:15 ` [Tarantool-patches] [PATCH v1 6/7] sql: VARBINARY and STRING in built-in functions imeevma
@ 2020-08-12 15:15 ` imeevma
  6 siblings, 0 replies; 8+ messages in thread
From: imeevma @ 2020-08-12 15:15 UTC (permalink / raw)
  To: v.shpilevoy; +Cc: tarantool-patches

After changing the way of checking the types of arguments, some of the
code in sql/func.c is no longer used. This patch removes this code.

Follow-up of #4159
---
 src/box/sql/func.c | 797 +++++----------------------------------------
 1 file changed, 83 insertions(+), 714 deletions(-)

diff --git a/src/box/sql/func.c b/src/box/sql/func.c
index 694bd0e83..651864189 100644
--- a/src/box/sql/func.c
+++ b/src/box/sql/func.c
@@ -493,44 +493,21 @@ absFunc(sql_context * context, int argc, sql_value ** argv)
 {
 	assert(argc == 1);
 	UNUSED_PARAMETER(argc);
-	switch (sql_value_type(argv[0])) {
-	case MP_UINT: {
-		sql_result_uint(context, sql_value_uint64(argv[0]));
-		break;
-	}
-	case MP_INT: {
+	enum mp_type type = sql_value_type(argv[0]);
+	if (type == MP_NIL)
+		return sql_result_null(context);
+	if (type == MP_UINT)
+		return sql_result_uint(context, sql_value_uint64(argv[0]));
+	if (type == MP_INT) {
 		int64_t value = sql_value_int64(argv[0]);
 		assert(value < 0);
-		sql_result_uint(context, -value);
-		break;
-	}
-	case MP_NIL:{
-			/* IMP: R-37434-19929 Abs(X) returns NULL if X is NULL. */
-			sql_result_null(context);
-			break;
-		}
-	case MP_BOOL:
-	case MP_BIN:
-	case MP_ARRAY:
-	case MP_MAP: {
-		diag_set(ClientError, ER_INCONSISTENT_TYPES, "number",
-			 mem_type_to_str(argv[0]));
-		context->is_aborted = true;
-		return;
-	}
-	default:{
-			/* Because sql_value_double() returns 0.0 if the argument is not
-			 * something that can be converted into a number, we have:
-			 * IMP: R-01992-00519 Abs(X) returns 0.0 if X is a string or blob
-			 * that cannot be converted to a numeric value.
-			 */
-			double rVal = sql_value_double(argv[0]);
-			if (rVal < 0)
-				rVal = -rVal;
-			sql_result_double(context, rVal);
-			break;
-		}
+		return sql_result_uint(context, -value);
 	}
+	assert(type == MP_DOUBLE);
+	double value = sql_value_double(argv[0]);
+	if (value < 0)
+		value = -value;
+	return sql_result_double(context, value);
 }
 
 /**
@@ -835,19 +812,14 @@ roundFunc(sql_context * context, int argc, sql_value ** argv)
 	if (argc == 2) {
 		if (sql_value_is_null(argv[1]))
 			return;
+		assert(sql_value_type(argv[1]) == MP_UINT);
 		n = sql_value_int(argv[1]);
 		if (n < 0)
 			n = 0;
 	}
 	if (sql_value_is_null(argv[0]))
 		return;
-	enum mp_type mp_type = sql_value_type(argv[0]);
-	if (mp_type_is_bloblike(mp_type)) {
-		diag_set(ClientError, ER_SQL_TYPE_MISMATCH,
-			 sql_value_to_diag_str(argv[0]), "numeric");
-		context->is_aborted = true;
-		return;
-	}
+	assert(sql_value_type(argv[0]) == MP_DOUBLE);
 	r = sql_value_double(argv[0]);
 	/* If Y==0 and X will fit in a 64-bit int,
 	 * handle the rounding directly,
@@ -989,12 +961,7 @@ randomBlob(sql_context * context, int argc, sql_value ** argv)
 	unsigned char *p;
 	assert(argc == 1);
 	UNUSED_PARAMETER(argc);
-	if (mp_type_is_bloblike(sql_value_type(argv[0]))) {
-		diag_set(ClientError, ER_SQL_TYPE_MISMATCH,
-			 sql_value_to_diag_str(argv[0]), "numeric");
-		context->is_aborted = true;
-		return;
-	}
+	assert(sql_value_type(argv[0]) == MP_UINT);
 	n = sql_value_int(argv[0]);
 	if (n < 1)
 		return;
@@ -1546,6 +1513,7 @@ zeroblobFunc(sql_context * context, int argc, sql_value ** argv)
 	i64 n;
 	assert(argc == 1);
 	UNUSED_PARAMETER(argc);
+	assert(sql_value_type(argv[0]) == MP_UINT);
 	n = sql_value_int64(argv[0]);
 	if (n < 0)
 		n = 0;
@@ -1968,18 +1936,10 @@ sum_step(struct sql_context *context, int argc, sql_value **argv)
 	assert(argc == 1);
 	UNUSED_PARAMETER(argc);
 	struct SumCtx *p = sql_aggregate_context(context, sizeof(*p));
-	int type = sql_value_type(argv[0]);
+	enum mp_type type = sql_value_type(argv[0]);
 	if (type == MP_NIL || p == NULL)
 		return;
-	if (type != MP_DOUBLE && type != MP_INT && type != MP_UINT) {
-		if (mem_apply_numeric_type(argv[0]) != 0) {
-			diag_set(ClientError, ER_SQL_TYPE_MISMATCH,
-				 sql_value_to_diag_str(argv[0]), "number");
-			context->is_aborted = true;
-			return;
-		}
-		type = sql_value_type(argv[0]);
-	}
+	assert(type == MP_DOUBLE || type == MP_INT || type == MP_UINT);
 	p->cnt++;
 	if (type == MP_INT || type == MP_UINT) {
 		int64_t v = sql_value_int64(argv[0]);
@@ -2246,663 +2206,72 @@ static struct {
 	uint16_t flags;
 	void (*call)(sql_context *ctx, int argc, sql_value **argv);
 	void (*finalize)(sql_context *ctx);
-	/** Members below are related to struct func_def. */
-	bool is_deterministic;
-	int param_count;
-	enum field_type returns;
-	enum func_aggregate aggregate;
-	bool export_to_sql;
 } sql_builtins[] = {
-	{.name = "ABS",
-	 .param_count = 1,
-	 .returns = FIELD_TYPE_NUMBER,
-	 .aggregate = FUNC_AGGREGATE_NONE,
-	 .is_deterministic = true,
-	 .flags = 0,
-	 .call = absFunc,
-	 .finalize = NULL,
-	 .export_to_sql = true,
-	}, {
-	 .name = "AVG",
-	 .param_count = 1,
-	 .returns = FIELD_TYPE_NUMBER,
-	 .is_deterministic = false,
-	 .aggregate = FUNC_AGGREGATE_GROUP,
-	 .flags = 0,
-	 .call = sum_step,
-	 .finalize = avgFinalize,
-	 .export_to_sql = true,
-	}, {
-	 .name = "CEIL",
-	 .call = sql_builtin_stub,
-	 .export_to_sql = false,
-	 .param_count = -1,
-	 .returns = FIELD_TYPE_ANY,
-	 .aggregate = FUNC_AGGREGATE_NONE,
-	 .is_deterministic = false,
-	 .flags = 0,
-	 .finalize = NULL,
-	}, {
-	 .name = "CEILING",
-	 .call = sql_builtin_stub,
-	 .export_to_sql = false,
-	 .param_count = -1,
-	 .returns = FIELD_TYPE_ANY,
-	 .aggregate = FUNC_AGGREGATE_NONE,
-	 .is_deterministic = false,
-	 .flags = 0,
-	 .finalize = NULL,
-	}, {
-	 .name = "CHAR",
-	 .param_count = -1,
-	 .returns = FIELD_TYPE_STRING,
-	 .is_deterministic = true,
-	 .aggregate = FUNC_AGGREGATE_NONE,
-	 .flags = 0,
-	 .call = charFunc,
-	 .finalize = NULL,
-	 .export_to_sql = true,
-	 }, {
-	 .name = "CHARACTER_LENGTH",
-	 .param_count = 1,
-	 .returns = FIELD_TYPE_INTEGER,
-	 .aggregate = FUNC_AGGREGATE_NONE,
-	 .is_deterministic = true,
-	 .flags = 0,
-	 .call = lengthFunc,
-	 .finalize = NULL,
-	 .export_to_sql = true,
-	}, {
-	 .name = "CHAR_LENGTH",
-	 .param_count = 1,
-	 .returns = FIELD_TYPE_INTEGER,
-	 .aggregate = FUNC_AGGREGATE_NONE,
-	 .is_deterministic = true,
-	 .flags = 0,
-	 .call = lengthFunc,
-	 .finalize = NULL,
-	 .export_to_sql = true,
-	}, {
-	 .name = "COALESCE",
-	 .param_count = -1,
-	 .returns = FIELD_TYPE_SCALAR,
-	 .aggregate = FUNC_AGGREGATE_NONE,
-	 .is_deterministic = true,
-	 .flags = SQL_FUNC_COALESCE,
-	 .call = sql_builtin_stub,
-	 .finalize = NULL,
-	 .export_to_sql = true,
-	}, {
-	 .name = "COUNT",
-	 .param_count = -1,
-	 .returns = FIELD_TYPE_INTEGER,
-	 .aggregate = FUNC_AGGREGATE_GROUP,
-	 .is_deterministic = false,
-	 .flags = 0,
-	 .call = countStep,
-	 .finalize = countFinalize,
-	 .export_to_sql = true,
-	}, {
-	 .name = "CURRENT_DATE",
-	 .call = sql_builtin_stub,
-	 .export_to_sql = false,
-	 .param_count = -1,
-	 .returns = FIELD_TYPE_ANY,
-	 .aggregate = FUNC_AGGREGATE_NONE,
-	 .is_deterministic = false,
-	 .flags = 0,
-	 .finalize = NULL,
-	}, {
-	 .name = "CURRENT_TIME",
-	 .call = sql_builtin_stub,
-	 .export_to_sql = false,
-	 .param_count = -1,
-	 .returns = FIELD_TYPE_ANY,
-	 .aggregate = FUNC_AGGREGATE_NONE,
-	 .is_deterministic = false,
-	 .flags = 0,
-	 .finalize = NULL,
-	}, {
-	 .name = "CURRENT_TIMESTAMP",
-	 .call = sql_builtin_stub,
-	 .export_to_sql = false,
-	 .param_count = -1,
-	 .returns = FIELD_TYPE_ANY,
-	 .aggregate = FUNC_AGGREGATE_NONE,
-	 .is_deterministic = false,
-	 .flags = 0,
-	 .finalize = NULL,
-	}, {
-	 .name = "DATE",
-	 .call = sql_builtin_stub,
-	 .export_to_sql = false,
-	 .param_count = -1,
-	 .returns = FIELD_TYPE_ANY,
-	 .aggregate = FUNC_AGGREGATE_NONE,
-	 .is_deterministic = false,
-	 .flags = 0,
-	 .finalize = NULL,
-	}, {
-	 .name = "DATETIME",
-	 .call = sql_builtin_stub,
-	 .export_to_sql = false,
-	 .param_count = -1,
-	 .returns = FIELD_TYPE_ANY,
-	 .aggregate = FUNC_AGGREGATE_NONE,
-	 .is_deterministic = false,
-	 .flags = 0,
-	 .finalize = NULL,
-	}, {
-	 .name = "EVERY",
-	 .call = sql_builtin_stub,
-	 .export_to_sql = false,
-	 .param_count = -1,
-	 .returns = FIELD_TYPE_ANY,
-	 .aggregate = FUNC_AGGREGATE_NONE,
-	 .is_deterministic = false,
-	 .flags = 0,
-	 .finalize = NULL,
-	}, {
-	 .name = "EXISTS",
-	 .call = sql_builtin_stub,
-	 .export_to_sql = false,
-	 .param_count = -1,
-	 .returns = FIELD_TYPE_ANY,
-	 .aggregate = FUNC_AGGREGATE_NONE,
-	 .is_deterministic = false,
-	 .flags = 0,
-	 .finalize = NULL,
-	}, {
-	 .name = "EXP",
-	 .call = sql_builtin_stub,
-	 .export_to_sql = false,
-	 .param_count = -1,
-	 .returns = FIELD_TYPE_ANY,
-	 .aggregate = FUNC_AGGREGATE_NONE,
-	 .is_deterministic = false,
-	 .flags = 0,
-	 .finalize = NULL,
-	}, {
-	 .name = "EXTRACT",
-	 .call = sql_builtin_stub,
-	 .export_to_sql = false,
-	 .param_count = -1,
-	 .returns = FIELD_TYPE_ANY,
-	 .aggregate = FUNC_AGGREGATE_NONE,
-	 .is_deterministic = false,
-	 .flags = 0,
-	 .finalize = NULL,
-	}, {
-	 .name = "FLOOR",
-	 .call = sql_builtin_stub,
-	 .export_to_sql = false,
-	 .param_count = -1,
-	 .returns = FIELD_TYPE_ANY,
-	 .aggregate = FUNC_AGGREGATE_NONE,
-	 .is_deterministic = false,
-	 .flags = 0,
-	 .finalize = NULL,
-	}, {
-	 .name = "GREATER",
-	 .call = sql_builtin_stub,
-	 .export_to_sql = false,
-	 .param_count = -1,
-	 .returns = FIELD_TYPE_ANY,
-	 .aggregate = FUNC_AGGREGATE_NONE,
-	 .is_deterministic = false,
-	 .flags = 0,
-	 .finalize = NULL,
-	}, {
-	 .name = "GREATEST",
-	 .param_count = -1,
-	 .returns = FIELD_TYPE_SCALAR,
-	 .aggregate = FUNC_AGGREGATE_NONE,
-	 .is_deterministic = true,
-	 .flags = SQL_FUNC_NEEDCOLL | SQL_FUNC_MAX,
-	 .call = minmaxFunc,
-	 .finalize = NULL,
-	 .export_to_sql = true,
-	}, {
-	 .name = "GROUP_CONCAT",
-	 .param_count = -1,
-	 .returns = FIELD_TYPE_STRING,
-	 .aggregate = FUNC_AGGREGATE_GROUP,
-	 .is_deterministic = false,
-	 .flags = 0,
-	 .call = groupConcatStep,
-	 .finalize = groupConcatFinalize,
-	 .export_to_sql = true,
-	}, {
-	 .name = "HEX",
-	 .param_count = 1,
-	 .returns = FIELD_TYPE_STRING,
-	 .aggregate = FUNC_AGGREGATE_NONE,
-	 .is_deterministic = true,
-	 .flags = 0,
-	 .call = hexFunc,
-	 .finalize = NULL,
-	 .export_to_sql = true,
-	}, {
-	 .name = "IFNULL",
-	 .param_count = 2,
-	 .returns = FIELD_TYPE_INTEGER,
-	 .aggregate = FUNC_AGGREGATE_NONE,
-	 .is_deterministic = true,
-	 .flags = SQL_FUNC_COALESCE,
-	 .call = sql_builtin_stub,
-	 .finalize = NULL,
-	 .export_to_sql = true,
-	}, {
-	 .name = "JULIANDAY",
-	 .call = sql_builtin_stub,
-	 .export_to_sql = false,
-	 .param_count = -1,
-	 .returns = FIELD_TYPE_ANY,
-	 .aggregate = FUNC_AGGREGATE_NONE,
-	 .is_deterministic = false,
-	 .flags = 0,
-	 .finalize = NULL,
-	}, {
-	 .name = "LEAST",
-	 .param_count = -1,
-	 .returns = FIELD_TYPE_SCALAR,
-	 .aggregate = FUNC_AGGREGATE_NONE,
-	 .is_deterministic = true,
-	 .flags = SQL_FUNC_NEEDCOLL | SQL_FUNC_MIN,
-	 .call = minmaxFunc,
-	 .finalize = NULL,
-	 .export_to_sql = true,
-	}, {
-	 .name = "LENGTH",
-	 .param_count = 1,
-	 .returns = FIELD_TYPE_INTEGER,
-	 .aggregate = FUNC_AGGREGATE_NONE,
-	 .is_deterministic = true,
-	 .flags = SQL_FUNC_LENGTH,
-	 .call = lengthFunc,
-	 .finalize = NULL,
-	 .export_to_sql = true,
-	}, {
-	 .name = "LESSER",
-	 .call = sql_builtin_stub,
-	 .export_to_sql = false,
-	 .param_count = -1,
-	 .returns = FIELD_TYPE_ANY,
-	 .aggregate = FUNC_AGGREGATE_NONE,
-	 .is_deterministic = false,
-	 .flags = 0,
-	 .finalize = NULL,
-	}, {
-	 .name = "LIKE",
-	 .param_count = -1,
-	 .returns = FIELD_TYPE_INTEGER,
-	 .aggregate = FUNC_AGGREGATE_NONE,
-	 .is_deterministic = true,
-	 .flags = SQL_FUNC_NEEDCOLL | SQL_FUNC_LIKE,
-	 .call = likeFunc,
-	 .finalize = NULL,
-	 .export_to_sql = true,
-	}, {
-	 .name = "LIKELIHOOD",
-	 .param_count = 2,
-	 .returns = FIELD_TYPE_BOOLEAN,
-	 .aggregate = FUNC_AGGREGATE_NONE,
-	 .is_deterministic = true,
-	 .flags = SQL_FUNC_UNLIKELY,
-	 .call = sql_builtin_stub,
-	 .finalize = NULL,
-	 .export_to_sql = true,
-	}, {
-	 .name = "LIKELY",
-	 .param_count = 1,
-	 .returns = FIELD_TYPE_BOOLEAN,
-	 .aggregate = FUNC_AGGREGATE_NONE,
-	 .is_deterministic = true,
-	 .flags = SQL_FUNC_UNLIKELY,
-	 .call = sql_builtin_stub,
-	 .finalize = NULL,
-	 .export_to_sql = true,
-	}, {
-	 .name = "LN",
-	 .call = sql_builtin_stub,
-	 .export_to_sql = false,
-	 .param_count = -1,
-	 .returns = FIELD_TYPE_ANY,
-	 .aggregate = FUNC_AGGREGATE_NONE,
-	 .is_deterministic = false,
-	 .flags = 0,
-	 .finalize = NULL,
-	}, {
-	 .name = "LOWER",
-	 .param_count = 1,
-	 .returns = FIELD_TYPE_STRING,
-	 .aggregate = FUNC_AGGREGATE_NONE,
-	 .is_deterministic = true,
-	 .flags = SQL_FUNC_DERIVEDCOLL | SQL_FUNC_NEEDCOLL,
-	 .call = LowerICUFunc,
-	 .finalize = NULL,
-	 .export_to_sql = true,
-	}, {
-	 .name = "MAX",
-	 .param_count = 1,
-	 .returns = FIELD_TYPE_SCALAR,
-	 .aggregate = FUNC_AGGREGATE_GROUP,
-	 .is_deterministic = false,
-	 .flags = SQL_FUNC_NEEDCOLL | SQL_FUNC_MAX,
-	 .call = minmaxStep,
-	 .finalize = minMaxFinalize,
-	 .export_to_sql = true,
-	}, {
-	 .name = "MIN",
-	 .param_count = 1,
-	 .returns = FIELD_TYPE_SCALAR,
-	 .aggregate = FUNC_AGGREGATE_GROUP,
-	 .is_deterministic = false,
-	 .flags = SQL_FUNC_NEEDCOLL | SQL_FUNC_MIN,
-	 .call = minmaxStep,
-	 .finalize = minMaxFinalize,
-	 .export_to_sql = true,
-	}, {
-	 .name = "MOD",
-	 .call = sql_builtin_stub,
-	 .export_to_sql = false,
-	 .param_count = -1,
-	 .returns = FIELD_TYPE_ANY,
-	 .aggregate = FUNC_AGGREGATE_NONE,
-	 .is_deterministic = false,
-	 .flags = 0,
-	 .finalize = NULL,
-	}, {
-	 .name = "NULLIF",
-	 .param_count = 2,
-	 .returns = FIELD_TYPE_SCALAR,
-	 .aggregate = FUNC_AGGREGATE_NONE,
-	 .is_deterministic = true,
-	 .flags = SQL_FUNC_NEEDCOLL,
-	 .call = nullifFunc,
-	 .finalize = NULL,
-	 .export_to_sql = true,
-	}, {
-	 .name = "OCTET_LENGTH",
-	 .call = sql_builtin_stub,
-	 .export_to_sql = false,
-	 .param_count = -1,
-	 .returns = FIELD_TYPE_ANY,
-	 .aggregate = FUNC_AGGREGATE_NONE,
-	 .is_deterministic = false,
-	 .flags = 0,
-	 .finalize = NULL,
-	}, {
-	 .name = "POSITION",
-	 .param_count = 2,
-	 .returns = FIELD_TYPE_INTEGER,
-	 .aggregate = FUNC_AGGREGATE_NONE,
-	 .is_deterministic = true,
-	 .flags = SQL_FUNC_NEEDCOLL,
-	 .call = position_func,
-	 .finalize = NULL,
-	 .export_to_sql = true,
-	}, {
-	 .name = "POWER",
-	 .call = sql_builtin_stub,
-	 .export_to_sql = false,
-	 .param_count = -1,
-	 .returns = FIELD_TYPE_ANY,
-	 .aggregate = FUNC_AGGREGATE_NONE,
-	 .is_deterministic = false,
-	 .flags = 0,
-	 .finalize = NULL,
-	}, {
-	 .name = "PRINTF",
-	 .param_count = -1,
-	 .returns = FIELD_TYPE_STRING,
-	 .aggregate = FUNC_AGGREGATE_NONE,
-	 .is_deterministic = true,
-	 .flags = 0,
-	 .call = printfFunc,
-	 .finalize = NULL,
-	 .export_to_sql = true,
-	}, {
-	 .name = "QUOTE",
-	 .param_count = 1,
-	 .returns = FIELD_TYPE_STRING,
-	 .aggregate = FUNC_AGGREGATE_NONE,
-	 .is_deterministic = true,
-	 .flags = 0,
-	 .call = quoteFunc,
-	 .finalize = NULL,
-	 .export_to_sql = true,
-	}, {
-	 .name = "RANDOM",
-	 .param_count = 0,
-	 .returns = FIELD_TYPE_INTEGER,
-	 .aggregate = FUNC_AGGREGATE_NONE,
-	 .is_deterministic = false,
-	 .flags = 0,
-	 .call = randomFunc,
-	 .finalize = NULL,
-	 .export_to_sql = true,
-	}, {
-	 .name = "RANDOMBLOB",
-	 .param_count = 1,
-	 .returns = FIELD_TYPE_VARBINARY,
-	 .aggregate = FUNC_AGGREGATE_NONE,
-	 .is_deterministic = false,
-	 .flags = 0,
-	 .call = randomBlob,
-	 .finalize = NULL,
-	 .export_to_sql = true,
-	}, {
-	 .name = "REPLACE",
-	 .param_count = 3,
-	 .returns = FIELD_TYPE_STRING,
-	 .aggregate = FUNC_AGGREGATE_NONE,
-	 .is_deterministic = true,
-	 .flags = SQL_FUNC_DERIVEDCOLL,
-	 .call = replaceFunc,
-	 .finalize = NULL,
-	 .export_to_sql = true,
-	}, {
-	 .name = "ROUND",
-	 .param_count = -1,
-	 .returns = FIELD_TYPE_INTEGER,
-	 .aggregate = FUNC_AGGREGATE_NONE,
-	 .is_deterministic = true,
-	 .flags = 0,
-	 .call = roundFunc,
-	 .finalize = NULL,
-	 .export_to_sql = true,
-	}, {
-	 .name = "ROW_COUNT",
-	 .param_count = 0,
-	 .returns = FIELD_TYPE_INTEGER,
-	 .aggregate = FUNC_AGGREGATE_NONE,
-	 .is_deterministic = true,
-	 .flags = 0,
-	 .call = sql_row_count,
-	 .finalize = NULL,
-	 .export_to_sql = true,
-	}, {
-	 .name = "SOME",
-	 .call = sql_builtin_stub,
-	 .export_to_sql = false,
-	 .param_count = -1,
-	 .returns = FIELD_TYPE_ANY,
-	 .aggregate = FUNC_AGGREGATE_NONE,
-	 .is_deterministic = false,
-	 .flags = 0,
-	 .finalize = NULL,
-	}, {
-	 .name = "SOUNDEX",
-	 .param_count = 1,
-	 .returns = FIELD_TYPE_STRING,
-	 .aggregate = FUNC_AGGREGATE_NONE,
-	 .is_deterministic = true,
-	 .flags = 0,
-	 .call = soundexFunc,
-	 .finalize = NULL,
-	 .export_to_sql = true,
-	}, {
-	 .name = "SQRT",
-	 .call = sql_builtin_stub,
-	 .export_to_sql = false,
-	 .param_count = -1,
-	 .returns = FIELD_TYPE_ANY,
-	 .aggregate = FUNC_AGGREGATE_NONE,
-	 .is_deterministic = false,
-	 .flags = 0,
-	 .finalize = NULL,
-	}, {
-	 .name = "STRFTIME",
-	 .call = sql_builtin_stub,
-	 .export_to_sql = false,
-	 .param_count = -1,
-	 .returns = FIELD_TYPE_ANY,
-	 .aggregate = FUNC_AGGREGATE_NONE,
-	 .is_deterministic = false,
-	 .flags = 0,
-	 .finalize = NULL,
-	}, {
-	 .name = "SUBSTR",
-	 .param_count = -1,
-	 .returns = FIELD_TYPE_STRING,
-	 .aggregate = FUNC_AGGREGATE_NONE,
-	 .is_deterministic = true,
-	 .flags = SQL_FUNC_DERIVEDCOLL,
-	 .call = substrFunc,
-	 .finalize = NULL,
-	 .export_to_sql = true,
-	}, {
-	 .name = "SUM",
-	 .param_count = 1,
-	 .returns = FIELD_TYPE_NUMBER,
-	 .aggregate = FUNC_AGGREGATE_GROUP,
-	 .is_deterministic = false,
-	 .flags = 0,
-	 .call = sum_step,
-	 .finalize = sumFinalize,
-	 .export_to_sql = true,
-	}, {
-	 .name = "TIME",
-	 .call = sql_builtin_stub,
-	 .export_to_sql = false,
-	 .param_count = -1,
-	 .returns = FIELD_TYPE_ANY,
-	 .aggregate = FUNC_AGGREGATE_NONE,
-	 .is_deterministic = false,
-	 .flags = 0,
-	 .finalize = NULL,
-	}, {
-	 .name = "TOTAL",
-	 .param_count = 1,
-	 .returns = FIELD_TYPE_NUMBER,
-	 .aggregate = FUNC_AGGREGATE_GROUP,
-	 .is_deterministic = false,
-	 .flags = 0,
-	 .call = sum_step,
-	 .finalize = totalFinalize,
-	 .export_to_sql = true,
-	}, {
-	 .name = "TRIM",
-	 .param_count = -1,
-	 .returns = FIELD_TYPE_STRING,
-	 .aggregate = FUNC_AGGREGATE_NONE,
-	 .is_deterministic = true,
-	 .flags = SQL_FUNC_DERIVEDCOLL,
-	 .call = trim_func,
-	 .finalize = NULL,
-	 .export_to_sql = true,
-	}, {
-	 .name = "TYPEOF",
-	 .param_count = 1,
-	 .returns = FIELD_TYPE_STRING,
-	 .aggregate = FUNC_AGGREGATE_NONE,
-	 .is_deterministic = true,
-	 .flags = SQL_FUNC_TYPEOF,
-	 .call = typeofFunc,
-	 .finalize = NULL,
-	 .export_to_sql = true,
-	}, {
-	 .name = "UNICODE",
-	 .param_count = 1,
-	 .returns = FIELD_TYPE_STRING,
-	 .aggregate = FUNC_AGGREGATE_NONE,
-	 .is_deterministic = true,
-	 .flags = 0,
-	 .call = unicodeFunc,
-	 .finalize = NULL,
-	 .export_to_sql = true,
-	}, {
-	 .name = "UNLIKELY",
-	 .param_count = 1,
-	 .returns = FIELD_TYPE_BOOLEAN,
-	 .aggregate = FUNC_AGGREGATE_NONE,
-	 .is_deterministic = true,
-	 .flags = SQL_FUNC_UNLIKELY,
-	 .call = sql_builtin_stub,
-	 .finalize = NULL,
-	 .export_to_sql = true,
-	}, {
-	 .name = "UPPER",
-	 .param_count = 1,
-	 .returns = FIELD_TYPE_STRING,
-	 .aggregate = FUNC_AGGREGATE_NONE,
-	 .is_deterministic = true,
-	 .flags = SQL_FUNC_DERIVEDCOLL | SQL_FUNC_NEEDCOLL,
-	 .call = UpperICUFunc,
-	 .finalize = NULL,
-	 .export_to_sql = true,
-	}, {
-	 .name = "VERSION",
-	 .param_count = 0,
-	 .returns = FIELD_TYPE_STRING,
-	 .aggregate = FUNC_AGGREGATE_NONE,
-	 .is_deterministic = true,
-	 .flags = 0,
-	 .call = sql_func_version,
-	 .finalize = NULL,
-	 .export_to_sql = true,
-	}, {
-	 .name = "ZEROBLOB",
-	 .param_count = 1,
-	 .returns = FIELD_TYPE_VARBINARY,
-	 .aggregate = FUNC_AGGREGATE_NONE,
-	 .is_deterministic = true,
-	 .flags = 0,
-	 .call = zeroblobFunc,
-	 .finalize = NULL,
-	 .export_to_sql = true,
-	}, {
-	 .name = "_sql_stat_get",
-	 .call = sql_builtin_stub,
-	 .export_to_sql = false,
-	 .param_count = -1,
-	 .returns = FIELD_TYPE_ANY,
-	 .aggregate = FUNC_AGGREGATE_NONE,
-	 .is_deterministic = false,
-	 .flags = 0,
-	 .finalize = NULL,
-	}, {
-	 .name = "_sql_stat_init",
-	 .call = sql_builtin_stub,
-	 .export_to_sql = false,
-	 .param_count = -1,
-	 .returns = FIELD_TYPE_ANY,
-	 .aggregate = FUNC_AGGREGATE_NONE,
-	 .is_deterministic = false,
-	 .flags = 0,
-	 .finalize = NULL,
-	}, {
-	 .name = "_sql_stat_push",
-	 .call = sql_builtin_stub,
-	 .export_to_sql = false,
-	 .param_count = -1,
-	 .returns = FIELD_TYPE_ANY,
-	 .aggregate = FUNC_AGGREGATE_NONE,
-	 .is_deterministic = false,
-	 .flags = 0,
-	 .finalize = NULL,
-	},
+	{"ABS", 0, absFunc, NULL},
+	{"AVG", 0, sum_step, avgFinalize},
+	{"CEIL", 0, sql_builtin_stub, NULL},
+	{"CEILING", 0, sql_builtin_stub, NULL},
+	{"CHAR", 0, charFunc, NULL},
+	{"CHARACTER_LENGTH", 0, lengthFunc, NULL},
+	{"CHAR_LENGTH", 0, lengthFunc, NULL},
+	{"COALESCE", SQL_FUNC_COALESCE, sql_builtin_stub, NULL},
+	{"COUNT", 0, countStep, countFinalize},
+	{"CURRENT_DATE", 0, sql_builtin_stub, NULL},
+	{"CURRENT_TIME", 0, sql_builtin_stub, NULL},
+	{"CURRENT_TIMESTAMP", 0, sql_builtin_stub, NULL},
+	{"DATE", 0, sql_builtin_stub, NULL},
+	{"DATETIME", 0, sql_builtin_stub, NULL},
+	{"EVERY", 0, sql_builtin_stub, NULL},
+	{"EXISTS", 0, sql_builtin_stub, NULL},
+	{"EXP", 0, sql_builtin_stub, NULL},
+	{"EXTRACT", 0, sql_builtin_stub, NULL},
+	{"FLOOR", 0, sql_builtin_stub, NULL},
+	{"GREATER", 0, sql_builtin_stub, NULL},
+	{"GREATEST", SQL_FUNC_NEEDCOLL | SQL_FUNC_MAX, minmaxFunc, NULL},
+	{"GROUP_CONCAT", 0, groupConcatStep, groupConcatFinalize},
+	{"HEX", 0, hexFunc, NULL},
+	{"IFNULL", SQL_FUNC_COALESCE, sql_builtin_stub, NULL},
+	{"JULIANDAY", 0, sql_builtin_stub, NULL},
+	{"LEAST", SQL_FUNC_NEEDCOLL | SQL_FUNC_MIN, minmaxFunc, NULL},
+	{"LENGTH", SQL_FUNC_LENGTH, lengthFunc, NULL},
+	{"LESSER", 0, sql_builtin_stub, NULL},
+	{"LIKE", SQL_FUNC_NEEDCOLL | SQL_FUNC_LIKE, likeFunc, NULL},
+	{"LIKELIHOOD", SQL_FUNC_UNLIKELY, sql_builtin_stub, NULL},
+	{"LIKELY", SQL_FUNC_UNLIKELY, sql_builtin_stub, NULL},
+	{"LN", 0, sql_builtin_stub, NULL},
+	{"LOWER", SQL_FUNC_DERIVEDCOLL | SQL_FUNC_NEEDCOLL, LowerICUFunc, NULL},
+	{"MAX", SQL_FUNC_NEEDCOLL | SQL_FUNC_MAX, minmaxStep, minMaxFinalize},
+	{"MIN", SQL_FUNC_NEEDCOLL | SQL_FUNC_MIN, minmaxStep, minMaxFinalize},
+	{"MOD", 0, sql_builtin_stub, NULL},
+	{"NULLIF", SQL_FUNC_NEEDCOLL, nullifFunc, NULL},
+	{"OCTET_LENGTH", 0, sql_builtin_stub, NULL},
+	{"POSITION", SQL_FUNC_NEEDCOLL, position_func, NULL},
+	{"POWER", 0, sql_builtin_stub, NULL},
+	{"PRINTF", 0, printfFunc, NULL},
+	{"QUOTE", 0, quoteFunc, NULL},
+	{"RANDOM", 0, randomFunc, NULL},
+	{"RANDOMBLOB", 0, randomBlob, NULL},
+	{"REPLACE", SQL_FUNC_DERIVEDCOLL, replaceFunc, NULL},
+	{"ROUND", 0, roundFunc, NULL},
+	{"ROW_COUNT", 0, sql_row_count, NULL},
+	{"SOME", 0, sql_builtin_stub, NULL},
+	{"SOUNDEX", 0, soundexFunc, NULL},
+	{"SQRT", 0, sql_builtin_stub, NULL},
+	{"STRFTIME", 0, sql_builtin_stub, NULL},
+	{"SUBSTR", SQL_FUNC_DERIVEDCOLL, substrFunc, NULL},
+	{"SUM", 0, sum_step, sumFinalize},
+	{"TIME", 0, sql_builtin_stub, NULL},
+	{"TOTAL", 0, sum_step, totalFinalize},
+	{"TRIM", SQL_FUNC_DERIVEDCOLL, trim_func, NULL},
+	{"TYPEOF", SQL_FUNC_TYPEOF, typeofFunc, NULL},
+	{"UNICODE", 0, unicodeFunc, NULL},
+	{"UNLIKELY", SQL_FUNC_UNLIKELY, sql_builtin_stub, NULL},
+	{"UPPER", SQL_FUNC_DERIVEDCOLL | SQL_FUNC_NEEDCOLL, UpperICUFunc, NULL},
+	{"VERSION", 0, sql_func_version, NULL},
+	{"ZEROBLOB", 0, zeroblobFunc, NULL},
+	{"_sql_stat_get", 0, sql_builtin_stub, NULL},
+	{"_sql_stat_init", 0, sql_builtin_stub, NULL},
+	{"_sql_stat_push", 0, sql_builtin_stub, NULL},
 };
 
 static struct func_vtab func_sql_builtin_vtab;
-- 
2.25.1

^ permalink raw reply	[flat|nested] 8+ messages in thread

end of thread, other threads:[~2020-08-12 15:15 UTC | newest]

Thread overview: 8+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-08-12 15:15 [Tarantool-patches] [PATCH v1 0/7] sql: properly check arguments types of built-in functions imeevma
2020-08-12 15:15 ` [Tarantool-patches] [PATCH v1 1/7] box: add has_vararg option for functions imeevma
2020-08-12 15:15 ` [Tarantool-patches] [PATCH v1 2/7] sql: do not return UNSIGNED in built-in functions imeevma
2020-08-12 15:15 ` [Tarantool-patches] [PATCH v1 3/7] sql: move built-in function definitions in _func imeevma
2020-08-12 15:15 ` [Tarantool-patches] [PATCH v1 4/7] box: add param_list to 'struct func' imeevma
2020-08-12 15:15 ` [Tarantool-patches] [PATCH v1 5/7] sql: check built-in functions argument types imeevma
2020-08-12 15:15 ` [Tarantool-patches] [PATCH v1 6/7] sql: VARBINARY and STRING in built-in functions imeevma
2020-08-12 15:15 ` [Tarantool-patches] [PATCH v1 7/7] sql: refactor sql/func.c imeevma

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox