diff --git a/src/box/field_def.h b/src/box/field_def.h
index 93e38ea552c3b27ecb87aa88cf7e5dce3b334059..9673dab4e9f88a28ff796284650b927c641a7475 100644
--- a/src/box/field_def.h
+++ b/src/box/field_def.h
@@ -87,6 +87,22 @@ affinity_type_str(enum affinity_type type);
 
 /** \endcond public */
 
+enum {
+	/**
+	 * This mask allows to store in VdbeOp.p5 operand of
+	 * OP_Eq, OP_Lt etc opcodes field type alongside with
+	 * flags.
+	 */
+	FIELD_TYPE_MASK = 15
+};
+
+/**
+ * For detailed explanation see context of OP_Eq, OP_Lt etc
+ * opcodes in vdbe.c.
+ */
+static_assert((int) field_type_MAX <= (int) FIELD_TYPE_MASK,
+	      "values of enum field_type should fit into 4 bits of VdbeOp.p5");
+
 extern const char *field_type_strs[];
 
 extern const char *on_conflict_action_strs[];
diff --git a/src/box/sql.c b/src/box/sql.c
index baa67da397853707b565cc0d97f870f81802352a..39b5fb2f725979bf5e29096e061dab58b991850e 100644
--- a/src/box/sql.c
+++ b/src/box/sql.c
@@ -992,7 +992,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)
@@ -1004,10 +1004,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 b8503c6b1852874b03ff296a8cf6988f51713915..9b3580ef78a4deafdb8e8a58af4c9d115b07a166 100644
--- a/src/box/sql/build.c
+++ b/src/box/sql/build.c
@@ -485,59 +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, uint32_t len)
-{
-	if (affinity_str == NULL)
-		return NULL;
-	size_t sz = (len + 1) * sizeof(enum field_type);
-	enum field_type *types =
-		(enum field_type *) sqlite3DbMallocRaw(sql_get(), sz);
-	for (uint32_t i = 0; i < len; ++i)
-		types[i] = sql_affinity_to_field_type(affinity_str[i]);
-	types[len] = field_type_MAX;
-	return types;
-}
-
 /*
  * Add a new column to the table currently being constructed.
  *
diff --git a/src/box/sql/delete.c b/src/box/sql/delete.c
index a6e7547f5c76a0f48b7e7eea6d6e996705f2df4f..5a3be48623ab153fbec66c0b175935bda05d9343 100644
--- a/src/box/sql/delete.c
+++ b/src/box/sql/delete.c
@@ -587,7 +587,7 @@ sql_generate_index_key(struct Parse *parse, struct index *index, int cursor,
 		sqlite3ExprCodeGetColumnOfTable(v, space->def, cursor, tabl_col,
 						reg_base + j);
 		/*
-		 * If the column affinity is REAL but the number
+		 * If the column type is NUMBER but the number
 		 * is an integer, then it might be stored in the
 		 * table as an integer (using a compact
 		 * representation) then converted to REAL by an
diff --git a/src/box/sql/expr.c b/src/box/sql/expr.c
index 36da1ecdb511cf2d235f92addda836e5673e684a..06359523dc34d00ea0b0361d6971cebf3256554a 100644
--- a/src/box/sql/expr.c
+++ b/src/box/sql/expr.c
@@ -121,6 +121,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
@@ -283,8 +296,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;
 }
 
@@ -2354,15 +2366,15 @@ sqlite3FindInIndex(Parse * pParse,	/* Parsing context */
 		pTab = p->pSrc->a[0].pTab;
 		assert(v);	/* sqlite3GetVdbe() has always been previously called */
 
-		int affinity_ok = 1;
+		bool type_is_suitable = true;
 		int i;
 
-		/* Check that the affinity that will be used to perform each
-		 * comparison is the same as the affinity of each column in table
+		/* Check that the type that will be used to perform each
+		 * comparison is the same as the type of each column in table
 		 * on the RHS of the IN operator.  If it not, it is not possible to
 		 * use any index of the RHS table.
 		 */
-		for (i = 0; i < nExpr && affinity_ok; i++) {
+		for (i = 0; i < nExpr && type_is_suitable; i++) {
 			Expr *pLhs = sqlite3VectorFieldSubexpr(pX->pLeft, i);
 			int iCol = pEList->a[i].pExpr->iColumn;
 			/* RHS table */
@@ -2374,10 +2386,10 @@ sqlite3FindInIndex(Parse * pParse,	/* Parsing context */
 			 * of columns match.
 			 */
 			if (idx_type != lhs_type)
-				affinity_ok = 0;
+				type_is_suitable = false;
 		}
 
-		if (affinity_ok) {
+		if (type_is_suitable) {
 			/*
 			 * Here we need real space since further
 			 * it is used in cursor opening routine.
@@ -2494,7 +2506,7 @@ sqlite3FindInIndex(Parse * pParse,	/* Parsing context */
 					sqlite3VdbeJumpHere(v, iAddr);
 				}
 			}	/* End loop over indexes */
-		}	/* End if( affinity_ok ) */
+		}
 	}
 
 	/* End attempt to optimize using an index */
@@ -2543,22 +2555,22 @@ sqlite3FindInIndex(Parse * pParse,	/* Parsing context */
 
 /*
  * Argument pExpr is an (?, ?...) IN(...) expression. This
- * function allocates and returns a nul-terminated string containing
- * the affinities to be used for each column of the comparison.
+ * function allocates and returns a terminated string containing
+ * the types to be used for each column of the comparison.
  *
  * It is the responsibility of the caller to ensure that the returned
  * string is eventually freed using sqlite3DbFree().
  */
-static char *
-exprINAffinity(Parse * pParse, Expr * pExpr)
+static enum field_type *
+expr_in_type(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++) {
@@ -2567,12 +2579,12 @@ 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);
+				zRet[i] = lhs;
 			}
 		}
-		zRet[nVal] = '\0';
+		zRet[nVal] = field_type_MAX;
 	}
 	return zRet;
 }
@@ -2686,12 +2698,12 @@ sqlite3CodeSubselect(Parse * pParse,	/* Parsing context */
 			 * SELECT or the <exprlist>.
 			 *
 			 * If the 'x' expression is a column value, or the SELECT...
-			 * statement returns a column value, then the affinity of that
+			 * statement returns a column value, then the type of that
 			 * column is used to build the index keys. If both 'x' and the
-			 * SELECT... statement are columns, then numeric affinity is used
-			 * if either column has NUMERIC or INTEGER affinity. If neither
-			 * 'x' nor the SELECT... statement are columns, then numeric affinity
-			 * is used.
+			 * SELECT... statement are columns, then NUMBER type is used
+			 * if either column has NUMBER or INTEGER type. If neither
+			 * 'x' nor the SELECT... statement are columns,
+			 * then NUMBER type is used.
 			 */
 			pExpr->iTable = pParse->nTab++;
 			pExpr->is_ephemeral = 1;
@@ -2721,8 +2733,8 @@ sqlite3CodeSubselect(Parse * pParse,	/* Parsing context */
 					int i;
 					sqlite3SelectDestInit(&dest, SRT_Set,
 							      pExpr->iTable, reg_eph);
-					dest.zAffSdst =
-					    exprINAffinity(pParse, pExpr);
+					dest.dest_type =
+						expr_in_type(pParse, pExpr);
 					assert((pExpr->iTable & 0x0000FFFF) ==
 					       pExpr->iTable);
 					pSelect->iLimit = 0;
@@ -2731,12 +2743,12 @@ sqlite3CodeSubselect(Parse * pParse,	/* Parsing context */
 					if (sqlite3Select
 					    (pParse, pSelect, &dest)) {
 						sqlite3DbFree(pParse->db,
-							      dest.zAffSdst);
+							      dest.dest_type);
 						sql_key_info_unref(key_info);
 						return 0;
 					}
 					sqlite3DbFree(pParse->db,
-						      dest.zAffSdst);
+						      dest.dest_type);
 					assert(pEList != 0);
 					assert(pEList->nExpr > 0);
 					for (i = 0; i < nVal; i++) {
@@ -2753,8 +2765,8 @@ sqlite3CodeSubselect(Parse * pParse,	/* Parsing context */
 				 *
 				 * For each expression, build an index key from the evaluation and
 				 * store it in the temporary table. If <expr> is a column, then use
-				 * that columns affinity when building index keys. If <expr> is not
-				 * a column, use numeric affinity.
+				 * that columns types when building index keys. If <expr> is not
+				 * a column, use NUMBER type.
 				 */
 				int i;
 				ExprList *pList = pExpr->x.pList;
@@ -2790,8 +2802,8 @@ sqlite3CodeSubselect(Parse * pParse,	/* Parsing context */
 	 				sqlite3VdbeAddOp4(v, OP_MakeRecord, r3,
 							  1, r2, (char *)types,
 							  sizeof(types));
-					sqlite3ExprCacheAffinityChange(pParse,
-								       r3, 1);
+					sql_expr_type_cache_change(pParse,
+								   r3, 1);
 					sqlite3VdbeAddOp2(v, OP_IdxInsert, r2,
 							  reg_eph);
 				}
@@ -2942,7 +2954,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 */
@@ -2956,7 +2967,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 = expr_in_type(pParse, pExpr);
 	nVector = sqlite3ExprVectorSize(pExpr->pLeft);
 	aiMap =
 	    (int *)sqlite3DbMallocZero(pParse->db,
@@ -3102,10 +3114,14 @@ 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,
-								    nVector);
-	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);
+	/*
+	 * zAff will be freed at the end of VDBE execution, since
+	 * it was passed with P4_DYNAMIC flag.
+	 */
+	zAff = NULL;
 	if (destIfFalse == destIfNull) {
 		/* Combine Step 3 and Step 5 into a single opcode */
 		sqlite3VdbeAddOp4Int(v, OP_NotFound, pExpr->iTable,
@@ -3494,11 +3510,11 @@ sqlite3ExprCacheClear(Parse * pParse)
 }
 
 /*
- * Record the fact that an affinity change has occurred on iCount
+ * Record the fact that an type change has occurred on iCount
  * registers starting with iStart.
  */
 void
-sqlite3ExprCacheAffinityChange(Parse * pParse, int iStart, int iCount)
+sql_expr_type_cache_change(Parse *pParse, int iStart, int iCount)
 {
 	sqlite3ExprCacheRemove(pParse, iStart, iCount);
 }
@@ -3734,7 +3750,7 @@ sqlite3ExprCodeTarget(Parse * pParse, Expr * pExpr, int target)
 			}
 			sqlite3VdbeAddOp2(v, OP_Cast, target, pExpr->type);
 			testcase(usedAsColumnCache(pParse, inReg, inReg));
-			sqlite3ExprCacheAffinityChange(pParse, inReg, 1);
+			sql_expr_type_cache_change(pParse, inReg, 1);
 			return inReg;
 		}
 #endif				/* SQLITE_OMIT_CAST */
@@ -3934,7 +3950,7 @@ sqlite3ExprCodeTarget(Parse * pParse, Expr * pExpr, int target)
 			} else {
 				/*
 				 * Otherwise, use first arg as
-				 * expression affinity.
+				 * expression type.
 				 */
 				if (pFarg && pFarg->nExpr > 0)
 					pExpr->type = pFarg->a[0].pExpr->type;
@@ -4162,11 +4178,8 @@ sqlite3ExprCodeTarget(Parse * pParse, Expr * pExpr, int target)
 					pExpr->iColumn].name, target));
 
 #ifndef SQLITE_OMIT_FLOATING_POINT
-			/* If the column has REAL affinity, it may currently be stored as an
+			/* If the column has type NUMBER, it may currently be stored as an
 			 * integer. Use OP_Realify to make sure it is really real.
-			 *
-			 * EVIDENCE-OF: R-60985-57662 SQLite will convert the value back to
-			 * floating point when extracting it from the record.
 			 */
 			if (pExpr->iColumn >= 0 && def->fields[
 				pExpr->iColumn].type == FIELD_TYPE_NUMBER) {
diff --git a/src/box/sql/fkey.c b/src/box/sql/fkey.c
index d5a7283827baf9370e2ab8bbe3d3aef04028e4f7..01fe1c84d3cbcdadac5ddcb1b8338a755f78352f 100644
--- a/src/box/sql/fkey.c
+++ b/src/box/sql/fkey.c
@@ -423,7 +423,7 @@ fkey_scan_children(struct Parse *parser, struct SrcList *src, struct Table *tab,
 	 * <parent-key1> = <child-key1> AND <parent-key2> = <child-key2> ...
 	 *
 	 * The collation sequence used for the comparison should
-	 * be that of the parent key columns. The affinity of the
+	 * be that of the parent key columns. The type of the
 	 * parent key column should be applied to each child key
 	 * value before the comparison takes place.
 	 */
@@ -783,7 +783,7 @@ fkey_action_trigger(struct Parse *pParse, struct Table *pTab, struct fkey *fkey,
 		 * Create the expression "old.to_col = from_col".
 		 * It is important that the "old.to_col" term is
 		 * on the LHS of the = operator, so that the
-		 * affinity and collation sequence associated with
+		 * type and collation sequence associated with
 		 * the parent table are used for the comparison.
 		 */
 		struct Expr *to_col =
diff --git a/src/box/sql/insert.c b/src/box/sql/insert.c
index 35f30f191be3f9a42453cf2943e6f86ebde52693..c69476117adc30d776f9b5e5086d48f23b17d366 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)
 {
@@ -630,7 +603,7 @@ sqlite3Insert(Parse * pParse,	/* Parser context */
 		/* If this is an INSERT on a view with an INSTEAD OF INSERT trigger,
 		 * do not attempt any conversions before assembling the record.
 		 * If this is a real table, attempt conversions as required by the
-		 * table column affinities.
+		 * table column types.
 		 */
 		if (!is_view)
 			sql_emit_table_types(v, pTab->def, regCols + 1);
@@ -1225,12 +1198,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 7407b559f712d3df105a75b0e946ebfc7027a4d2..4d2894616be4302460a90af7666192d34613628c 100644
--- a/src/box/sql/select.c
+++ b/src/box/sql/select.c
@@ -134,7 +134,7 @@ sqlite3SelectDestInit(SelectDest * pDest, int eDest, int iParm, int reg_eph)
 	pDest->eDest = (u8) eDest;
 	pDest->iSDParm = iParm;
 	pDest->reg_eph = reg_eph;
-	pDest->zAffSdst = 0;
+	pDest->dest_type = NULL;
 	pDest->iSdst = 0;
 	pDest->nSdst = 0;
 }
@@ -1200,18 +1200,16 @@ 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,
-						strlen(pDest->zAffSdst));
+					field_type_sequence_dup(pParse,
+								pDest->dest_type,
+								nResultCol);
 				sqlite3VdbeAddOp4(v, OP_MakeRecord, regResult,
 						  nResultCol, r1, (char *)types,
 						  P4_DYNAMIC);
-				sqlite3ExprCacheAffinityChange(pParse,
-							       regResult,
-							       nResultCol);
+				sql_expr_type_cache_change(pParse,
+							   regResult,
+							   nResultCol);
 				sqlite3VdbeAddOp2(v, OP_IdxInsert, r1, pDest->reg_eph);
 				sqlite3ReleaseTempReg(pParse, r1);
 			}
@@ -1255,9 +1253,9 @@ selectInnerLoop(Parse * pParse,		/* The parser context */
 			} else {
 				sqlite3VdbeAddOp2(v, OP_ResultRow, regResult,
 						  nResultCol);
-				sqlite3ExprCacheAffinityChange(pParse,
-							       regResult,
-							       nResultCol);
+				sql_expr_type_cache_change(pParse,
+							   regResult,
+							   nResultCol);
 			}
 			break;
 		}
@@ -1628,16 +1626,13 @@ 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,
-								   strlen(pDest->zAffSdst));
+				field_type_sequence_dup(pParse, pDest->dest_type,
+							nColumn);
 			sqlite3VdbeAddOp4(v, OP_MakeRecord, regRow, nColumn,
 					  regTupleid, (char *)types,
 					  P4_DYNAMIC);
-			sqlite3ExprCacheAffinityChange(pParse, regRow, nColumn);
+			sql_expr_type_cache_change(pParse, regRow, nColumn);
 			sqlite3VdbeAddOp2(v, OP_IdxInsert, regTupleid, pDest->reg_eph);
 			break;
 		}
@@ -1652,9 +1647,9 @@ generateSortTail(Parse * pParse,	/* Parsing context */
 			if (eDest == SRT_Output) {
 				sqlite3VdbeAddOp2(v, OP_ResultRow, pDest->iSdst,
 						  nColumn);
-				sqlite3ExprCacheAffinityChange(pParse,
-							       pDest->iSdst,
-							       nColumn);
+				sql_expr_type_cache_change(pParse,
+							   pDest->iSdst,
+							   nColumn);
 			} else {
 				sqlite3VdbeAddOp1(v, OP_Yield, pDest->iSDParm);
 			}
@@ -1968,9 +1963,7 @@ sqlite3SelectAddColumnTypeAndCollation(Parse * pParse,		/* Parsing contexts */
 	a = pSelect->pEList->a;
 	for (uint32_t i = 0; i < pTab->def->field_count; i++) {
 		p = a[i].pExpr;
-		enum field_type type = sql_expr_type(p);
-		pTab->def->fields[i].affinity = sql_field_type_to_affinity(type);
-		pTab->def->fields[i].type = type;
+		pTab->def->fields[i].type = sql_expr_type(p);
 		bool is_found;
 		uint32_t coll_id;
 
@@ -3090,13 +3083,13 @@ 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,
-								   strlen(dest->zAffSdst));
+				field_type_sequence_dup(parse, dest->dest_type,
+							in->nSdst);
 			sqlite3VdbeAddOp4(v, OP_MakeRecord, in->iSdst,
 					  in->nSdst, r1, (char *)types,
 					  P4_DYNAMIC);
-			sqlite3ExprCacheAffinityChange(parse, in->iSdst,
-						       in->nSdst);
+			sql_expr_type_cache_change(parse, in->iSdst,
+						   in->nSdst);
 			sqlite3VdbeAddOp2(v, OP_IdxInsert, r1, dest->reg_eph);
 			sqlite3ReleaseTempReg(parse, r1);
 			break;
@@ -3141,8 +3134,7 @@ generateOutputSubroutine(struct Parse *parse, struct Select *p,
 			assert(dest->eDest == SRT_Output);
 			sqlite3VdbeAddOp2(v, OP_ResultRow, in->iSdst,
 					  in->nSdst);
-			sqlite3ExprCacheAffinityChange(parse, in->iSdst,
-						       in->nSdst);
+			sql_expr_type_cache_change(parse, in->iSdst, in->nSdst);
 			break;
 		}
 	}
@@ -5370,7 +5362,7 @@ updateAccumulator(Parse * pParse, AggInfo * pAggInfo)
 		sqlite3VdbeAddOp3(v, OP_AggStep0, 0, regAgg, pF->iMem);
 		sqlite3VdbeAppendP4(v, pF->pFunc, P4_FUNCDEF);
 		sqlite3VdbeChangeP5(v, (u8) nArg);
-		sqlite3ExprCacheAffinityChange(pParse, regAgg, nArg);
+		sql_expr_type_cache_change(pParse, regAgg, nArg);
 		sqlite3ReleaseTempRange(pParse, regAgg, nArg);
 		if (addrNext) {
 			sqlite3VdbeResolveLabel(v, addrNext);
diff --git a/src/box/sql/sqliteInt.h b/src/box/sql/sqliteInt.h
index 61f3165501eedde7edc354beb500bcfe7ce5e215..7d4675f592879c7d099cc6efed5c30a5250b5ea7 100644
--- a/src/box/sql/sqliteInt.h
+++ b/src/box/sql/sqliteInt.h
@@ -1629,7 +1629,7 @@ struct sqlite3 {
 #define SQLITE_MAGIC_ZOMBIE   0x64cffc7f	/* Close with last statement close */
 
 /**
- * SQL type definition. Now it is an alias to affinity, but in
+ * SQL type definition. Now it is an alias to type, but in
  * future it will have some attributes like number of chars in
  * VARCHAR(<number of chars>).
  */
@@ -1793,23 +1793,17 @@ struct Savepoint {
 				 (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.
+ * Additional bit values that can be ORed with an type without
+ * changing the type.
  *
  * The SQLITE_NOTNULL flag is a combination of NULLEQ and JUMPIFNULL.
  * It causes an assert() to fire if either operand to a comparison
  * 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_JUMPIFNULL   0x10	/* jumps if either operand is NULL */
 #define SQLITE_STOREP2      0x20	/* Store result in reg[P2] rather than jump */
+#define SQLITE_KEEPNULL     0x40	/* Used by vector == or <> */
 #define SQLITE_NULLEQ       0x80	/* NULL=NULL */
 #define SQLITE_NOTNULL      0x90	/* Assert that operands are never NULL */
 
@@ -2556,7 +2550,7 @@ struct Select {
  *
  *     SRT_Set         The result must be a single column.  Store each
  *                     row of result as the key in table pDest->iSDParm.
- *                     Apply the affinity pDest->affSdst before storing
+ *                     Apply the type pDest->det_type before storing
  *                     results.  Used to implement "IN (SELECT ...)".
  *
  *     SRT_EphemTab    Create an temporary table pDest->iSDParm and store
@@ -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 */
+	/** Type used when eDest==SRT_Set */
+	enum field_type *dest_type;
 	int iSDParm;		/* A parameter used by the eDest disposal method */
 	/** Register containing ephemeral's space pointer. */
 	int reg_eph;
@@ -3445,19 +3440,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, uint32_t len);
-
 /**
  * Compile view, i.e. create struct Select from
  * 'CREATE VIEW...' string, and assign cursors to each table from
@@ -3658,7 +3640,7 @@ void sqlite3ExprCachePush(Parse *);
 void sqlite3ExprCachePop(Parse *);
 void sqlite3ExprCacheRemove(Parse *, int, int);
 void sqlite3ExprCacheClear(Parse *);
-void sqlite3ExprCacheAffinityChange(Parse *, int, int);
+void sql_expr_type_cache_change(Parse *, int, int);
 void sqlite3ExprCode(Parse *, Expr *, int);
 void sqlite3ExprCodeCopy(Parse *, Expr *, int);
 void sqlite3ExprCodeFactorable(Parse *, Expr *, int);
@@ -4239,33 +4221,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);
@@ -4311,6 +4266,13 @@ expr_cmp_mutual_type(struct Expr *pExpr);
 enum field_type
 sql_expr_type(struct 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
@@ -4414,7 +4376,7 @@ void sqlite3ValueFree(sqlite3_value *);
 sqlite3_value *sqlite3ValueNew(sqlite3 *);
 int sqlite3ValueFromExpr(sqlite3 *, Expr *, enum field_type type,
 			 sqlite3_value **);
-void sqlite3ValueApplyAffinity(sqlite3_value *, enum field_type type);
+void sql_value_apply_type(sqlite3_value *val, enum field_type type);
 #ifndef SQLITE_AMALGAMATION
 extern const unsigned char sqlite3OpcodeProperty[];
 extern const char sqlite3StrBINARY[];
@@ -4662,19 +4624,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 02abc00888df582b3cf33bb2deafc90350e8d284..51ba9ac9f36d713f11f01c64cc5c851b3a8724f6 100644
--- a/src/box/sql/vdbe.c
+++ b/src/box/sql/vdbe.c
@@ -289,7 +289,7 @@ allocateCursor(
  * if there is an exact integer representation of the quantity.
  */
 static int
-applyNumericAffinity(Mem *pRec, int bTryForInt)
+mem_apply_numeric_type(Mem *pRec, int bTryForInt)
 {
 	double rValue;
 	i64 iValue;
@@ -301,7 +301,7 @@ applyNumericAffinity(Mem *pRec, int bTryForInt)
 	} else {
 		pRec->u.r = rValue;
 		pRec->flags |= MEM_Real;
-		if (bTryForInt) sqlite3VdbeIntegerAffinity(pRec);
+		if (bTryForInt) mem_apply_integer_type(pRec);
 	}
 	return 0;
 }
@@ -382,7 +382,7 @@ int sqlite3_value_numeric_type(sqlite3_value *pVal) {
 	int eType = sqlite3_value_type(pVal);
 	if (eType==SQLITE_TEXT) {
 		Mem *pMem = (Mem*)pVal;
-		applyNumericAffinity(pMem, 0);
+		mem_apply_numeric_type(pMem, 0);
 		eType = sqlite3_value_type(pVal);
 	}
 	return eType;
@@ -393,7 +393,7 @@ int sqlite3_value_numeric_type(sqlite3_value *pVal) {
  * not the internal Mem* type.
  */
 void
-sqlite3ValueApplyAffinity(
+sql_value_apply_type(
 	sqlite3_value *pVal,
 	enum field_type type)
 {
@@ -421,7 +421,7 @@ static u16 SQLITE_NOINLINE computeNumericType(Mem *pMem)
  * Return the numeric type for pMem, either MEM_Int or MEM_Real or both or
  * none.
  *
- * Unlike applyNumericAffinity(), this routine does not modify pMem->flags.
+ * Unlike mem_apply_numeric_type(), this routine does not modify pMem->flags.
  * But it does set pMem->u.r and pMem->u.i appropriately.
  */
 static u16 numericType(Mem *pMem)
@@ -1681,7 +1681,7 @@ case OP_Remainder: {           /* same as TK_REM, in1, in2, out3 */
 		pOut->u.r = rB;
 		MemSetTypeFlag(pOut, MEM_Real);
 		if (((type1|type2)&MEM_Real)==0 && !bIntint) {
-			sqlite3VdbeIntegerAffinity(pOut);
+			mem_apply_integer_type(pOut);
 		}
 #endif
 	}
@@ -2023,14 +2023,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
@@ -2046,6 +2038,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).
@@ -2073,14 +2069,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
@@ -2119,7 +2107,6 @@ case OP_Le:               /* same as TK_LE, jump, in1, in3 */
 case OP_Gt:               /* same as TK_GT, jump, in1, in3 */
 case OP_Ge: {             /* same as TK_GE, jump, in1, in3 */
 	int res, res2;      /* Result of the comparison of pIn1 against pIn3 */
-	char affinity;      /* Affinity to use for comparison */
 	u32 flags1;         /* Copy of initial value of pIn1->flags */
 	u32 flags3;         /* Copy of initial value of pIn3->flags */
 
@@ -2164,17 +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) {
+		enum field_type type = pOp->p5 & FIELD_TYPE_MASK;
+		if (sql_type_is_numeric(type)) {
 			if ((flags1 | flags3)&MEM_Str) {
 				if ((flags1 & (MEM_Int|MEM_Real|MEM_Str))==MEM_Str) {
-					applyNumericAffinity(pIn1,0);
+					mem_apply_numeric_type(pIn1, 0);
 					testcase( flags3!=pIn3->flags); /* Possible if pIn1==pIn3 */
 					flags3 = pIn3->flags;
 				}
 				if ((flags3 & (MEM_Int|MEM_Real|MEM_Str))==MEM_Str) {
-					if (applyNumericAffinity(pIn3,0) != 0) {
+					if (mem_apply_numeric_type(pIn3, 0) != 0) {
 						diag_set(ClientError,
 							 ER_SQL_TYPE_MISMATCH,
 							 sqlite3_value_text(pIn3),
@@ -2194,7 +2180,7 @@ case OP_Ge: {             /* same as TK_GE, jump, in1, in3 */
 				res = 0;
 				goto compare_op;
 			}
-		} else if (affinity==AFFINITY_TEXT) {
+		} else if (type == FIELD_TYPE_STRING) {
 			if ((flags1 & MEM_Str)==0 && (flags1 & (MEM_Int|MEM_Real))!=0) {
 				testcase( pIn1->flags & MEM_Int);
 				testcase( pIn1->flags & MEM_Real);
@@ -2842,7 +2828,7 @@ case OP_ApplyType: {
  * in an index.  The OP_Column opcode can decode the record later.
  *
  * P4 may be a string that is P2 characters long.  The nth character of the
- * string indicates the column affinity that should be used for the nth
+ * string indicates the column type that should be used for the nth
  * field of the index key.
  *
  * If P4 is NULL then all index fields have type SCALAR.
@@ -2885,8 +2871,7 @@ case OP_MakeRecord: {
 	pOut = &aMem[pOp->p3];
 	memAboutToChange(p, pOut);
 
-	/* Apply the requested affinity to all inputs
-	 */
+	/* Apply the requested types to all inputs */
 	assert(pData0<=pLast);
 	if (types != NULL) {
 		pRec = pData0;
@@ -3520,7 +3505,7 @@ case OP_SeekGT: {       /* jump, in3 */
 		 */
 		pIn3 = &aMem[reg_ipk];
 		if ((pIn3->flags & (MEM_Int|MEM_Real|MEM_Str))==MEM_Str) {
-			applyNumericAffinity(pIn3, 0);
+			mem_apply_numeric_type(pIn3, 0);
 		}
 		int64_t i;
 		if ((pIn3->flags & MEM_Int) == MEM_Int) {
diff --git a/src/box/sql/vdbeInt.h b/src/box/sql/vdbeInt.h
index 32b1b6c174f09213815463fbb8c55b589c7fe451..1720fd1c7d25d270106f59c01f507706f03f5192 100644
--- a/src/box/sql/vdbeInt.h
+++ b/src/box/sql/vdbeInt.h
@@ -231,7 +231,6 @@ struct Mem {
 #define MEM_Blob      0x0010	/* Value is a BLOB */
 #define MEM_Bool      0x0020    /* Value is a bool */
 #define MEM_Ptr       0x0040	/* Value is a generic pointer */
-#define MEM_AffMask   0x003f	/* Mask of affinity bits */
 #define MEM_Frame     0x0080	/* Value is a VdbeFrame object */
 #define MEM_Undefined 0x0100	/* Value is undefined */
 #define MEM_Cleared   0x0200	/* NULL set by OP_Null, not from data */
@@ -480,7 +479,7 @@ int sqlite3VdbeMemStringify(Mem *, u8);
 int sqlite3VdbeIntValue(Mem *, int64_t *);
 int sqlite3VdbeMemIntegerify(Mem *, bool is_forced);
 int sqlite3VdbeRealValue(Mem *, double *);
-int sqlite3VdbeIntegerAffinity(Mem *);
+int mem_apply_integer_type(Mem *);
 int sqlite3VdbeMemRealify(Mem *);
 int sqlite3VdbeMemNumerify(Mem *);
 int sqlite3VdbeMemCast(Mem *, enum field_type type);
diff --git a/src/box/sql/vdbeapi.c b/src/box/sql/vdbeapi.c
index 9e57af051ff9c7e3f4ea187da128217dbd20da88..07c496207d85cf28d0149d2de256482c19f3cc79 100644
--- a/src/box/sql/vdbeapi.c
+++ b/src/box/sql/vdbeapi.c
@@ -280,7 +280,7 @@ sqlite3_value_type(sqlite3_value * pVal)
 		SQLITE_INTEGER,	/* 0x1e */
 		SQLITE_NULL,	/* 0x1f */
 	};
-	return aType[pVal->flags & MEM_AffMask];
+	return aType[pVal->flags & MEM_TypeMask];
 }
 
 /* Make a copy of an sqlite3_value object
diff --git a/src/box/sql/vdbeaux.c b/src/box/sql/vdbeaux.c
index 975f114df3dab18e86c55f6d36c98581bbb53383..5f2cb8448dd873df82a86ac276c9e73a50ccf382 100644
--- a/src/box/sql/vdbeaux.c
+++ b/src/box/sql/vdbeaux.c
@@ -3493,8 +3493,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().
  */
@@ -3508,7 +3507,7 @@ sqlite3VdbeGetBoundValue(Vdbe * v, int iVar, u8 aff)
 			sqlite3_value *pRet = sqlite3ValueNew(v->db);
 			if (pRet) {
 				sqlite3VdbeMemCopy((Mem *) pRet, pMem);
-				sqlite3ValueApplyAffinity(pRet, aff);
+				sql_value_apply_type(pRet, aff);
 			}
 			return pRet;
 		}
diff --git a/src/box/sql/vdbemem.c b/src/box/sql/vdbemem.c
index a5dd7400a0ab68233b8f26fb815f9f811778a1f5..2865fd68c305c8c16929c48c7136a7d6ec384bc0 100644
--- a/src/box/sql/vdbemem.c
+++ b/src/box/sql/vdbemem.c
@@ -504,7 +504,7 @@ sqlite3VdbeRealValue(Mem * pMem, double *v)
  * MEM_Int if we can.
  */
 int
-sqlite3VdbeIntegerAffinity(Mem * pMem)
+mem_apply_integer_type(Mem *pMem)
 {
 	int rc;
 	i64 ix;
@@ -584,7 +584,7 @@ sqlite3VdbeMemNumerify(Mem * pMem)
 				return SQLITE_ERROR;
 			pMem->u.r = v;
 			MemSetTypeFlag(pMem, MEM_Real);
-			sqlite3VdbeIntegerAffinity(pMem);
+			mem_apply_integer_type(pMem);
 		}
 	}
 	assert((pMem->flags & (MEM_Int | MEM_Real | MEM_Null)) != 0);
@@ -593,10 +593,10 @@ sqlite3VdbeMemNumerify(Mem * pMem)
 }
 
 /*
- * Cast the datatype of the value in pMem according to the affinity
- * "aff".  Casting is different from applying affinity in that a cast
+ * Cast the datatype of the value in pMem according to the type
+ * @type.  Casting is different from applying type in that a cast
  * is forced.  In other words, the value is converted into the desired
- * affinity even if that results in loss of data.  This routine is
+ * type even if that results in loss of data.  This routine is
  * used (for example) to implement the SQL "cast()" operator.
  */
 int
@@ -643,7 +643,7 @@ sqlite3VdbeMemCast(Mem * pMem, enum field_type type)
 		assert(type == FIELD_TYPE_STRING);
 		assert(MEM_Str == (MEM_Blob >> 3));
 		pMem->flags |= (pMem->flags & MEM_Blob) >> 3;
-		sqlite3ValueApplyAffinity(pMem, FIELD_TYPE_STRING);
+			sql_value_apply_type(pMem, FIELD_TYPE_STRING);
 		assert(pMem->flags & MEM_Str || pMem->db->mallocFailed);
 		pMem->flags &= ~(MEM_Int | MEM_Real | MEM_Blob | MEM_Zero);
 		return SQLITE_OK;
@@ -1152,7 +1152,7 @@ valueNew(sqlite3 * db, struct ValueNewStat4Ctx *p)
  * error occurs, output parameter (*ppVal) is set to point to a value
  * object containing the result before returning SQLITE_OK.
  *
- * Affinity aff is applied to the result of the function before returning.
+ * Type @type is applied to the result of the function before returning.
  * If the result is a text value, the sqlite3_value object uses encoding
  * enc.
  *
@@ -1222,7 +1222,7 @@ valueFromFunction(sqlite3 * db,	/* The database connection */
 		rc = ctx.isError;
 		sqlite3ErrorMsg(pCtx->pParse, "%s", sqlite3_value_text(pVal));
 	} else {
-		sqlite3ValueApplyAffinity(pVal, type);
+		sql_value_apply_type(pVal, type);
 		assert(rc == SQLITE_OK);
 	}
 	pCtx->pParse->rc = rc;
@@ -1285,7 +1285,7 @@ valueFromExpr(sqlite3 * db,	/* The database connection */
 		testcase(rc != SQLITE_OK);
 		if (*ppVal) {
 			sqlite3VdbeMemCast(*ppVal, pExpr->type);
-			sqlite3ValueApplyAffinity(*ppVal, type);
+			sql_value_apply_type(*ppVal, type);
 		}
 		return rc;
 	}
@@ -1318,9 +1318,9 @@ valueFromExpr(sqlite3 * db,	/* The database connection */
 		}
 		if ((op == TK_INTEGER || op == TK_FLOAT) &&
 		    type == FIELD_TYPE_SCALAR) {
-			sqlite3ValueApplyAffinity(pVal, FIELD_TYPE_NUMBER);
+			sql_value_apply_type(pVal, FIELD_TYPE_NUMBER);
 		} else {
-			sqlite3ValueApplyAffinity(pVal, type);
+			sql_value_apply_type(pVal, type);
 		}
 		if (pVal->flags & (MEM_Int | MEM_Real))
 			pVal->flags &= ~MEM_Str;
@@ -1339,7 +1339,7 @@ valueFromExpr(sqlite3 * db,	/* The database connection */
 			} else {
 				pVal->u.i = -pVal->u.i;
 			}
-			sqlite3ValueApplyAffinity(pVal, type);
+			sql_value_apply_type(pVal, type);
 		}
 	} else if (op == TK_NULL) {
 		pVal = valueNew(db, pCtx);
@@ -1502,7 +1502,7 @@ stat4ValueFromExpr(Parse * pParse,	/* Parse context */
 				rc = sqlite3VdbeMemCopy((Mem *) pVal,
 							&v->aVar[iBindVar - 1]);
 				if (rc == SQLITE_OK) {
-					sqlite3ValueApplyAffinity(pVal, type);
+					sql_value_apply_type(pVal, type);
 				}
 				pVal->db = pParse->db;
 			}
@@ -1536,7 +1536,7 @@ stat4ValueFromExpr(Parse * pParse,	/* Parse context */
  * vector components that match either of the two latter criteria listed
  * above.
  *
- * Before any value is appended to the record, the affinity of the
+ * Before any value is appended to the record, the type of the
  * corresponding column within index pIdx is applied to it. Before
  * this function returns, output parameter *pnExtract is set to the
  * number of values appended to the record.
diff --git a/src/box/sql/where.c b/src/box/sql/where.c
index f602f9062005a50d5a4a2e0c16fa38fb2f3fa7d2..bd6f15bf5f2c61863a7ca07e1b8d007648799973 100644
--- a/src/box/sql/where.c
+++ b/src/box/sql/where.c
@@ -295,7 +295,7 @@ whereScanNext(WhereScan * pScan)
 					}
 					if ((pTerm->eOperator & pScan->
 					     opMask) != 0) {
-						/* Verify the affinity and collating sequence match */
+						/* Verify the type and collating sequence match */
 						if ((pTerm->eOperator & WO_ISNULL) == 0) {
 							pX = pTerm->pExpr;
 							enum field_type expr_type =
@@ -1139,15 +1139,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 519111d4e9483a0ac1e03c5914fdc4180e29fd40..99906b1963cbe7c5ad440e1e57dd0bbfc6bbe010 100644
--- a/src/box/sql/wherecode.c
+++ b/src/box/sql/wherecode.c
@@ -363,71 +363,68 @@ disableTerm(WhereLevel * pLevel, WhereTerm * pTerm)
 	}
 }
 
-/*
- * Code an OP_ApplyType opcode to apply the column type string types
- * to the n registers starting at base.
+/**
+ * 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
- * beginning and end of zAff are ignored.  If all entries in zAff are
- * AFFINITY_BLOB, then no code gets generated.
+ * As an optimization, SCALAR entries (which are no-ops) at the
+ * beginning and end of @types are ignored.  If all entries in
+ * @types are 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.
+ * This routine makes its own copy of @types so that the caller is
+ * free to modify @types after this routine returns.
  */
 static void
-codeApplyAffinity(Parse * pParse, int base, int n, char *zAff)
+emit_apply_type(Parse *pParse, int base, int n, enum field_type *types)
 {
 	Vdbe *v = pParse->pVdbe;
-	if (zAff == 0) {
+	if (types == NULL) {
 		assert(pParse->db->mallocFailed);
 		return;
 	}
 	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 && types[0] == FIELD_TYPE_SCALAR) {
 		n--;
 		base++;
-		zAff++;
+		types++;
 	}
-	while (n > 1 && zAff[n - 1] == AFFINITY_BLOB) {
+	while (n > 1 && types[n - 1] == FIELD_TYPE_SCALAR) {
 		n--;
 	}
 
 	if (n > 0) {
-		enum field_type *types =
-			sql_affinity_str_to_field_type_str(zAff, n);
-		sqlite3VdbeAddOp4(v, OP_ApplyType, base, n, 0, (char *)types,
-				  P4_DYNAMIC);
-		sqlite3ExprCacheAffinityChange(pParse, base, n);
+		enum field_type *types_dup = field_type_sequence_dup(pParse,
+								     types, n);
+		sqlite3VdbeAddOp4(v, OP_ApplyType, base, n, 0,
+				  (char *) types_dup, P4_DYNAMIC);
+		sql_expr_type_cache_change(pParse, base, n);
 	}
 }
 
-/*
- * Expression pRight, which is the RHS of a comparison operation, is
+/**
+ * Expression @rhs, 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:
+ * Before the comparison operation, types @types are to be applied
+ * to the @rhs 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.
+ *   * the comparison will be performed with no type, or
+ *   * the type change in @types is guaranteed not to change the value.
  */
 static void
-updateRangeAffinityStr(Expr * pRight,	/* RHS of comparison */
-		       int n,		/* Number of vector elements in comparison */
-		       char *zAff)	/* Affinity string to modify */
+expr_cmp_update_rhs_type(struct Expr *rhs, int n, enum field_type *types)
 {
-	int i;
-	for (i = 0; i < n; i++) {
-		enum field_type type = sql_affinity_to_field_type(zAff[i]);
-		Expr *p = sqlite3VectorFieldSubexpr(pRight, i);
+	for (int i = 0; i < n; i++) {
+		Expr *p = sqlite3VectorFieldSubexpr(rhs, 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, types[i]) == FIELD_TYPE_SCALAR ||
+		    sql_expr_needs_no_type_change(p, types[i])) {
+			types[i] = FIELD_TYPE_SCALAR;
 		}
 	}
 }
@@ -651,7 +648,7 @@ codeEqualityTerm(Parse * pParse,	/* The parsing context */
  * In the example above nEq==2.  But this subroutine works for any value
  * of nEq including 0.  If nEq==0, this routine is nearly a no-op.
  * The only thing it does is allocate the pLevel->iMem memory cell and
- * compute the affinity string.
+ * compute the types array.
  *
  * The nExtraReg parameter is 0 or 1.  It is 0 if all WHERE clause constraints
  * are == or IN and are covered by the nEq.  nExtraReg is 1 if there is
@@ -666,27 +663,27 @@ codeEqualityTerm(Parse * pParse,	/* The parsing context */
  * this routine allocates an additional nEq memory cells for internal
  * use.
  *
- * Before returning, *pzAff is set to point to a buffer containing a
- * copy of the column affinity string of the index allocated using
+ * Before returning, @types is set to point to a buffer containing a
+ * copy of the column types array 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:
+ * with equality constraints that use SCALAR type are set to
+ * SCALAR. This is to deal with SQL such as the following:
  *
- *   CREATE TABLE t1(a TEXT PRIMARY KEY, b);
+ *   CREATE TABLE t1(a TEXT PRIMARY KEY, b BLOB);
  *   SELECT ... FROM t1 AS t2, t1 WHERE t1.a = t2.b;
  *
- * In the example above, the index on t1(a) has TEXT affinity. But since
- * the right hand side of the equality constraint (t2.b) has BLOB/NONE affinity,
+ * In the example above, the index on t1(a) has STRING type. But since
+ * the right hand side of the equality constraint (t2.b) has SCALAR type,
  * 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.
+ * a key to search the index. Hence the first byte in the returned type
+ * 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 **res_type)
 {
 	u16 nEq;		/* The number of == or IN constraints to code */
 	u16 nSkip;		/* Number of left-most columns to skip */
@@ -710,12 +707,8 @@ 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);
-	assert(zAff != 0 || pParse->db->mallocFailed);
+	enum field_type *type = sql_index_type_str(pParse->db, idx_def);
+	assert(type != NULL || pParse->db->mallocFailed);
 
 	if (nSkip) {
 		int iIdxCur = pLevel->iIdxCur;
@@ -740,7 +733,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];
@@ -762,13 +754,13 @@ codeAllEqualityTerms(Parse * pParse,	/* Parsing context */
 		}
 		if (pTerm->eOperator & WO_IN) {
 			if (pTerm->pExpr->flags & EP_xIsSelect) {
-				/* No affinity ever needs to be (or should be) applied to a value
+				/* No type ever needs to be (or should be) applied to a value
 				 * from the RHS of an "? IN (SELECT ...)" expression. The
 				 * sqlite3FindInIndex() routine has already ensured that the
-				 * affinity of the comparison has been applied to the value.
+				 * type of the comparison has been applied to the value.
 				 */
-				if (zAff)
-					zAff[j] = AFFINITY_BLOB;
+				if (type != NULL)
+					type[j] = FIELD_TYPE_SCALAR;
 			}
 		} else if ((pTerm->eOperator & WO_ISNULL) == 0) {
 			Expr *pRight = pTerm->pExpr->pRight;
@@ -777,21 +769,19 @@ codeAllEqualityTerms(Parse * pParse,	/* Parsing context */
 						  pLevel->addrBrk);
 				VdbeCoverage(v);
 			}
-			if (zAff) {
-				enum field_type type =
+			if (type != NULL) {
+				enum field_type rhs_type =
 					sql_expr_type(pRight);
-				enum field_type idx_type =
-					sql_affinity_to_field_type(zAff[j]);
-				if (sql_type_result(type, idx_type) ==
+				if (sql_type_result(rhs_type, type[j]) ==
 				    FIELD_TYPE_SCALAR) {
-					zAff[j] = AFFINITY_BLOB;
+					type[j] = FIELD_TYPE_SCALAR;
 				}
-				if (sql_expr_needs_no_type_change(pRight, idx_type))
-					zAff[j] = AFFINITY_BLOB;
+				if (sql_expr_needs_no_type_change(pRight, type[j]))
+					type[j] = FIELD_TYPE_SCALAR;
 			}
 		}
 	}
-	*pzAff = zAff;
+	*res_type = type;
 	return regBase;
 }
 
@@ -1000,8 +990,10 @@ 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 */
+		/* Types for start of range constraint. */
+		enum field_type *start_types;
+		/* Types for end of range constraint */
+		enum field_type *end_types = NULL;
 		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
@@ -1115,10 +1107,14 @@ 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]);
+					 &start_types);
+		if (start_types != NULL && nTop) {
+			uint32_t len = 0;
+			for (enum field_type *tmp = &start_types[nEq];
+			     *tmp != field_type_MAX; tmp++, len++);
+			uint32_t sz = len * sizeof(enum field_type);
+			end_types = sqlite3DbMallocRaw(db, sz);
+			memcpy(end_types, &start_types[nEq], sz);
 		}
 		addrNxt = pLevel->addrNxt;
 
@@ -1146,9 +1142,9 @@ sqlite3WhereCodeOneLoopStart(WhereInfo * pWInfo,	/* Complete information about t
 				VdbeCoverage(v);
 			}
 
-			if (zStartAff) {
-				updateRangeAffinityStr(pRight, nBtm,
-						       &zStartAff[nEq]);
+			if (start_types) {
+				expr_cmp_update_rhs_type(pRight, nBtm,
+							 &start_types[nEq]);
 			}
 			nConstraint += nBtm;
 			testcase(pRangeStart->wtFlags & TERM_VIRTUAL);
@@ -1192,8 +1188,8 @@ sqlite3WhereCodeOneLoopStart(WhereInfo * pWInfo,	/* Complete information about t
 				}
 			}
 		}
-		codeApplyAffinity(pParse, regBase, nConstraint - bSeekPastNull,
-				  zStartAff);
+		emit_apply_type(pParse, regBase, nConstraint - bSeekPastNull,
+				start_types);
 		if (pLoop->nSkip > 0 && nConstraint == pLoop->nSkip) {
 			/* The skip-scan logic inside the call to codeAllEqualityConstraints()
 			 * above has already left the cursor sitting on the correct row,
@@ -1243,10 +1239,10 @@ sqlite3WhereCodeOneLoopStart(WhereInfo * pWInfo,	/* Complete information about t
 						  addrNxt);
 				VdbeCoverage(v);
 			}
-			if (zEndAff) {
-				updateRangeAffinityStr(pRight, nTop, zEndAff);
-				codeApplyAffinity(pParse, regBase + nEq, nTop,
-						  zEndAff);
+			if (end_types) {
+				expr_cmp_update_rhs_type(pRight, nTop, end_types);
+				emit_apply_type(pParse, regBase + nEq, nTop,
+						end_types);
 			} else {
 				assert(pParse->db->mallocFailed);
 			}
@@ -1263,8 +1259,8 @@ sqlite3WhereCodeOneLoopStart(WhereInfo * pWInfo,	/* Complete information about t
 			endEq = 0;
 			nConstraint++;
 		}
-		sqlite3DbFree(db, zStartAff);
-		sqlite3DbFree(db, zEndAff);
+		sqlite3DbFree(db, start_types);
+		sqlite3DbFree(db, end_types);
 
 		/* Top of the loop body */
 		pLevel->p2 = sqlite3VdbeCurrentAddr(v);
diff --git a/src/box/sql/whereexpr.c b/src/box/sql/whereexpr.c
index 1fb5fa59331a1be0ddd485eb5ed3a5cf896f869a..b016d18efe1c3025bb9ccfcbd68cb4809e95856b 100644
--- a/src/box/sql/whereexpr.c
+++ b/src/box/sql/whereexpr.c
@@ -272,7 +272,7 @@ like_optimization_is_valid(Parse *pParse, Expr *pExpr, Expr **ppPrefix,
 	    sql_expr_type(pLeft) != FIELD_TYPE_STRING) {
 		/* IMP: R-02065-49465 The left-hand side of the
 		 * LIKE operator must be the name of an indexed
-		 * column with TEXT affinity.
+		 * column with STRING type.
 		 */
 		return 0;
 	}
@@ -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);
 		}
@@ -748,7 +749,7 @@ exprAnalyzeOrTerm(SrcList * pSrc,	/* the FROM clause */
 				} else if (pOrTerm->u.leftColumn != iColumn) {
 					okToChngToIN = 0;
 				} else {
-					/* If the right-hand side is also a column, then the affinities
+					/* If the right-hand side is also a column, then the types
 					 * of both right and left sides must be such that no type
 					 * conversions are required on the right.  (Ticket #2249)
 					 */
@@ -824,7 +825,7 @@ exprAnalyzeOrTerm(SrcList * pSrc,	/* the FROM clause */
  *   1.  The SQLITE_Transitive optimization must be enabled
  *   2.  Must be either an == or an IS operator
  *   3.  Not originating in the ON clause of an OUTER JOIN
- *   4.  The affinities of A and B must be compatible
+ *   4.  The types of A and B must be compatible
  *   5a. Both operands use the same collating sequence OR
  *   5b. The overall collating sequence is BINARY
  * If this routine returns TRUE, that means that the RHS can be substituted
diff --git a/test/sql/types.result b/test/sql/types.result
index 85d83e11e2e30c565e256b578868da9e37ca1c84..9043cbfd507bd2e733a563b6e6da86dc26549d32 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 79c7eb2458324bc89d87483a0e5d7aa72bbf2229..02ab9b42b912aa79472e2a1375efeefe5d5cdc36 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')