[tarantool-patches] Re: [PATCH 7/8] sql: clean-up affinity from SQL source code

n.pettik korablev at tarantool.org
Wed Jan 16 17:26:32 MSK 2019


>> diff --git a/src/box/sql/expr.c b/src/box/sql/expr.c
>> index 32606dac3..22b64b526 100644
>> --- a/src/box/sql/expr.c
>> +++ b/src/box/sql/expr.c
>> @@ -311,8 +311,7 @@ binaryCompareP5(Expr * pExpr1, Expr * pExpr2, int jumpIfNull)
>>  {
>>  	enum field_type lhs = sql_expr_type(pExpr2);
>>  	enum field_type rhs = sql_expr_type(pExpr1);
>> -	u8 type_mask = sql_field_type_to_affinity(sql_type_result(rhs, lhs)) |
>> -		       (u8) jumpIfNull;
>> +	u8 type_mask = sql_type_result(rhs, lhs) | (u8) jumpIfNull;
> 
> 1. Are you sure that we can | jumpIfNull with enum field_type? Look at
> this code:
> 
> 	#define SQLITE_KEEPNULL     0x08	/* Used by vector == or <> */
> 	#define SQLITE_JUMPIFNULL   0x10	/* jumps if either operand is NULL */
> 	#define SQLITE_STOREP2      0x20	/* Store result in reg[P2] rather than jump */
> 	#define SQLITE_NULLEQ       0x80	/* NULL=NULL */
> 	#define SQLITE_NOTNULL      0x90	/* Assert that operands are never NULL */
> 
> SQLite states that these values can be safely ORed with type, but
> SQLITE_KEEPNULL == FIELD_TYPE_MAP - mess.

Thx, it is really mess. Funny thing - it didn’t break anything :)
Fixed:

diff --git a/src/box/sql/sqliteInt.h b/src/box/sql/sqliteInt.h
index d4425eaa0..822cc40df 100644
--- a/src/box/sql/sqliteInt.h
+++ b/src/box/sql/sqliteInt.h
@@ -1807,7 +1801,7 @@ struct Savepoint {
  * operator is NULL.  It is added to certain comparison operators to
  * prove that the operands are always NOT NULL.
  */
-#define SQLITE_KEEPNULL     0x08       /* Used by vector == or <> */
+#define SQLITE_KEEPNULL     0x40       /* Used by vector == or <> */

> 
>>  	return type_mask;
>>  }
>> diff --git a/src/box/sql/select.c b/src/box/sql/select.c
>> index cc3e2f2fd..f3008094b 100644
>> --- a/src/box/sql/select.c
>> +++ b/src/box/sql/select.c
>> @@ -3067,10 +3061,9 @@ generateOutputSubroutine(struct Parse *parse, struct Select *p,
>>  			int r1;
>>  			testcase(in->nSdst > 1);
>>  			r1 = sqlite3GetTempReg(parse);
>> -			const char *type_str =
>> -				sql_affinity_str_to_field_type_str(dest->zAffSdst);
>>  			sqlite3VdbeAddOp4(v, OP_MakeRecord, in->iSdst,
>> -					  in->nSdst, r1, type_str, P4_DYNAMIC);
>> +					  in->nSdst, r1, dest->zAffSdst,
>> +					  in->nSdst);
> 
> 2. As we learned from the previous patch, p4 here is not
> a length. It is a type of memory - static or dynamic.

In fact, it’s length and type of allocation :)
Anyway, I’ve reworked this place (alongside with other similar to this)
since now we are using array of field types which are not terminated
with \0.

Diff:

diff --git a/src/box/sql/expr.c b/src/box/sql/expr.c
index 07a7349a2..db7ddad06 100644
--- a/src/box/sql/expr.c
+++ b/src/box/sql/expr.c
@@ -106,6 +106,19 @@ sql_expr_type(struct Expr *pExpr)
        return pExpr->type;
 }
 
+enum field_type *
+field_type_sequence_dup(struct Parse *parse, enum field_type *types,
+                       uint32_t len)
+{
+       uint32_t sz = (len + 1) * sizeof(enum field_type);
+       enum field_type *ret_types = sqlite3DbMallocRaw(parse->db, sz);
+       if (ret_types == NULL)
+               return NULL;
+       memcpy(ret_types, types, sz);
+       ret_types[len] = field_type_MAX;
+       return ret_types;
+}
+

diff --git a/src/box/sql/select.c b/src/box/sql/select.c
index f3008094b..92b563e22 100644
--- a/src/box/sql/select.c
+++ b/src/box/sql/select.c
@@ -3061,9 +3064,12 @@ generateOutputSubroutine(struct Parse *parse, struct Select *p,
                        int r1;
                        testcase(in->nSdst > 1);
                        r1 = sqlite3GetTempReg(parse);
+                       enum field_type *types =
+                               field_type_sequence_dup(parse, dest->zAffSdst,
+                                                       in->nSdst);
                        sqlite3VdbeAddOp4(v, OP_MakeRecord, in->iSdst,
-                                         in->nSdst, r1, dest->zAffSdst,
-                                         in->nSdst);
+                                         in->nSdst, r1, (char *)types,
+                                         P4_DYNAMIC);

diff --git a/src/box/sql/sqliteInt.h b/src/box/sql/sqliteInt.h
index 807ca16c6..d4425eaa0 100644
--- a/src/box/sql/sqliteInt.h
+++ b/src/box/sql/sqliteInt.h
@@ -4242,6 +4253,13 @@ sql_index_type_is_ok(struct Expr *expr, enum field_type idx_type);
 enum field_type
 sql_expr_type(Expr *pExpr);
 
+/**
+ * This function duplicates first @len entries of types array
+ * and terminates new array with field_type_MAX member.
+ */
+enum field_type *
+field_type_sequence_dup(struct Parse *parse, enum field_type *types,
+                       uint32_t len);


> 
>>  			sqlite3ExprCacheAffinityChange(parse, in->iSdst,
>>  						       in->nSdst);
>>  			sqlite3VdbeAddOp2(v, OP_IdxInsert, r1, dest->reg_eph);
>> diff --git a/src/box/sql/where.c b/src/box/sql/where.c
>> index ac5390f92..88c45aaa3 100644
>> --- a/src/box/sql/where.c
>> +++ b/src/box/sql/where.c
>> @@ -701,7 +701,6 @@ termCanDriveIndex(WhereTerm * pTerm,	/* WHERE clause term to check */
>>  		return 0;
>>  	if (pTerm->u.leftColumn < 0)
>>  		return 0;
>> -	aff = pSrc->pTab->aCol[pTerm->u.leftColumn].affinity;
>>  	if (!sqlite3IndexAffinityOk(pTerm->pExpr, aff))
> 
> 3. This function does not exist since the previous commit. Looks
> like this code is not even compiled.

Yep, it is dead code under #ifndef SQLITE_OMIT_AUTOMATIC_INDEX
Auto-indexes haven’t been working since January of 2018.
I used IDE auto-refactoring tool and for some reason it missed this place.
Fixed in previous patch.

> 
>>  		return 0;
>>  	return 1;
> 
> 4. AFFINITY_MASK still exists.

Removed:

diff --git a/src/box/sql/vdbe.c b/src/box/sql/vdbe.c
index ef5a03087..c3f596d4f 100644
--- a/src/box/sql/vdbe.c
+++ b/src/box/sql/vdbe.c
@@ -2022,14 +2022,6 @@ case OP_Cast: {                  /* in1 */
  * jump to address P2.  Or if the SQLITE_STOREP2 flag is set in P5, then
  * store the result of comparison in register P2.
  *
- * The AFFINITY_MASK portion of P5 must be an affinity character -
- * AFFINITY_TEXT, AFFINITY_INTEGER, and so forth. An attempt is made
- * to coerce both inputs according to this affinity before the
- * comparison is made. If the AFFINITY_MASK is 0x00, then numeric
- * affinity is used. Note that the affinity conversions are stored
- * back into the input registers P1 and P3.  So this opcode can cause
- * persistent changes to registers P1 and P3.
- *
  * Once any conversions have taken place, and neither value is NULL,
  * the values are compared. If both values are blobs then memcmp() is
  * used to determine the results of the comparison.  If both values
@@ -2045,6 +2037,10 @@ case OP_Cast: {                  /* in1 */
  * of comparison is true.  If either operand is NULL then the result is false.
  * If neither operand is NULL the result is the same as it would be if
  * the SQLITE_NULLEQ flag were omitted from P5.
+ * P5 also can contain type to be applied to operands. Note that
+ * the type conversions are stored back into the input registers
+ * P1 and P3.  So this opcode can cause persistent changes to
+ * registers P1 and P3.
  *
  * If both SQLITE_STOREP2 and SQLITE_KEEPNULL flags are set then the
  * content of r[P2] is only changed if the new value is NULL or 0 (false).
@@ -2072,14 +2068,6 @@ case OP_Cast: {                  /* in1 */
  * reg(P3) is NULL then the take the jump.  If the SQLITE_JUMPIFNULL
  * bit is clear then fall through if either operand is NULL.
  *
- * The AFFINITY_MASK portion of P5 must be an affinity character -
- * AFFINITY_TEXT, AFFINITY_INTEGER, and so forth. An attempt is made
- * to coerce both inputs according to this affinity before the
- * comparison is made. If the AFFINITY_MASK is 0x00, then numeric
- * affinity is used. Note that the affinity conversions are stored
- * back into the input registers P1 and P3.  So this opcode can cause
- * persistent changes to registers P1 and P3.
- *
  * Once any conversions have taken place, and neither value is NULL,
  * the values are compared. If both values are blobs then memcmp() is
  * used to determine the results of the comparison.  If both values
@@ -2163,8 +2151,15 @@ case OP_Ge: {             /* same as TK_GE, jump, in1, in3 */
                        break;
                }
        } else {
-               /* Neither operand is NULL.  Do a comparison. */
-               affinity = pOp->p5 & AFFINITY_MASK;
+               /*
+                * Neither operand is NULL. Do a comparison.
+                * 15 is 1111 in a binary form.
+                * Since all existing types can be fitted in 4 bits
+                * (field_type_MAX == 10), it is enough to 'and'
+                * p5 with this constant. Node that all other flags
+                * that can be stored in p5 are >= 16.
+                */
+               affinity = pOp->p5 & 15;
                if (sql_type_is_numeric(affinity)) {
                        if ((flags1 | flags3)&MEM_Str) {
                                if ((flags1 & (MEM_Int|MEM_Real|MEM_Str))==MEM_Str) {

> 
> 5. wherecode.c and vdbeaux.c still use AFFINITY_BLOB in comments.

Cleaned:

diff --git a/src/box/sql/vdbeaux.c b/src/box/sql/vdbeaux.c
index 08eaddbfc..dbde4d832 100644
--- a/src/box/sql/vdbeaux.c
+++ b/src/box/sql/vdbeaux.c
@@ -3492,8 +3492,7 @@ sqlite3VdbeDb(Vdbe * v)
 /*
  * Return a pointer to an sqlite3_value structure containing the value bound
  * parameter iVar of VM v. Except, if the value is an SQL NULL, return
- * 0 instead. Unless it is NULL, apply affinity aff (one of the AFFINITY_*
- * constants) to the value before returning it.
+ * 0 instead. Unless it is NULL, apply type to the value before returning it.
  *
  * The returned value must be freed by the caller using sqlite3ValueFree().
  */
diff --git a/src/box/sql/wherecode.c b/src/box/sql/wherecode.c
index c28ff33f9..01c1a2c6c 100644
--- a/src/box/sql/wherecode.c
+++ b/src/box/sql/wherecode.c
@@ -367,9 +367,9 @@ disableTerm(WhereLevel * pLevel, WhereTerm * pTerm)
  * Code an OP_ApplyType opcode to apply the column type string types
  * to the n registers starting at base.
  *
- * As an optimization, AFFINITY_BLOB entries (which are no-ops) at the
+ * As an optimization, SCALAR entries (which are no-ops) at the
  * beginning and end of zAff are ignored.  If all entries in zAff are
- * AFFINITY_BLOB, then no code gets generated.
+ * SCALAR, then no code gets generated.
  *
  * This routine makes its own copy of zAff so that the caller is free
  * to modify zAff after this routine returns.
@@ -384,8 +384,9 @@ codeApplyAffinity(Parse * pParse, int base, int n, enum field_type *zAff)
        }
        assert(v != 0);
 
-       /* Adjust base and n to skip over AFFINITY_BLOB entries at the beginning
-        * and end of the affinity string.
+       /*
+        * Adjust base and n to skip over SCALAR entries at the
+        * beginning and end of the type sequence.
         */
        while (n > 0 && zAff[0] == FIELD_TYPE_SCALAR) {
                n--;
@@ -409,8 +410,8 @@ codeApplyAffinity(Parse * pParse, int base, int n, enum field_type *zAff)
  * Expression pRight, which is the RHS of a comparison operation, is
  * either a vector of n elements or, if n==1, a scalar expression.
  * Before the comparison operation, affinity zAff is to be applied
- * to the pRight values. This function modifies characters within the
- * affinity string to AFFINITY_BLOB if either:
+ * to the pRight values. This function modifies entries within the
+ * field sequence to SCALAR if either:
  *
  *   * the comparison will be performed with no affinity, or
  *   * the affinity change in zAff is guaranteed not to change the value.
@@ -669,7 +670,7 @@ codeEqualityTerm(Parse * pParse,    /* The parsing context */
  * copy of the column affinity string of the index allocated using
  * sqlite3DbMalloc(). Except, entries in the copy of the string associated
  * with equality constraints that use BLOB or NONE affinity are set to
- * AFFINITY_BLOB. This is to deal with SQL such as the following:
+ * SCALAR. This is to deal with SQL such as the following:
  *
  *   CREATE TABLE t1(a TEXT PRIMARY KEY, b);
  *   SELECT ... FROM t1 AS t2, t1 WHERE t1.a = t2.b;
@@ -678,7 +679,7 @@ codeEqualityTerm(Parse * pParse,    /* The parsing context */
  * the right hand side of the equality constraint (t2.b) has BLOB/NONE affinity,
  * no conversion should be attempted before using a t2.b value as part of
  * a key to search the index. Hence the first byte in the returned affinity
- * string in this example would be set to AFFINITY_BLOB.
+ * string in this example would be set to SCALAR.
  */
 static int
 codeAllEqualityTerms(Parse * pParse,   /* Parsing context */

Still some comments and variables contain ‘affinity’ term.
We can either accept it as a synonym to type, or fix
it within only code-style-fix commit.

Whole patch:

commit 53561119ee99de9d5a781c5e83c49374634c4b97
Author: Nikita Pettik <korablev at tarantool.org>
Date:   Sun Dec 23 18:10:11 2018 +0200

    sql: clean-up affinity from SQL source code
    
    Replace remains of affinity usage in SQL parser, query optimizer and
    VDBE. Don't add affinity to field definition when table is encoded into
    msgpack.  Remove field type <-> affinity converters, since now we can
    operate directly on field type.
    
    Part of #3698

diff --git a/src/box/sql.c b/src/box/sql.c
index a498cd8fe..8e2f5b6c7 100644
--- a/src/box/sql.c
+++ b/src/box/sql.c
@@ -997,7 +997,7 @@ sql_encode_table(struct region *region, struct Table *table, uint32_t *size)
                uint32_t cid = def->fields[i].coll_id;
                struct field_def *field = &def->fields[i];
                const char *default_str = field->default_value;
-               int base_len = 5;
+               int base_len = 4;
                if (cid != COLL_NONE)
                        base_len += 1;
                if (default_str != NULL)
@@ -1009,10 +1009,6 @@ sql_encode_table(struct region *region, struct Table *table, uint32_t *size)
                assert(def->fields[i].is_nullable ==
                       action_is_nullable(def->fields[i].nullable_action));
                mpstream_encode_str(&stream, field_type_strs[field->type]);
-               mpstream_encode_str(&stream, "affinity");
-               enum affinity_type aff =
-                       sql_field_type_to_affinity(def->fields[i].type);
-               mpstream_encode_uint(&stream, aff);
                mpstream_encode_str(&stream, "is_nullable");
                mpstream_encode_bool(&stream, def->fields[i].is_nullable);
                mpstream_encode_str(&stream, "nullable_action");
diff --git a/src/box/sql/build.c b/src/box/sql/build.c
index a0a8334f7..9eaa4300a 100644
--- a/src/box/sql/build.c
+++ b/src/box/sql/build.c
@@ -485,60 +485,6 @@ sql_field_retrieve(Parse *parser, Table *table, uint32_t id)
        return field;
 }
 
-enum field_type
-sql_affinity_to_field_type(enum affinity_type affinity)
-{
-       switch (affinity) {
-               case AFFINITY_INTEGER:
-                       return FIELD_TYPE_INTEGER;
-               case AFFINITY_REAL:
-                       return FIELD_TYPE_NUMBER;
-               case AFFINITY_TEXT:
-                       return FIELD_TYPE_STRING;
-               case AFFINITY_BLOB:
-                       return FIELD_TYPE_SCALAR;
-               case AFFINITY_UNDEFINED:
-                       return FIELD_TYPE_ANY;
-               default:
-                       unreachable();
-       }
-}
-
-enum affinity_type
-sql_field_type_to_affinity(enum field_type field_type)
-{
-       switch (field_type) {
-               case FIELD_TYPE_INTEGER:
-               case FIELD_TYPE_UNSIGNED:
-                       return AFFINITY_INTEGER;
-               case FIELD_TYPE_NUMBER:
-                       return AFFINITY_REAL;
-               case FIELD_TYPE_STRING:
-                       return AFFINITY_TEXT;
-               case FIELD_TYPE_SCALAR:
-                       return AFFINITY_BLOB;
-               case FIELD_TYPE_ANY:
-                       return AFFINITY_UNDEFINED;
-               default:
-                       unreachable();
-       }
-}
-
-enum field_type *
-sql_affinity_str_to_field_type_str(const char *affinity_str)
-{
-       if (affinity_str == NULL)
-               return NULL;
-       size_t len = strlen(affinity_str) + 1;
-       size_t sz = len * sizeof(enum field_type);
-       enum field_type *types =
-               (enum field_type *) sqlite3DbMallocRaw(sql_get(), sz);
-       for (uint32_t i = 0; i < len - 1; ++i)
-               types[i] = sql_affinity_to_field_type(affinity_str[i]);
-       types[len - 1] = field_type_MAX;
-       return types;
-}
-
 /*
  * Add a new column to the table currently being constructed.
  *
diff --git a/src/box/sql/expr.c b/src/box/sql/expr.c
index d3a8644ce..b0a595b97 100644
--- a/src/box/sql/expr.c
+++ b/src/box/sql/expr.c
@@ -107,6 +107,19 @@ sql_expr_type(struct Expr *pExpr)
        return pExpr->type;
 }
 
+enum field_type *
+field_type_sequence_dup(struct Parse *parse, enum field_type *types,
+                       uint32_t len)
+{
+       uint32_t sz = (len + 1) * sizeof(enum field_type);
+       enum field_type *ret_types = sqlite3DbMallocRaw(parse->db, sz);
+       if (ret_types == NULL)
+               return NULL;
+       memcpy(ret_types, types, sz);
+       ret_types[len] = field_type_MAX;
+       return ret_types;
+}
+
 /*
  * Set the collating sequence for expression pExpr to be the collating
  * sequence named by pToken.   Return a pointer to a new Expr node that
@@ -308,8 +321,7 @@ binaryCompareP5(Expr * pExpr1, Expr * pExpr2, int jumpIfNull)
 {
        enum field_type lhs = sql_expr_type(pExpr2);
        enum field_type rhs = sql_expr_type(pExpr1);
-       u8 type_mask = sql_field_type_to_affinity(sql_type_result(rhs, lhs)) |
-                      (u8) jumpIfNull;
+       u8 type_mask = sql_type_result(rhs, lhs) | (u8) jumpIfNull;
        return type_mask;
 }
 
@@ -2574,16 +2586,16 @@ sqlite3FindInIndex(Parse * pParse,      /* Parsing context */
  * It is the responsibility of the caller to ensure that the returned
  * string is eventually freed using sqlite3DbFree().
  */
-static char *
+static enum field_type *
 exprINAffinity(Parse * pParse, Expr * pExpr)
 {
        Expr *pLeft = pExpr->pLeft;
        int nVal = sqlite3ExprVectorSize(pLeft);
        Select *pSelect = (pExpr->flags & EP_xIsSelect) ? pExpr->x.pSelect : 0;
-       char *zRet;
 
        assert(pExpr->op == TK_IN);
-       zRet = sqlite3DbMallocZero(pParse->db, nVal + 1);
+       uint32_t sz = (nVal + 1) * sizeof(enum field_type);
+       enum field_type *zRet = sqlite3DbMallocZero(pParse->db, sz);
        if (zRet) {
                int i;
                for (i = 0; i < nVal; i++) {
@@ -2592,12 +2604,14 @@ exprINAffinity(Parse * pParse, Expr * pExpr)
                        if (pSelect) {
                                struct Expr *e = pSelect->pEList->a[i].pExpr;
                                enum field_type rhs = sql_expr_type(e);
-                               zRet[i] = sql_field_type_to_affinity(sql_type_result(rhs, lhs));
+                               zRet[i] = sql_type_result(rhs, lhs);
                        } else {
-                               zRet[i] = sql_field_type_to_affinity(lhs);
+                               if (lhs == FIELD_TYPE_ANY)
+                                       lhs = FIELD_TYPE_SCALAR;
+                               zRet[i] = lhs;
                        }
                }
-               zRet[nVal] = '\0';
+               zRet[nVal] = field_type_MAX;
        }
        return zRet;
 }
@@ -2969,7 +2983,6 @@ sqlite3ExprCodeIN(Parse * pParse, /* Parsing and code generating context */
        int rLhsOrig;           /* LHS values prior to reordering by aiMap[] */
        Vdbe *v;                /* Statement under construction */
        int *aiMap = 0;         /* Map from vector field to index column */
-       char *zAff = 0;         /* Affinity string for comparisons */
        int nVector;            /* Size of vectors for this IN operator */
        int iDummy;             /* Dummy parameter to exprCodeVector() */
        Expr *pLeft;            /* The LHS of the IN operator */
@@ -2983,7 +2996,8 @@ sqlite3ExprCodeIN(Parse * pParse, /* Parsing and code generating context */
        pLeft = pExpr->pLeft;
        if (sqlite3ExprCheckIN(pParse, pExpr))
                return;
-       zAff = exprINAffinity(pParse, pExpr);
+       /* Type sequence for comparisons. */
+       enum field_type *zAff = exprINAffinity(pParse, pExpr);
        nVector = sqlite3ExprVectorSize(pExpr->pLeft);
        aiMap =
            (int *)sqlite3DbMallocZero(pParse->db,
@@ -3129,9 +3143,8 @@ sqlite3ExprCodeIN(Parse * pParse, /* Parsing and code generating context */
         * of the RHS using the LHS as a probe.  If found, the result is
         * true.
         */
-       enum field_type *types = sql_affinity_str_to_field_type_str(zAff);
-       types[nVector] = field_type_MAX;
-       sqlite3VdbeAddOp4(v, OP_ApplyType, rLhs, nVector, 0, (char *)types,
+       zAff[nVector] = field_type_MAX;
+       sqlite3VdbeAddOp4(v, OP_ApplyType, rLhs, nVector, 0, (char*)zAff,
                          P4_DYNAMIC);
        if (destIfFalse == destIfNull) {
                /* Combine Step 3 and Step 5 into a single opcode */
@@ -3213,7 +3226,9 @@ sqlite3ExprCodeIN(Parse * pParse, /* Parsing and code generating context */
        if (rLhs != rLhsOrig)
                sqlite3ReleaseTempReg(pParse, rLhs);
        sqlite3ExprCachePop(pParse);
+       sqlite3DbFree(pParse->db, aiMap);
        VdbeComment((v, "end IN expr"));
+       return;
  sqlite3ExprCodeIN_oom_error:
        sqlite3DbFree(pParse->db, aiMap);
        sqlite3DbFree(pParse->db, zAff);
diff --git a/src/box/sql/insert.c b/src/box/sql/insert.c
index a02a74e88..a382f413f 100644
--- a/src/box/sql/insert.c
+++ b/src/box/sql/insert.c
@@ -41,33 +41,6 @@
 #include "bit/bit.h"
 #include "box/box.h"
 
-char *
-sql_space_index_affinity_str(struct sqlite3 *db, struct space_def *space_def,
-                            struct index_def *idx_def)
-{
-       uint32_t column_count = idx_def->key_def->part_count;
-       char *aff = (char *)sqlite3DbMallocRaw(db, column_count + 1);
-       if (aff == NULL)
-               return NULL;
-       /*
-        * Table may occasionally come from non-SQL API, so lets
-        * gentle process this case by setting default affinity
-        * for it.
-        */
-       if (space_def->fields == NULL) {
-               memset(aff, AFFINITY_BLOB, column_count);
-       } else {
-               for (uint32_t i = 0; i < column_count; i++) {
-                       aff[i] = sql_space_index_part_affinity(space_def,
-                                                              idx_def, i);
-                       if (aff[i] == AFFINITY_UNDEFINED)
-                               aff[i] = AFFINITY_BLOB;
-               }
-       }
-       aff[column_count] = '\0';
-       return aff;
-}
-
 enum field_type *
 sql_index_type_str(struct sqlite3 *db, const struct index_def *idx_def)
 {
@@ -1251,12 +1224,12 @@ xferOptimization(Parse * pParse,        /* Parser context */
        if (dest->def->field_count != src->def->field_count)
                return 0;
        for (i = 0; i < (int)dest->def->field_count; i++) {
-               enum affinity_type dest_affinity =
-                       dest->def->fields[i].affinity;
-               enum affinity_type src_affinity =
-                       src->def->fields[i].affinity;
-               /* Affinity must be the same on all columns. */
-               if (dest_affinity != src_affinity)
+               enum field_type dest_type =
+                       dest->def->fields[i].type;
+               enum field_type src_type =
+                       src->def->fields[i].type;
+               /* Type must be the same on all columns. */
+               if (dest_type != src_type)
                        return 0;
                uint32_t id;
                if (sql_column_collation(dest->def, i, &id) !=
diff --git a/src/box/sql/select.c b/src/box/sql/select.c
index 8a8ccc68c..92b563e22 100644
--- a/src/box/sql/select.c
+++ b/src/box/sql/select.c
@@ -1200,11 +1200,10 @@ selectInnerLoop(Parse * pParse,         /* The parser context */
                                               regOrig, nResultCol, nPrefixReg);
                        } else {
                                int r1 = sqlite3GetTempReg(pParse);
-                               assert(sqlite3Strlen30(pDest->zAffSdst) ==
-                                      (unsigned int)nResultCol);
                                enum field_type *types =
-                                       sql_affinity_str_to_field_type_str(
-                                               pDest->zAffSdst);
+                                       field_type_sequence_dup(pParse,
+                                                               pDest->zAffSdst,
+                                                               nResultCol);
                                sqlite3VdbeAddOp4(v, OP_MakeRecord, regResult,
                                                  nResultCol, r1, (char *)types,
                                                  P4_DYNAMIC);
@@ -1626,11 +1625,9 @@ generateSortTail(Parse * pParse, /* Parsing context */
                        break;
                }
        case SRT_Set:{
-                       assert((unsigned int)nColumn ==
-                              sqlite3Strlen30(pDest->zAffSdst));
-
                        enum field_type *types =
-                               sql_affinity_str_to_field_type_str(pDest->zAffSdst);
+                               field_type_sequence_dup(pParse, pDest->zAffSdst,
+                                                       nColumn);
                        sqlite3VdbeAddOp4(v, OP_MakeRecord, regRow, nColumn,
                                          regTupleid, (char *)types,
                                          P4_DYNAMIC);
@@ -1975,7 +1972,6 @@ sqlite3SelectAddColumnTypeAndCollation(Parse * pParse,            /* Parsing contexts */
                enum field_type type = sql_expr_type(p);
                if (type == FIELD_TYPE_ANY)
                        type = FIELD_TYPE_SCALAR;
-               pTab->def->fields[i].affinity = sql_field_type_to_affinity(type);
                pTab->def->fields[i].type = type;
                bool is_found;
                uint32_t coll_id;
@@ -3069,7 +3065,8 @@ generateOutputSubroutine(struct Parse *parse, struct Select *p,
                        testcase(in->nSdst > 1);
                        r1 = sqlite3GetTempReg(parse);
                        enum field_type *types =
-                               sql_affinity_str_to_field_type_str(dest->zAffSdst);
+                               field_type_sequence_dup(parse, dest->zAffSdst,
+                                                       in->nSdst);
                        sqlite3VdbeAddOp4(v, OP_MakeRecord, in->iSdst,
                                          in->nSdst, r1, (char *)types,
                                          P4_DYNAMIC);
diff --git a/src/box/sql/sqliteInt.h b/src/box/sql/sqliteInt.h
index 209e7bf0f..822cc40df 100644
--- a/src/box/sql/sqliteInt.h
+++ b/src/box/sql/sqliteInt.h
@@ -1792,12 +1792,6 @@ struct Savepoint {
                                 (X) == FIELD_TYPE_NUMBER || \
                                 (X) == FIELD_TYPE_UNSIGNED)
 
-/*
- * The AFFINITY_MASK values masks off the significant bits of an
- * affinity value.
- */
-#define AFFINITY_MASK     0x47
-
 /*
  * Additional bit values that can be ORed with an affinity without
  * changing the affinity.
@@ -1807,7 +1801,7 @@ struct Savepoint {
  * operator is NULL.  It is added to certain comparison operators to
  * prove that the operands are always NOT NULL.
  */
-#define SQLITE_KEEPNULL     0x08       /* Used by vector == or <> */
+#define SQLITE_KEEPNULL     0x40       /* Used by vector == or <> */
 #define SQLITE_JUMPIFNULL   0x10       /* jumps if either operand is NULL */
 #define SQLITE_STOREP2      0x20       /* Store result in reg[P2] rather than jump */
 #define SQLITE_NULLEQ       0x80       /* NULL=NULL */
@@ -2615,7 +2609,8 @@ struct Select {
  */
 struct SelectDest {
        u8 eDest;               /* How to dispose of the results.  On of SRT_* above. */
-       char *zAffSdst;         /* Affinity used when eDest==SRT_Set */
+       /* Affinity used when eDest==SRT_Set */
+       enum field_type *zAffSdst;
        int iSDParm;            /* A parameter used by the eDest disposal method */
        /** Register containing ephemeral's space pointer. */
        int reg_eph;
@@ -3431,19 +3426,6 @@ sql_create_view(struct Parse *parse_context, struct Token *begin,
                struct Token *name, struct ExprList *aliases,
                struct Select *select, bool if_exists);
 
-/**
- * Helper to convert SQLite affinity to corresponding
- * Tarantool field type.
- **/
-enum field_type
-sql_affinity_to_field_type(enum affinity_type affinity);
-
-enum affinity_type
-sql_field_type_to_affinity(enum field_type field_type);
-
-enum field_type *
-sql_affinity_str_to_field_type_str(const char *affinity_str);
-
 /**
  * Compile view, i.e. create struct Select from
  * 'CREATE VIEW...' string, and assign cursors to each table from
@@ -4225,33 +4207,6 @@ int sqlite3VarintLen(u64 v);
 #define getVarint    sqlite3GetVarint
 #define putVarint    sqlite3PutVarint
 
-/**
- * Return a pointer to the column affinity string associated with
- * given index. A column affinity string has one character for
- * each column in the table, according to the affinity of the
- * column:
- *
- *  Character      Column affinity
- *  ------------------------------
- *  'A'            BLOB
- *  'B'            TEXT
- *  'C'            NUMERIC
- *  'D'            INTEGER
- *  'F'            REAL
- *
- * Memory for the buffer containing the column index affinity string
- * is allocated on heap.
- *
- * @param db Database handle.
- * @param space_def Definition of space index belongs to.
- * @param idx_def Definition of index from which affinity
- *                to be extracted.
- * @retval Allocated affinity string, or NULL on OOM.
- */
-char *
-sql_space_index_affinity_str(struct sqlite3 *db, struct space_def *space_def,
-                            struct index_def *idx_def);
-
 /** Return string consisting of fields types of given index. */
 enum field_type *
 sql_index_type_str(struct sqlite3 *db, const struct index_def *idx_def);
@@ -4292,6 +4247,13 @@ sql_expr_cmp_type_is_compatible(struct Expr *expr, enum field_type idx_type);
 enum field_type
 sql_expr_type(Expr *pExpr);
 
+/**
+ * This function duplicates first @len entries of types array
+ * and terminates new array with field_type_MAX member.
+ */
+enum field_type *
+field_type_sequence_dup(struct Parse *parse, enum field_type *types,
+                       uint32_t len);
 
 /**
  * Convert z to a 64-bit signed integer.  z must be decimal. This
@@ -4641,19 +4603,6 @@ int
 sql_stat4_column(struct sqlite3 *db, const char *record, uint32_t col_num,
                 sqlite3_value **res);
 
-/**
- * Return the affinity for a single column of an index.
- *
- * @param def Definition of space @idx belongs to.
- * @param idx Index to be investigated.
- * @param partno Affinity of this part to be returned.
- *
- * @retval Affinity of @partno index part.
- */
-enum affinity_type
-sql_space_index_part_affinity(struct space_def *def, struct index_def *idx,
-                             uint32_t partno);
-
 /*
  * The interface to the LEMON-generated parser
  */
diff --git a/src/box/sql/vdbe.c b/src/box/sql/vdbe.c
index 61d73b676..c3f596d4f 100644
--- a/src/box/sql/vdbe.c
+++ b/src/box/sql/vdbe.c
@@ -2022,14 +2022,6 @@ case OP_Cast: {                  /* in1 */
  * jump to address P2.  Or if the SQLITE_STOREP2 flag is set in P5, then
  * store the result of comparison in register P2.
  *
- * The AFFINITY_MASK portion of P5 must be an affinity character -
- * AFFINITY_TEXT, AFFINITY_INTEGER, and so forth. An attempt is made
- * to coerce both inputs according to this affinity before the
- * comparison is made. If the AFFINITY_MASK is 0x00, then numeric
- * affinity is used. Note that the affinity conversions are stored
- * back into the input registers P1 and P3.  So this opcode can cause
- * persistent changes to registers P1 and P3.
- *
  * Once any conversions have taken place, and neither value is NULL,
  * the values are compared. If both values are blobs then memcmp() is
  * used to determine the results of the comparison.  If both values
@@ -2045,6 +2037,10 @@ case OP_Cast: {                  /* in1 */
  * of comparison is true.  If either operand is NULL then the result is false.
  * If neither operand is NULL the result is the same as it would be if
  * the SQLITE_NULLEQ flag were omitted from P5.
+ * P5 also can contain type to be applied to operands. Note that
+ * the type conversions are stored back into the input registers
+ * P1 and P3.  So this opcode can cause persistent changes to
+ * registers P1 and P3.
  *
  * If both SQLITE_STOREP2 and SQLITE_KEEPNULL flags are set then the
  * content of r[P2] is only changed if the new value is NULL or 0 (false).
@@ -2072,14 +2068,6 @@ case OP_Cast: {                  /* in1 */
  * reg(P3) is NULL then the take the jump.  If the SQLITE_JUMPIFNULL
  * bit is clear then fall through if either operand is NULL.
  *
- * The AFFINITY_MASK portion of P5 must be an affinity character -
- * AFFINITY_TEXT, AFFINITY_INTEGER, and so forth. An attempt is made
- * to coerce both inputs according to this affinity before the
- * comparison is made. If the AFFINITY_MASK is 0x00, then numeric
- * affinity is used. Note that the affinity conversions are stored
- * back into the input registers P1 and P3.  So this opcode can cause
- * persistent changes to registers P1 and P3.
- *
  * Once any conversions have taken place, and neither value is NULL,
  * the values are compared. If both values are blobs then memcmp() is
  * used to determine the results of the comparison.  If both values
@@ -2163,9 +2151,16 @@ case OP_Ge: {             /* same as TK_GE, jump, in1, in3 */
                        break;
                }
        } else {
-               /* Neither operand is NULL.  Do a comparison. */
-               affinity = pOp->p5 & AFFINITY_MASK;
-               if (affinity>=AFFINITY_INTEGER) {
+               /*
+                * Neither operand is NULL. Do a comparison.
+                * 15 is 1111 in a binary form.
+                * Since all existing types can be fitted in 4 bits
+                * (field_type_MAX == 10), it is enough to 'and'
+                * p5 with this constant. Node that all other flags
+                * that can be stored in p5 are >= 16.
+                */
+               affinity = pOp->p5 & 15;
+               if (sql_type_is_numeric(affinity)) {
                        if ((flags1 | flags3)&MEM_Str) {
                                if ((flags1 & (MEM_Int|MEM_Real|MEM_Str))==MEM_Str) {
                                        applyNumericAffinity(pIn1,0);
@@ -2193,7 +2188,7 @@ case OP_Ge: {             /* same as TK_GE, jump, in1, in3 */
                                res = 0;
                                goto compare_op;
                        }
-               } else if (affinity==AFFINITY_TEXT) {
+               } else if (affinity == FIELD_TYPE_STRING) {
                        if ((flags1 & MEM_Str)==0 && (flags1 & (MEM_Int|MEM_Real))!=0) {
                                testcase( pIn1->flags & MEM_Int);
                                testcase( pIn1->flags & MEM_Real);
diff --git a/src/box/sql/vdbeaux.c b/src/box/sql/vdbeaux.c
index 08eaddbfc..dbde4d832 100644
--- a/src/box/sql/vdbeaux.c
+++ b/src/box/sql/vdbeaux.c
@@ -3492,8 +3492,7 @@ sqlite3VdbeDb(Vdbe * v)
 /*
  * Return a pointer to an sqlite3_value structure containing the value bound
  * parameter iVar of VM v. Except, if the value is an SQL NULL, return
- * 0 instead. Unless it is NULL, apply affinity aff (one of the AFFINITY_*
- * constants) to the value before returning it.
+ * 0 instead. Unless it is NULL, apply type to the value before returning it.
  *
  * The returned value must be freed by the caller using sqlite3ValueFree().
  */
diff --git a/src/box/sql/where.c b/src/box/sql/where.c
index 219009ef8..6b3383bec 100644
--- a/src/box/sql/where.c
+++ b/src/box/sql/where.c
@@ -699,7 +699,6 @@ termCanDriveIndex(WhereTerm * pTerm,        /* WHERE clause term to check */
                return 0;
        if (pTerm->u.leftColumn < 0)
                return 0;
-       aff = pSrc->pTab->aCol[pTerm->u.leftColumn].affinity;
        if (!sql_expr_cmp_type_is_compatible(pTerm->pExpr, aff))
                return 0;
        return 1;
@@ -1137,15 +1136,6 @@ whereRangeAdjust(WhereTerm * pTerm, LogEst nNew)
        return nRet;
 }
 
-enum affinity_type
-sql_space_index_part_affinity(struct space_def *def, struct index_def *idx,
-                             uint32_t partno)
-{
-       assert(partno < idx->key_def->part_count);
-       uint32_t fieldno = idx->key_def->parts[partno].fieldno;
-       return def->fields[fieldno].affinity;
-}
-
 /*
  * This function is called to estimate the number of rows visited by a
  * range-scan on a skip-scan index. For example:
diff --git a/src/box/sql/wherecode.c b/src/box/sql/wherecode.c
index 33b860f36..01c1a2c6c 100644
--- a/src/box/sql/wherecode.c
+++ b/src/box/sql/wherecode.c
@@ -367,15 +367,15 @@ disableTerm(WhereLevel * pLevel, WhereTerm * pTerm)
  * Code an OP_ApplyType opcode to apply the column type string types
  * to the n registers starting at base.
  *
- * As an optimization, AFFINITY_BLOB entries (which are no-ops) at the
+ * As an optimization, SCALAR entries (which are no-ops) at the
  * beginning and end of zAff are ignored.  If all entries in zAff are
- * AFFINITY_BLOB, then no code gets generated.
+ * SCALAR, then no code gets generated.
  *
  * This routine makes its own copy of zAff so that the caller is free
  * to modify zAff after this routine returns.
  */
 static void
-codeApplyAffinity(Parse * pParse, int base, int n, char *zAff)
+codeApplyAffinity(Parse * pParse, int base, int n, enum field_type *zAff)
 {
        Vdbe *v = pParse->pVdbe;
        if (zAff == 0) {
@@ -384,23 +384,23 @@ codeApplyAffinity(Parse * pParse, int base, int n, char *zAff)
        }
        assert(v != 0);
 
-       /* Adjust base and n to skip over AFFINITY_BLOB entries at the beginning
-        * and end of the affinity string.
+       /*
+        * Adjust base and n to skip over SCALAR entries at the
+        * beginning and end of the type sequence.
         */
-       while (n > 0 && zAff[0] == AFFINITY_BLOB) {
+       while (n > 0 && zAff[0] == FIELD_TYPE_SCALAR) {
                n--;
                base++;
                zAff++;
        }
-       while (n > 1 && zAff[n - 1] == AFFINITY_BLOB) {
+       while (n > 1 && zAff[n - 1] == FIELD_TYPE_SCALAR) {
                n--;
        }
 
        if (n > 0) {
-               enum field_type *types =
-                       sql_affinity_str_to_field_type_str(zAff);
-               types[n] = field_type_MAX;
-               sqlite3VdbeAddOp4(v, OP_ApplyType, base, n, 0, (char *)types,
+               enum field_type *types = field_type_sequence_dup(pParse, zAff,
+                                                                n);
+               sqlite3VdbeAddOp4(v, OP_ApplyType, base, n, 0, (char *) types,
                                  P4_DYNAMIC);
                sqlite3ExprCacheAffinityChange(pParse, base, n);
        }
@@ -410,8 +410,8 @@ codeApplyAffinity(Parse * pParse, int base, int n, char *zAff)
  * Expression pRight, which is the RHS of a comparison operation, is
  * either a vector of n elements or, if n==1, a scalar expression.
  * Before the comparison operation, affinity zAff is to be applied
- * to the pRight values. This function modifies characters within the
- * affinity string to AFFINITY_BLOB if either:
+ * to the pRight values. This function modifies entries within the
+ * field sequence to SCALAR if either:
  *
  *   * the comparison will be performed with no affinity, or
  *   * the affinity change in zAff is guaranteed not to change the value.
@@ -419,16 +419,15 @@ codeApplyAffinity(Parse * pParse, int base, int n, char *zAff)
 static void
 updateRangeAffinityStr(Expr * pRight,  /* RHS of comparison */
                       int n,           /* Number of vector elements in comparison */
-                      char *zAff)      /* Affinity string to modify */
+                      enum field_type *zAff)   /* Affinity string to modify */
 {
        int i;
        for (i = 0; i < n; i++) {
-               enum field_type type = sql_affinity_to_field_type(zAff[i]);
                Expr *p = sqlite3VectorFieldSubexpr(pRight, i);
                enum field_type expr_type = sql_expr_type(p);
-               if (sql_type_result(expr_type, type) == FIELD_TYPE_SCALAR ||
-                       sql_expr_needs_no_type_change(p, type)) {
-                       zAff[i] = AFFINITY_BLOB;
+               if (sql_type_result(expr_type, zAff[i]) == FIELD_TYPE_SCALAR ||
+                   sql_expr_needs_no_type_change(p, zAff[i])) {
+                       zAff[i] = FIELD_TYPE_SCALAR;
                }
        }
 }
@@ -671,7 +670,7 @@ codeEqualityTerm(Parse * pParse,    /* The parsing context */
  * copy of the column affinity string of the index allocated using
  * sqlite3DbMalloc(). Except, entries in the copy of the string associated
  * with equality constraints that use BLOB or NONE affinity are set to
- * AFFINITY_BLOB. This is to deal with SQL such as the following:
+ * SCALAR. This is to deal with SQL such as the following:
  *
  *   CREATE TABLE t1(a TEXT PRIMARY KEY, b);
  *   SELECT ... FROM t1 AS t2, t1 WHERE t1.a = t2.b;
@@ -680,14 +679,14 @@ codeEqualityTerm(Parse * pParse,  /* The parsing context */
  * the right hand side of the equality constraint (t2.b) has BLOB/NONE affinity,
  * no conversion should be attempted before using a t2.b value as part of
  * a key to search the index. Hence the first byte in the returned affinity
- * string in this example would be set to AFFINITY_BLOB.
+ * string in this example would be set to SCALAR.
  */
 static int
 codeAllEqualityTerms(Parse * pParse,   /* Parsing context */
                     WhereLevel * pLevel,       /* Which nested loop of the FROM we are coding */
                     int bRev,          /* Reverse the order of IN operators */
                     int nExtraReg,     /* Number of extra registers to allocate */
-                    char **pzAff)      /* OUT: Set to point to affinity string */
+                    enum field_type **pzAff)   /* OUT: Set to point to affinity string */
 {
        u16 nEq;                /* The number of == or IN constraints to code */
        u16 nSkip;              /* Number of left-most columns to skip */
@@ -711,11 +710,7 @@ codeAllEqualityTerms(Parse * pParse,       /* Parsing context */
        nReg = pLoop->nEq + nExtraReg;
        pParse->nMem += nReg;
 
-
-       struct space *space = space_by_id(idx_def->space_id);
-       assert(space != NULL);
-       char *zAff = sql_space_index_affinity_str(pParse->db, space->def,
-                                                 idx_def);
+       enum field_type *zAff = sql_index_type_str(pParse->db, idx_def);
        assert(zAff != 0 || pParse->db->mallocFailed);
 
        if (nSkip) {
@@ -741,7 +736,6 @@ codeAllEqualityTerms(Parse * pParse,        /* Parsing context */
 
        /* Evaluate the equality constraints
         */
-       assert(zAff == 0 || (int)strlen(zAff) >= nEq);
        for (j = nSkip; j < nEq; j++) {
                int r1;
                pTerm = pLoop->aLTerm[j];
@@ -769,7 +763,7 @@ codeAllEqualityTerms(Parse * pParse,        /* Parsing context */
                                 * affinity of the comparison has been applied to the value.
                                 */
                                if (zAff)
-                                       zAff[j] = AFFINITY_BLOB;
+                                       zAff[j] = FIELD_TYPE_SCALAR;
                        }
                } else if ((pTerm->eOperator & WO_ISNULL) == 0) {
                        Expr *pRight = pTerm->pExpr->pRight;
@@ -781,14 +775,13 @@ codeAllEqualityTerms(Parse * pParse,      /* Parsing context */
                        if (zAff) {
                                enum field_type type =
                                        sql_expr_type(pRight);
-                               enum field_type idx_type =
-                                       sql_affinity_to_field_type(zAff[j]);
+                               enum field_type idx_type = zAff[j];
                                if (sql_type_result(type, idx_type) ==
                                    FIELD_TYPE_SCALAR) {
-                                       zAff[j] = AFFINITY_BLOB;
+                                       zAff[j] = FIELD_TYPE_SCALAR;
                                }
                                if (sql_expr_needs_no_type_change(pRight, idx_type))
-                                       zAff[j] = AFFINITY_BLOB;
+                                       zAff[j] = FIELD_TYPE_SCALAR;
                        }
                }
        }
@@ -1001,8 +994,8 @@ sqlite3WhereCodeOneLoopStart(WhereInfo * pWInfo,   /* Complete information about t
                int iIdxCur;    /* The VDBE cursor for the index */
                int nExtraReg = 0;      /* Number of extra registers needed */
                int op;         /* Instruction opcode */
-               char *zStartAff;        /* Affinity for start of range constraint */
-               char *zEndAff = 0;      /* Affinity for end of range constraint */
+               enum field_type *zStartAff;     /* Affinity for start of range constraint */
+               enum field_type *zEndAff = NULL;        /* Affinity for end of range constraint */
                u8 bSeekPastNull = 0;   /* True to seek past initial nulls */
                u8 bStopAtNull = 0;     /* Add condition to terminate at NULLs */
                int force_integer_reg = -1;  /* If non-negative: number of
@@ -1117,9 +1110,13 @@ sqlite3WhereCodeOneLoopStart(WhereInfo * pWInfo, /* Complete information about t
                regBase =
                    codeAllEqualityTerms(pParse, pLevel, bRev, nExtraReg,
                                         &zStartAff);
-               assert(zStartAff == 0 || sqlite3Strlen30(zStartAff) >= nEq);
-               if (zStartAff && nTop) {
-                       zEndAff = sqlite3DbStrDup(db, &zStartAff[nEq]);
+               if (zStartAff != NULL && nTop) {
+                       uint32_t len = 0;
+                       for (enum field_type *tmp = &zStartAff[nEq];
+                            *tmp != field_type_MAX; tmp++, len++);
+                       uint32_t sz = len * sizeof(enum field_type);
+                       zEndAff = sqlite3DbMallocRaw(db, sz);
+                       memcpy(zEndAff, &zStartAff[nEq], sz);
                }
                addrNxt = pLevel->addrNxt;
 
diff --git a/src/box/sql/whereexpr.c b/src/box/sql/whereexpr.c
index 6ffd5ae84..96838f4b2 100644
--- a/src/box/sql/whereexpr.c
+++ b/src/box/sql/whereexpr.c
@@ -284,7 +284,8 @@ like_optimization_is_valid(Parse *pParse, Expr *pExpr, Expr **ppPrefix,
                Vdbe *pReprepare = pParse->pReprepare;
                int iCol = pRight->iColumn;
                pVal =
-                   sqlite3VdbeGetBoundValue(pReprepare, iCol, AFFINITY_BLOB);
+                   sqlite3VdbeGetBoundValue(pReprepare, iCol,
+                                            FIELD_TYPE_SCALAR);
                if (pVal && sqlite3_value_type(pVal) == SQLITE_TEXT) {
                        z = (char *)sqlite3_value_text(pVal);
                }
diff --git a/test/sql/types.result b/test/sql/types.result
index 915a6341a..df4dc151e 100644
--- a/test/sql/types.result
+++ b/test/sql/types.result
@@ -33,20 +33,19 @@ box.sql.execute("CREATE TABLE t1 (id TEXT PRIMARY KEY, a REAL, b INT, c TEXT, d
 ...
 box.space.T1:format()
 ---
-- [{'affinity': 66, 'type': 'string', 'nullable_action': 'abort', 'name': 'ID', 'is_nullable': false},
-  {'affinity': 69, 'type': 'number', 'nullable_action': 'none', 'name': 'A', 'is_nullable': true},
-  {'affinity': 68, 'type': 'integer', 'nullable_action': 'none', 'name': 'B', 'is_nullable': true},
-  {'affinity': 66, 'type': 'string', 'nullable_action': 'none', 'name': 'C', 'is_nullable': true},
-  {'affinity': 65, 'type': 'scalar', 'nullable_action': 'none', 'name': 'D', 'is_nullable': true}]
+- [{'type': 'string', 'nullable_action': 'abort', 'name': 'ID', 'is_nullable': false},
+  {'type': 'number', 'nullable_action': 'none', 'name': 'A', 'is_nullable': true},
+  {'type': 'integer', 'nullable_action': 'none', 'name': 'B', 'is_nullable': true},
+  {'type': 'string', 'nullable_action': 'none', 'name': 'C', 'is_nullable': true},
+  {'type': 'scalar', 'nullable_action': 'none', 'name': 'D', 'is_nullable': true}]
 ...
 box.sql.execute("CREATE VIEW v1 AS SELECT b + a, b - a FROM t1;")
 ---
 ...
 box.space.V1:format()
 ---
-- [{'affinity': 69, 'type': 'number', 'nullable_action': 'none', 'name': 'b + a',
-    'is_nullable': true}, {'affinity': 69, 'type': 'number', 'nullable_action': 'none',
-    'name': 'b - a', 'is_nullable': true}]
+- [{'type': 'number', 'nullable_action': 'none', 'name': 'b + a', 'is_nullable': true},
+  {'type': 'number', 'nullable_action': 'none', 'name': 'b - a', 'is_nullable': true}]
 ...
 -- gh-2494: index's part also features correct declared type.
 --
diff --git a/test/sql/upgrade.result b/test/sql/upgrade.result
index 79c7eb245..02ab9b42b 100644
--- a/test/sql/upgrade.result
+++ b/test/sql/upgrade.result
@@ -80,12 +80,12 @@ box.sql.execute("CREATE TRIGGER t2t AFTER INSERT ON t BEGIN INSERT INTO t_out VA
 ...
 box.space._space.index['name']:get('T')
 ---
-- [513, 1, 'T', 'memtx', 1, {}, [{'affinity': 68, 'type': 'integer', 'nullable_action': 'abort',
-      'name': 'X', 'is_nullable': false}]]
+- [513, 1, 'T', 'memtx', 1, {}, [{'type': 'integer', 'nullable_action': 'abort', 'name': 'X',
+      'is_nullable': false}]]
 ...
 box.space._space.index['name']:get('T_OUT')
 ---
-- [514, 1, 'T_OUT', 'memtx', 1, {}, [{'affinity': 68, 'type': 'integer', 'nullable_action': 'abort',
+- [514, 1, 'T_OUT', 'memtx', 1, {}, [{'type': 'integer', 'nullable_action': 'abort',
       'name': 'X', 'is_nullable': false}]]
 ...
 t1t = box.space._trigger:get('T1T')





More information about the Tarantool-patches mailing list