[tarantool-patches] Re: [PATCH v3] sql: add index_def to struct Index

Kirill Shcherbatov kshcherbatov at tarantool.org
Mon Jun 18 21:45:27 MSK 2018


Thank you for the patch. Glad to see indexes so much simplified.
Would you kindly use post program next time? The message was smashed and this made me cry a bit ;)
Please, fix following 16 comments (2 memory problems and 14 minor codestyle fixes):

>> Branch: https://github.com/tarantool/tarantool/tree/sb/gh-3369-use-index-def-in-select-and-where
>> Issue: https://github.com/tarantool/tarantool/issues/3369
>> 
>>  src/box/sql.c           |  54 +++---
>>  src/box/sql/analyze.c   |  40 ++--
>>  src/box/sql/build.c     | 488 +++++++++++++++++++++++-------------------------
>>  src/box/sql/delete.c    |  17 +-
>>  src/box/sql/expr.c      |  60 +++---
>>  src/box/sql/fkey.c      |  47 ++---
>>  src/box/sql/insert.c    | 148 ++++++---------
>>  src/box/sql/pragma.c    |  32 ++--
>>  src/box/sql/select.c    |   2 +-
>>  src/box/sql/sqliteInt.h |  63 +------
>>  src/box/sql/trigger.c   |   2 -
>>  src/box/sql/update.c    |  10 +-
>>  src/box/sql/vdbeaux.c   |   2 +-
>>  src/box/sql/vdbemem.c   |   4 +-
>>  src/box/sql/where.c     | 168 ++++++++---------
>>  src/box/sql/wherecode.c |  54 +++---
>>  src/box/sql/whereexpr.c |  15 --
>>  17 files changed, 532 insertions(+), 674 deletions(-)
>> 
>> diff --git a/src/box/sql.c b/src/box/sql.c
>> index 7379cb418..213f8e453 100644
>> --- a/src/box/sql.c
>> +++ b/src/box/sql.c
>> @@ -1442,8 +1442,8 @@ int tarantoolSqlite3MakeTableFormat(Table *pTable, void *buf)
>>  
>>  	/* If table's PK is single column which is INTEGER, then
>>  	 * treat it as strict type, not affinity.  */
>> -	if (pk_idx && pk_idx->nColumn == 1) {
>> -		int pk = pk_idx->aiColumn[0];
>> +	if (pk_idx != NULL && pk_idx->def->key_def->part_count == 1) {
>> +		int pk = pk_idx->def->key_def->parts[0].fieldno;
>>  		if (def->fields[pk].type == FIELD_TYPE_INTEGER)
>>  			pk_forced_int = pk;
>>  	}
>> @@ -1552,20 +1552,19 @@ tarantoolSqlite3MakeTableOpts(Table *pTable, const char *zSql, char *buf)
>>   */
>>  int tarantoolSqlite3MakeIdxParts(SqliteIndex *pIndex, void *buf)
>>  {
>> -	struct space_def *def = pIndex->pTable->def;
>> -	assert(def != NULL);
>> +	struct field_def *fields = pIndex->pTable->def->fields;
>> +	struct key_def *key_def = pIndex->def->key_def;
>>  	const struct Enc *enc = get_enc(buf);
>> -	struct SqliteIndex *primary_index;
>> -	char *base = buf, *p;
>> -	int pk_forced_int = -1;
>> -
>> -	primary_index = sqlite3PrimaryKeyIndex(pIndex->pTable);
>> +	char *base = buf;
>> +	uint32_t pk_forced_int = UINT32_MAX;
>> +	struct SqliteIndex *primary_index =
>> +		sqlite3PrimaryKeyIndex(pIndex->pTable);
>>  
>>  	/* If table's PK is single column which is INTEGER, then
>>  	 * treat it as strict type, not affinity.  */
>> -	if (primary_index->nColumn == 1) {
>> -		int pk = primary_index->aiColumn[0];
>> -		if (def->fields[pk].type == FIELD_TYPE_INTEGER)
>> +	if (primary_index->def->key_def->part_count == 1) {
>> +		int pk = primary_index->def->key_def->parts[0].fieldno;
>> +		if (fields[pk].type == FIELD_TYPE_INTEGER)
>>  			pk_forced_int = pk;
>>  	}
>>  
>> @@ -1575,46 +1574,45 @@ int tarantoolSqlite3MakeIdxParts(SqliteIndex *pIndex, void *buf)
>>  	 * primary key columns. Query planner depends on this particular
>>  	 * data layout.
>>  	 */
>> -	int i, n = pIndex->nColumn;
>> -
>> -	p = enc->encode_array(base, n);
>> -	for (i = 0; i < n; i++) {
>> -		int col = pIndex->aiColumn[i];
>> -		assert(def->fields[col].is_nullable ==
>> -		       action_is_nullable(def->fields[col].nullable_action));
>> +	struct key_part *part = key_def->parts;
>> +	char *p = enc->encode_array(base, key_def->part_count);
>> +	for (uint32_t i = 0; i < key_def->part_count; ++i, ++part) {
>> +		uint32_t col = part->fieldno;
>> +		assert(fields[col].is_nullable ==
>> +		       action_is_nullable(fields[col].nullable_action));
>>  		const char *t;
>>  		if (pk_forced_int == col) {
>>  			t = "integer";
>>  		} else {
>> -			enum affinity_type affinity = def->fields[col].affinity;
>> -			t = convertSqliteAffinity(affinity,
>> -						  def->fields[col].is_nullable);
>> +			t = convertSqliteAffinity(fields[col].affinity,
>> +						  fields[col].is_nullable);
>>  		}
>>  		/* do not decode default collation */
>> -		uint32_t cid = pIndex->coll_id_array[i];
>> +		uint32_t cid = part->coll_id;
>>  		p = enc->encode_map(p, cid == COLL_NONE ? 5 : 6);
>>  		p = enc->encode_str(p, "type", sizeof("type")-1);
>>  		p = enc->encode_str(p, t, strlen(t));
>>  		p = enc->encode_str(p, "field", sizeof("field")-1);
>>  		p = enc->encode_uint(p, col);
>>  		if (cid != COLL_NONE) {
>> -			p = enc->encode_str(p, "collation", sizeof("collation")-1);
>> +			p = enc->encode_str(p, "collation",
>> +					    sizeof("collation") - 1);
>>  			p = enc->encode_uint(p, cid);
>>  		}
>>  		p = enc->encode_str(p, "is_nullable", 11);
>> -		p = enc->encode_bool(p, def->fields[col].is_nullable);
>> +		p = enc->encode_bool(p, fields[col].is_nullable);
>>  		p = enc->encode_str(p, "nullable_action", 15);
>>  		const char *action_str =
>> -			on_conflict_action_strs[def->fields[col].nullable_action];
>> +			on_conflict_action_strs[fields[col].nullable_action];
>>  		p = enc->encode_str(p, action_str, strlen(action_str));
>>  
>>  		p = enc->encode_str(p, "sort_order", 10);
>> -		enum sort_order sort_order = pIndex->sort_order[i];
>> +		enum sort_order sort_order = part->sort_order;
>>  		assert(sort_order < sort_order_MAX);
>>  		const char *sort_order_str = sort_order_strs[sort_order];
>>  		p = enc->encode_str(p, sort_order_str, strlen(sort_order_str));
>>  	}
>> -	return (int)(p - base);
>> +	return p - base;
>>  }
>>  
>>  /*
>> diff --git a/src/box/sql/analyze.c b/src/box/sql/analyze.c
>> index afc824a1a..31de7ab05 100644
>> --- a/src/box/sql/analyze.c
>> +++ b/src/box/sql/analyze.c
>> @@ -849,7 +849,6 @@ analyzeOneTable(Parse * pParse,	/* Parser context */
>>  		int addrRewind;	/* Address of "OP_Rewind iIdxCur" */
>>  		int addrNextRow;	/* Address of "next_row:" */
>>  		const char *zIdxName;	/* Name of the index */
>> -		int nColTest;	/* Number of columns to test for changes */
>>  
>>  		if (pOnlyIdx && pOnlyIdx != pIdx)
>>  			continue;
>> @@ -860,9 +859,9 @@ analyzeOneTable(Parse * pParse,	/* Parser context */
>>  		if (IsPrimaryKeyIndex(pIdx)) {
>>  			zIdxName = pTab->def->name;
>>  		} else {
>> -			zIdxName = pIdx->zName;
>> +			zIdxName = pIdx->def->name;
>>  		}
>> -		nColTest = index_column_count(pIdx);
>> +		int nColTest = pIdx->def->key_def->part_count;
>>  
>>  		/* Populate the register containing the index name. */
>>  		sqlite3VdbeLoadString(v, regIdxname, zIdxName);
>> @@ -917,7 +916,7 @@ analyzeOneTable(Parse * pParse,	/* Parser context */
>>  		sqlite3VdbeAddOp3(v, OP_OpenRead, iIdxCur, pIdx->tnum,
>>  				  space_ptr_reg);
>>  		sql_vdbe_set_p4_key_def(pParse, pIdx);
>> -		VdbeComment((v, "%s", pIdx->zName));
>> +		VdbeComment((v, "%s", pIdx->def->name));
>>  
>>  		/* Invoke the stat_init() function. The arguments are:
>>  		 *
>> @@ -969,7 +968,7 @@ analyzeOneTable(Parse * pParse,	/* Parser context */
>>  			 */
>>  			sqlite3VdbeAddOp0(v, OP_Goto);
>>  			addrNextRow = sqlite3VdbeCurrentAddr(v);
>> -			if (nColTest == 1 && index_is_unique(pIdx)) {
>> +			if (nColTest == 1 && pIdx->def->opts.is_unique) {
>>  				/* For a single-column UNIQUE index, once we have found a non-NULL
>>  				 * row, we know that all the rest will be distinct, so skip
>>  				 * subsequent distinctness tests.
>> @@ -978,13 +977,12 @@ analyzeOneTable(Parse * pParse,	/* Parser context */
>>  						  endDistinctTest);
>>  				VdbeCoverage(v);
>>  			}
>> -			for (i = 0; i < nColTest; i++) {
>> -				uint32_t id;
>> -				struct coll *coll =
>> -					sql_index_collation(pIdx, i, &id);
>> +			struct key_part *part = pIdx->def->key_def->parts;
>> +			for (i = 0; i < nColTest; ++i, ++part) {
>> +				struct coll *coll = part->coll;
>>  				sqlite3VdbeAddOp2(v, OP_Integer, i, regChng);
>>  				sqlite3VdbeAddOp3(v, OP_Column, iIdxCur,
>> -						  pIdx->aiColumn[i], regTemp);
>> +						  part->fieldno, regTemp);
>>  				aGotoChng[i] =
>>  				    sqlite3VdbeAddOp4(v, OP_Ne, regTemp, 0,
>>  						      regPrev + i, (char *)coll,
>> @@ -1006,7 +1004,8 @@ analyzeOneTable(Parse * pParse,	/* Parser context */
>>  			for (i = 0; i < nColTest; i++) {
>>  				sqlite3VdbeJumpHere(v, aGotoChng[i]);
>>  				sqlite3VdbeAddOp3(v, OP_Column, iIdxCur,
>> -						  pIdx->aiColumn[i],
>> +						  pIdx->def->key_def->
>> +							  parts[i].fieldno,
>>  						  regPrev + i);
>>  			}
>>  			sqlite3VdbeResolveLabel(v, endDistinctTest);
>> @@ -1022,15 +1021,14 @@ analyzeOneTable(Parse * pParse,	/* Parser context */
>>  		 */
>>  		assert(regKey == (regStat4 + 2));
>>  		Index *pPk = sqlite3PrimaryKeyIndex(pIdx->pTable);
>> -		int j, k, regKeyStat;
>> -		int nPkColumn = (int)index_column_count(pPk);
>> -		regKeyStat = sqlite3GetTempRange(pParse, nPkColumn);
>> -		for (j = 0; j < nPkColumn; j++) {
>> -			k = pPk->aiColumn[j];
>> -			assert(k >= 0 && k < (int)pTab->def->field_count);
>> -			sqlite3VdbeAddOp3(v, OP_Column, iIdxCur, k, regKeyStat + j);
>> -			VdbeComment((v, "%s",
>> -				pTab->def->fields[pPk->aiColumn[j]].name));
>> +		int nPkColumn = (int) pPk->def->key_def->part_count;
>> +		int regKeyStat = sqlite3GetTempRange(pParse, nPkColumn);
>> +		for (int j = 0; j < nPkColumn; ++j) {
>> +			int k = pPk->def->key_def->parts[j].fieldno;
>> +			assert(k >= 0 && k < (int) pTab->def->field_count);
>> +			sqlite3VdbeAddOp3(v, OP_Column, iIdxCur, k,
>> +					  regKeyStat + j);
>> +			VdbeComment((v, "%s", pTab->def->fields[k].name));
>>  		}
>>  		sqlite3VdbeAddOp3(v, OP_MakeRecord, regKeyStat,
>>  				  nPkColumn, regKey);
>> @@ -1150,7 +1148,7 @@ analyzeTable(Parse * pParse, Table * pTab, Index * pOnlyIdx)
>>  	iStatCur = pParse->nTab;
>>  	pParse->nTab += 3;
>>  	if (pOnlyIdx) {
>> -		openStatTable(pParse, iStatCur, pOnlyIdx->zName, "idx");
>> +		openStatTable(pParse, iStatCur, pOnlyIdx->def->name, "idx");
>>  	} else {
>>  		openStatTable(pParse, iStatCur, pTab->def->name, "tbl");
>>  	}
>> diff --git a/src/box/sql/build.c b/src/box/sql/build.c
>> index 62d687b17..f18727c61 100644
>> --- a/src/box/sql/build.c
>> +++ b/src/box/sql/build.c
>> @@ -253,6 +253,8 @@ freeIndex(sqlite3 * db, Index * p)
>>  {
>>  	sql_expr_delete(db, p->pPartIdxWhere, false);
>>  	sql_expr_list_delete(db, p->aColExpr);
>> +	if (p->def != NULL)
>> +		index_def_delete(p->def);
>>  	sqlite3DbFree(db, p->zColAff);
>>  	sqlite3DbFree(db, p);
>>  }
>> @@ -271,7 +273,8 @@ sqlite3UnlinkAndDeleteIndex(sqlite3 * db, Index * pIndex)
>>  
>>  	struct session *user_session = current_session();
>>  
>> -	pIndex = sqlite3HashInsert(&pIndex->pTable->idxHash, pIndex->zName, 0);
>> +	pIndex = sqlite3HashInsert(&pIndex->pTable->idxHash,
>> +				   pIndex->def->name, 0);
>>  	if (ALWAYS(pIndex)) {
>>  		if (pIndex->pTable->pIndex == pIndex) {
>>  			pIndex->pTable->pIndex = pIndex->pNext;
>> @@ -388,7 +391,7 @@ deleteTable(sqlite3 * db, Table * pTable)
>>  		pNext = pIndex->pNext;
>>  		assert(pIndex->pSchema == pTable->pSchema);
>>  		if ((db == 0 || db->pnBytesFreed == 0)) {
>> -			char *zName = pIndex->zName;
>> +			char *zName = pIndex->def->name;
>>  			TESTONLY(Index *
>>  				 pOld =) sqlite3HashInsert(&pTable->idxHash,
>>  							   zName, 0);
>> @@ -1058,7 +1061,7 @@ sqlite3AddCollateType(Parse * pParse, Token * pToken)
>>  	Table *p = pParse->pNewTable;
>>  	if (p == NULL)
>>  		return;
>> -	int i = p->def->field_count - 1;
>> +	uint32_t i = p->def->field_count - 1;
>>  	sqlite3 *db = pParse->db;
>>  	char *zColl = sqlite3NameFromToken(db, pToken);
>>  	if (!zColl)
>> @@ -1066,22 +1069,20 @@ sqlite3AddCollateType(Parse * pParse, Token * pToken)
>>  	uint32_t *id = &p->def->fields[i].coll_id;
>>  	p->aCol[i].coll = sql_get_coll_seq(pParse, zColl, id);
>>  	if (p->aCol[i].coll != NULL) {
>> -		Index *pIdx;
>>  		/* If the column is declared as "<name> PRIMARY KEY COLLATE <type>",
>>  		 * then an index may have been created on this column before the
>>  		 * collation type was added. Correct this if it is the case.
>>  		 */
>> -		for (pIdx = p->pIndex; pIdx; pIdx = pIdx->pNext) {
>> -			assert(pIdx->nColumn == 1);
>> -			if (pIdx->aiColumn[0] == i) {
>> -				id = &pIdx->coll_id_array[0];
>> -				pIdx->coll_array[0] =
>> +		for (struct Index *pIdx = p->pIndex; pIdx; pIdx = pIdx->pNext) {
>> +			assert(pIdx->def->key_def->part_count == 1);
>> +			if (pIdx->def->key_def->parts[0].fieldno == i) {
>> +				pIdx->def->key_def->parts[0].coll_id = *id;
>> +				pIdx->def->key_def->parts[0].coll =
>>  					sql_column_collation(p->def, i, id);
>>  			}
>>  		}
>> -	} else {
>> -		sqlite3DbFree(db, zColl);
>>  	}
>> +	sqlite3DbFree(db, zColl);
>>  }
>>  
>>  struct coll *
>> @@ -1111,66 +1112,6 @@ sql_column_collation(struct space_def *def, uint32_t column, uint32_t *coll_id)
>>  	return space->format->fields[column].coll;
>>  }
>>  
>> -struct key_def*
>> -sql_index_key_def(struct Index *idx)
>> -{
>> -	uint32_t space_id = SQLITE_PAGENO_TO_SPACEID(idx->tnum);
>> -	uint32_t index_id = SQLITE_PAGENO_TO_INDEXID(idx->tnum);
>> -	struct space *space = space_by_id(space_id);
>> -	assert(space != NULL);
>> -	struct index *index = space_index(space, index_id);
>> -	assert(index != NULL && index->def != NULL);
>> -	return index->def->key_def;
>> -}
>> -
>> -struct coll *
>> -sql_index_collation(Index *idx, uint32_t column, uint32_t *coll_id)
>> -{
>> -	assert(idx != NULL);
>> -	uint32_t space_id = SQLITE_PAGENO_TO_SPACEID(idx->pTable->tnum);
>> -	struct space *space = space_by_id(space_id);
>> -
>> -	assert(column < idx->nColumn);
>> -	/*
>> -	 * If space is still under construction, or it is
>> -	 * an ephemeral space, then fetch collation from
>> -	 * SQL internal structure.
>> -	 */
>> -	if (space == NULL) {
>> -		assert(column < idx->nColumn);
>> -		*coll_id = idx->coll_id_array[column];
>> -		return idx->coll_array[column];
>> -	}
>> -
>> -	struct key_def *key_def = sql_index_key_def(idx);
>> -	assert(key_def != NULL && key_def->part_count >= column);
>> -	*coll_id = key_def->parts[column].coll_id;
>> -	return key_def->parts[column].coll;
>> -}
>> -
>> -enum sort_order
>> -sql_index_column_sort_order(Index *idx, uint32_t column)
>> -{
>> -	assert(idx != NULL);
>> -	uint32_t space_id = SQLITE_PAGENO_TO_SPACEID(idx->pTable->tnum);
>> -	struct space *space = space_by_id(space_id);
>> -
>> -	assert(column < idx->nColumn);
>> -	/*
>> -	 * If space is still under construction, or it is
>> -	 * an ephemeral space, then fetch collation from
>> -	 * SQL internal structure.
>> -	 */
>> -	if (space == NULL) {
>> -		assert(column < idx->nColumn);
>> -		return idx->sort_order[column];
>> -	}
>> -
>> -	struct key_def *key_def = sql_index_key_def(idx);
>> -	assert(key_def != NULL && key_def->part_count >= column);
>> -	return key_def->parts[column].sort_order;
>> -}
>> -
>>  /**
>>   * Return true if space which corresponds to
>>   * given table has view option.
>> @@ -1383,14 +1324,16 @@ createTableStmt(sqlite3 * db, Table * p)
>>  	return zStmt;
>>  }
>>  
>> -/* Return true if value x is found any of the first nCol entries of aiCol[]
>> - */
>>  static int
>> -hasColumn(const i16 * aiCol, int nCol, int x)
>> +hasColumn(const struct key_part *key_parts, int nCol, uint32_t fieldno)
>>  {
>> -	while (nCol-- > 0)
>> -		if (x == *(aiCol++))
>> +	int i = 0;
>> +	while (i < nCol) {
>> +		if (fieldno == key_parts->fieldno)
>>  			return 1;
>> +		key_parts++;
>> +		i++;
>> +	}
>>  	return 0;
>>  }
>>  
>> @@ -1410,13 +1353,12 @@ static void
>>  convertToWithoutRowidTable(Parse * pParse, Table * pTab)
>>  {
>>  	Index *pPk;
>> -	int i, j;
>>  	sqlite3 *db = pParse->db;
>>  
>>  	/* Mark every PRIMARY KEY column as NOT NULL (except for imposter tables)
>>  	 */
>>  	if (!db->init.imposterTable) {
>> -		for (i = 0; i < (int)pTab->def->field_count; i++) {
>> +		for (uint32_t i = 0; i < pTab->def->field_count; i++) {
>>  			if (pTab->aCol[i].is_primkey) {
>>  				pTab->def->fields[i].nullable_action
>>  					= ON_CONFLICT_ACTION_ABORT;
>> @@ -1454,14 +1396,28 @@ convertToWithoutRowidTable(Parse * pParse, Table * pTab)
>>  		 * "PRIMARY KEY(a,b,a,b,c,b,c,d)" into just "PRIMARY KEY(a,b,c,d)".  Later
>>  		 * code assumes the PRIMARY KEY contains no repeated columns.
>>  		 */
>> -		for (i = j = 1; i < pPk->nColumn; i++) {
>> -			if (hasColumn(pPk->aiColumn, j, pPk->aiColumn[i])) {
>> -				pPk->nColumn--;
>> -			} else {
>> -				pPk->aiColumn[j++] = pPk->aiColumn[i];
>> +
>> +		struct key_part *parts = pPk->def->key_def->parts;
>> +		uint32_t part_count = pPk->def->key_def->part_count;
>> +		uint32_t new_part_count = part_count;
>> +
>> +		for (uint32_t i = 1; i < part_count; i++) {
>> +			if (hasColumn(parts, i, parts[i].fieldno)){
>> +				new_part_count--;
>> +				bool is_found = false;
>> +				for (uint32_t j = i + 1; j < part_count; j++){
>> +					if (!(hasColumn(parts, j,
>> +							parts[j].fieldno))) {
>> +						parts[i] = parts[j];
>> +						is_found = true;
>> +						break;
>> +					}
>> +				}
>> +				if (!(is_found))
>> +					break;
>>  			}
>>  		}
>> -		pPk->nColumn = j;
>> +		pPk->def->key_def->part_count = new_part_count;
>>  	}
>>  	assert(pPk != 0);
>>  }
>> @@ -1543,7 +1499,7 @@ createIndex(Parse * pParse, Index * pIndex, int iSpaceId, int iIndexId,
>>  	}
>>  	sqlite3VdbeAddOp4(v,
>>  			  OP_String8, 0, iFirstCol + 2, 0,
>> -			  sqlite3DbStrDup(pParse->db, pIndex->zName),
>> +			  sqlite3DbStrDup(pParse->db, pIndex->def->name),
>>  			  P4_DYNAMIC);
>>  	sqlite3VdbeAddOp4(v, OP_String8, 0, iFirstCol + 3, 0, "tree",
>>  			  P4_STATIC);
>> @@ -1580,7 +1536,7 @@ makeIndexSchemaRecord(Parse * pParse,
>>  
>>  	sqlite3VdbeAddOp4(v,
>>  			  OP_String8, 0, iFirstCol, 0,
>> -			  sqlite3DbStrDup(pParse->db, pIndex->zName),
>> +			  sqlite3DbStrDup(pParse->db, pIndex->def->name),
>>  			  P4_DYNAMIC);
>>  
>>  	if (pParse->pNewTable) {
>> @@ -2652,14 +2608,15 @@ sqlite3RefillIndex(Parse * pParse, Index * pIndex, int memRootPage)
>>  	} else {
>>  		tnum = pIndex->tnum;
>>  	}
>> -	struct key_def *def = key_def_dup(sql_index_key_def(pIndex));
>> +	struct key_def *def = key_def_dup(pIndex->def->key_def);
>>  	if (def == NULL) {
>>  		sqlite3OomFault(db);
>>  		return;
>>  	}
>>  	/* Open the sorter cursor if we are to use one. */
>>  	iSorter = pParse->nTab++;
>> -	sqlite3VdbeAddOp4(v, OP_SorterOpen, iSorter, 0, pIndex->nColumn,
>> +	sqlite3VdbeAddOp4(v, OP_SorterOpen, iSorter, 0,
>> +			  pIndex->def->key_def->part_count,
>>  			  (char *)def, P4_KEYDEF);
1. Maybe a little better like this?
-                         pIndex->def->key_def->part_count,
-                         (char *)def, P4_KEYDEF);
+                         pIndex->def->key_def->part_count, (char *)def,
+                         P4_KEYDEF);

>>  
>>  	/* Open the table. Loop through all rows of the table, inserting index
>> @@ -2692,7 +2649,7 @@ sqlite3RefillIndex(Parse * pParse, Index * pIndex, int memRootPage)
>>  		sqlite3VdbeGoto(v, j2);
>>  		addr2 = sqlite3VdbeCurrentAddr(v);
>>  		sqlite3VdbeAddOp4Int(v, OP_SorterCompare, iSorter, j2,
>> -				     regRecord, pIndex->nColumn);
>> +				     regRecord, pIndex->def->key_def->part_count);
2. Out of margin
-                                    regRecord, pIndex->def->key_def->part_count);
+                                    regRecord,
+                                    pIndex->def->key_def->part_count);

>>  		VdbeCoverage(v);
>>  		sqlite3UniqueConstraint(pParse, ON_CONFLICT_ACTION_ABORT,
>>  					pIndex);
>> @@ -2730,24 +2687,13 @@ sqlite3AllocateIndexObject(sqlite3 * db,	/* Database connection */
>>  	int nByte;		/* Bytes of space for Index object + arrays */
>>  
>>  	nByte = ROUND8(sizeof(Index)) +		    /* Index structure   */
>> -	    ROUND8(sizeof(struct coll *) * nCol) +  /* Index.coll_array  */
>> -	    ROUND8(sizeof(uint32_t) * nCol) +       /* Index.coll_id_array*/
>> -	    ROUND8(sizeof(LogEst) * (nCol + 1) +    /* Index.aiRowLogEst */
>> -		   sizeof(i16) * nCol +		    /* Index.aiColumn    */
>> -		   sizeof(enum sort_order) * nCol); /* Index.sort_order  */
>> +	    ROUND8(sizeof(LogEst) * (nCol + 1));    /* Index.aiRowLogEst */
>>  	p = sqlite3DbMallocZero(db, nByte + nExtra);
>>  	if (p) {
>>  		char *pExtra = ((char *)p) + ROUND8(sizeof(Index));
>> -		p->coll_array = (struct coll **)pExtra;
>> -		pExtra += ROUND8(sizeof(struct coll **) * nCol);
>> -		p->coll_id_array = (uint32_t *) pExtra;
>> -		pExtra += ROUND8(sizeof(uint32_t) * nCol);
>>  		p->aiRowLogEst = (LogEst *) pExtra;
>>  		pExtra += sizeof(LogEst) * (nCol + 1);
>> -		p->aiColumn = (i16 *) pExtra;
>>  		pExtra += sizeof(i16) * nCol;
>> -		p->sort_order = (enum sort_order *) pExtra;
>> -		p->nColumn = nCol;
>>  		*ppExtra = ((char *)p) + nByte;
>>  	}
>>  	return p;
>> @@ -2836,18 +2782,133 @@ addIndexToTable(Index * pIndex, Table * pTab)
>>  	}
>>  }
>>  
>> -bool
>> -index_is_unique(Index *idx)
>> +static inline void
>> +append_string_part(struct region *r, const char *str,
>> +		  size_t *total_sql_size, Parse *parse)
>>  {
>> -	assert(idx != NULL);
>> -	uint32_t space_id = SQLITE_PAGENO_TO_SPACEID(idx->tnum);
>> -	uint32_t index_id = SQLITE_PAGENO_TO_INDEXID(idx->tnum);
>> -	struct space *space = space_by_id(space_id);
>> -	assert(space != NULL);
>> -	struct index *tnt_index = space_index(space, index_id);
>> -	assert(tnt_index != NULL);
>> +	char * str_part = region_alloc(r, strlen(str));
>> +	if (str_part == NULL){
>> +		diag_set(OutOfMemory, strlen(str),
>> +			 "region_alloc", "str_part");
>> +		parse->rc = SQL_TARANTOOL_ERROR;
>> +		parse->nErr++;
>> +	}
>> +	memcpy(str_part, str, strlen(str));
>> +	*total_sql_size += strlen(str);
>> +}
>> +
>> +void static
3. GCC doesn't accept such specifiers order in default build.
Please change this to "static void".
You also better look to the Travis times to times.

>> +set_index_def(Parse *parse, Index *index, Table *table, uint32_t iid,
>> +	      const char *name, uint32_t name_len, int on_error,
>> +	      struct ExprList *expr_list, u8 idx_type)
>> +{
>> +	struct space_def *space_def = table->def;
>> +	struct index_opts opts;
>> +	index_opts_create(&opts);
>> +	opts.is_unique = on_error != ON_CONFLICT_ACTION_NONE;
>> +
>> +	struct key_def *key_def = key_def_new(expr_list->nExpr);
>> +	if (key_def == NULL) {
>> +		parse->rc = SQL_TARANTOOL_ERROR;
>> +		parse->nErr++;
>> +		return;
>> +	}
>> +
>> +	/*
>> +	 * Build initial parts of SQL statement.
>> +	 */
>> +
>> +	struct region *r = &fiber()->gc;
4. Please, rebase to the master. There would be parser region that should be used here, I believe.

>> +	size_t total_sql_size = 0;
>> +
>> +	if (idx_type == SQLITE_IDXTYPE_APPDEF) {
>> +		append_string_part(r, "CREATE INDEX ", &total_sql_size,
>> +				   parse);
>> +		append_string_part(r, name, &total_sql_size, parse);
>> +		append_string_part(r, " ON ", &total_sql_size, parse);
>> +		append_string_part(r, space_def->name, &total_sql_size,
>> +				   parse);
>> +		append_string_part(r, " (", &total_sql_size, parse);
>> +	}
>> +
>> +	for (int i = 0; i < expr_list->nExpr; i++) {
>> +		Expr *expr = expr_list->a[i].pExpr;
>> +		sql_resolve_self_reference(parse, table, NC_IdxExpr, expr, 0);
>> +		if (parse->nErr > 0)
>> +			return;
5. If I not mistaken, key_def is leaking here, with other returns and at the end of the function.

>> +
>> +		Expr *column_expr = sqlite3ExprSkipCollate(expr);
>> +		if (column_expr->op != TK_COLUMN) {
>> +			sqlite3ErrorMsg(parse,
>> +					"functional indexes aren't supported "
>> +					"in the current version");
>> +			return;
>> +		}
>> +
>> +		uint32_t fieldno = column_expr->iColumn;
>> +		uint32_t coll_id;
>> +		struct coll *coll;
>> +		if (expr->op == TK_COLLATE) {
>> +			coll = sql_get_coll_seq(parse, expr->u.zToken,
>> +						&coll_id);
>> +
>> +			if (idx_type == SQLITE_IDXTYPE_APPDEF) {
>> +				append_string_part(r, name,
>> +						   &total_sql_size, parse);
>> +				append_string_part(r, " COLLATE ",
>> +						   &total_sql_size, parse);
>> +				const char *coll_name = expr->u.zToken;
>> +				append_string_part(r, coll_name,
>> +						   &total_sql_size, parse);
>> +				append_string_part(r, ", ",
>> +						   &total_sql_size, parse);
>> +			}
>> +		} else {
>> +			coll = sql_column_collation(space_def, fieldno,
>> +						    &coll_id);
>> +			if (idx_type == SQLITE_IDXTYPE_APPDEF) {
>> +				append_string_part(r, name,
>> +						   &total_sql_size, parse);
>> +				append_string_part(r, ", ",
>> +						   &total_sql_size, parse);
>> +			}
>> +		}
>> +
>> +		/*
>> +		* Tarantool: DESC indexes are not supported so far.
>> +		* See gh-3016.
>> +		*/
>> +		key_def_set_part(key_def, i, fieldno,
>> +				 space_def->fields[fieldno].type,
>> +				 space_def->fields[fieldno].nullable_action,
>> +				 coll, coll_id, SORT_ORDER_ASC);
>> +	}
>>  
>> -	return tnt_index->def->opts.is_unique;
>> +	if (parse->nErr > 0) {
>> +		index->def = NULL;
>> +		return;
>> +	}
>> +
>> +	if (idx_type == SQLITE_IDXTYPE_APPDEF) {
>> +		memcpy(region_alloc(r, 1), "\0", 1);
>> +		total_sql_size += 1;
>> +		opts.sql = region_join(r, total_sql_size);
>> +
>> +		/*
>> +		 * fix last ", " with ")\0" to finish the statement.
>> +		 */
>> +		opts.sql[total_sql_size - 3] = ')';
>> +		opts.sql[total_sql_size - 2] = '\0';
>> +	}
>> +
>> +	struct key_def *pk_key_def;
>> +	if (idx_type == SQLITE_IDXTYPE_APPDEF)
>> +		pk_key_def = table->pIndex->def->key_def;
>> +	else
>> +		pk_key_def = NULL;
>> +
>> +	index->def = index_def_new(space_def->id, iid, name, name_len,
>> +				   TREE, &opts, key_def, pk_key_def);
>>  }
>>  
>>  void
>> @@ -2856,16 +2917,14 @@ sql_create_index(struct Parse *parse, struct Token *token,
>>  		 int on_error, struct Token *start, struct Expr *where,
>>  		 enum sort_order sort_order, bool if_not_exist, u8 idx_type)
>>  {
>> -	Table *pTab = 0;	/* Table to be indexed */
>> -	Index *pIndex = 0;	/* The index to be created */
>> -	char *zName = 0;	/* Name of the index */
>> -	int nName;		/* Number of characters in zName */
>> -	int i, j;
>> +	Table *pTab = NULL;	/* Table to be indexed */
>> +	Index *pIndex = NULL;	/* The index to be created */
>> +	char *name = NULL;	/* Name of the index */
>> +	int name_len;		/* Number of characters in zName */
>>  	DbFixer sFix;		/* For assigning database names to pTable */
>>  	sqlite3 *db = parse->db;
>> -	struct ExprList_item *col_listItem;	/* For looping over col_list */
>>  	int nExtra = 0;		/* Space allocated for zExtra[] */
>> -	char *zExtra = 0;	/* Extra space after the Index object */
>> +	char *zExtra = NULL;	/* Extra space after the Index object */
>>  	struct session *user_session = current_session();
>>  
>>  	if (db->mallocFailed || parse->nErr > 0) {
>> @@ -2939,24 +2998,24 @@ sql_create_index(struct Parse *parse, struct Token *token,
>>  	 * our own name.
>>  	 */
>>  	if (token) {
>> -		zName = sqlite3NameFromToken(db, token);
>> -		if (zName == 0)
>> +		name = sqlite3NameFromToken(db, token);
>> +		if (name == NULL)
>>  			goto exit_create_index;
>>  		assert(token->z != 0);
>>  		if (!db->init.busy) {
>> -			if (sqlite3HashFind(&db->pSchema->tblHash, zName) !=
>> +			if (sqlite3HashFind(&db->pSchema->tblHash, name) !=
>>  			    NULL) {
>>  				sqlite3ErrorMsg(parse,
>>  						"there is already a table named %s",
>> -						zName);
>> +						name);
>>  				goto exit_create_index;
>>  			}
>>  		}
>> -		if (sqlite3HashFind(&pTab->idxHash, zName) != NULL) {
>> +		if (sqlite3HashFind(&pTab->idxHash, name) != NULL) {
>>  			if (!if_not_exist) {
>>  				sqlite3ErrorMsg(parse,
>>  						"index %s.%s already exists",
>> -						pTab->def->name, zName);
>> +						pTab->def->name, name);
>>  			} else {
>>  				assert(!db->init.busy);
>>  			}
>> @@ -2968,10 +3027,9 @@ sql_create_index(struct Parse *parse, struct Token *token,
>>  		for (pLoop = pTab->pIndex, n = 1; pLoop;
>>  		     pLoop = pLoop->pNext, n++) {
>>  		}
>> -		zName =
>> -		    sqlite3MPrintf(db, "sqlite_autoindex_%s_%d", pTab->def->name,
>> -				   n);
>> -		if (zName == 0) {
>> +		name = sqlite3MPrintf(db, "sqlite_autoindex_%s_%d",
>> +				      pTab->def->name, n);
>> +		if (name == NULL) {
>>  			goto exit_create_index;
>>  		}
>>  	}
>> @@ -2997,31 +3055,27 @@ sql_create_index(struct Parse *parse, struct Token *token,
>>  		sqlite3ExprListCheckLength(parse, col_list, "index");
>>  	}
>>  
>> -	/* Figure out how many bytes of space are required to store explicitly
>> -	 * specified collation sequence names.
>> -	 */
>> -	for (i = 0; i < col_list->nExpr; i++) {
>> -		Expr *pExpr = col_list->a[i].pExpr;
>> -		assert(pExpr != 0);
>> -		if (pExpr->op == TK_COLLATE) {
>> -			nExtra += (1 + sqlite3Strlen30(pExpr->u.zToken));
>> -		}
>> -	}
>> -
>>  	/*
>>  	 * Allocate the index structure.
>>  	 */
>> -	nName = sqlite3Strlen30(zName);
>> +	name_len = sqlite3Strlen30(name);
>> +
>> +	if (name_len > BOX_NAME_MAX) {
>> +		sqlite3ErrorMsg(parse,
>> +				"%s.%s exceeds indexes' names length limit",
>> +				pTab->def->name, name);
>> +		goto exit_create_index;
>> +	}
>> +
>> +	if (sqlite3CheckIdentifierName(parse, name) != SQLITE_OK)
>> +		goto exit_create_index;
>> +
>>  	pIndex = sqlite3AllocateIndexObject(db, col_list->nExpr,
>> -					    nName + nExtra + 1, &zExtra);
>> +					    name_len + nExtra + 1, &zExtra);
>>  	if (db->mallocFailed) {
>>  		goto exit_create_index;
>>  	}
>>  	assert(EIGHT_BYTE_ALIGNMENT(pIndex->aiRowLogEst));
>> -	assert(EIGHT_BYTE_ALIGNMENT(pIndex->coll_array));
>> -	pIndex->zName = zExtra;
>> -	zExtra += nName + 1;
>> -	memcpy(pIndex->zName, zName, nName + 1);
>>  	pIndex->pTable = pTab;
>>  	pIndex->onError = (u8) on_error;
>>  	/*
>> @@ -3036,7 +3090,6 @@ sql_create_index(struct Parse *parse, struct Token *token,
>>  		pIndex->idxType = idx_type;
>>  	}
>>  	pIndex->pSchema = db->pSchema;
>> -	pIndex->nColumn = col_list->nExpr;
>>  	/* Tarantool have access to each column by any index */
>>  	if (where) {
>>  		sql_resolve_self_reference(parse, pTab, NC_PartIdx, where,
>> @@ -3045,60 +3098,27 @@ sql_create_index(struct Parse *parse, struct Token *token,
>>  		where = NULL;
>>  	}
>>  
>> -	/* Analyze the list of expressions that form the terms of the index and
>> -	 * report any errors.  In the common case where the expression is exactly
>> -	 * a table column, store that column in aiColumn[].  For general expressions,
>> -	 * populate pIndex->aColExpr and store XN_EXPR (-2) in aiColumn[].
>> -	 *
>> +	/*
>>  	 * TODO: Issue a warning if two or more columns of the index are identical.
>>  	 * TODO: Issue a warning if the table primary key is used as part of the
>>  	 * index key.
>>  	 */
>> -	for (i = 0, col_listItem = col_list->a; i < col_list->nExpr;
>> -	     i++, col_listItem++) {
>> -		Expr *pCExpr;	/* The i-th index expression */
>> -		sql_resolve_self_reference(parse, pTab, NC_IdxExpr,
>> -					   col_listItem->pExpr, NULL);
>> -		if (parse->nErr > 0)
>> -			goto exit_create_index;
>> -		pCExpr = sqlite3ExprSkipCollate(col_listItem->pExpr);
>> -		if (pCExpr->op != TK_COLUMN) {
>> -			sqlite3ErrorMsg(parse,
>> -					"functional indexes aren't supported "
>> -					"in the current version");
>> -			goto exit_create_index;
>> -		} else {
>> -			j = pCExpr->iColumn;
>> -			assert(j <= 0x7fff);
>> -			if (j < 0) {
>> -				j = pTab->iPKey;
>> -			}
>> -			pIndex->aiColumn[i] = (i16) j;
>> -		}
>> -		struct coll *coll;
>> -		uint32_t id;
>> -		if (col_listItem->pExpr->op == TK_COLLATE) {
>> -			const char *coll_name = col_listItem->pExpr->u.zToken;
>> -			coll = sql_get_coll_seq(parse, coll_name, &id);
>>  
>> -			if (coll == NULL &&
>> -			    sqlite3StrICmp(coll_name, "binary") != 0) {
>> -				goto exit_create_index;
>> -			}
>> -		} else if (j >= 0) {
>> -			coll = sql_column_collation(pTab->def, j, &id);
>> -		} else {
>> -			id = COLL_NONE;
>> -			coll = NULL;
>> -		}
>> -		pIndex->coll_array[i] = coll;
>> -		pIndex->coll_id_array[i] = id;
>> +	uint32_t max_iid = 0;
>> +	for (Index *index = pTab->pIndex; index; index = index->pNext) {
>> +		max_iid = max_iid > index->def->iid ?
>> +			  max_iid :
>> +			  index->def->iid + 1;
>> +	}
>>  
>> -		/* Tarantool: DESC indexes are not supported so far.
>> -		 * See gh-3016.
>> -		 */
>> -		pIndex->sort_order[i] = SORT_ORDER_ASC;
>> +	set_index_def(parse, pIndex, pTab, max_iid, name, name_len, on_error,
>> +		      col_list, idx_type);
>> +
>> +	if (pIndex->def == NULL ||
>> +	    !index_def_is_valid(pIndex->def, pTab->def->name)) {
>> +		goto exit_create_index;
>>  	}
>> +
>>  	if (pTab == parse->pNewTable) {
>>  		/* This routine has been called to create an automatic index as a
>>  		 * result of a PRIMARY KEY or UNIQUE clause on a column definition, or
>> @@ -3123,25 +3143,27 @@ sql_create_index(struct Parse *parse, struct Token *token,
>>  		 */
>>  		Index *pIdx;
>>  		for (pIdx = pTab->pIndex; pIdx; pIdx = pIdx->pNext) {
>> -			int k;
>> +			uint32_t k;
>>  			assert(IsUniqueIndex(pIdx));
>>  			assert(pIdx->idxType != SQLITE_IDXTYPE_APPDEF);
>>  			assert(IsUniqueIndex(pIndex));
>>  
>> -			if (pIdx->nColumn != pIndex->nColumn)
>> +			if (pIdx->def->key_def->part_count !=
>> +			    pIndex->def->key_def->part_count) {
>>  				continue;
>> -			for (k = 0; k < pIdx->nColumn; k++) {
>> -				assert(pIdx->aiColumn[k] >= 0);
>> -				if (pIdx->aiColumn[k] != pIndex->aiColumn[k])
>> +			}
>> +			for (k = 0; k < pIdx->def->key_def->part_count; k++) {
>> +				if (pIdx->def->key_def->parts[k].fieldno !=
>> +				    pIndex->def->key_def->parts[k].fieldno) {
>>  					break;
>> +				}
>>  				struct coll *coll1, *coll2;
>> -				uint32_t id;
>> -				coll1 = sql_index_collation(pIdx, k, &id);
>> -				coll2 = sql_index_collation(pIndex, k, &id);
>> +				coll1 = pIdx->def->key_def->parts[k].coll;
>> +				coll2 = pIndex->def->key_def->parts[k].coll;
>>  				if (coll1 != coll2)
>>  					break;
>>  			}
>> -			if (k == pIdx->nColumn) {
>> +			if (k == pIdx->def->key_def->part_count) {
>>  				if (pIdx->onError != pIndex->onError) {
>>  					/* This constraint creates the same index as a previous
>>  					 * constraint specified somewhere in the CREATE TABLE statement.
>> @@ -3175,7 +3197,7 @@ sql_create_index(struct Parse *parse, struct Token *token,
>>  	assert(parse->nErr == 0);
>>  	if (db->init.busy) {
>>  		Index *p;
>> -		p = sqlite3HashInsert(&pTab->idxHash, pIndex->zName, pIndex);
>> +		p = sqlite3HashInsert(&pTab->idxHash, pIndex->def->name, pIndex);
>>  		if (p) {
>>  			assert(p == pIndex);	/* Malloc must have failed */
>>  			sqlite3OomFault(db);
>> @@ -3273,44 +3295,7 @@ sql_create_index(struct Parse *parse, struct Token *token,
>>  	sql_expr_delete(db, where, false);
>>  	sql_expr_list_delete(db, col_list);
>>  	sqlite3SrcListDelete(db, tbl_name);
>> -	sqlite3DbFree(db, zName);
>> -}
>> -
>> -/**
>> - * Return number of columns in given index.
>> - * If space is ephemeral, use internal
>> - * SQL structure to fetch the value.
>> - */
>> -uint32_t
>> -index_column_count(const Index *idx)
>> -{
>> -	assert(idx != NULL);
>> -	uint32_t space_id = SQLITE_PAGENO_TO_SPACEID(idx->tnum);
>> -	struct space *space = space_by_id(space_id);
>> -	/* It is impossible to find an ephemeral space by id. */
>> -	if (space == NULL)
>> -		return idx->nColumn;
>> -
>> -	uint32_t index_id = SQLITE_PAGENO_TO_INDEXID(idx->tnum);
>> -	struct index *index = space_index(space, index_id);
>> -	assert(index != NULL);
>> -	return index->def->key_def->part_count;
>> -}
>> -
>> -/** Return true if given index is unique and not nullable. */
>> -bool
>> -index_is_unique_not_null(const Index *idx)
>> -{
>> -	assert(idx != NULL);
>> -	uint32_t space_id = SQLITE_PAGENO_TO_SPACEID(idx->tnum);
>> -	struct space *space = space_by_id(space_id);
>> -	assert(space != NULL);
>> -
>> -	uint32_t index_id = SQLITE_PAGENO_TO_INDEXID(idx->tnum);
>> -	struct index *index = space_index(space, index_id);
>> -	assert(index != NULL);
>> -	return (index->def->opts.is_unique &&
>> -		!index->def->key_def->is_nullable);
>> +	sqlite3DbFree(db, name);
>>  }
>>  
>>  void
>> @@ -3938,18 +3923,19 @@ sqlite3UniqueConstraint(Parse * pParse,	/* Parsing context */
>>      )
>>  {
>>  	char *zErr;
>> -	int j;
>> +	uint32_t j;
>>  	StrAccum errMsg;
>>  	Table *pTab = pIdx->pTable;
>>  
>>  	sqlite3StrAccumInit(&errMsg, pParse->db, 0, 0, 200);
>>  	if (pIdx->aColExpr) {
>> -		sqlite3XPrintf(&errMsg, "index '%q'", pIdx->zName);
>> +		sqlite3XPrintf(&errMsg, "index '%q'", pIdx->def->name);
>>  	} else {
>> -		for (j = 0; j < pIdx->nColumn; j++) {
>> +		struct key_part *part = pIdx->def->key_def->parts;
>> +		for (j = 0; j < pIdx->def->key_def->part_count; j++, part++) {
>>  			char *zCol;
>> -			assert(pIdx->aiColumn[j] >= 0);
>> -			zCol = pTab->def->fields[pIdx->aiColumn[j]].name;
>> +			uint32_t fieldno = part->fieldno;
>> +			zCol = pTab->def->fields[fieldno].name;
>>  			if (j)
>>  				sqlite3StrAccumAppend(&errMsg, ", ", 2);
>>  			sqlite3XPrintf(&errMsg, "%s.%s", pTab->def->name, zCol);
>> @@ -3972,11 +3958,11 @@ static bool
>>  collationMatch(struct coll *coll, struct Index *index)
>>  {
>>  	assert(coll != NULL);
>> -	for (int i = 0; i < index->nColumn; i++) {
>> -		uint32_t id;
>> -		struct coll *idx_coll = sql_index_collation(index, i, &id);
>> -		assert(idx_coll != 0 || index->aiColumn[i] < 0);
>> -		if (index->aiColumn[i] >= 0 && coll == idx_coll)
>> +	struct key_part *part = index->def->key_def->parts;
>> +	for (uint32_t i = 0; i < index->def->key_def->part_count; i++, part++) {
>> +		struct coll *idx_coll = part->coll;
>> +		assert(idx_coll != NULL);
>> +		if (coll == idx_coll)
>>  			return true;
>>  	}
>>  	return false;
>> diff --git a/src/box/sql/delete.c b/src/box/sql/delete.c
>> index ddad54b3e..0314382f7 100644
>> --- a/src/box/sql/delete.c
>> +++ b/src/box/sql/delete.c
>> @@ -209,7 +209,7 @@ sql_table_delete_from(struct Parse *parse, struct SrcList *tab_list,
>>  		} else {
>>  			pk = sqlite3PrimaryKeyIndex(table);
>>  			assert(pk != NULL);
>> -			pk_len = index_column_count(pk);
>> +			pk_len = pk->def->key_def->part_count;
>>  			parse->nMem += pk_len;
>>  			sqlite3VdbeAddOp2(v, OP_OpenTEphemeral, eph_cursor,
>>  					  pk_len);
>> @@ -251,12 +251,11 @@ sql_table_delete_from(struct Parse *parse, struct SrcList *tab_list,
>>  
>>  		/* Extract the primary key for the current row */
>>  		if (!is_view) {
>> -			for (int i = 0; i < pk_len; i++) {
>> -				assert(pk->aiColumn[i] >= 0);
>> +			struct key_part *part = pk->def->key_def->parts;
>> +			for (int i = 0; i < pk_len; i++, part++) {
>>  				sqlite3ExprCodeGetColumnOfTable(v, table->def,
>>  								tab_cursor,
>> -								pk->
>> -								aiColumn[i],
>> +								part->fieldno,
>>  								reg_pk + i);
>>  			}
>>  		} else {
>> @@ -326,7 +325,7 @@ sql_table_delete_from(struct Parse *parse, struct SrcList *tab_list,
>>  			sqlite3VdbeAddOp3(v, OP_OpenWrite, tab_cursor,
>>  					  table->tnum, space_ptr_reg);
>>  			sql_vdbe_set_p4_key_def(parse, pk);
>> -			VdbeComment((v, "%s", pk->zName));
>> +			VdbeComment((v, "%s", pk->def->name));
>>  
>>  			if (one_pass == ONEPASS_MULTI)
>>  				sqlite3VdbeJumpHere(v, iAddrOnce);
>> @@ -536,14 +535,14 @@ sql_generate_index_key(struct Parse *parse, struct Index *index, int cursor,
>>  			*part_idx_label = 0;
>>  		}
>>  	}
>> -	int col_cnt = index_column_count(index);
>> +	int col_cnt = index->def->key_def->part_count;
>>  	int reg_base = sqlite3GetTempRange(parse, col_cnt);
>>  	if (prev != NULL && (reg_base != reg_prev ||
>>  			     prev->pPartIdxWhere != NULL))
>>  		prev = NULL;
>>  	for (int j = 0; j < col_cnt; j++) {
>> -		if (prev != NULL && prev->aiColumn[j] == index->aiColumn[j]
>> -		    && prev->aiColumn[j] != XN_EXPR) {
>> +		if (prev->def->key_def->parts[j].fieldno ==
>> +		    index->def->key_def->parts[j].fieldno && prev == NULL) {
>>  			/*
>>  			 * This column was already computed by the
>>  			 * previous index.
>> diff --git a/src/box/sql/expr.c b/src/box/sql/expr.c
>> index 8866f6fed..a756535f0 100644
>> --- a/src/box/sql/expr.c
>> +++ b/src/box/sql/expr.c
>> @@ -2422,20 +2422,24 @@ sqlite3FindInIndex(Parse * pParse,	/* Parsing context */
>>  			     pIdx = pIdx->pNext) {
>>  				Bitmask colUsed; /* Columns of the index used */
>>  				Bitmask mCol;	/* Mask for the current column */
>> -				if (pIdx->nColumn < nExpr)
>> +				uint32_t part_count = pIdx->def->key_def->
>> +					part_count;
>> +				struct key_part *parts = pIdx->def->key_def->
>> +					parts;
>> +				if ((int)part_count < nExpr)
>>  					continue;
>>  				/* Maximum nColumn is BMS-2, not BMS-1, so that we can compute
>>  				 * BITMASK(nExpr) without overflowing
>>  				 */
>> -				testcase(pIdx->nColumn == BMS - 2);
>> -				testcase(pIdx->nColumn == BMS - 1);
>> -				if (pIdx->nColumn >= BMS - 1)
>> +				testcase(part_count == BMS - 2);
>> +				testcase(>part_count == BMS - 1);
>> +				if (part_count >= BMS - 1)
>>  					continue;
>>  				if (mustBeUnique) {
>> -					if (pIdx->nColumn > nExpr
>> -					    || (pIdx->nColumn > nExpr
>> -					    && !index_is_unique(pIdx))) {
>> -							continue;	/* This index is not unique over the IN RHS columns */
>> +					if ((int)part_count > nExpr
>> +					    || !pIdx->def->opts.is_unique) {
>> +						/* This index is not unique over the IN RHS columns */
>> +						continue;
>>  					}
>>  				}
>>  
>> @@ -2449,12 +2453,13 @@ sqlite3FindInIndex(Parse * pParse,	/* Parsing context */
>>  					int j;
>>  
>>  					for (j = 0; j < nExpr; j++) {
>> -						if (pIdx->aiColumn[j] !=
>> -						    pRhs->iColumn) {
>> +						if ((int) parts[j].fieldno
>> +						    != pRhs->iColumn) {
>>  							continue;
>>  						}
>> -						struct coll *idx_coll;
>> -						idx_coll = sql_index_collation(pIdx, j, &id);
>> +
>> +						struct coll *idx_coll =
>> +							     parts[j].coll;
>>  						if (pReq != NULL &&
>>  						    pReq != idx_coll) {
>>  							continue;
>> @@ -2483,17 +2488,16 @@ sqlite3FindInIndex(Parse * pParse,	/* Parsing context */
>>  							  0, 0, 0,
>>  							  sqlite3MPrintf(db,
>>  							  "USING INDEX %s FOR IN-OPERATOR",
>> -							  pIdx->zName),
>> +							  pIdx->def->name),
>>  							  P4_DYNAMIC);
>>  					emit_open_cursor(pParse, iTab,
>>  							 pIdx->tnum);
>>  					sql_vdbe_set_p4_key_def(pParse, pIdx);
>> -					VdbeComment((v, "%s", pIdx->zName));
>> +					VdbeComment((v, "%s", pIdx->def->name));
>>  					assert(IN_INDEX_INDEX_DESC ==
>>  					       IN_INDEX_INDEX_ASC + 1);
>>  					eType = IN_INDEX_INDEX_ASC +
>> -						sql_index_column_sort_order(pIdx,
>> -									    0);
>> +						parts[0].sort_order;
>>  
>>  					if (prRhsHasNull) {
>>  #ifdef SQLITE_ENABLE_COLUMN_USED_MASK
>> @@ -2515,7 +2519,7 @@ sqlite3FindInIndex(Parse * pParse,	/* Parsing context */
>>  							/* Tarantool: Check for null is performed on first key of the index.  */
>>  							sqlite3SetHasNullFlag(v,
>>  									      iTab,
>> -									      pIdx->aiColumn[0],
>> +									      parts[0].fieldno,
>>  									      *prRhsHasNull);
>>  						}
>>  					}
>> @@ -3146,12 +3150,12 @@ sqlite3ExprCodeIN(Parse * pParse,	/* Parsing and code generating context */
>>  		struct Index *pk = sqlite3PrimaryKeyIndex(tab);
>>  		assert(pk);
>>  
>> +		uint32_t fieldno = pk->def->key_def->parts[0].fieldno;
>>  		enum affinity_type affinity =
>> -			tab->def->fields[pk->aiColumn[0]].affinity;
>> -		if (pk->nColumn == 1
>> +			tab->def->fields[fieldno].affinity;
>> +		if (pk->def->key_def->part_count == 1
>>  		    && affinity == AFFINITY_INTEGER
>> -		    && pk->aiColumn[0] < nVector) {
>> -			int reg_pk = rLhs + pk->aiColumn[0];
>> +		    && (int) fieldno < nVector) { int reg_pk = rLhs + (int)fieldno;
>>  			sqlite3VdbeAddOp2(v, OP_MustBeInt, reg_pk, destIfFalse);
>>  		}
>>  	}
>> @@ -3483,17 +3487,9 @@ sqlite3ExprCodeLoadIndexColumn(Parse * pParse,	/* The parsing context */
>>  			       int regOut	/* Store the index column value in this register */
>>      )
>>  {
>> -	i16 iTabCol = pIdx->aiColumn[iIdxCol];
>> -	if (iTabCol == XN_EXPR) {
>> -		assert(pIdx->aColExpr);
>> -		assert(pIdx->aColExpr->nExpr > iIdxCol);
>> -		pParse->iSelfTab = iTabCur;
>> -		sqlite3ExprCodeCopy(pParse, pIdx->aColExpr->a[iIdxCol].pExpr,
>> -				    regOut);
>> -	} else {
>> -		sqlite3ExprCodeGetColumnOfTable(pParse->pVdbe, pIdx->pTable->def,
>> -						iTabCur, iTabCol, regOut);
>> -	}
>> +	i16 iTabCol = pIdx->def->key_def->parts[iIdxCol].fieldno;
>> +	sqlite3ExprCodeGetColumnOfTable(pParse->pVdbe, pIdx->pTable->def,
>> +					iTabCur, iTabCol, regOut);
>>  }
>>  
>>  void
>> diff --git a/src/box/sql/fkey.c b/src/box/sql/fkey.c
>> index 70ebef89f..79320eced 100644
>> --- a/src/box/sql/fkey.c
>> +++ b/src/box/sql/fkey.c
>> @@ -256,8 +256,8 @@ sqlite3FkLocateIndex(Parse * pParse,	/* Parse context to store any error in */
>>  	}
>>  
>>  	for (pIdx = pParent->pIndex; pIdx; pIdx = pIdx->pNext) {
>> -		int nIdxCol = index_column_count(pIdx);
>> -		if (nIdxCol == nCol && index_is_unique(pIdx)
>> +		int nIdxCol = pIdx->def->key_def->part_count;
>> +		if (nIdxCol == nCol && pIdx->def->opts.is_unique
>>  		    && pIdx->pPartIdxWhere == 0) {
>>  			/* pIdx is a UNIQUE index (or a PRIMARY KEY) and has the right number
>>  			 * of columns. If each indexed column corresponds to a foreign key
>> @@ -286,8 +286,10 @@ sqlite3FkLocateIndex(Parse * pParse,	/* Parse context to store any error in */
>>  				 * the default collation sequences for each column.
>>  				 */
>>  				int i, j;
>> -				for (i = 0; i < nCol; i++) {
>> -					i16 iCol = pIdx->aiColumn[i];	/* Index of column in parent tbl */
>> +				struct key_part *part =
>> +					pIdx->def->key_def->parts;
>> +				for (i = 0; i < nCol; i++, part++) {
>> +					i16 iCol = (int) part->fieldno;	/* Index of column in parent tbl */
>>  					char *zIdxCol;	/* Name of indexed column */
>>  
>>  					if (iCol < 0)
>> @@ -302,9 +304,7 @@ sqlite3FkLocateIndex(Parse * pParse,	/* Parse context to store any error in */
>>  					def_coll = sql_column_collation(pParent->def,
>>  									iCol,
>>  									&id);
>> -					struct coll *coll =
>> -						sql_index_collation(pIdx, i,
>> -								    &id);
>> +					struct coll *coll = part->coll;
>>  					if (def_coll != coll)
>>  						break;
>>  
>> @@ -464,13 +464,15 @@ fkLookupParent(Parse * pParse,	/* Parse context */
>>  				for (i = 0; i < nCol; i++) {
>>  					int iChild = aiCol[i] + 1 + regData;
>>  					int iParent =
>> -					    pIdx->aiColumn[i] + 1 + regData;
>> -					assert(pIdx->aiColumn[i] >= 0);
>> +						(int) pIdx->def->key_def->parts[i].fieldno
>> +						+ 1 + regData;
>>  					assert(aiCol[i] != pTab->iPKey);
>> -					if (pIdx->aiColumn[i] == pTab->iPKey) {
>> +					if ((int)pIdx->def->key_def->
>> +						parts[i].fieldno == pTab->iPKey) {
>>  						/* The parent key is a composite key that includes the IPK column */
>>  						iParent = regData;
>>  					}
>> +
>>  					sqlite3VdbeAddOp3(v, OP_Ne, iChild,
>>  							  iJump, iParent);
>>  					VdbeCoverage(v);
>> @@ -622,7 +624,7 @@ fkScanChildren(Parse * pParse,	/* Parse context */
>>  	Vdbe *v = sqlite3GetVdbe(pParse);
>>  
>>  	assert(pIdx == 0 || pIdx->pTable == pTab);
>> -	assert(pIdx == 0 || (int)index_column_count(pIdx) == pFKey->nCol);
>> +	assert(pIdx == 0 || (int) pIdx->def->key_def->part_count == pFKey->nCol);
>>  	assert(pIdx != 0);
>>  
>>  	if (nIncr < 0) {
>> @@ -646,7 +648,7 @@ fkScanChildren(Parse * pParse,	/* Parse context */
>>  		i16 iCol;	/* Index of column in child table */
>>  		const char *zCol;	/* Name of column in child table */
>>  
>> -		iCol = pIdx ? pIdx->aiColumn[i] : -1;
>> +		iCol = pIdx ? pIdx->def->key_def->parts[i].fieldno : -1;
-               iCol = pIdx ? pIdx->def->key_def->parts[i].fieldno : -1;
+               iCol = pIdx != NULL ?
+                       (i16)pIdx->def->key_def->parts[i].fieldno : -1;

>>  		pLeft = exprTableRegister(pParse, pTab, regData, iCol);
>>  		iCol = aiCol ? aiCol[i] : pFKey->aCol[0].iFrom;
>>  		assert(iCol >= 0);
>> @@ -671,10 +673,9 @@ fkScanChildren(Parse * pParse,	/* Parse context */
>>  		Expr *pEq, *pAll = 0;
>>  		Index *pPk = sqlite3PrimaryKeyIndex(pTab);
>>  		assert(pIdx != 0);
>> -		int col_count = index_column_count(pPk);
>> +		int col_count = pPk->def->key_def->part_count;
>>  		for (i = 0; i < col_count; i++) {
>> -			i16 iCol = pIdx->aiColumn[i];
>> -			assert(iCol >= 0);
>> +			i16 iCol = (int) pIdx->def->key_def->parts[i].fieldno;
>>  			pLeft = exprTableRegister(pParse, pTab, regData, iCol);
>>  			pRight =
>>  				exprTableColumn(db, pTab->def,
>> @@ -992,7 +993,6 @@ sqlite3FkCheck(Parse * pParse,	/* Parse context */
>>  			if (aiCol[i] == pTab->iPKey) {
>>  				aiCol[i] = -1;
>>  			}
>> -			assert(pIdx == 0 || pIdx->aiColumn[i] >= 0);
>>  		}
>>  
>>  		pParse->nTab++;
>> @@ -1126,10 +1126,10 @@ sqlite3FkOldmask(Parse * pParse,	/* Parse context */
>>  			Index *pIdx = 0;
>>  			sqlite3FkLocateIndex(pParse, pTab, p, &pIdx, 0);
>>  			if (pIdx) {
>> -				int nIdxCol = index_column_count(pIdx);
>> +				int nIdxCol = pIdx->def->key_def->part_count;
>>  				for (i = 0; i < nIdxCol; i++) {
>> -					assert(pIdx->aiColumn[i] >= 0);
>> -					mask |= COLUMN_MASK(pIdx->aiColumn[i]);
>> +					mask |= COLUMN_MASK(pIdx->def->
>> +						key_def->parts[i].fieldno);
>>  				}
>>  			}
>>  		}
>> @@ -1264,11 +1264,12 @@ fkActionTrigger(Parse * pParse,	/* Parse context */
>>  			       || (pTab->iPKey >= 0
>>  				   && pTab->iPKey <
>>  				      (int)pTab->def->field_count));
>> -			assert(pIdx == 0 || pIdx->aiColumn[i] >= 0);
>> +
>> +			uint32_t fieldno = pIdx != NULL ?
>> +					     pIdx->def->key_def->parts[i].fieldno
>> +					   : pTab->iPKey;
6. GCC looks like a little more pedantic.
-                                          : pTab->iPKey;

+                                          : (uint32_t)pTab->iPKey;
7. Please put ":" at the prev. line.

>>  			sqlite3TokenInit(&tToCol,
>> -					 pTab->def->fields[pIdx ? pIdx->
>> -						    aiColumn[i] : pTab->iPKey].
>> -					 name);
>> +					 pTab->def->fields[fieldno].name);
>>  			sqlite3TokenInit(&tFromCol,
>>  					 pFKey->pFrom->def->fields[
>>  						iFromCol].name);
>> diff --git a/src/box/sql/insert.c b/src/box/sql/insert.c
>> index 59c61c703..fc9f85165 100644
>> --- a/src/box/sql/insert.c
>> +++ b/src/box/sql/insert.c
>> @@ -89,7 +89,7 @@ sqlite3IndexAffinityStr(sqlite3 * db, Index * pIdx)
>>  		 * up.
>>  		 */
>>  		int n;
>> -		int nColumn = index_column_count(pIdx);
>> +		int nColumn = pIdx->def->key_def->part_count;
>>  		pIdx->zColAff =
>>  		    (char *)sqlite3DbMallocRaw(0, nColumn + 1);
>>  		if (!pIdx->zColAff) {
>> @@ -97,22 +97,8 @@ sqlite3IndexAffinityStr(sqlite3 * db, Index * pIdx)
>>  			return 0;
>>  		}
>>  		for (n = 0; n < nColumn; n++) {
>> -			i16 x = pIdx->aiColumn[n];
>> -			if (x >= 0) {
>> -				char affinity = pIdx->pTable->
>> -					def->fields[x].affinity;
>> -				pIdx->zColAff[n] = affinity;
>> -			} else {
>> -				char aff;
>> -				assert(x == XN_EXPR);
>> -				assert(pIdx->aColExpr != 0);
>> -				aff =
>> -				    sqlite3ExprAffinity(pIdx->aColExpr->a[n].
>> -							pExpr);
>> -				if (aff == 0)
>> -					aff = AFFINITY_BLOB;
>> -				pIdx->zColAff[n] = aff;
>> -			}
>> +			i16 x = pIdx->def->key_def->parts[n].fieldno;
>> +			pIdx->zColAff[n] = pIdx->pTable->def->fields[x].affinity;
>>  		}
>>  		pIdx->zColAff[n] = 0;
>>  	}
>> @@ -645,7 +631,7 @@ sqlite3Insert(Parse * pParse,	/* Parser context */
>>  		     pIdx = pIdx->pNext, i++) {
>>  			assert(pIdx);
>>  			aRegIdx[i] = ++pParse->nMem;
>> -			pParse->nMem += index_column_count(pIdx);
>> +			pParse->nMem += pIdx->def->key_def->part_count;
>>  		}
>>  	}
>>  
>> @@ -1088,7 +1074,7 @@ sqlite3GenerateConstraintChecks(Parse * pParse,		/* The parser context */
>>  	nCol = pTab->def->field_count;
>>  
>>  	pPk = sqlite3PrimaryKeyIndex(pTab);
>> -	nPkField = index_column_count(pPk);
>> +	nPkField = pPk->def->key_def->part_count;
>>  
>>  	/* Record that this module has started */
>>  	VdbeModuleComment((v, "BEGIN: GenCnstCks(%d,%d,%d,%d,%d)",
>> @@ -1252,38 +1238,27 @@ sqlite3GenerateConstraintChecks(Parse * pParse,		/* The parser context */
>>  		 * the insert or update.  Store that record in the aRegIdx[ix] register
>>  		 */
>>  		regIdx = aRegIdx[ix] + 1;
>> -		int nIdxCol = (int)index_column_count(pIdx);
>> +		int nIdxCol = pIdx->def->key_def->part_count;
>>  		for (i = 0; i < nIdxCol; i++) {
>> -			int iField = pIdx->aiColumn[i];
>> +			int iField = (int) pIdx->def->key_def->parts[i].fieldno;
>>  			int x;
>> -			if (iField == XN_EXPR) {
>> -				pParse->ckBase = regNewData + 1;
>> -				sqlite3ExprCodeCopy(pParse,
>> -						    pIdx->aColExpr->a[i].pExpr,
>> -						    regIdx + i);
>> -				pParse->ckBase = 0;
>> -				VdbeComment((v, "%s column %d", pIdx->zName,
>> -					     i));
>> -			} else {
>> -				/* OP_SCopy copies value in separate register,
>> -				 * which later will be used by OP_NoConflict.
>> -				 * But OP_NoConflict is necessary only in cases
>> -				 * when bytecode is needed for proper UNIQUE
>> -				 * constraint handling.
>> -				 */
>> -				if (uniqueByteCodeNeeded) {
>> -					if (iField == pTab->iPKey)
>> -						x = regNewData;
>> -					else
>> -						x = iField + regNewData + 1;
>> -
>> -					assert(iField >= 0);
>> -					sqlite3VdbeAddOp2(v, OP_SCopy,
>> -							  x, regIdx + i);
>> -					VdbeComment((v, "%s",
>> -						     pTab->def->fields[
>> -							iField].name));
>> -				}
>> +			/* OP_SCopy copies value in separate register,
>> +			 * which later will be used by OP_NoConflict.
>> +			 * But OP_NoConflict is necessary only in cases
>> +			 * when bytecode is needed for proper UNIQUE
>> +			 * constraint handling.
>> +			 */
>> +			if (uniqueByteCodeNeeded) {
>> +				if (iField == pTab->iPKey)
>> +					x = regNewData;
>> +				else
>> +					x = iField + regNewData + 1;
>> +
>> +				assert(iField >= 0);
>> +				sqlite3VdbeAddOp2(v, OP_SCopy,
>> +						  x, regIdx + i);
>> +				VdbeComment((v, "%s",
>> +					     pTab->def->fields[iField].name));
>>  			}
>>  		}
>>  
>> @@ -1293,8 +1268,12 @@ sqlite3GenerateConstraintChecks(Parse * pParse,		/* The parser context */
>>  			/* If PK is marked as INTEGER, use it as strict type,
>>  			 * not as affinity. Emit code for type checking */
>>  			if (nIdxCol == 1) {
>> -				reg_pk = regNewData + 1 + pIdx->aiColumn[0];
>> -				if (pTab->zColAff[pIdx->aiColumn[0]] ==
>> +				reg_pk = regNewData + 1 +
>> +					pIdx->def->key_def->parts[0].fieldno;
>> +
>> +				int fieldno = (int)pIdx->def->key_def->
>> +					parts[0].fieldno;
>> +				if (pTab->zColAff[fieldno] ==
>>  				    AFFINITY_INTEGER) {
>>  					int skip_if_null = sqlite3VdbeMakeLabel(v);
>>  					if ((pTab->tabFlags & TF_Autoincrement) != 0) {
>> @@ -1311,8 +1290,8 @@ sqlite3GenerateConstraintChecks(Parse * pParse,		/* The parser context */
>>  			}
>>  
>>  			sqlite3VdbeAddOp3(v, OP_MakeRecord, regNewData + 1,
>> -					pTab->def->field_count, aRegIdx[ix]);
>> -			VdbeComment((v, "for %s", pIdx->zName));
>> +					  pTab->def->field_count, aRegIdx[ix]);
>> +			VdbeComment((v, "for %s", pIdx->def->name));
>>  		}
>>  
>>  		/* In an UPDATE operation, if this index is the PRIMARY KEY
>> @@ -1400,7 +1379,7 @@ sqlite3GenerateConstraintChecks(Parse * pParse,		/* The parser context */
>>  		if (uniqueByteCodeNeeded) {
>>  			sqlite3VdbeAddOp4Int(v, OP_NoConflict, iThisCur,
>>  					     addrUniqueOk, regIdx,
>> -					     index_column_count(pIdx));
>> +					     pIdx->def->key_def->part_count);
>>  		}
>>  		VdbeCoverage(v);
>>  
>> @@ -1410,19 +1389,17 @@ sqlite3GenerateConstraintChecks(Parse * pParse,		/* The parser context */
>>  								 nPkField);
>>  		if (isUpdate || on_error == ON_CONFLICT_ACTION_REPLACE) {
>>  			int x;
>> -			int nPkCol = index_column_count(pPk);
>> +			int nPkCol = pPk->def->key_def->part_count;
>>  			/* Extract the PRIMARY KEY from the end of the index entry and
>>  			 * store it in registers regR..regR+nPk-1
>>  			 */
>>  			if (pIdx != pPk) {
>>  				for (i = 0; i < nPkCol; i++) {
>> -					assert(pPk->aiColumn[i] >= 0);
>> -					x = pPk->aiColumn[i];
>> +					x = pPk->def->key_def->parts[i].fieldno;
>>  					sqlite3VdbeAddOp3(v, OP_Column,
>>  							  iThisCur, x, regR + i);
>>  					VdbeComment((v, "%s.%s", pTab->def->name,
>> -						pTab->def->fields[
>> -							pPk->aiColumn[i]].name));
>> +						pTab->def->fields[x].name));
>>  				}
>>  			}
>>  			if (isUpdate && uniqueByteCodeNeeded) {
>> @@ -1440,10 +1417,11 @@ sqlite3GenerateConstraintChecks(Parse * pParse,		/* The parser context */
>>  					      regIdx : regR);
>>  
>>  				for (i = 0; i < nPkCol; i++) {
>> -					uint32_t id;
>> -					char *p4 = (char *)sql_index_collation(pPk, i, &id);
>> -					x = pPk->aiColumn[i];
>> -					assert(x >= 0);
>> +					char *p4 = (char *) pPk->def->key_def->parts[i].coll;
>> +					x = pPk->def->key_def->parts[i].fieldno;
>> +					if (pPk->tnum==0) {
>> +						x = -1;
>> +					}
8. Redundant {}
-                                       if (pPk->tnum==0) {
+                                       if (pPk->tnum == 0)
                                                x = -1;
-                                       }

>>  					if (i == (nPkCol - 1)) {
>>  						addrJump = addrUniqueOk;
>>  						op = OP_Eq;
>> @@ -1620,8 +1598,8 @@ sqlite3OpenTableAndIndices(Parse * pParse,	/* Parsing context */
>>  		    IsPrimaryKeyIndex(pIdx) ||		/* Condition 2 */
>>  		    sqlite3FkReferences(pTab) ||	/* Condition 3 */
>>  		    /* Condition 4 */
>> -		    (index_is_unique(pIdx) && pIdx->onError !=
>> -		     ON_CONFLICT_ACTION_DEFAULT &&
>> +		    (pIdx->def->opts.is_unique &&
>> +		     pIdx->onError != ON_CONFLICT_ACTION_DEFAULT &&
>>  		     /* Condition 4.1 */
>>  		     pIdx->onError != ON_CONFLICT_ACTION_ABORT) ||
>>  		     /* Condition 4.2 */
>> @@ -1639,7 +1617,7 @@ sqlite3OpenTableAndIndices(Parse * pParse,	/* Parsing context */
>>  						  space_ptr_reg);
>>  				sql_vdbe_set_p4_key_def(pParse, pIdx);
>>  				sqlite3VdbeChangeP5(v, p5);
>> -				VdbeComment((v, "%s", pIdx->zName));
>> +				VdbeComment((v, "%s", pIdx->def->name));
>>  			}
>>  		}
>>  	}
>> @@ -1676,35 +1654,23 @@ xferCompatibleIndex(Index * pDest, Index * pSrc)
>>  	uint32_t i;
>>  	assert(pDest && pSrc);
>>  	assert(pDest->pTable != pSrc->pTable);
>> -	uint32_t nDestCol = index_column_count(pDest);
>> -	uint32_t nSrcCol = index_column_count(pSrc);
>> +	uint32_t nDestCol = pDest->def->key_def->part_count;
>> +	uint32_t nSrcCol = pSrc->def->key_def->part_count;
>>  	if (nDestCol != nSrcCol) {
>>  		return 0;	/* Different number of columns */
>>  	}
>>  	if (pDest->onError != pSrc->onError) {
>>  		return 0;	/* Different conflict resolution strategies */
>>  	}
>> -	for (i = 0; i < nSrcCol; i++) {
>> -		if (pSrc->aiColumn[i] != pDest->aiColumn[i]) {
>> +	struct key_part *src_part = pSrc->def->key_def->parts;
>> +	struct key_part *dest_part = pDest->def->key_def->parts;
>> +	for (i = 0; i < nSrcCol; i++, src_part++, dest_part++) {
>> +		if (src_part->fieldno != dest_part->fieldno)
>>  			return 0;	/* Different columns indexed */
>> -		}
>> -		if (pSrc->aiColumn[i] == XN_EXPR) {
>> -			assert(pSrc->aColExpr != 0 && pDest->aColExpr != 0);
>> -			if (sqlite3ExprCompare(pSrc->aColExpr->a[i].pExpr,
>> -					       pDest->aColExpr->a[i].pExpr,
>> -					       -1) != 0) {
>> -				return 0;	/* Different expressions in the index */
>> -			}
>> -		}
>> -		if (sql_index_column_sort_order(pSrc, i) !=
>> -		    sql_index_column_sort_order(pDest, i)) {
>> +		if (src_part->sort_order != dest_part->sort_order)
>>  			return 0;	/* Different sort orders */
>> -		}
>> -		uint32_t id;
>> -		if (sql_index_collation(pSrc, i, &id) !=
>> -		    sql_index_collation(pDest, i, &id)) {
>> +		if (src_part->coll != dest_part->coll)
>>  			return 0;	/* Different collating sequences */
>> -		}
>>  	}
>>  	if (sqlite3ExprCompare(pSrc->pPartIdxWhere, pDest->pPartIdxWhere, -1)) {
>>  		return 0;	/* Different WHERE clauses */
>> @@ -1876,16 +1842,14 @@ xferOptimization(Parse * pParse,	/* Parser context */
>>  		}
>>  	}
>>  	for (pDestIdx = pDest->pIndex; pDestIdx; pDestIdx = pDestIdx->pNext) {
>> -		if (index_is_unique(pDestIdx)) {
>> +		if (pDestIdx->def->opts.is_unique)
>>  			destHasUniqueIdx = 1;
>> -		}
>>  		for (pSrcIdx = pSrc->pIndex; pSrcIdx; pSrcIdx = pSrcIdx->pNext) {
>>  			if (xferCompatibleIndex(pDestIdx, pSrcIdx))
>>  				break;
>>  		}
>> -		if (pSrcIdx == 0) {
>> +		if (pSrcIdx == 0)
>>  			return 0;	/* pDestIdx has no corresponding index in pSrc */
>> -		}
9. Please, like this:
-               if (pSrcIdx == 0)
-                       return 0;       /* pDestIdx has no corresponding index in pSrc */
+               /* pDestIdx has no corresponding index in pSrc. */
+               if (pSrcIdx == NULL)
+                       return 0;

>>  	}
>>  	/* Get server checks. */
>>  	ExprList *pCheck_src = space_checks_expr_list(
>> @@ -1960,11 +1924,11 @@ xferOptimization(Parse * pParse,	/* Parser context */
>>  		assert(pSrcIdx);
>>  		emit_open_cursor(pParse, iSrc, pSrcIdx->tnum);
>>  		sql_vdbe_set_p4_key_def(pParse, pSrcIdx);
>> -		VdbeComment((v, "%s", pSrcIdx->zName));
>> +		VdbeComment((v, "%s", pSrcIdx->def->name));
>>  		emit_open_cursor(pParse, iDest, pDestIdx->tnum);
>>  		sql_vdbe_set_p4_key_def(pParse, pDestIdx);
>>  		sqlite3VdbeChangeP5(v, OPFLAG_BULKCSR);
>> -		VdbeComment((v, "%s", pDestIdx->zName));
>> +		VdbeComment((v, "%s", pDestIdx->def->name));
>>  		addr1 = sqlite3VdbeAddOp2(v, OP_Rewind, iSrc, 0);
>>  		VdbeCoverage(v);
>>  		sqlite3VdbeAddOp2(v, OP_RowData, iSrc, regData);
>> diff --git a/src/box/sql/pragma.c b/src/box/sql/pragma.c
>> index 9dab5a7fd..45896811b 100644
>> --- a/src/box/sql/pragma.c
>> +++ b/src/box/sql/pragma.c
>> @@ -370,8 +370,11 @@ sqlite3Pragma(Parse * pParse, Token * pId,	/* First part of [schema.]id field */
>>  						for (k = 1;
>>  						     k <=
>>  						     (int)pTab->def->field_count
>> -						     && pPk->aiColumn[k - 1] !=
>> -						     i; k++) {
>> +						     && (int) pPk->def->
>> +							     key_def->
>> +							     parts[k - 1].
>> +							     fieldno != i;
>> +						     k++) {
>>  						}
>>  					}
>>  					bool nullable =
>> @@ -430,7 +433,7 @@ sqlite3Pragma(Parse * pParse, Token * pId,	/* First part of [schema.]id field */
>>  					size_t avg_tuple_size_idx =
>>  						sql_index_tuple_size(space, idx);
>>  					sqlite3VdbeMultiLoad(v, 2, "sii",
>> -							     pIdx->zName,
>> +							     pIdx->def->name,
>>  							     avg_tuple_size_idx,
>>  							     index_field_tuple_est(pIdx, 0));
>>  					sqlite3VdbeAddOp2(v, OP_ResultRow, 1,
>> @@ -459,11 +462,13 @@ sqlite3Pragma(Parse * pParse, Token * pId,	/* First part of [schema.]id field */
>>  						 */
>>  						pParse->nMem = 3;
>>  					}
>> -					mx = index_column_count(pIdx);
>> +					mx = pIdx->def->key_def->part_count;
>>  					assert(pParse->nMem <=
>>  					       pPragma->nPragCName);
>> -					for (i = 0; i < mx; i++) {
>> -						i16 cnum = pIdx->aiColumn[i];
>> +					struct key_part *part =
>> +						pIdx->def->key_def->parts;
>> +					for (i = 0; i < mx; i++, part++) {
>> +						i16 cnum = (int) part->fieldno;
>>  						assert(pIdx->pTable);
>>  						sqlite3VdbeMultiLoad(v, 1,
>>  								     "iis", i,
>> @@ -477,19 +482,18 @@ sqlite3Pragma(Parse * pParse, Token * pId,	/* First part of [schema.]id field */
>>  								     name);
>>  						if (pPragma->iArg) {
>>  							const char *c_n;
>> -							uint32_t id;
>> +							uint32_t id =
>> +								part->coll_id;
>>  							struct coll *coll =
>> -								sql_index_collation(pIdx, i, &id);
>> +								part->coll;
>>  							if (coll != NULL)
>>  								c_n = coll_by_id(id)->name;
>>  							else
>>  								c_n = "BINARY";
>> -							enum sort_order sort_order;
>> -							sort_order = sql_index_column_sort_order(pIdx,
>> -												 i);
>>  							sqlite3VdbeMultiLoad(v,
>>  									     4,
>>  									     "isi",
>> +									     part->
>>  									     sort_order,
>>  									     c_n,
>>  									     i <
>> @@ -519,10 +523,8 @@ sqlite3Pragma(Parse * pParse, Token * pId,	/* First part of [schema.]id field */
>>  						    { "c", "u", "pk" };
>>  						sqlite3VdbeMultiLoad(v, 1,
>>  								     "isisi", i,
>> -								     pIdx->
>> -								     zName,
>> -								     index_is_unique
>> -								     (pIdx),
>> +								     pIdx->def->name,
>> +								     pIdx->def->opts.is_unique,
>>  								     azOrigin
>>  								     [pIdx->
>>  								      idxType],
>> diff --git a/src/box/sql/select.c b/src/box/sql/select.c
>> index 2aa35a114..2646a99c3 100644
>> --- a/src/box/sql/select.c
>> +++ b/src/box/sql/select.c
>> @@ -4291,7 +4291,7 @@ sqlite3IndexedByLookup(Parse * pParse, struct SrcList_item *pFrom)
>>  		char *zIndexedBy = pFrom->u1.zIndexedBy;
>>  		Index *pIdx;
>>  		for (pIdx = pTab->pIndex;
>> -		     pIdx && strcmp(pIdx->zName, zIndexedBy);
>> +		     pIdx && strcmp(pIdx->def->name, zIndexedBy);
>>  		     pIdx = pIdx->pNext) ;
>>  		if (!pIdx) {
>>  			sqlite3ErrorMsg(pParse, "no such index: %s", zIndexedBy,
>> diff --git a/src/box/sql/sqliteInt.h b/src/box/sql/sqliteInt.h
>> index 01351a183..36b46ed4f 100644
>> --- a/src/box/sql/sqliteInt.h
>> +++ b/src/box/sql/sqliteInt.h
>> @@ -2071,21 +2071,6 @@ struct UnpackedRecord {
>>   * Each SQL index is represented in memory by an
>>   * instance of the following structure.
>>   *
>> - * The columns of the table that are to be indexed are described
>> - * by the aiColumn[] field of this structure.  For example, suppose
>> - * we have the following table and index:
>> - *
>> - *     CREATE TABLE Ex1(c1 int, c2 int, c3 text);
>> - *     CREATE INDEX Ex2 ON Ex1(c3,c1);
>> - *
>> - * In the Table structure describing Ex1, nCol==3 because there are
>> - * three columns in the table.  In the Index structure describing
>> - * Ex2, nColumn==2 since 2 of the 3 columns of Ex1 are indexed.
>> - * The value of aiColumn is {2, 0}.  aiColumn[0]==2 because the
>> - * first column to be indexed (c3) has an index of 2 in Ex1.aCol[].
>> - * The second column to be indexed (c1) has an index of 0 in
>> - * Ex1.aCol[], hence Ex2.aiColumn[1]==0.
>> - *
>>   * The Index.onError field determines whether or not the indexed columns
>>   * must be unique and what to do if they are not.  When Index.onError=
>>   * ON_CONFLICT_ACTION_NONE, it means this is not a unique index.
>> @@ -2102,27 +2087,19 @@ struct UnpackedRecord {
>>   * program is executed). See convertToWithoutRowidTable() for details.
>>   */
>>  struct Index {
>> -	char *zName;		/* Name of this index */
>> -	i16 *aiColumn;		/* Which columns are used by this index.  1st is 0 */
>>  	LogEst *aiRowLogEst;	/* From ANALYZE: Est. rows selected by each column */
>>  	Table *pTable;		/* The SQL table being indexed */
>>  	char *zColAff;		/* String defining the affinity of each column */
>>  	Index *pNext;		/* The next index associated with the same table */
>>  	Schema *pSchema;	/* Schema containing this index */
>> -	/** Sorting order for each column. */
>> -	enum sort_order *sort_order;
>> -	/** Array of collation sequences for index. */
>> -	struct coll **coll_array;
>> -	/** Array of collation identifiers. */
>> -	uint32_t *coll_id_array;
>>  	Expr *pPartIdxWhere;	/* WHERE clause for partial indices */
>>  	ExprList *aColExpr;	/* Column expressions */
>>  	int tnum;		/* DB Page containing root of this index */
>> -	u16 nColumn;		/* Number of columns stored in the index */
>>  	u8 onError;		/* ON_CONFLICT_ACTION_ABORT, _IGNORE, _REPLACE,
>>  				 * or _NONE
>>  				 */
>>  	unsigned idxType:2;	/* 1==UNIQUE, 2==PRIMARY KEY, 0==CREATE INDEX */
10. Will you keep an informative tarantool-style comment here?

>> +	struct index_def *def;
>>  };
>>  
>>  /**
>> @@ -2161,11 +2138,6 @@ index_field_tuple_est(struct Index *idx, uint32_t field);
>>  #define IsUniqueIndex(X)      (((X)->idxType == SQLITE_IDXTYPE_UNIQUE) || \
>>  				((X)->idxType == SQLITE_IDXTYPE_PRIMARYKEY))
>>  
>> -/* The Index.aiColumn[] values are normally positive integer.  But
>> - * there are some negative values that have special meaning:
>> - */
>> -#define XN_EXPR      (-2)	/* Indexed column is an expression */
>> -
>>  #ifdef DEFAULT_TUPLE_COUNT
>>  #undef DEFAULT_TUPLE_COUNT
>>  #endif
>> @@ -3526,37 +3498,10 @@ void sqlite3AddCollateType(Parse *, Token *);
>>   */
>>  struct coll *
>>  sql_column_collation(struct space_def *def, uint32_t column, uint32_t *coll_id);
>> -/**
>> - * Return name of given column collation from index.
>> - *
>> - * @param idx Index which is used to fetch column.
>> - * @param column Number of column.
>> - * @param[out] coll_id Collation identifier.
>> - * @retval Pointer to collation.
>> - */
>> -struct coll *
>> -sql_index_collation(Index *idx, uint32_t column, uint32_t *id);
>> +
>>  bool
>>  space_is_view(Table *);
>>  
>> -/**
>> - * Return key_def of provided struct Index.
>> - * @param idx Pointer to `struct Index` object.
>> - * @retval Pointer to `struct key_def`.
>> - */
>> -struct key_def*
>> -sql_index_key_def(struct Index *idx);
>> -
>> -/**
>> - * Return sort order of given column from index.
>> - *
>> - * @param idx Index which is used to fetch column.
>> - * @param column Number of column.
>> - * @retval Sort order of requested column.
>> - */
>> -enum sort_order
>> -sql_index_column_sort_order(Index *idx, uint32_t column);
>> -
>>  void sqlite3EndTable(Parse *, Token *, Token *, Select *);
>>  int
>>  emit_open_cursor(Parse *, int, int);
>> @@ -3607,8 +3552,6 @@ void sqlite3SrcListAssignCursors(Parse *, SrcList *);
>>  void sqlite3IdListDelete(sqlite3 *, IdList *);
>>  void sqlite3SrcListDelete(sqlite3 *, SrcList *);
>>  Index *sqlite3AllocateIndexObject(sqlite3 *, i16, int, char **);
>> -bool
>> -index_is_unique(Index *);
>>  
>>  /**
>>   * Create a new index for an SQL table.  name is the name of the
>> @@ -4293,8 +4236,6 @@ int sqlite3InvokeBusyHandler(BusyHandler *);
>>  int
>>  sql_analysis_load(struct sqlite3 *db);
>>  
>> -uint32_t
>> -index_column_count(const Index *);
>>  bool
>>  index_is_unique_not_null(const Index *);
>>  void sqlite3RegisterLikeFunctions(sqlite3 *, int);
>> diff --git a/src/box/sql/trigger.c b/src/box/sql/trigger.c
>> index e1126b2d2..ea3521133 100644
>> --- a/src/box/sql/trigger.c
>> +++ b/src/box/sql/trigger.c
>> @@ -872,8 +872,6 @@ codeRowTrigger(Parse * pParse,	/* Current parse context */
>>  	pSubParse->pToplevel = pTop;
>>  	pSubParse->eTriggerOp = pTrigger->op;
>>  	pSubParse->nQueryLoop = pParse->nQueryLoop;
>> -	struct region *region = &fiber()->gc;
>> -	pSubParse->region_initial_size = region_used(region);
>>  
>>  	v = sqlite3GetVdbe(pSubParse);
>>  	if (v) {
>> diff --git a/src/box/sql/update.c b/src/box/sql/update.c
>> index 590aad28b..6545b3b06 100644
>> --- a/src/box/sql/update.c
>> +++ b/src/box/sql/update.c
>> @@ -237,14 +237,14 @@ sqlite3Update(Parse * pParse,		/* The parser context */
>>  	 */
>>  	for (j = 0, pIdx = pTab->pIndex; pIdx; pIdx = pIdx->pNext, j++) {
>>  		int reg;
>> -		int nIdxCol = index_column_count(pIdx);
>> +		int nIdxCol = pIdx->def->key_def->part_count;
>>  		if (chngPk || hasFK || pIdx->pPartIdxWhere || pIdx == pPk) {
>>  			reg = ++pParse->nMem;
>>  			pParse->nMem += nIdxCol;
>>  		} else {
>>  			reg = 0;
>>  			for (i = 0; i < nIdxCol; i++) {
>> -				i16 iIdxCol = pIdx->aiColumn[i];
>> +				i16 iIdxCol = pIdx->def->key_def->parts[i].fieldno;
>>  				if (iIdxCol < 0 || aXRef[iIdxCol] >= 0) {
>>  					reg = ++pParse->nMem;
>>  					pParse->nMem += nIdxCol;
>> @@ -306,7 +306,7 @@ sqlite3Update(Parse * pParse,		/* The parser context */
>>  		nPk = nKey;
>>  	} else {
>>  		assert(pPk != 0);
>> -		nPk = index_column_count(pPk);
>> +		nPk = pPk->def->key_def->part_count;
>>  	}
>>  	iPk = pParse->nMem + 1;
>>  	pParse->nMem += nPk;
>> @@ -333,9 +333,9 @@ sqlite3Update(Parse * pParse,		/* The parser context */
>>  		}
>>  	} else {
>>  		for (i = 0; i < nPk; i++) {
>> -			assert(pPk->aiColumn[i] >= 0);
>>  			sqlite3ExprCodeGetColumnOfTable(v, pTab->def, iDataCur,
>> -							pPk->aiColumn[i],
>> +							pPk->def->key_def->
>> +								parts[i].fieldno,
>>  							iPk + i);
>>  		}
>>  	}
>> diff --git a/src/box/sql/vdbeaux.c b/src/box/sql/vdbeaux.c
>> index 679bd0bc1..520b309d9 100644
>> --- a/src/box/sql/vdbeaux.c
>> +++ b/src/box/sql/vdbeaux.c
>> @@ -1165,7 +1165,7 @@ sql_vdbe_set_p4_key_def(struct Parse *parse, struct Index *idx)
>>  	struct Vdbe *v = parse->pVdbe;
>>  	assert(v != NULL);
>>  	assert(idx != NULL);
>> -	struct key_def *def = key_def_dup(sql_index_key_def(idx));
>> +	struct key_def *def = key_def_dup(idx->def->key_def);
>>  	if (def == NULL)
>>  		sqlite3OomFault(parse->db);
>>  	else
>> diff --git a/src/box/sql/vdbemem.c b/src/box/sql/vdbemem.c
>> index f408b7701..51b5d516e 100644
>> --- a/src/box/sql/vdbemem.c
>> +++ b/src/box/sql/vdbemem.c
>> @@ -1087,7 +1087,7 @@ valueNew(sqlite3 * db, struct ValueNewStat4Ctx *p)
>>  			Index *pIdx = p->pIdx;	/* Index being probed */
>>  			int nByte;	/* Bytes of space to allocate */
>>  			int i;	/* Counter variable */
>> -			int nCol = index_column_count(pIdx);
>> +			int nCol = pIdx->def->key_def->part_count;
>>  
>>  			nByte = sizeof(Mem) * nCol +
>>  				ROUND8(sizeof(UnpackedRecord));
>> @@ -1095,7 +1095,7 @@ valueNew(sqlite3 * db, struct ValueNewStat4Ctx *p)
>>  			    (UnpackedRecord *) sqlite3DbMallocZero(db, nByte);
>>  			if (pRec == NULL)
>>  				return NULL;
>> -			pRec->key_def = key_def_dup(sql_index_key_def(pIdx));
>> +			pRec->key_def = key_def_dup(pIdx->def->key_def);
>>  			if (pRec->key_def == NULL) {
>>  				sqlite3DbFree(db, pRec);
>>  				sqlite3OomFault(db);
>> diff --git a/src/box/sql/where.c b/src/box/sql/where.c
>> index e79164781..9f5de50f9 100644
>> --- a/src/box/sql/where.c
>> +++ b/src/box/sql/where.c
>> @@ -265,11 +265,6 @@ whereScanNext(WhereScan * pScan)
>>  			for (pTerm = pWC->a + k; k < pWC->nTerm; k++, pTerm++) {
>>  				if (pTerm->leftCursor == iCur
>>  				    && pTerm->u.leftColumn == iColumn
>> -				    && (iColumn != XN_EXPR
>> -					|| sqlite3ExprCompare(pTerm->pExpr->
>> -							      pLeft,
>> -							      pScan->pIdxExpr,
>> -							      iCur) == 0)
>>  				    && (pScan->iEquiv <= 1
>>  					|| !ExprHasProperty(pTerm->pExpr,
>>  							    EP_FromJoin))
>> @@ -376,19 +371,21 @@ whereScanInit(WhereScan * pScan,	/* The WhereScan object being initialized */
>>  	pScan->is_column_seen = false;
>>  	if (pIdx) {
>>  		int j = iColumn;
>> -		iColumn = pIdx->aiColumn[j];
>> -		if (iColumn == XN_EXPR) {
>> -			pScan->pIdxExpr = pIdx->aColExpr->a[j].pExpr;
>> -		} else if (iColumn >= 0) {
>> +		iColumn = pIdx->def->key_def->parts[j].fieldno;
>> +		/*
>> +		 * pIdx->tnum == 0 means that pIdx is a fake integer
>> +		 * primary key index
>> +		 */
11. Out of comment-type margin
-                * pIdx->tnum == 0 means that pIdx is a fake integer
-                * primary key index
+                * pIdx->tnum == 0 means that pIdx is a fake
+                * integer primary key index.

>> +		if (pIdx->tnum == 0)
>> +			iColumn = -1;
>> +
>> +		if (iColumn >= 0) {
>>  			char affinity =
>>  				pIdx->pTable->def->fields[iColumn].affinity;
>>  			pScan->idxaff = affinity;
>> -			uint32_t id;
>> -			pScan->coll = sql_index_collation(pIdx, j, &id);
>> +			pScan->coll = pIdx->def->key_def->parts[j].coll;
>>  			pScan->is_column_seen = true;
>>  		}
>> -	} else if (iColumn == XN_EXPR) {
>> -		return 0;
>>  	}
>>  	pScan->opMask = opMask;
>>  	pScan->k = 0;
>> @@ -464,18 +461,17 @@ findIndexCol(Parse * pParse,	/* Parse context */
>>  	     Index * pIdx,	/* Index to match column of */
>>  	     int iCol)		/* Column of index to match */
>>  {
>> +	struct key_part *part_to_match = &pIdx->def->key_def->parts[iCol];
>>  	for (int i = 0; i < pList->nExpr; i++) {
>>  		Expr *p = sqlite3ExprSkipCollate(pList->a[i].pExpr);
>> -		if (p->op == TK_COLUMN &&
>> -		    p->iColumn == pIdx->aiColumn[iCol] &&
>> -		    p->iTable == iBase) {
>> +		if (p->op == TK_COLUMN && p->iTable == iBase &&
>> +		    p->iColumn == (int) part_to_match->fieldno) {
>>  			bool is_found;
>>  			uint32_t id;
>>  			struct coll *coll = sql_expr_coll(pParse,
>>  							  pList->a[i].pExpr,
>>  							  &is_found, &id);
>> -			if (is_found &&
>> -			    coll == sql_index_collation(pIdx, iCol, &id)) {
>> +			if (is_found && coll == part_to_match->coll) {
>>  				return i;
>>  			}
12. Extra braces:
-                       if (is_found && coll == part_to_match->coll) {
+                       if (is_found && coll == part_to_match->coll)
                                return i;
-                       }

>>  		}
>> @@ -484,27 +480,6 @@ findIndexCol(Parse * pParse,	/* Parse context */
>>  	return -1;
>>  }
>>  
>> -/*
>> - * Return TRUE if the iCol-th column of index pIdx is NOT NULL
>> - */
>> -static int
>> -indexColumnNotNull(Index * pIdx, int iCol)
>> -{
>> -	int j;
>> -	assert(pIdx != 0);
>> -	assert(iCol >= 0 && iCol < (int)index_column_count(pIdx));
>> -	j = pIdx->aiColumn[iCol];
>> -	if (j >= 0) {
>> -		return !pIdx->pTable->def->fields[j].is_nullable;
>> -	} else if (j == (-1)) {
>> -		return 1;
>> -	} else {
>> -		assert(j == (-2));
>> -		return 0;	/* Assume an indexed expression can always yield a NULL */
>> -
>> -	}
>> -}
>> -
>>  /*
>>   * Return true if the DISTINCT expression-list passed as the third argument
>>   * is redundant.
>> @@ -556,9 +531,9 @@ isDistinctRedundant(Parse * pParse,		/* Parsing context */
>>  	 *      contain a "col=X" term are subject to a NOT NULL constraint.
>>  	 */
>>  	for (pIdx = pTab->pIndex; pIdx; pIdx = pIdx->pNext) {
>> -		if (!index_is_unique(pIdx))
>> +		if (!pIdx->def->opts.is_unique)
>>  			continue;
>> -		int col_count = index_column_count(pIdx);
>> +		int col_count = pIdx->def->key_def->part_count;
>>  		for (i = 0; i < col_count; i++) {
>>  			if (0 ==
>>  			    sqlite3WhereFindTerm(pWC, iBase, i, ~(Bitmask) 0,
>> @@ -566,11 +541,12 @@ isDistinctRedundant(Parse * pParse,		/* Parsing context */
>>  				if (findIndexCol
>>  				    (pParse, pDistinct, iBase, pIdx, i) < 0)
>>  					break;
>> -				if (indexColumnNotNull(pIdx, i) == 0)
>> +				uint32_t j = pIdx->def->key_def->parts[i].fieldno;
>> +				if (!pIdx->pTable->def->fields[j].is_nullable == 0)
13. !.. == 0 is looking confusing for me.. Please, fix it.

>>  					break;
>>  			}
>>  		}
>> -		if (i == (int)index_column_count(pIdx)) {
>> +		if (i == (int) pIdx->def->key_def->part_count) {
>>  			/* This index implies that the DISTINCT qualifier is redundant. */
>>  			return 1;
>>  		}
>> @@ -1107,7 +1083,7 @@ whereRangeAdjust(WhereTerm * pTerm, LogEst nNew)
>>  char
>>  sqlite3IndexColumnAffinity(sqlite3 * db, Index * pIdx, int iCol)
>>  {
>> -	assert(iCol >= 0 && iCol < (int)index_column_count(pIdx));
>> +	assert(iCol >= 0 && iCol < (int) pIdx->def->key_def->part_count);
>>  	if (!pIdx->zColAff) {
>>  		if (sqlite3IndexAffinityStr(db, pIdx) == 0)
>>  			return AFFINITY_BLOB;
>> @@ -1169,13 +1145,12 @@ whereRangeSkipScanEst(Parse * pParse,		/* Parsing & code generating context */
>>  	int nUpper = index->def->opts.stat->sample_count + 1;
>>  	int rc = SQLITE_OK;
>>  	u8 aff = sqlite3IndexColumnAffinity(db, p, nEq);
>> -	uint32_t id;
>>  
>>  	sqlite3_value *p1 = 0;	/* Value extracted from pLower */
>>  	sqlite3_value *p2 = 0;	/* Value extracted from pUpper */
>>  	sqlite3_value *pVal = 0;	/* Value extracted from record */
>>  
>> -	struct coll *pColl = sql_index_collation(p, nEq, &id);
>> +	struct coll *pColl = p->def->key_def->parts[nEq].coll;
>>  	if (pLower) {
>>  		rc = sqlite3Stat4ValueFromExpr(pParse, pLower->pExpr->pRight,
>>  					       aff, &p1);
>> @@ -1371,7 +1346,7 @@ whereRangeScanEst(Parse * pParse,	/* Parsing & code generating context */
>>  			       || (pLower->eOperator & (WO_GT | WO_GE)) != 0);
>>  			assert(pUpper == 0
>>  			       || (pUpper->eOperator & (WO_LT | WO_LE)) != 0);
>> -			if (sql_index_column_sort_order(p, nEq) !=
>> +			if (p->def->key_def->parts[nEq].sort_order !=
>>  			    SORT_ORDER_ASC) {
>>  				/* The roles of pLower and pUpper are swapped for a DESC index */
>>  				SWAP(pLower, pUpper);
>> @@ -1521,7 +1496,7 @@ whereEqualScanEst(Parse * pParse,	/* Parsing & code generating context */
>>  	int bOk;
>>  
>>  	assert(nEq >= 1);
>> -	assert(nEq <= (int)index_column_count(p));
>> +	assert(nEq <= (int) p->def->key_def->part_count);
>>  	assert(pBuilder->nRecValid < nEq);
>>  
>>  	/* If values are not available for all fields of the index to the left
>> @@ -1542,7 +1517,7 @@ whereEqualScanEst(Parse * pParse,	/* Parsing & code generating context */
>>  
>>  	whereKeyStats(pParse, p, pRec, 0, a);
>>  	WHERETRACE(0x10, ("equality scan regions %s(%d): %d\n",
>> -			  p->zName, nEq - 1, (int)a[1]));
>> +			  p->def->name, nEq - 1, (int)a[1]));
>>  	*pnRow = a[1];
>>  
>>  	return rc;
>> @@ -1674,7 +1649,7 @@ whereLoopPrint(WhereLoop * p, WhereClause * pWC)
>>  			   pItem->zAlias ? pItem->zAlias : pTab->def->name);
>>  #endif
>>  	const char *zName;
>> -	if (p->pIndex && (zName = p->pIndex->zName) != 0) {
>> +	if (p->pIndex && (zName = p->pIndex->def->name) != 0) {
>>  		if (strncmp(zName, "sqlite_autoindex_", 17) == 0) {
>>  			int i = sqlite3Strlen30(zName) - 1;
>>  			while (zName[i] != '_')
>> @@ -2236,7 +2211,7 @@ whereRangeVectorLen(Parse * pParse,	/* Parsing context */
>>  	int nCmp = sqlite3ExprVectorSize(pTerm->pExpr->pLeft);
>>  	int i;
>>  
>> -	nCmp = MIN(nCmp, (int)(index_column_count(pIdx) - nEq));
>> +	nCmp = MIN(nCmp, (int)(pIdx->def->key_def->part_count - nEq));
>>  	for (i = 1; i < nCmp; i++) {
>>  		/* Test if comparison i of pTerm is compatible with column (i+nEq)
>>  		 * of the index. If not, exit the loop.
>> @@ -2257,11 +2232,10 @@ whereRangeVectorLen(Parse * pParse,	/* Parsing context */
>>  		 * order of the index column is the same as the sort order of the
>>  		 * leftmost index column.
>>  		 */
>> -		if (pLhs->op != TK_COLUMN
>> -		    || pLhs->iTable != iCur
>> -		    || pLhs->iColumn != pIdx->aiColumn[i + nEq]
>> -		    || sql_index_column_sort_order(pIdx, i + nEq) !=
>> -		       sql_index_column_sort_order(pIdx, nEq)) {
>> +		if (pLhs->op != TK_COLUMN || pLhs->iTable != iCur
>> +		    || pLhs->iColumn != (int)pIdx->def->key_def->parts[i + nEq].fieldno
>> +		    || pIdx->def->key_def->parts[i + nEq].sort_order !=
>> +		       pIdx->def->key_def->parts[nEq].sort_order) {
>>  			break;
>>  		}
14. Extra braces:
-                      pIdx->def->key_def->parts[nEq].sort_order) {
+                      pIdx->def->key_def->parts[nEq].sort_order)
                        break;
-               }

>>  
>> @@ -2275,7 +2249,7 @@ whereRangeVectorLen(Parse * pParse,	/* Parsing context */
>>  		pColl = sql_binary_compare_coll_seq(pParse, pLhs, pRhs, &id);
>>  		if (pColl == 0)
>>  			break;
>> -	        if (sql_index_collation(pIdx, i + nEq, &id) != pColl)
>> +		if (pIdx->def->key_def->parts[(i + nEq)].coll != pColl)
>>  			break;
>>  	}
>>  	return i;
>> @@ -2318,13 +2292,13 @@ whereLoopAddBtreeIndex(WhereLoopBuilder * pBuilder,	/* The WhereLoop factory */
>>  	LogEst rSize;		/* Number of rows in the table */
>>  	LogEst rLogSize;	/* Logarithm of table size */
>>  	WhereTerm *pTop = 0, *pBtm = 0;	/* Top and bottom range constraints */
>> -	uint32_t nProbeCol = index_column_count(pProbe);
>> +	uint32_t nProbeCol = pProbe->def->key_def->part_count;
>>  
>>  	pNew = pBuilder->pNew;
>>  	if (db->mallocFailed)
>>  		return SQLITE_NOMEM_BKPT;
>>  	WHERETRACE(0x800, ("BEGIN addBtreeIdx(%s), nEq=%d\n",
>> -			   pProbe->zName, pNew->nEq));
>> +			   pProbe->def->name, pNew->nEq));
>>  
>>  	assert((pNew->wsFlags & WHERE_TOP_LIMIT) == 0);
>>  	if (pNew->wsFlags & WHERE_BTM_LIMIT) {
>> @@ -2374,8 +2348,9 @@ whereLoopAddBtreeIndex(WhereLoopBuilder * pBuilder,	/* The WhereLoop factory */
>>  		LogEst nOutUnadjusted;	/* nOut before IN() and WHERE adjustments */
>>  		int nIn = 0;
>>  		int nRecValid = pBuilder->nRecValid;
>> +		uint32_t j = pProbe->def->key_def->parts[saved_nEq].fieldno;
>>  		if ((eOp == WO_ISNULL || (pTerm->wtFlags & TERM_VNULL) != 0)
>> -		    && indexColumnNotNull(pProbe, saved_nEq)
>> +		    && !pProbe->pTable->def->fields[j].is_nullable
>>  		    ) {
>>  			continue;	/* ignore IS [NOT] NULL constraints on NOT NULL columns */
>>  		}
>> @@ -2445,14 +2420,16 @@ whereLoopAddBtreeIndex(WhereLoopBuilder * pBuilder,	/* The WhereLoop factory */
>>  							 */
>>  			}
>>  		} else if (eOp & WO_EQ) {
>> -			int iCol = pProbe->aiColumn[saved_nEq];
>> +			int iCol = pProbe->def->key_def->parts[saved_nEq].fieldno;
>>  			pNew->wsFlags |= WHERE_COLUMN_EQ;
>>  			assert(saved_nEq == pNew->nEq);
>> -			if ((iCol > 0 && nInMul == 0
>> -				&& saved_nEq == nProbeCol - 1)
>> -			    ) {
>> -				if (iCol >= 0 &&
>> -				    !index_is_unique_not_null(pProbe)) {
>> +			if ((iCol > 0 && nInMul == 0 &&
>> +			     saved_nEq == nProbeCol - 1)) {
>> +				bool index_is_unique_not_null =
>> +					pProbe->def->key_def->is_nullable &&
>> +					pProbe->def->opts.is_unique;
>> +				if (pProbe->tnum != 0 &&
>> +				    !index_is_unique_not_null) {
>>  					pNew->wsFlags |= WHERE_UNQ_WANTED;
>>  				} else {
>>  					pNew->wsFlags |= WHERE_ONEROW;
>> @@ -2514,8 +2491,7 @@ whereLoopAddBtreeIndex(WhereLoopBuilder * pBuilder,	/* The WhereLoop factory */
>>  			assert(eOp & (WO_ISNULL | WO_EQ | WO_IN));
>>  
>>  			assert(pNew->nOut == saved_nOut);
>> -			if (pTerm->truthProb <= 0
>> -			    && pProbe->aiColumn[saved_nEq] >= 0) {
>> +			if (pTerm->truthProb <= 0 && pProbe->tnum != 0 ) {
>>  				assert((eOp & WO_IN) || nIn == 0);
>>  				testcase(eOp & WO_IN);
>>  				pNew->nOut += pTerm->truthProb;
>> @@ -2671,7 +2647,7 @@ whereLoopAddBtreeIndex(WhereLoopBuilder * pBuilder,	/* The WhereLoop factory */
>>  	}
>>  
>>  	WHERETRACE(0x800, ("END addBtreeIdx(%s), nEq=%d, rc=%d\n",
>> -			   pProbe->zName, saved_nEq, rc));
>> +			   pProbe->def->name, saved_nEq, rc));
>>  	return rc;
>>  }
>>  
>> @@ -2715,7 +2691,7 @@ indexMightHelpWithOrderBy(WhereLoopBuilder * pBuilder,
>>  	ExprList *pOB;
>>  	ExprList *aColExpr;
>>  	int ii, jj;
>> -	int nIdxCol = index_column_count(pIndex);
>> +	int nIdxCol = pIndex->def->key_def->part_count;
>>  	if (index_is_unordered(pIndex))
>>  		return 0;
>>  	if ((pOB = pBuilder->pWInfo->pOrderBy) == 0)
>> @@ -2726,13 +2702,12 @@ indexMightHelpWithOrderBy(WhereLoopBuilder * pBuilder,
>>  			if (pExpr->iColumn < 0)
>>  				return 1;
>>  			for (jj = 0; jj < nIdxCol; jj++) {
>> -				if (pExpr->iColumn == pIndex->aiColumn[jj])
>> +				if (pExpr->iColumn == (int)
>> +				    pIndex->def->key_def->parts[jj].fieldno)
>>  					return 1;
>>  			}
>>  		} else if ((aColExpr = pIndex->aColExpr) != 0) {
>>  			for (jj = 0; jj < nIdxCol; jj++) {
>> -				if (pIndex->aiColumn[jj] != XN_EXPR)
>> -					continue;
>>  				if (sqlite3ExprCompare
>>  				    (pExpr, aColExpr->a[jj].pExpr,
>>  				     iCursor) == 0) {
>> @@ -2815,7 +2790,6 @@ whereLoopAddBtree(WhereLoopBuilder * pBuilder,	/* WHERE clause information */
>>  	Index *pProbe;		/* An index we are evaluating */
>>  	Index sPk;		/* A fake index object for the primary key */
>>  	LogEst aiRowEstPk[2];	/* The aiRowLogEst[] value for the sPk index */
>> -	i16 aiColumnPk = -1;	/* The aColumn[] value for the sPk index */
>>  	SrcList *pTabList;	/* The FROM clause */
>>  	struct SrcList_item *pSrc;	/* The FROM clause btree term to add */
>>  	WhereLoop *pNew;	/* Template WhereLoop object */
>> @@ -2846,11 +2820,27 @@ whereLoopAddBtree(WhereLoopBuilder * pBuilder,	/* WHERE clause information */
>>  		 */
>>  		Index *pFirst;	/* First of real indices on the table */
>>  		memset(&sPk, 0, sizeof(Index));
>> -		sPk.nColumn = 1;
>> -		sPk.aiColumn = &aiColumnPk;
>>  		sPk.aiRowLogEst = aiRowEstPk;
>>  		sPk.onError = ON_CONFLICT_ACTION_REPLACE;
>>  		sPk.pTable = pTab;
>> +
>> +		struct key_def *key_def = key_def_new(1);
>> +		if (key_def == NULL)
>> +			return SQLITE_ERROR;
>> +
>> +		key_def_set_part(key_def, 0, 0, pTab->def->fields[0].type,
>> +				 ON_CONFLICT_ACTION_ABORT,
>> +				 NULL, COLL_NONE, SORT_ORDER_ASC);
>> +
>> +		struct index_opts index_opts = index_opts_default;
>> +
>> +		sPk.def = index_def_new(pTab->def->id, 0, "primary",
>> +					sizeof("primary") - 1, TREE, &index_opts,
>> +					key_def, NULL);
>> +
>> +		if (sPk.def == NULL)
>> +			return SQLITE_ERROR;
15. key_def is leaking here, same as in other errors bellow and at the end of the function.

>> +
>>  		aiRowEstPk[0] = sql_space_tuple_log_count(pTab);
>>  		aiRowEstPk[1] = 0;
>>  		pFirst = pSrc->pTab->pIndex;
>> @@ -3325,8 +3315,8 @@ wherePathSatisfiesOrderBy(WhereInfo * pWInfo,	/* The WHERE clause */
>>  				   index_is_unordered(pIndex)) {
>>  				return 0;
>>  			} else {
>> -				nColumn = index_column_count(pIndex);
>> -				isOrderDistinct = index_is_unique(pIndex);
>> +				nColumn = pIndex->def->key_def->part_count;
>> +				isOrderDistinct = pIndex->def->opts.is_unique;
>>  			}
>>  
>>  			/* Loop through all columns of the index and deal with the ones
>> @@ -3387,9 +3377,10 @@ wherePathSatisfiesOrderBy(WhereInfo * pWInfo,	/* The WHERE clause */
>>  				 * (revIdx) for the j-th column of the index.
>>  				 */
>>  				if (pIndex) {
>> -					iColumn = pIndex->aiColumn[j];
>> -					revIdx = sql_index_column_sort_order(pIndex,
>> -									     j);
>> +					iColumn = pIndex->def->key_def->
>> +						parts[j].fieldno;
>> +					revIdx = pIndex->def->key_def->
>> +						parts[j].sort_order;
>>  					if (iColumn == pIndex->pTable->iPKey)
>>  						iColumn = -1;
>>  				} else {
>> @@ -3442,8 +3433,7 @@ wherePathSatisfiesOrderBy(WhereInfo * pWInfo,	/* The WHERE clause */
>>  								      pOrderBy->a[i].pExpr,
>>  								      &is_found, &id);
>>  						struct coll *idx_coll =
>> -							sql_index_collation(pIndex,
>> -									    j, &id);
>> +							pIndex->def->key_def->parts[j].coll;
>>  						if (is_found &&
>>  						    coll != idx_coll)
>>  							continue;
>> @@ -4105,13 +4095,13 @@ whereShortCut(WhereLoopBuilder * pBuilder)
>>  	} else {
>>  		for (pIdx = pTab->pIndex; pIdx; pIdx = pIdx->pNext) {
>>  			int opMask;
>> -			int nIdxCol = index_column_count(pIdx);
>> +			int nIdxCol = pIdx->def->key_def->part_count;
>>  			assert(pLoop->aLTermSpace == pLoop->aLTerm);
>> -			if (!index_is_unique(pIdx)
>> +			if (!pIdx->def->opts.is_unique
>>  			    || pIdx->pPartIdxWhere != 0
>> -			    || nIdxCol > ArraySize(pLoop->aLTermSpace)
>> -			    )
>> +			    || nIdxCol > ArraySize(pLoop->aLTermSpace)) {
>>  				continue;
>> +			}
>>  			opMask = WO_EQ;
>>  			for (j = 0; j < nIdxCol; j++) {
>>  				pTerm =
>> @@ -4650,7 +4640,7 @@ sqlite3WhereBegin(Parse * pParse,	/* The parser context */
>>  					wctrlFlags & WHERE_ORDERBY_MIN) == 0) {
>>  					sqlite3VdbeChangeP5(v, OPFLAG_SEEKEQ);	/* Hint to COMDB2 */
>>  				}
>> -				VdbeComment((v, "%s", pIx->zName));
>> +				VdbeComment((v, "%s", pIx->def->name));
>>  #ifdef SQLITE_ENABLE_COLUMN_USED_MASK
>>  				{
>>  					u64 colUsed = 0;
>> @@ -4781,7 +4771,7 @@ sqlite3WhereEnd(WhereInfo * pWInfo)
>>  		if (pLevel->addrSkip) {
>>  			sqlite3VdbeGoto(v, pLevel->addrSkip);
>>  			VdbeComment((v, "next skip-scan on %s",
>> -				     pLoop->pIndex->zName));
>> +				     pLoop->pIndex->def->name));
>>  			sqlite3VdbeJumpHere(v, pLevel->addrSkip);
>>  			sqlite3VdbeJumpHere(v, pLevel->addrSkip - 2);
>>  		}
>> diff --git a/src/box/sql/wherecode.c b/src/box/sql/wherecode.c
>> index 09b267194..22bb76013 100644
>> --- a/src/box/sql/wherecode.c
>> +++ b/src/box/sql/wherecode.c
>> @@ -47,9 +47,7 @@
>>  static const char *
>>  explainIndexColumnName(Index * pIdx, int i)
>>  {
>> -	i = pIdx->aiColumn[i];
>> -	if (i == XN_EXPR)
>> -		return "<expr>";
>> +	i = pIdx->def->key_def->parts[i].fieldno;
>>  	return pIdx->pTable->def->fields[i].name;
>>  }
>>  
>> @@ -222,7 +220,7 @@ sqlite3WhereExplainOneScan(Parse * pParse,	/* Parse context */
>>  			}
>>  			if (zFmt) {
>>  				sqlite3StrAccumAppend(&str, " USING ", 7);
>> -				sqlite3XPrintf(&str, zFmt, pIdx->zName);
>> +				sqlite3XPrintf(&str, zFmt, pIdx->def->name);
>>  				explainIndexRange(&str, pLoop);
>>  			}
>>  		} else if ((flags & WHERE_IPK) != 0
>> @@ -463,7 +461,7 @@ codeEqualityTerm(Parse * pParse,	/* The parsing context */
>>  		int *aiMap = 0;
>>  
>>  		if (pLoop->pIndex != 0 &&
>> -		    sql_index_column_sort_order(pLoop->pIndex, iEq)) {
>> +		    pLoop->pIndex->def->key_def->parts[iEq].sort_order) {
>>  			testcase(iEq == 0);
>>  			testcase(bRev);
>>  			bRev = !bRev;
>> @@ -708,7 +706,7 @@ codeAllEqualityTerms(Parse * pParse,	/* Parsing context */
>>  		sqlite3VdbeAddOp1(v, (bRev ? OP_Last : OP_Rewind), iIdxCur);
>>  		VdbeCoverageIf(v, bRev == 0);
>>  		VdbeCoverageIf(v, bRev != 0);
>> -		VdbeComment((v, "begin skip-scan on %s", pIdx->zName));
>> +		VdbeComment((v, "begin skip-scan on %s", pIdx->def->name));
>>  		j = sqlite3VdbeAddOp0(v, OP_Goto);
>>  		pLevel->addrSkip =
>>  		    sqlite3VdbeAddOp4Int(v, (bRev ? OP_SeekLT : OP_SeekGT),
>> @@ -718,8 +716,8 @@ codeAllEqualityTerms(Parse * pParse,	/* Parsing context */
>>  		sqlite3VdbeJumpHere(v, j);
>>  		for (j = 0; j < nSkip; j++) {
>>  			sqlite3VdbeAddOp3(v, OP_Column, iIdxCur,
>> -					  pIdx->aiColumn[j], regBase + j);
>> -			testcase(pIdx->aiColumn[j] == XN_EXPR);
>> +					  pIdx->def->key_def->parts[j].fieldno,
>> +					  regBase + j);
>>  			VdbeComment((v, "%s", explainIndexColumnName(pIdx, j)));
>>  		}
>>  	}
>> @@ -1245,10 +1243,10 @@ sqlite3WhereCodeOneLoopStart(WhereInfo * pWInfo,	/* Complete information about t
>>  		assert(pWInfo->pOrderBy == 0
>>  		       || pWInfo->pOrderBy->nExpr == 1
>>  		       || (pWInfo->wctrlFlags & WHERE_ORDERBY_MIN) == 0);
>> -		int nIdxCol = index_column_count(pIdx);
>> +		int nIdxCol = pIdx->def->key_def->part_count;
>>  		if ((pWInfo->wctrlFlags & WHERE_ORDERBY_MIN) != 0
>>  		    && pWInfo->nOBSat > 0 && (nIdxCol > nEq)) {
>> -			j = pIdx->aiColumn[nEq];
>> +			j = pIdx->def->key_def->parts[nEq].fieldno;
>>  			/* Allow seek for column with `NOT NULL` == false attribute.
>>  			 * If a column may contain NULL-s, the comparator installed
>>  			 * by Tarantool is prepared to seek using a NULL value.
>> @@ -1259,8 +1257,7 @@ sqlite3WhereCodeOneLoopStart(WhereInfo * pWInfo,	/* Complete information about t
>>  			 * FYI: entries in an index are ordered as follows:
>>  			 *      NULL, ... NULL, min_value, ...
>>  			 */
>> -			if ((j >= 0 && pIdx->pTable->def->fields[j].is_nullable)
>> -			    || j == XN_EXPR) {
>> +			if (pIdx->pTable->def->fields[j].is_nullable) {
>>  				assert(pLoop->nSkip == 0);
>>  				bSeekPastNull = 1;
>>  				nExtraReg = 1;
>> @@ -1299,17 +1296,15 @@ sqlite3WhereCodeOneLoopStart(WhereInfo * pWInfo,	/* Complete information about t
>>  				assert((bRev & ~1) == 0);
>>  				pLevel->iLikeRepCntr <<= 1;
>>  				pLevel->iLikeRepCntr |=
>> -					bRev ^ (sql_index_column_sort_order(pIdx, nEq) ==
>> +					bRev ^ (pIdx->def->key_def->
>> +						  parts[nEq].sort_order ==
>>  						SORT_ORDER_DESC);
>>  			}
>>  #endif
>>  			if (pRangeStart == 0) {
>> -				j = pIdx->aiColumn[nEq];
>> -				if ((j >= 0 &&
>> -				     pIdx->pTable->def->fields[j].is_nullable)||
>> -				    j == XN_EXPR) {
>> +				j = pIdx->def->key_def->parts[nEq].fieldno;
>> +				if (pIdx->pTable->def->fields[j].is_nullable)
>>  					bSeekPastNull = 1;
>> -				}
>>  			}
>>  		}
>>  		assert(pRangeEnd == 0
>> @@ -1320,7 +1315,7 @@ sqlite3WhereCodeOneLoopStart(WhereInfo * pWInfo,	/* Complete information about t
>>  		 * start and end terms (pRangeStart and pRangeEnd).
>>  		 */
>>  		if ((nEq < nIdxCol &&
>> -		     bRev == (sql_index_column_sort_order(pIdx, nEq) ==
>> +		     bRev == (pIdx->def->key_def->parts[nEq].sort_order ==
>>  			      SORT_ORDER_ASC)) ||
>>  		    (bRev && nIdxCol == nEq)) {
>>  			SWAP(pRangeEnd, pRangeStart);
>> @@ -1386,9 +1381,10 @@ sqlite3WhereCodeOneLoopStart(WhereInfo * pWInfo,	/* Complete information about t
>>  		}
>>  		struct Index *pk = sqlite3PrimaryKeyIndex(pIdx->pTable);
>>  		assert(pk);
>> -		int nPkCol = index_column_count(pk);
>> +		int nPkCol = pk->def->key_def->part_count;
>> +		uint32_t zero_fieldno = pk->def->key_def->parts[0].fieldno;
>>  		char affinity =
>> -			pIdx->pTable->def->fields[pk->aiColumn[0]].affinity;
>> +			pIdx->pTable->def->fields[zero_fieldno].affinity;
>>  		if (nPkCol == 1 && affinity == AFFINITY_INTEGER) {
>>  			/* Right now INTEGER PRIMARY KEY is the only option to
>>  			 * get Tarantool's INTEGER column type. Need special handling
>> @@ -1397,7 +1393,8 @@ sqlite3WhereCodeOneLoopStart(WhereInfo * pWInfo,	/* Complete information about t
>>  			 */
>>  			int limit = pRangeStart == NULL ? nEq : nEq + 1;
>>  			for (int i = 0; i < limit; i++) {
>> -				if (pIdx->aiColumn[i] == pk->aiColumn[0]) {
>> +				if (pIdx->def->key_def->parts[i].fieldno ==
>> +				    zero_fieldno) {
>>  					/* Here: we know for sure that table has INTEGER
>>  					   PRIMARY KEY, single column, and Index we're
>>  					   trying to use for scan contains this column. */
>> @@ -1506,10 +1503,10 @@ sqlite3WhereCodeOneLoopStart(WhereInfo * pWInfo,	/* Complete information about t
>>  			/* pIdx is a covering index.  No need to access the main table. */
>>  		}  else if (iCur != iIdxCur) {
>>  			Index *pPk = sqlite3PrimaryKeyIndex(pIdx->pTable);
>> -			int nPkCol = index_column_count(pPk);
>> +			int nPkCol = pPk->def->key_def->part_count;
>>  			int iKeyReg = sqlite3GetTempRange(pParse, nPkCol);
>>  			for (j = 0; j < nPkCol; j++) {
>> -				k = pPk->aiColumn[j];
>> +				k = pPk->def->key_def->parts[j].fieldno;
>>  				sqlite3VdbeAddOp3(v, OP_Column, iIdxCur, k,
>>  						  iKeyReg + j);
>>  			}
>> @@ -1614,7 +1611,7 @@ sqlite3WhereCodeOneLoopStart(WhereInfo * pWInfo,	/* Complete information about t
>>  		 */
>>  		if ((pWInfo->wctrlFlags & WHERE_DUPLICATES_OK) == 0) {
>>  			Index *pPk = sqlite3PrimaryKeyIndex(pTab);
>> -			int nPkCol = index_column_count(pPk);
>> +			int nPkCol = pPk->def->key_def->part_count;
>>  			regRowset = pParse->nTab++;
>>  			sqlite3VdbeAddOp2(v, OP_OpenTEphemeral,
>>  					  regRowset, nPkCol);
>> @@ -1718,13 +1715,16 @@ sqlite3WhereCodeOneLoopStart(WhereInfo * pWInfo,	/* Complete information about t
>>  						int iSet =
>>  						    ((ii == pOrWc->nTerm - 1) ? -1 : ii);
>>  						Index *pPk = sqlite3PrimaryKeyIndex (pTab);
>> -						int nPk = index_column_count(pPk);
>> +						int nPk = pPk->def->key_def->part_count;
>>  						int iPk;
>>  
>>  						/* Read the PK into an array of temp registers. */
>>  						r = sqlite3GetTempRange(pParse, nPk);
>>  						for (iPk = 0; iPk < nPk; iPk++) {
>> -							int iCol = pPk->aiColumn[iPk];
>> +							int iCol = pPk->def->
>> +								key_def->
>> +								parts[iPk].
>> +								fieldno;
>>  							sqlite3ExprCodeGetColumnToReg
>>  								(pParse, pTab->def,
>>  								 iCol, iCur,
>> diff --git a/src/box/sql/whereexpr.c b/src/box/sql/whereexpr.c
>> index aa6d4524d..40e4e2577 100644
>> --- a/src/box/sql/whereexpr.c
>> +++ b/src/box/sql/whereexpr.c
>> @@ -903,7 +903,6 @@ exprMightBeIndexed(SrcList * pFrom,	/* The FROM clause */
>>  		   int *piColumn	/* Write the referenced table column number here */
>>      )
>>  {
>> -	Index *pIdx;
>>  	int i;
>>  	int iCur;
>>  
>> @@ -930,20 +929,6 @@ exprMightBeIndexed(SrcList * pFrom,	/* The FROM clause */
>>  	for (i = 0; mPrereq > 1; i++, mPrereq >>= 1) {
>>  	}
>>  	iCur = pFrom->a[i].iCursor;
>> -	for (pIdx = pFrom->a[i].pTab->pIndex; pIdx; pIdx = pIdx->pNext) {
>> -		if (pIdx->aColExpr == 0)
>> -			continue;
>> -		for (i = 0; i < pIdx->nColumn; i++) {
>> -			if (pIdx->aiColumn[i] != XN_EXPR)
>> -				continue;
>> -			if (sqlite3ExprCompare
>> -			    (pExpr, pIdx->aColExpr->a[i].pExpr, iCur) == 0) {
>> -				*piCur = iCur;
>> -				*piColumn = XN_EXPR;
>> -				return 1;
>> -			}
>> -		}
>> -	}
16. You don't need iCur anymore; please delete it and SrcList * pFrom argument that would come useless.
Please don't dorget to beautify function head to match our codestyle as you are going to touch it.

>>  	return 0;
>>  }
>>  
>> -- 




More information about the Tarantool-patches mailing list