[Tarantool-patches] [PATCH v5 03/17] sql: use ApplyType to check function arguments

imeevma at tarantool.org imeevma at tarantool.org
Tue Jul 14 18:48:18 MSK 2020


Hi! Thank you for the review. My answers and new patch below.


On 13.07.2020 17:56, Nikita Pettik wrote:
> On 13 Jul 08:33, imeevma at tarantool.org wrote:
>> After this patch, the function arguments will be checked using ApplyType
>> opcode before they are passed to the function. This means that the rules
>> for implicitly casting values ​​that were specified as arguments are
>> defined by this opcode.
>>
>> Closes #4159
>> ---
>>  src/box/sql/expr.c                          |    5 +
>>  src/box/sql/func.c                          |  360 +++-
>>  src/box/sql/select.c                        |   31 +
>>  src/box/sql/sqlInt.h                        |   32 +
>>  src/box/sql/vdbe.c                          |   12 +-
>>  test/sql-tap/cse.test.lua                   |    8 +-
>>  test/sql-tap/func.test.lua                  |   48 +-
>>  test/sql-tap/orderby1.test.lua              |    2 +-
>>  test/sql-tap/position.test.lua              |    6 +-
>>  test/sql/boolean.result                     |   32 +-
>>  test/sql/gh-4159-function-argumens.result   | 2131 +++++++++++++++++--
>>  test/sql/gh-4159-function-argumens.test.sql |  272 +++
>>  test/sql/types.result                       |   76 +-
>>  test/sql/types.test.lua                     |    2 +-
>>  14 files changed, 2584 insertions(+), 433 deletions(-)
>>
>> diff --git a/src/box/sql/expr.c b/src/box/sql/expr.c
>> index 7aee240a3..aa5477c6a 100644
>> --- a/src/box/sql/expr.c
>> +++ b/src/box/sql/expr.c
>> @@ -4104,6 +4104,11 @@ sqlExprCodeTarget(Parse * pParse, Expr * pExpr, int target)
>>  			} else {
>>  				r1 = 0;
>>  			}
>> +			if (func->def->language == FUNC_LANGUAGE_SQL_BUILTIN) {
>> +				struct func_sql_builtin *f =
>> +					(struct func_sql_builtin *)func;
>> +				sql_emit_func_types(v, &f->args, 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/func.c b/src/box/sql/func.c
>> index 9d4c26081..66edc3792 100644
>> --- a/src/box/sql/func.c
>> +++ b/src/box/sql/func.c
>> @@ -459,17 +459,10 @@ 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:{
>> +	case MP_BIN: {
>
> Let's get rid of switch-cases, they are really redundant now.
> If you are afraid that diff will become big enough, you can
> refactor each function in a separate patch.
>
Done, in next patches.

>> @@ -1550,6 +1514,15 @@ replaceFunc(sql_context * context, int argc, sql_value ** argv)
>>  
>>  	assert(argc == 3);
>>  	UNUSED_PARAMETER(argc);
>> +	assert(sql_value_type(argv[0]) == MP_NIL ||
>> +	       sql_value_type(argv[0]) == MP_STR ||
>> +	       sql_value_type(argv[0]) == MP_BIN);
>
> Let's evaluate each type of argv only once.
>
Fixed, in next patches.

>> +	assert(sql_value_type(argv[1]) == MP_NIL ||
>> +	       sql_value_type(argv[1]) == MP_STR ||
>> +	       sql_value_type(argv[1]) == MP_BIN);
>> +	assert(sql_value_type(argv[2]) == MP_NIL ||
>> +	       sql_value_type(argv[2]) == MP_STR ||
>> +	       sql_value_type(argv[2]) == MP_BIN);
>>  	zStr = sql_value_text(argv[0]);
>>  	if (zStr == 0)
>>  		return;
>> @@ -1858,13 +1831,9 @@ soundexFunc(sql_context * context, int argc, sql_value ** argv)
>>  		1, 2, 6, 2, 3, 0, 1, 0, 2, 0, 2, 0, 0, 0, 0, 0,
>>  	};
>>  	assert(argc == 1);
>> -	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]), "text");
>> -		context->is_aborted = true;
>> -		return;
>> -	}
>> +	assert(sql_value_type(argv[0]) == MP_NIL ||
>> +	       sql_value_type(argv[0]) == MP_STR ||
>> +	       sql_value_type(argv[0]) == MP_BIN);
>
> Ditto
>
>>  	zIn = (u8 *) sql_value_text(argv[0]);
>>  	if (zIn == 0)
>>  		zIn = (u8 *) "";
>>  	int param_count;
>>  	uint32_t min_count;
>>  	uint32_t max_count;
>> +	enum field_type *types;
>> +	enum field_type recurrent_type;
>> +	bool is_blob_like_str;
>
> What are these members supposed to mean?
>
Added comments.
>>  	enum field_type returns;
>>  	enum func_aggregate aggregate;
>>  	bool export_to_sql;
>> diff --git a/src/box/sql/select.c b/src/box/sql/select.c
>> index 4b069addb..fe56ede1b 100644
>> --- a/src/box/sql/select.c
>> +++ b/src/box/sql/select.c
>> @@ -124,6 +124,34 @@ clearSelect(sql * db, Select * p, int bFree)
>>  	}
>>  }
>>  
>> +void
>> +sql_emit_func_types(struct Vdbe *vdbe, struct sql_builtin_func_args *args,
>> +		    int reg, uint32_t argc)
>
> -> sql_emit_func_arg_type_check()
>
Thanks, fixed.

>> +{
>> +	assert(argc <= args->max_count);
>> +	enum field_type recurrent_type = args->recurrent_type;
>> +	assert(args->max_count > 0 || recurrent_type == FIELD_TYPE_ANY);
>> +	/*
>> +	 * It makes no sense to check types of the MEMs if all
>> +	 * arguments should be of type ANY.
>> +	 */
>> +	if (recurrent_type == FIELD_TYPE_ANY)
>> +		return;
>> +	size_t size = (argc + 1) * sizeof(enum field_type);
>> +	enum field_type *types = sqlDbMallocZero(sql_get(), size);
>> +	for (uint32_t i = 0; i < argc; ++i) {
>> +		if (args->types == NULL)
>> +			types[i] = args->recurrent_type;
>> +		else
>> +			types[i] = args->types[i];
>> +	}
>
> I don't understand what's going on here.
>
Changed this part of the function. Added comments.

>> +	types[argc] = field_type_MAX;
>> +	sqlVdbeAddOp4(vdbe, OP_ApplyType, reg, argc, 0, (char *)types,
>> +		      P4_DYNAMIC);
>> +	if (args->is_blob_like_str)
>> +		sqlVdbeChangeP5(vdbe, OPFLAG_BLOB_LIKE_STRING);
>> +}
>> +
>>  /*
>>   * Initialize a SelectDest structure.
>>   */
>>  void
>>  sql_emit_table_types(struct Vdbe *v, struct space_def *def, int reg);
>>  
>> +/**
>> + * Code an OP_ApplyType opcode that will force types for given
>
> Not force, but try to cast implicitly. Force cast is implemented as CAST
> operator.
>
Thanks, fixed.

>> + * range of register starting from @a reg. These values then will
>> + * be used as arguments of a function.
>> + *
>> + * @param vdbe VDBE.
>> + * @param args Information about arguments of the function.
>> + * @param reg Register where types will be placed.
>> + * @param argc Number of arguments.
>> + */
>> +void
>> +sql_emit_func_types(struct Vdbe *vdbe, struct sql_builtin_func_args *args,
>> +		    int reg, uint32_t argc);
>> +
>>  enum field_type
>>  sql_type_result(enum field_type lhs, enum field_type rhs);
>>  
>> @@ -4406,6 +4424,20 @@ struct sql_builtin_func_args {
>>  	uint32_t min_count;
>>  	/** Max number of arguments. */
>>  	uint32_t max_count;
>> +	/**
>> +	 * If function arguments may not be of the same type, all
>> +	 * argument types are described here.
>> +	 */
>> +	enum field_type *types;
>> +	/**
>> +	 * Contains the type of arguments if all arguments to the
>> +	 * function are of the same type.
>> +	 */
>> +	enum field_type recurrent_type;
>
> Recurrent is quite unsuitable name. Why not always use array
> of type? Is it big deal to fill and store it?
>
Fixed.

>> +	/**
>> +	 * TRUE if the function should treat the BLOB as STRING.
>> +	 */
>> +	bool is_blob_like_str;
>
> Why do you need this attribute?
>
>>  };
>>  
>>  struct func_sql_builtin {
>> diff --git a/src/box/sql/vdbe.c b/src/box/sql/vdbe.c
>> index 863f38f5d..a4bf84bcc 100644
>> --- a/src/box/sql/vdbe.c
>> +++ b/src/box/sql/vdbe.c
>> @@ -2882,7 +2882,7 @@ case OP_Fetch: {
>>  	break;
>>  }
>>  
>> -/* Opcode: ApplyType P1 P2 * P4 *
>> +/* Opcode: ApplyType P1 P2 * P4 P5
>>   * Synopsis: type(r[P1 at P2])
>>   *
>>   * Check that types of P2 registers starting from register P1 are
>> @@ -2890,6 +2890,9 @@ case OP_Fetch: {
>>   * value and the given type are incompatible according to
>>   * field_mp_plain_type_is_compatible(), but both are numeric,
>>   * this opcode attempts to convert the value to the type.
>> + *
>> + * If P5 contains the OPFLAG_BLOB_LIKE_STRING flag, the BLOB
>> + * values ​​are processed as if they had the field type STRING.
>
> I see this unprintable symbols for the third time. Did you review
> your own patch?
>
I look at them in 'less', which is the default for 'git diff'.
Also in Sublime Text sometimes. But these characters are not shown
there. I will try to fix this issue in general.

>
>>  case OP_ApplyType: {
>>  	enum field_type *types = pOp->p4.types;
>> @@ -2904,6 +2907,13 @@ case OP_ApplyType: {
>>  			pIn1++;
>>  			continue;
>>  		}
>
> Need more info for this chunk.
>
Added a comment.

>> +		if ((pOp->p5 & OPFLAG_BLOB_LIKE_STRING) != 0) {
>> +			if (type == FIELD_TYPE_STRING &&
>> +			    mem_mp_type(pIn1) == MP_BIN) {
>> +				pIn1++;
>> +				continue;
>> +			}
>> +		}
>>  		if (!mp_type_is_numeric(mem_mp_type(pIn1)) ||
>>  		    !sql_type_is_numeric(type) ||
>>  		    mem_convert_to_numeric(pIn1, type, false) != 0) {
>> diff --git a/test/sql-tap/cse.test.lua b/test/sql-tap/cse.test.lua
>> index 341b6de01..3c2076a1d 100755
>> --- a/test/sql-tap/cse.test.lua
>> +++ b/test/sql-tap/cse.test.lua
>> @@ -195,23 +195,23 @@ test:do_execsql_test(
>>  
>>  
>>  
>> -test:do_execsql_test(
>> +test:do_catchsql_test(
>>      "cse-1.13",
>>      [[
>>          SELECT upper(b), typeof(b), b FROM t1
>>      ]], {
>>          -- <cse-1.13>
>> -        "11", "integer", 11, "21", "integer", 21
>> +        1, "Type mismatch: can not convert 11 to string"
>>          -- </cse-1.13>
>>      })
>
> DId not look at test changes yet. Please, elaborate on notes above
> firstly.
>
>>  



New patch:

>From dab7a97dbf0a9e498b24e8baeb5ff6c1338a5e43 Mon Sep 17 00:00:00 2001
From: Mergen Imeev <imeevma at gmail.com>
Date: Mon, 13 Jul 2020 19:50:34 +0300
Subject: [PATCH] sql: use ApplyType to check function arguments

This patch allows us to use the ApplyType opcode to check the type of
function arguments. This allows us not to fix the implicit cast inside
functions every time the implicit cast rules change, for example, due to
the addition of a new type.

Part of #4159

diff --git a/src/box/sql/expr.c b/src/box/sql/expr.c
index bc2182446..0c810b73b 100644
--- a/src/box/sql/expr.c
+++ b/src/box/sql/expr.c
@@ -4120,6 +4120,8 @@ 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/func.c b/src/box/sql/func.c
index 487cdafe1..511061b50 100644
--- a/src/box/sql/func.c
+++ b/src/box/sql/func.c
@@ -2230,12 +2230,21 @@ static struct {
 	/** Members below are related to struct func_def. */
 	bool is_deterministic;
 	int param_count;
+	/** Field type of the first argument. */
+	enum field_type first_arg;
+	/** Field type of all arguments except the first. */
+	enum field_type args;
+	/** TRUE if the function treats the BLOB as STRING. */
+	bool is_blob_like_str;
 	enum field_type returns;
 	enum func_aggregate aggregate;
 	bool export_to_sql;
 } sql_builtins[] = {
 	{.name = "ABS",
 	 .param_count = 1,
+	 .first_arg = FIELD_TYPE_ANY,
+	 .args = FIELD_TYPE_ANY,
+	 .is_blob_like_str = false,
 	 .returns = FIELD_TYPE_NUMBER,
 	 .aggregate = FUNC_AGGREGATE_NONE,
 	 .is_deterministic = true,
@@ -2246,6 +2255,9 @@ static struct {
 	}, {
 	 .name = "AVG",
 	 .param_count = 1,
+	 .first_arg = FIELD_TYPE_ANY,
+	 .args = FIELD_TYPE_ANY,
+	 .is_blob_like_str = false,
 	 .returns = FIELD_TYPE_NUMBER,
 	 .is_deterministic = false,
 	 .aggregate = FUNC_AGGREGATE_GROUP,
@@ -2258,6 +2270,9 @@ static struct {
 	 .call = sql_builtin_stub,
 	 .export_to_sql = false,
 	 .param_count = -1,
+	 .first_arg = FIELD_TYPE_ANY,
+	 .args = FIELD_TYPE_ANY,
+	 .is_blob_like_str = false,
 	 .returns = FIELD_TYPE_ANY,
 	 .aggregate = FUNC_AGGREGATE_NONE,
 	 .is_deterministic = false,
@@ -2268,6 +2283,9 @@ static struct {
 	 .call = sql_builtin_stub,
 	 .export_to_sql = false,
 	 .param_count = -1,
+	 .first_arg = FIELD_TYPE_ANY,
+	 .args = FIELD_TYPE_ANY,
+	 .is_blob_like_str = false,
 	 .returns = FIELD_TYPE_ANY,
 	 .aggregate = FUNC_AGGREGATE_NONE,
 	 .is_deterministic = false,
@@ -2276,6 +2294,9 @@ static struct {
 	}, {
 	 .name = "CHAR",
 	 .param_count = -1,
+	 .first_arg = FIELD_TYPE_ANY,
+	 .args = FIELD_TYPE_ANY,
+	 .is_blob_like_str = false,
 	 .returns = FIELD_TYPE_STRING,
 	 .is_deterministic = true,
 	 .aggregate = FUNC_AGGREGATE_NONE,
@@ -2286,6 +2307,9 @@ static struct {
 	 }, {
 	 .name = "CHARACTER_LENGTH",
 	 .param_count = 1,
+	 .first_arg = FIELD_TYPE_ANY,
+	 .args = FIELD_TYPE_ANY,
+	 .is_blob_like_str = false,
 	 .returns = FIELD_TYPE_INTEGER,
 	 .aggregate = FUNC_AGGREGATE_NONE,
 	 .is_deterministic = true,
@@ -2296,6 +2320,9 @@ static struct {
 	}, {
 	 .name = "CHAR_LENGTH",
 	 .param_count = 1,
+	 .first_arg = FIELD_TYPE_ANY,
+	 .args = FIELD_TYPE_ANY,
+	 .is_blob_like_str = false,
 	 .returns = FIELD_TYPE_INTEGER,
 	 .aggregate = FUNC_AGGREGATE_NONE,
 	 .is_deterministic = true,
@@ -2306,6 +2333,9 @@ static struct {
 	}, {
 	 .name = "COALESCE",
 	 .param_count = -1,
+	 .first_arg = FIELD_TYPE_ANY,
+	 .args = FIELD_TYPE_ANY,
+	 .is_blob_like_str = false,
 	 .returns = FIELD_TYPE_SCALAR,
 	 .aggregate = FUNC_AGGREGATE_NONE,
 	 .is_deterministic = true,
@@ -2316,6 +2346,9 @@ static struct {
 	}, {
 	 .name = "COUNT",
 	 .param_count = -1,
+	 .first_arg = FIELD_TYPE_ANY,
+	 .args = FIELD_TYPE_ANY,
+	 .is_blob_like_str = false,
 	 .returns = FIELD_TYPE_INTEGER,
 	 .aggregate = FUNC_AGGREGATE_GROUP,
 	 .is_deterministic = false,
@@ -2328,6 +2361,9 @@ static struct {
 	 .call = sql_builtin_stub,
 	 .export_to_sql = false,
 	 .param_count = -1,
+	 .first_arg = FIELD_TYPE_ANY,
+	 .args = FIELD_TYPE_ANY,
+	 .is_blob_like_str = false,
 	 .returns = FIELD_TYPE_ANY,
 	 .aggregate = FUNC_AGGREGATE_NONE,
 	 .is_deterministic = false,
@@ -2338,6 +2374,9 @@ static struct {
 	 .call = sql_builtin_stub,
 	 .export_to_sql = false,
 	 .param_count = -1,
+	 .first_arg = FIELD_TYPE_ANY,
+	 .args = FIELD_TYPE_ANY,
+	 .is_blob_like_str = false,
 	 .returns = FIELD_TYPE_ANY,
 	 .aggregate = FUNC_AGGREGATE_NONE,
 	 .is_deterministic = false,
@@ -2348,6 +2387,9 @@ static struct {
 	 .call = sql_builtin_stub,
 	 .export_to_sql = false,
 	 .param_count = -1,
+	 .first_arg = FIELD_TYPE_ANY,
+	 .args = FIELD_TYPE_ANY,
+	 .is_blob_like_str = false,
 	 .returns = FIELD_TYPE_ANY,
 	 .aggregate = FUNC_AGGREGATE_NONE,
 	 .is_deterministic = false,
@@ -2358,6 +2400,9 @@ static struct {
 	 .call = sql_builtin_stub,
 	 .export_to_sql = false,
 	 .param_count = -1,
+	 .first_arg = FIELD_TYPE_ANY,
+	 .args = FIELD_TYPE_ANY,
+	 .is_blob_like_str = false,
 	 .returns = FIELD_TYPE_ANY,
 	 .aggregate = FUNC_AGGREGATE_NONE,
 	 .is_deterministic = false,
@@ -2368,6 +2413,9 @@ static struct {
 	 .call = sql_builtin_stub,
 	 .export_to_sql = false,
 	 .param_count = -1,
+	 .first_arg = FIELD_TYPE_ANY,
+	 .args = FIELD_TYPE_ANY,
+	 .is_blob_like_str = false,
 	 .returns = FIELD_TYPE_ANY,
 	 .aggregate = FUNC_AGGREGATE_NONE,
 	 .is_deterministic = false,
@@ -2378,6 +2426,9 @@ static struct {
 	 .call = sql_builtin_stub,
 	 .export_to_sql = false,
 	 .param_count = -1,
+	 .first_arg = FIELD_TYPE_ANY,
+	 .args = FIELD_TYPE_ANY,
+	 .is_blob_like_str = false,
 	 .returns = FIELD_TYPE_ANY,
 	 .aggregate = FUNC_AGGREGATE_NONE,
 	 .is_deterministic = false,
@@ -2388,6 +2439,9 @@ static struct {
 	 .call = sql_builtin_stub,
 	 .export_to_sql = false,
 	 .param_count = -1,
+	 .first_arg = FIELD_TYPE_ANY,
+	 .args = FIELD_TYPE_ANY,
+	 .is_blob_like_str = false,
 	 .returns = FIELD_TYPE_ANY,
 	 .aggregate = FUNC_AGGREGATE_NONE,
 	 .is_deterministic = false,
@@ -2398,6 +2452,9 @@ static struct {
 	 .call = sql_builtin_stub,
 	 .export_to_sql = false,
 	 .param_count = -1,
+	 .first_arg = FIELD_TYPE_ANY,
+	 .args = FIELD_TYPE_ANY,
+	 .is_blob_like_str = false,
 	 .returns = FIELD_TYPE_ANY,
 	 .aggregate = FUNC_AGGREGATE_NONE,
 	 .is_deterministic = false,
@@ -2408,6 +2465,9 @@ static struct {
 	 .call = sql_builtin_stub,
 	 .export_to_sql = false,
 	 .param_count = -1,
+	 .first_arg = FIELD_TYPE_ANY,
+	 .args = FIELD_TYPE_ANY,
+	 .is_blob_like_str = false,
 	 .returns = FIELD_TYPE_ANY,
 	 .aggregate = FUNC_AGGREGATE_NONE,
 	 .is_deterministic = false,
@@ -2418,6 +2478,9 @@ static struct {
 	 .call = sql_builtin_stub,
 	 .export_to_sql = false,
 	 .param_count = -1,
+	 .first_arg = FIELD_TYPE_ANY,
+	 .args = FIELD_TYPE_ANY,
+	 .is_blob_like_str = false,
 	 .returns = FIELD_TYPE_ANY,
 	 .aggregate = FUNC_AGGREGATE_NONE,
 	 .is_deterministic = false,
@@ -2428,6 +2491,9 @@ static struct {
 	 .call = sql_builtin_stub,
 	 .export_to_sql = false,
 	 .param_count = -1,
+	 .first_arg = FIELD_TYPE_ANY,
+	 .args = FIELD_TYPE_ANY,
+	 .is_blob_like_str = false,
 	 .returns = FIELD_TYPE_ANY,
 	 .aggregate = FUNC_AGGREGATE_NONE,
 	 .is_deterministic = false,
@@ -2436,6 +2502,9 @@ static struct {
 	}, {
 	 .name = "GREATEST",
 	 .param_count = -1,
+	 .first_arg = FIELD_TYPE_ANY,
+	 .args = FIELD_TYPE_ANY,
+	 .is_blob_like_str = false,
 	 .returns = FIELD_TYPE_SCALAR,
 	 .aggregate = FUNC_AGGREGATE_NONE,
 	 .is_deterministic = true,
@@ -2446,6 +2515,9 @@ static struct {
 	}, {
 	 .name = "GROUP_CONCAT",
 	 .param_count = -1,
+	 .first_arg = FIELD_TYPE_ANY,
+	 .args = FIELD_TYPE_ANY,
+	 .is_blob_like_str = false,
 	 .returns = FIELD_TYPE_STRING,
 	 .aggregate = FUNC_AGGREGATE_GROUP,
 	 .is_deterministic = false,
@@ -2456,6 +2528,9 @@ static struct {
 	}, {
 	 .name = "HEX",
 	 .param_count = 1,
+	 .first_arg = FIELD_TYPE_ANY,
+	 .args = FIELD_TYPE_ANY,
+	 .is_blob_like_str = false,
 	 .returns = FIELD_TYPE_STRING,
 	 .aggregate = FUNC_AGGREGATE_NONE,
 	 .is_deterministic = true,
@@ -2466,6 +2541,9 @@ static struct {
 	}, {
 	 .name = "IFNULL",
 	 .param_count = 2,
+	 .first_arg = FIELD_TYPE_ANY,
+	 .args = FIELD_TYPE_ANY,
+	 .is_blob_like_str = false,
 	 .returns = FIELD_TYPE_INTEGER,
 	 .aggregate = FUNC_AGGREGATE_NONE,
 	 .is_deterministic = true,
@@ -2478,6 +2556,9 @@ static struct {
 	 .call = sql_builtin_stub,
 	 .export_to_sql = false,
 	 .param_count = -1,
+	 .first_arg = FIELD_TYPE_ANY,
+	 .args = FIELD_TYPE_ANY,
+	 .is_blob_like_str = false,
 	 .returns = FIELD_TYPE_ANY,
 	 .aggregate = FUNC_AGGREGATE_NONE,
 	 .is_deterministic = false,
@@ -2486,6 +2567,9 @@ static struct {
 	}, {
 	 .name = "LEAST",
 	 .param_count = -1,
+	 .first_arg = FIELD_TYPE_ANY,
+	 .args = FIELD_TYPE_ANY,
+	 .is_blob_like_str = false,
 	 .returns = FIELD_TYPE_SCALAR,
 	 .aggregate = FUNC_AGGREGATE_NONE,
 	 .is_deterministic = true,
@@ -2496,6 +2580,9 @@ static struct {
 	}, {
 	 .name = "LENGTH",
 	 .param_count = 1,
+	 .first_arg = FIELD_TYPE_ANY,
+	 .args = FIELD_TYPE_ANY,
+	 .is_blob_like_str = false,
 	 .returns = FIELD_TYPE_INTEGER,
 	 .aggregate = FUNC_AGGREGATE_NONE,
 	 .is_deterministic = true,
@@ -2508,6 +2595,9 @@ static struct {
 	 .call = sql_builtin_stub,
 	 .export_to_sql = false,
 	 .param_count = -1,
+	 .first_arg = FIELD_TYPE_ANY,
+	 .args = FIELD_TYPE_ANY,
+	 .is_blob_like_str = false,
 	 .returns = FIELD_TYPE_ANY,
 	 .aggregate = FUNC_AGGREGATE_NONE,
 	 .is_deterministic = false,
@@ -2516,6 +2606,9 @@ static struct {
 	}, {
 	 .name = "LIKE",
 	 .param_count = -1,
+	 .first_arg = FIELD_TYPE_ANY,
+	 .args = FIELD_TYPE_ANY,
+	 .is_blob_like_str = false,
 	 .returns = FIELD_TYPE_INTEGER,
 	 .aggregate = FUNC_AGGREGATE_NONE,
 	 .is_deterministic = true,
@@ -2526,6 +2619,9 @@ static struct {
 	}, {
 	 .name = "LIKELIHOOD",
 	 .param_count = 2,
+	 .first_arg = FIELD_TYPE_ANY,
+	 .args = FIELD_TYPE_ANY,
+	 .is_blob_like_str = false,
 	 .returns = FIELD_TYPE_BOOLEAN,
 	 .aggregate = FUNC_AGGREGATE_NONE,
 	 .is_deterministic = true,
@@ -2536,6 +2632,9 @@ static struct {
 	}, {
 	 .name = "LIKELY",
 	 .param_count = 1,
+	 .first_arg = FIELD_TYPE_ANY,
+	 .args = FIELD_TYPE_ANY,
+	 .is_blob_like_str = false,
 	 .returns = FIELD_TYPE_BOOLEAN,
 	 .aggregate = FUNC_AGGREGATE_NONE,
 	 .is_deterministic = true,
@@ -2548,6 +2647,9 @@ static struct {
 	 .call = sql_builtin_stub,
 	 .export_to_sql = false,
 	 .param_count = -1,
+	 .first_arg = FIELD_TYPE_ANY,
+	 .args = FIELD_TYPE_ANY,
+	 .is_blob_like_str = false,
 	 .returns = FIELD_TYPE_ANY,
 	 .aggregate = FUNC_AGGREGATE_NONE,
 	 .is_deterministic = false,
@@ -2556,6 +2658,9 @@ static struct {
 	}, {
 	 .name = "LOWER",
 	 .param_count = 1,
+	 .first_arg = FIELD_TYPE_ANY,
+	 .args = FIELD_TYPE_ANY,
+	 .is_blob_like_str = false,
 	 .returns = FIELD_TYPE_STRING,
 	 .aggregate = FUNC_AGGREGATE_NONE,
 	 .is_deterministic = true,
@@ -2566,6 +2671,9 @@ static struct {
 	}, {
 	 .name = "MAX",
 	 .param_count = 1,
+	 .first_arg = FIELD_TYPE_ANY,
+	 .args = FIELD_TYPE_ANY,
+	 .is_blob_like_str = false,
 	 .returns = FIELD_TYPE_SCALAR,
 	 .aggregate = FUNC_AGGREGATE_GROUP,
 	 .is_deterministic = false,
@@ -2576,6 +2684,9 @@ static struct {
 	}, {
 	 .name = "MIN",
 	 .param_count = 1,
+	 .first_arg = FIELD_TYPE_ANY,
+	 .args = FIELD_TYPE_ANY,
+	 .is_blob_like_str = false,
 	 .returns = FIELD_TYPE_SCALAR,
 	 .aggregate = FUNC_AGGREGATE_GROUP,
 	 .is_deterministic = false,
@@ -2588,6 +2699,9 @@ static struct {
 	 .call = sql_builtin_stub,
 	 .export_to_sql = false,
 	 .param_count = -1,
+	 .first_arg = FIELD_TYPE_ANY,
+	 .args = FIELD_TYPE_ANY,
+	 .is_blob_like_str = false,
 	 .returns = FIELD_TYPE_ANY,
 	 .aggregate = FUNC_AGGREGATE_NONE,
 	 .is_deterministic = false,
@@ -2596,6 +2710,9 @@ static struct {
 	}, {
 	 .name = "NULLIF",
 	 .param_count = 2,
+	 .first_arg = FIELD_TYPE_ANY,
+	 .args = FIELD_TYPE_ANY,
+	 .is_blob_like_str = false,
 	 .returns = FIELD_TYPE_SCALAR,
 	 .aggregate = FUNC_AGGREGATE_NONE,
 	 .is_deterministic = true,
@@ -2608,6 +2725,9 @@ static struct {
 	 .call = sql_builtin_stub,
 	 .export_to_sql = false,
 	 .param_count = -1,
+	 .first_arg = FIELD_TYPE_ANY,
+	 .args = FIELD_TYPE_ANY,
+	 .is_blob_like_str = false,
 	 .returns = FIELD_TYPE_ANY,
 	 .aggregate = FUNC_AGGREGATE_NONE,
 	 .is_deterministic = false,
@@ -2616,6 +2736,9 @@ static struct {
 	}, {
 	 .name = "POSITION",
 	 .param_count = 2,
+	 .first_arg = FIELD_TYPE_ANY,
+	 .args = FIELD_TYPE_ANY,
+	 .is_blob_like_str = false,
 	 .returns = FIELD_TYPE_INTEGER,
 	 .aggregate = FUNC_AGGREGATE_NONE,
 	 .is_deterministic = true,
@@ -2628,6 +2751,9 @@ static struct {
 	 .call = sql_builtin_stub,
 	 .export_to_sql = false,
 	 .param_count = -1,
+	 .first_arg = FIELD_TYPE_ANY,
+	 .args = FIELD_TYPE_ANY,
+	 .is_blob_like_str = false,
 	 .returns = FIELD_TYPE_ANY,
 	 .aggregate = FUNC_AGGREGATE_NONE,
 	 .is_deterministic = false,
@@ -2636,6 +2762,9 @@ static struct {
 	}, {
 	 .name = "PRINTF",
 	 .param_count = -1,
+	 .first_arg = FIELD_TYPE_ANY,
+	 .args = FIELD_TYPE_ANY,
+	 .is_blob_like_str = false,
 	 .returns = FIELD_TYPE_STRING,
 	 .aggregate = FUNC_AGGREGATE_NONE,
 	 .is_deterministic = true,
@@ -2646,6 +2775,9 @@ static struct {
 	}, {
 	 .name = "QUOTE",
 	 .param_count = 1,
+	 .first_arg = FIELD_TYPE_ANY,
+	 .args = FIELD_TYPE_ANY,
+	 .is_blob_like_str = false,
 	 .returns = FIELD_TYPE_STRING,
 	 .aggregate = FUNC_AGGREGATE_NONE,
 	 .is_deterministic = true,
@@ -2656,6 +2788,9 @@ static struct {
 	}, {
 	 .name = "RANDOM",
 	 .param_count = 0,
+	 .first_arg = FIELD_TYPE_ANY,
+	 .args = FIELD_TYPE_ANY,
+	 .is_blob_like_str = false,
 	 .returns = FIELD_TYPE_INTEGER,
 	 .aggregate = FUNC_AGGREGATE_NONE,
 	 .is_deterministic = false,
@@ -2666,6 +2801,9 @@ static struct {
 	}, {
 	 .name = "RANDOMBLOB",
 	 .param_count = 1,
+	 .first_arg = FIELD_TYPE_ANY,
+	 .args = FIELD_TYPE_ANY,
+	 .is_blob_like_str = false,
 	 .returns = FIELD_TYPE_VARBINARY,
 	 .aggregate = FUNC_AGGREGATE_NONE,
 	 .is_deterministic = false,
@@ -2676,6 +2814,9 @@ static struct {
 	}, {
 	 .name = "REPLACE",
 	 .param_count = 3,
+	 .first_arg = FIELD_TYPE_ANY,
+	 .args = FIELD_TYPE_ANY,
+	 .is_blob_like_str = false,
 	 .returns = FIELD_TYPE_STRING,
 	 .aggregate = FUNC_AGGREGATE_NONE,
 	 .is_deterministic = true,
@@ -2686,6 +2827,9 @@ static struct {
 	}, {
 	 .name = "ROUND",
 	 .param_count = -1,
+	 .first_arg = FIELD_TYPE_ANY,
+	 .args = FIELD_TYPE_ANY,
+	 .is_blob_like_str = false,
 	 .returns = FIELD_TYPE_INTEGER,
 	 .aggregate = FUNC_AGGREGATE_NONE,
 	 .is_deterministic = true,
@@ -2696,6 +2840,9 @@ static struct {
 	}, {
 	 .name = "ROW_COUNT",
 	 .param_count = 0,
+	 .first_arg = FIELD_TYPE_ANY,
+	 .args = FIELD_TYPE_ANY,
+	 .is_blob_like_str = false,
 	 .returns = FIELD_TYPE_INTEGER,
 	 .aggregate = FUNC_AGGREGATE_NONE,
 	 .is_deterministic = true,
@@ -2708,6 +2855,9 @@ static struct {
 	 .call = sql_builtin_stub,
 	 .export_to_sql = false,
 	 .param_count = -1,
+	 .first_arg = FIELD_TYPE_ANY,
+	 .args = FIELD_TYPE_ANY,
+	 .is_blob_like_str = false,
 	 .returns = FIELD_TYPE_ANY,
 	 .aggregate = FUNC_AGGREGATE_NONE,
 	 .is_deterministic = false,
@@ -2716,6 +2866,9 @@ static struct {
 	}, {
 	 .name = "SOUNDEX",
 	 .param_count = 1,
+	 .first_arg = FIELD_TYPE_ANY,
+	 .args = FIELD_TYPE_ANY,
+	 .is_blob_like_str = false,
 	 .returns = FIELD_TYPE_STRING,
 	 .aggregate = FUNC_AGGREGATE_NONE,
 	 .is_deterministic = true,
@@ -2728,6 +2881,9 @@ static struct {
 	 .call = sql_builtin_stub,
 	 .export_to_sql = false,
 	 .param_count = -1,
+	 .first_arg = FIELD_TYPE_ANY,
+	 .args = FIELD_TYPE_ANY,
+	 .is_blob_like_str = false,
 	 .returns = FIELD_TYPE_ANY,
 	 .aggregate = FUNC_AGGREGATE_NONE,
 	 .is_deterministic = false,
@@ -2738,6 +2894,9 @@ static struct {
 	 .call = sql_builtin_stub,
 	 .export_to_sql = false,
 	 .param_count = -1,
+	 .first_arg = FIELD_TYPE_ANY,
+	 .args = FIELD_TYPE_ANY,
+	 .is_blob_like_str = false,
 	 .returns = FIELD_TYPE_ANY,
 	 .aggregate = FUNC_AGGREGATE_NONE,
 	 .is_deterministic = false,
@@ -2746,6 +2905,9 @@ static struct {
 	}, {
 	 .name = "SUBSTR",
 	 .param_count = -1,
+	 .first_arg = FIELD_TYPE_ANY,
+	 .args = FIELD_TYPE_ANY,
+	 .is_blob_like_str = false,
 	 .returns = FIELD_TYPE_STRING,
 	 .aggregate = FUNC_AGGREGATE_NONE,
 	 .is_deterministic = true,
@@ -2756,6 +2918,9 @@ static struct {
 	}, {
 	 .name = "SUM",
 	 .param_count = 1,
+	 .first_arg = FIELD_TYPE_ANY,
+	 .args = FIELD_TYPE_ANY,
+	 .is_blob_like_str = false,
 	 .returns = FIELD_TYPE_NUMBER,
 	 .aggregate = FUNC_AGGREGATE_GROUP,
 	 .is_deterministic = false,
@@ -2768,6 +2933,9 @@ static struct {
 	 .call = sql_builtin_stub,
 	 .export_to_sql = false,
 	 .param_count = -1,
+	 .first_arg = FIELD_TYPE_ANY,
+	 .args = FIELD_TYPE_ANY,
+	 .is_blob_like_str = false,
 	 .returns = FIELD_TYPE_ANY,
 	 .aggregate = FUNC_AGGREGATE_NONE,
 	 .is_deterministic = false,
@@ -2776,6 +2944,9 @@ static struct {
 	}, {
 	 .name = "TOTAL",
 	 .param_count = 1,
+	 .first_arg = FIELD_TYPE_ANY,
+	 .args = FIELD_TYPE_ANY,
+	 .is_blob_like_str = false,
 	 .returns = FIELD_TYPE_NUMBER,
 	 .aggregate = FUNC_AGGREGATE_GROUP,
 	 .is_deterministic = false,
@@ -2786,6 +2957,9 @@ static struct {
 	}, {
 	 .name = "TRIM",
 	 .param_count = -1,
+	 .first_arg = FIELD_TYPE_ANY,
+	 .args = FIELD_TYPE_ANY,
+	 .is_blob_like_str = false,
 	 .returns = FIELD_TYPE_STRING,
 	 .aggregate = FUNC_AGGREGATE_NONE,
 	 .is_deterministic = true,
@@ -2796,6 +2970,9 @@ static struct {
 	}, {
 	 .name = "TYPEOF",
 	 .param_count = 1,
+	 .first_arg = FIELD_TYPE_ANY,
+	 .args = FIELD_TYPE_ANY,
+	 .is_blob_like_str = false,
 	 .returns = FIELD_TYPE_STRING,
 	 .aggregate = FUNC_AGGREGATE_NONE,
 	 .is_deterministic = true,
@@ -2806,6 +2983,9 @@ static struct {
 	}, {
 	 .name = "UNICODE",
 	 .param_count = 1,
+	 .first_arg = FIELD_TYPE_ANY,
+	 .args = FIELD_TYPE_ANY,
+	 .is_blob_like_str = false,
 	 .returns = FIELD_TYPE_STRING,
 	 .aggregate = FUNC_AGGREGATE_NONE,
 	 .is_deterministic = true,
@@ -2816,6 +2996,9 @@ static struct {
 	}, {
 	 .name = "UNLIKELY",
 	 .param_count = 1,
+	 .first_arg = FIELD_TYPE_ANY,
+	 .args = FIELD_TYPE_ANY,
+	 .is_blob_like_str = false,
 	 .returns = FIELD_TYPE_BOOLEAN,
 	 .aggregate = FUNC_AGGREGATE_NONE,
 	 .is_deterministic = true,
@@ -2826,6 +3009,9 @@ static struct {
 	}, {
 	 .name = "UPPER",
 	 .param_count = 1,
+	 .first_arg = FIELD_TYPE_ANY,
+	 .args = FIELD_TYPE_ANY,
+	 .is_blob_like_str = false,
 	 .returns = FIELD_TYPE_STRING,
 	 .aggregate = FUNC_AGGREGATE_NONE,
 	 .is_deterministic = true,
@@ -2836,6 +3022,9 @@ static struct {
 	}, {
 	 .name = "VERSION",
 	 .param_count = 0,
+	 .first_arg = FIELD_TYPE_ANY,
+	 .args = FIELD_TYPE_ANY,
+	 .is_blob_like_str = false,
 	 .returns = FIELD_TYPE_STRING,
 	 .aggregate = FUNC_AGGREGATE_NONE,
 	 .is_deterministic = true,
@@ -2846,6 +3035,9 @@ static struct {
 	}, {
 	 .name = "ZEROBLOB",
 	 .param_count = 1,
+	 .first_arg = FIELD_TYPE_ANY,
+	 .args = FIELD_TYPE_ANY,
+	 .is_blob_like_str = false,
 	 .returns = FIELD_TYPE_VARBINARY,
 	 .aggregate = FUNC_AGGREGATE_NONE,
 	 .is_deterministic = true,
@@ -2858,6 +3050,9 @@ static struct {
 	 .call = sql_builtin_stub,
 	 .export_to_sql = false,
 	 .param_count = -1,
+	 .first_arg = FIELD_TYPE_ANY,
+	 .args = FIELD_TYPE_ANY,
+	 .is_blob_like_str = false,
 	 .returns = FIELD_TYPE_ANY,
 	 .aggregate = FUNC_AGGREGATE_NONE,
 	 .is_deterministic = false,
@@ -2868,6 +3063,9 @@ static struct {
 	 .call = sql_builtin_stub,
 	 .export_to_sql = false,
 	 .param_count = -1,
+	 .first_arg = FIELD_TYPE_ANY,
+	 .args = FIELD_TYPE_ANY,
+	 .is_blob_like_str = false,
 	 .returns = FIELD_TYPE_ANY,
 	 .aggregate = FUNC_AGGREGATE_NONE,
 	 .is_deterministic = false,
@@ -2878,6 +3076,9 @@ static struct {
 	 .call = sql_builtin_stub,
 	 .export_to_sql = false,
 	 .param_count = -1,
+	 .first_arg = FIELD_TYPE_ANY,
+	 .args = FIELD_TYPE_ANY,
+	 .is_blob_like_str = false,
 	 .returns = FIELD_TYPE_ANY,
 	 .aggregate = FUNC_AGGREGATE_NONE,
 	 .is_deterministic = false,
@@ -2929,6 +3130,9 @@ func_sql_builtin_new(struct func_def *def)
 	func->flags = sql_builtins[idx].flags;
 	func->call = sql_builtins[idx].call;
 	func->finalize = sql_builtins[idx].finalize;
+	func->first_arg = sql_builtins[idx].first_arg;
+	func->args = sql_builtins[idx].args;
+	func->is_blob_like_str = sql_builtins[idx].is_blob_like_str;
 	def->param_count = sql_builtins[idx].param_count;
 	def->is_deterministic = sql_builtins[idx].is_deterministic;
 	def->returns = sql_builtins[idx].returns;
diff --git a/src/box/sql/select.c b/src/box/sql/select.c
index 4b069addb..426247bd7 100644
--- a/src/box/sql/select.c
+++ b/src/box/sql/select.c
@@ -124,6 +124,40 @@ 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)
+		return;
+	struct func_sql_builtin *f = (struct func_sql_builtin *)func;
+	/*
+	 * There is no need to check types of the MEMs if all
+	 * arguments should be of type ANY.
+	 */
+	if (f->first_arg == FIELD_TYPE_ANY && f->args  == FIELD_TYPE_ANY)
+		return;
+	size_t size = (argc + 1) * sizeof(enum field_type);
+	enum field_type *types = sqlDbMallocZero(sql_get(), size);
+	/* Field type of the first argument of the function. */
+	types[0] = f->first_arg;
+	/*
+	 * The field types of all arguments except the first are
+	 * the same.
+	 */
+	for (uint32_t i = 1; i < argc; ++i)
+		types[i] = f->args;
+	types[argc] = field_type_MAX;
+	sqlVdbeAddOp4(vdbe, OP_ApplyType, reg, argc, 0, (char *)types,
+		      P4_DYNAMIC);
+	/*
+	 * If a function treats a BLOB as a STRING, we should
+	 * allow it to get a BLOB in place of a STRING.
+	 */
+	if (f->is_blob_like_str)
+		sqlVdbeChangeP5(vdbe, OPFLAG_BLOB_LIKE_STRING);
+}
+
 /*
  * Initialize a SelectDest structure.
  */
@@ -5414,6 +5448,7 @@ updateAccumulator(Parse * pParse, AggInfo * pAggInfo)
 			sqlVdbeAddOp4(v, OP_CollSeq, regHit, 0, 0,
 					  (char *)coll, P4_COLLSEQ);
 		}
+		sql_emit_func_arg_type_check(v, pF->func, regAgg, nArg);
 		sqlVdbeAddOp3(v, OP_AggStep0, 0, regAgg, pF->iMem);
 		sqlVdbeAppendP4(v, pF->func, P4_FUNC);
 		sqlVdbeChangeP5(v, (u8) nArg);
diff --git a/src/box/sql/sqlInt.h b/src/box/sql/sqlInt.h
index 37283e506..54f1594d0 100644
--- a/src/box/sql/sqlInt.h
+++ b/src/box/sql/sqlInt.h
@@ -2307,6 +2307,8 @@ struct Parse {
 #define OPFLAG_SYSTEMSP      0x20	/* OP_Open**: set if space pointer
 					 * points to system space.
 					 */
+/** OP_ApplyType: Treat BLOB as STRING. */
+#define OPFLAG_BLOB_LIKE_STRING		0x01
 
 /**
  * Prepare vdbe P5 flags for OP_{IdxInsert, IdxReplace, Update}
@@ -3881,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 args Information about arguments 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);
 
@@ -4402,6 +4418,25 @@ struct func_sql_builtin {
 	struct func base;
 	/** A bitmask of SQL flags. */
 	uint16_t flags;
+	/**
+	 * In built-in functions, all arguments except the first
+	 * have the same field type. The first argument may have a
+	 * different field type.
+	 *
+	 * Field type of the first argument.
+	 */
+	enum field_type first_arg;
+	/** Field type of all arguments except the first. */
+	enum field_type args;
+	/**
+	 * Some built-in functions work with BLOB arguments in the
+	 * same way as with STRING arguments. This flag allows us
+	 * to pass BLOB arguments instead of STRING arguments in
+	 * this case.
+	 *
+	 * TRUE if the function treats the BLOB as STRING.
+	 */
+	bool is_blob_like_str;
 	/**
 	 * A VDBE-memory-compatible call method.
 	 * SQL built-ins don't use func base class "call"
diff --git a/src/box/sql/vdbe.c b/src/box/sql/vdbe.c
index 41a4750da..9d25d10b0 100644
--- a/src/box/sql/vdbe.c
+++ b/src/box/sql/vdbe.c
@@ -2882,7 +2882,7 @@ case OP_Fetch: {
 	break;
 }
 
-/* Opcode: ApplyType P1 P2 * P4 *
+/* Opcode: ApplyType P1 P2 * P4 P5
  * Synopsis: type(r[P1 at P2])
  *
  * Check that types of P2 registers starting from register P1 are
@@ -2890,6 +2890,9 @@ case OP_Fetch: {
  * value and the given type are incompatible according to
  * field_mp_plain_type_is_compatible(), but both are numeric,
  * this opcode attempts to convert the value to the type.
+ *
+ * If P5 contains the OPFLAG_BLOB_LIKE_STRING flag, the BLOB
+ * values are processed as if they had the field type STRING.
  */
 case OP_ApplyType: {
 	enum field_type *types = pOp->p4.types;
@@ -2901,6 +2904,19 @@ case OP_ApplyType: {
 		assert(pIn1 <= &p->aMem[(p->nMem+1 - p->nCursor)]);
 		assert(memIsValid(pIn1));
 		if (!mem_is_type_compatible(pIn1, type)) {
+			/*
+			 * If function works with BLOB argument
+			 * the same way it works with STRING
+			 * arguments, we should allow BLOB
+			 * arguments in place of STRING arguments.
+			 */
+			if ((pOp->p5 & OPFLAG_BLOB_LIKE_STRING) != 0) {
+				if (type == FIELD_TYPE_STRING &&
+				    mem_mp_type(pIn1) == MP_BIN) {
+					pIn1++;
+					continue;
+				}
+			}
 			/* Implicit cast is allowed only to numeric type. */
 			if (!sql_type_is_numeric(type))
 				goto type_mismatch;


More information about the Tarantool-patches mailing list