Tarantool development patches archive
 help / color / mirror / Atom feed
From: Kirill Shcherbatov <kshcherbatov@tarantool.org>
To: tarantool-patches@freelists.org,
	Ivan Koptelov <ivan.koptelov@tarantool.org>
Cc: "v.shpilevoy@tarantool.org" <v.shpilevoy@tarantool.org>
Subject: [tarantool-patches] Re: [PATCH v3] sql: add index_def to struct Index
Date: Mon, 18 Jun 2018 21:45:27 +0300	[thread overview]
Message-ID: <b0c08a99-5779-4800-07da-759d89620e33@tarantool.org> (raw)
In-Reply-To: <9A0A687D-2E0B-4AB9-B223-1A1287817824@tarantool.org>

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;
>>  }
>>  
>> -- 

  reply	other threads:[~2018-06-18 18:45 UTC|newest]

Thread overview: 23+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2018-06-13  7:30 Ivan Koptelov
2018-06-18 18:45 ` Kirill Shcherbatov [this message]
2018-06-21 12:57   ` [tarantool-patches] Re: [PATCH v4] " Ivan Koptelov
2018-06-22  8:46     ` Kirill Shcherbatov
2018-06-27 17:46       ` [tarantool-patches] Re: [PATCH v5] " Ivan Koptelov
2018-06-27 17:57         ` Kirill Shcherbatov
2018-06-28 18:49           ` Vladislav Shpilevoy
2018-06-29 13:49             ` [tarantool-patches] Re: [PATCH v6] " Ivan Koptelov
2018-06-29 20:46               ` Vladislav Shpilevoy
     [not found]                 ` <146c3bd4-e9e6-f943-5a42-c6db966d1c9c@tarantool.org>
2018-07-03  9:00                   ` [tarantool-patches] Re: [PATCH v8] " Ivan Koptelov
2018-07-03  9:46                 ` [tarantool-patches] Re: [PATCH v8.5] " Ivan Koptelov
2018-07-03 12:13                   ` Vladislav Shpilevoy
2018-07-03 11:37                 ` [tarantool-patches] Re: [PATCH v9] " Ivan Koptelov
2018-07-03 23:54                   ` n.pettik
2018-07-04  0:08                     ` Vladislav Shpilevoy
2018-07-04  9:17                       ` n.pettik
2018-07-04 15:55                     ` [tarantool-patches] Re: [PATCH v11] " Ivan Koptelov
2018-07-04 19:28                       ` n.pettik
2018-07-05 14:50                         ` Ivan Koptelov
2018-07-06  0:51                           ` n.pettik
2018-07-08 14:17                             ` [tarantool-patches] Re: [PATCH v2] " Ivan Koptelov
2018-07-04 10:46                   ` [tarantool-patches] Re: [PATCH v9] " Kirill Yukhin
2018-07-04 12:10                     ` Kirill Yukhin

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=b0c08a99-5779-4800-07da-759d89620e33@tarantool.org \
    --to=kshcherbatov@tarantool.org \
    --cc=ivan.koptelov@tarantool.org \
    --cc=tarantool-patches@freelists.org \
    --cc=v.shpilevoy@tarantool.org \
    --subject='[tarantool-patches] Re: [PATCH v3] sql: add index_def to struct Index' \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link

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