From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from localhost (localhost [127.0.0.1]) by turing.freelists.org (Avenir Technologies Mail Multiplex) with ESMTP id 106FC2411E for ; Wed, 13 Jun 2018 03:30:31 -0400 (EDT) Received: from turing.freelists.org ([127.0.0.1]) by localhost (turing.freelists.org [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id hIhrfC2NxyAE for ; Wed, 13 Jun 2018 03:30:30 -0400 (EDT) Received: from smtp53.i.mail.ru (smtp53.i.mail.ru [94.100.177.113]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by turing.freelists.org (Avenir Technologies Mail Multiplex) with ESMTPS id 54F17241C2 for ; Wed, 13 Jun 2018 03:30:28 -0400 (EDT) From: Ivan Koptelov Content-Type: multipart/alternative; boundary="Apple-Mail=_ECB47D6D-69B1-4BF0-94FD-048F95FFBE64" Mime-Version: 1.0 (Mac OS X Mail 10.3 \(3273\)) Subject: [tarantool-patches] Re: [PATCH v3] sql: add index_def to struct Index Message-Id: <9A0A687D-2E0B-4AB9-B223-1A1287817824@tarantool.org> Date: Wed, 13 Jun 2018 10:30:23 +0300 Sender: tarantool-patches-bounce@freelists.org Errors-to: tarantool-patches-bounce@freelists.org Reply-To: tarantool-patches@freelists.org List-help: List-unsubscribe: List-software: Ecartis version 1.0.0 List-Id: tarantool-patches List-subscribe: List-owner: List-post: List-archive: To: tarantool-patches@freelists.org Cc: Vladislav Shpilevoy --Apple-Mail=_ECB47D6D-69B1-4BF0-94FD-048F95FFBE64 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset=utf-8 > 1. Please, do not paste SMTP headers in the body. >=20 > 2. Looks like in the diff below all tabs are turned into 4 spaces. = Please > cope with it. Maybe, your IDE made it. > >=20 > > Now every sqlite struct Index is created with tnt struct > > index_def inside. This allows us to use tnt index_def > > in work with sqlite indexes in the same manner as with > > tnt index and is a step to remove sqlite Index with > > tnt index. > > Fields coll_array, coll_id_array, aiColumn, sort_order > > and zName are removed from Index. All usages of this > > fields changed to usage of corresponding index_def > > fields. > > index_is_unique(), sql_index_collation() and > > index_column_count() are removed with calls of > > index_def corresponding fields. > >=20 > > Closes: #3369 > > Github branch: = https://github.com/tarantool/tarantool/tree/sb/gh-3369-use-index-def-in-se= lect-and-where = >=20 > 3. Please, put branch link below ---. >=20 > > --- >=20 > Here. And do not forget about link to the issue. >=20 > Branch: > Issue: >=20 > > src/box/sql.c | 18 +-- > > src/box/sql/analyze.c | 24 +-- > > src/box/sql/build.c | 398 = ++++++++++++++++++++++++------------------------ > > src/box/sql/delete.c | 16 +- > > src/box/sql/expr.c | 59 ++++--- > > src/box/sql/fkey.c | 41 +++-- > > src/box/sql/insert.c | 134 +++++++--------- > > src/box/sql/pragma.c | 19 ++- > > src/box/sql/select.c | 2 +- > > src/box/sql/sqliteInt.h | 25 +-- > > src/box/sql/trigger.c | 2 - > > src/box/sql/update.c | 10 +- > > src/box/sql/vdbemem.c | 2 +- > > src/box/sql/where.c | 140 ++++++++--------- > > src/box/sql/wherecode.c | 43 +++--- > > src/box/sql/whereexpr.c | 15 -- > > 16 files changed, 433 insertions(+), 515 deletions(-) > >> diff --git a/src/box/sql/build.c b/src/box/sql/build.c > > index 28e4d7a4d..74fb66565 100644 > > --- a/src/box/sql/build.c > > +++ b/src/box/sql/build.c > > @@ -1072,11 +1075,9 @@ sqlite3AddCollateType(Parse * pParse, Token * = pToken) > > * collation type was added. Correct this if it is the case. > > */ > > for (pIdx =3D p->pIndex; pIdx; pIdx =3D pIdx->pNext) { > > - assert(pIdx->nColumn =3D=3D 1); > > - if (pIdx->aiColumn[0] =3D=3D i) { > > - id =3D &pIdx->coll_id_array[0]; > > - pIdx->coll_array[0] =3D > > - sql_column_collation(p->def, i, id); > > + assert(pIdx->def->key_def->part_count =3D=3D 1); > > + if ((int)pIdx->def->key_def->parts[0].fieldno =3D=3D i) { > > + id =3D &pIdx->def->key_def->parts[0].coll_id; >=20 > 4. I have just noticed the zColl leaks here. It is not deleted if > coll !=3D NULL, but must. >=20 > 5. Here you have removed coll * initialization. > sql_column_collation function here was used to set coll id and = collation, > by column number. But now this whole cycle does nothing as you can = see. > Please, return this initialization. You sill must init > key_def->parts[0].coll_id and coll. >=20 > > } > > } > > } else { > > @@ -1123,52 +1124,10 @@ sql_index_key_def(struct Index *idx) > > enum sort_order > > sql_index_column_sort_order(Index *idx, uint32_t column) >=20 > 6. Now this function is useless one line wrapper. Please, > remove it too. >=20 > > { > > - assert(idx !=3D NULL); > > - uint32_t space_id =3D SQLITE_PAGENO_TO_SPACEID(idx->pTable->tnum); > > - struct space *space =3D 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 =3D=3D NULL) { > > - assert(column < idx->nColumn); > > - return idx->sort_order[column]; > > - } > > - > > - struct key_def *key_def =3D sql_index_key_def(idx); > > - assert(key_def !=3D NULL && key_def->part_count >=3D column); > > - return key_def->parts[column].sort_order; > > + return idx->def->key_def->parts[column].sort_order; > > } > >=20 > > /** > > @@ -1383,14 +1342,16 @@ createTableStmt(sqlite3 * db, Table * p) > > return zStmt; > > } > >=20 > > -/* 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, const struct = key_part key_part) >=20 > 7. Passing a struct by value is very bad idea. Please, don't do it = ever. > And looks like here fieldno is enough. >=20 > > { > > - while (nCol-- > 0) > > - if (x =3D=3D *(aiCol++)) > > + int i =3D 0; > > + while (i < nCol) { > > + if (key_part.fieldno =3D=3D key_parts->fieldno) > > return 1; > > + key_parts++; > > + i++; > > + } > > return 0; > > } > >=20 > > @@ -1410,13 +1371,13 @@ static void > > convertToWithoutRowidTable(Parse * pParse, Table * pTab) > > { > > Index *pPk; > > - int i, j; > > + uint32_t i, j; >=20 > 8. Please, do not predeclare cycle iterators when possible. It is > SQLite code style, not Tarantool. When you change the SQLite code, > it must turn into Tarantool style. >=20 > > @@ -1454,14 +1415,17 @@ 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 =3D j =3D 1; i < pPk->nColumn; i++) { > > - if (hasColumn(pPk->aiColumn, j, pPk->aiColumn[i])) { > > - pPk->nColumn--; > > + for (i =3D j =3D 1; i < pPk->def->key_def->part_count; i++) { > > + if (hasColumn(pPk->def->key_def->parts, j, > > + pPk->def->key_def->parts[i])) { > > + pPk->def->key_def->part_count--; > > } else { > > - pPk->aiColumn[j++] =3D pPk->aiColumn[i]; > > + pPk->def->key_def->parts[j++] =3D > > + pPk->def->key_def->parts[i]; > > } >=20 > 9. Wrong alignments almost on all new lines. And please, save a > key_def->parts in a separate variable and use it instead of the full > path pPk->def->key_def->parts. It is too long to be used 4 times on > 6 lines. >=20 > 10. I see that cycle iterates until pPk->def->key_def->part_count, > but this variable is decremented inside the cycle. So the last > columns are not checked. This value must be saved in a separate > variable before usage as a 'for' guard. >=20 > > } > > - pPk->nColumn =3D j; > > + > > + pPk->def->key_def->part_count =3D j; >=20 > 11. This line makes no sense. You have already updated part_count > inside the cycle. Either you update here, or in the cycle. >=20 > > } > > assert(pPk !=3D 0); > > } > > @@ -2654,8 +2618,9 @@ sqlite3RefillIndex(Parse * pParse, Index * = pIndex, int memRootPage) > > } > > /* Open the sorter cursor if we are to use one. */ > > iSorter =3D pParse->nTab++; > > - sqlite3VdbeAddOp4(v, OP_SorterOpen, iSorter, 0, pIndex->nColumn, > > - (char *)def, P4_KEYDEF); > > + sqlite3VdbeAddOp4(v, OP_SorterOpen, iSorter, 0, > > + pIndex->def->key_def->part_count, > > + (char *)def, P4_KEYDEF); >=20 > 12. Wrong alignment. >=20 > >=20 > > /* Open the table. Loop through all rows of the table, inserting = index > > * records into the sorter. > > @@ -2687,7 +2652,7 @@ sqlite3RefillIndex(Parse * pParse, Index * = pIndex, int memRootPage) > > sqlite3VdbeGoto(v, j2); > > addr2 =3D sqlite3VdbeCurrentAddr(v); > > sqlite3VdbeAddOp4Int(v, OP_SorterCompare, iSorter, j2, > > - regRecord, pIndex->nColumn); > > + regRecord, pIndex->def->key_def->part_count); >=20 > 13. Same. >=20 > > VdbeCoverage(v); > > sqlite3UniqueConstraint(pParse, ON_CONFLICT_ACTION_ABORT, > > pIndex); > > @@ -2733,16 +2698,11 @@ sqlite3AllocateIndexObject(sqlite3 * db, /* = Database connection */ > > p =3D sqlite3DbMallocZero(db, nByte + nExtra); > > if (p) { > > char *pExtra =3D ((char *)p) + ROUND8(sizeof(Index)); > > - p->coll_array =3D (struct coll **)pExtra; >=20 > 14. I still see coll_array in build.c in comments. >=20 > > pExtra +=3D ROUND8(sizeof(struct coll **) * nCol); > > - p->coll_id_array =3D (uint32_t *) pExtra; >=20 > 15. Same. >=20 > > pExtra +=3D ROUND8(sizeof(uint32_t) * nCol); > > p->aiRowLogEst =3D (LogEst *) pExtra; > > pExtra +=3D sizeof(LogEst) * (nCol + 1); > > - p->aiColumn =3D (i16 *) pExtra; >=20 > 16. Same. And in very many places. >=20 > > pExtra +=3D sizeof(i16) * nCol; > > - p->sort_order =3D (enum sort_order *) pExtra; > > - p->nColumn =3D nCol; > > *ppExtra =3D ((char *)p) + nByte; >=20 > 17. You have removed the fields, but did not update > sqlite3AllocateIndexObject that still allocates memory for them. >=20 > > } > > return p; > > @@ -2831,18 +2791,119 @@ addIndexToTable(Index * pIndex, Table * = pTab) > > } > > } > >=20 > > -bool > > -index_is_unique(Index *idx) > > +void > > +append(struct region *r, const char *str, size_t *total_sql_size) > > { > > - assert(idx !=3D NULL); > > - uint32_t space_id =3D SQLITE_PAGENO_TO_SPACEID(idx->tnum); > > - uint32_t index_id =3D SQLITE_PAGENO_TO_INDEXID(idx->tnum); > > - struct space *space =3D space_by_id(space_id); > > - assert(space !=3D NULL); > > - struct index *tnt_index =3D space_index(space, index_id); > > - assert(tnt_index !=3D NULL); > > + memcpy(region_alloc(r, strlen(str)), str, strlen(str)); > > + *total_sql_size +=3D strlen(str); > > +} > 18. Please, rename append function. This name is too common. And > make it static inline. >=20 > 19. You do not check region_alloc fails. >=20 > > + > > +char * > > +create_sql(const char *idx_name, struct space_def *space_def, = ExprList *expr_list) >=20 > 20. Same as 18. >=20 > 21. Out of 80 symbols. >=20 > 22. Please, use struct ExprList, not ExprList. >=20 > 23. I still can not understand why can not you merge this function = into > index_def building. It is needed in a single place, and makes = redundant > scan of ExprList. >=20 > > +{ > > + struct region *r =3D &fiber()->gc; > > + size_t total_sql_size =3D 0; > > + append(r, "CREATE INDEX ", &total_sql_size); > > + append(r, idx_name, &total_sql_size); > > + append(r, " ON ", &total_sql_size); > > + append(r, space_def->name, &total_sql_size); > > + append(r, " (", &total_sql_size); > > + > > + for (int i =3D 0; i < expr_list->nExpr; i++){ > > + Expr *expr =3D expr_list->a[i].pExpr; > > + assert(expr->op =3D=3D TK_COLLATE || expr->op =3D=3D TK_COLUMN); > > + Expr *column_expr =3D sqlite3ExprSkipCollate(expr); > > + const char *name =3D space_def->fields[column_expr->iColumn].name; > > + > > + if (expr->op =3D=3D TK_COLLATE){ > > + append(r, name, &total_sql_size); > > + append(r, " COLLATE ", &total_sql_size); > > + const char *coll_name =3D expr->u.zToken; > > + append(r, coll_name, &total_sql_size); > > + append(r, ", ", &total_sql_size); > > + } else { > > + append(r, name, &total_sql_size); > > + append(r, ", ", &total_sql_size); > > + } > > + } > > + > > + memcpy(region_alloc(r, 1), "\0", 1); > > + total_sql_size +=3D 1; > > + char *res =3D region_join(r, total_sql_size); > > + > > + /* > > + * fix last ", " with ")\0" > > + */ > > + res[strlen(res) - 2] =3D ')'; > > + res[strlen(res) - 1] =3D '\0'; >=20 > 24. strlen has O(N) complexity. And here you already know the > result: total_sql_size. >=20 > > + return res; > > +} > > + > > +void > > +set_index_def(Parse *parse, Index *index, Table *table, uint32_t = iid, > > + const char *name, uint32_t name_len, int on_error, > > + ExprList *expr_list, u8 idx_type) >=20 > 25. Still bad alignment. And make this function be static. >=20 >=20 > > +{ > > + struct space_def *space_def =3D table->def; > > + struct index_opts opts; > > + index_opts_create(&opts); > > + opts.is_unique =3D on_error !=3D ON_CONFLICT_ACTION_NONE; > > + > > + struct key_def *key_def =3D key_def_new(expr_list->nExpr); > > + if (key_def =3D=3D NULL) >=20 > 26. If key_def_new fails, you should inform the parser or > db: either set Parse.nErr and Parser.rc, or sqlite3OomFaul(). Same > about all other errors got from Tarantool functions. >=20 > > + return; > > + > > + for (int i =3D 0; i < expr_list->nExpr; i++) { > > + Expr *expr =3D expr_list->a[i].pExpr; > > + sql_resolve_self_reference(parse, table, NC_IdxExpr, > > + expr, 0); >=20 > 27. Bad alignment. And as I can see it fits in one line. >=20 > Please check alignment during another self-review iteration in > the whole patch. I will not mention it below again. >=20 > > + if (parse->nErr > 0) > > + return; > > + > > + Expr *column_expr =3D sqlite3ExprSkipCollate(expr); > > + if (column_expr->op !=3D TK_COLUMN) { > > + sqlite3ErrorMsg(parse, > > + "functional indexes aren't supported " > > + "in the current version"); > > + return; > > + } > > + > > + uint32_t fieldno =3D column_expr->iColumn; > > + > > + uint32_t coll_id; > > + struct coll *coll; > > + if (expr->op =3D=3D TK_COLLATE) > > + coll =3D sql_get_coll_seq(parse, expr->u.zToken, > > + &coll_id); > > + else > > + coll =3D sql_column_collation(space_def, fieldno, > > + &coll_id); >=20 > 28. Please, use {}, when 'if' body consists of multiple lines. >=20 > > + > > + if (sqlite3StrICmp(expr->u.zToken, "binary") !=3D 0 && > > + coll =3D=3D NULL && expr->op =3D=3D TK_COLLATE) >=20 > 29. This check is needed in 'if (expr->op =3D=3D TK_COLLATE)' above. > And why do you do this youself? sql_get_coll_seq already sets > the nErr in parser for this error. >=20 > > + return; > > + > > + /* Tarantool: DESC indexes are not supported so far. > > + * See gh-3016. >=20 > 30. Please, obey Tarantool comment style: > /* > * My comment inside the function. > * Second line. > */ >=20 > And wrap the comment line on 66 ruler. >=20 > > void > > @@ -2853,12 +2914,11 @@ sql_create_index(struct Parse *parse, struct = Token *token, > > { > > Table *pTab =3D 0; /* Table to be indexed */ > > Index *pIndex =3D 0; /* The index to be created */ > > - char *zName =3D 0; /* Name of the index */ > > + char *name =3D 0; /* Name of the index */ >=20 > 31. Please, use explicit NULL. >=20 > > int nName; /* Number of characters in zName */ > > - int i, j; > > + int i; > > DbFixer sFix; /* For assigning database names to pTable */ > > sqlite3 *db =3D parse->db; > > - struct ExprList_item *col_listItem; /* For looping over col_list = */ > > int nExtra =3D 0; /* Space allocated for zExtra[] */ > > char *zExtra =3D 0; /* Extra space after the Index object */ > > struct session *user_session =3D current_session(); > > @@ -3159,6 +3189,7 @@ sql_create_index(struct Parse *parse, struct = Token *token, > > } > > if (idx_type =3D=3D SQLITE_IDXTYPE_PRIMARYKEY) > > pIdx->idxType =3D idx_type; > > + > > goto exit_create_index; >=20 > 32. Garbage diff. Please, find other garbage diffs and remove them. >=20 > > } > > } > > @@ -3268,28 +3299,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); >=20 > 33. I have noticed than on line 3056 collation names add extra bytes > to struct Index object. Can you please investigate if they are not > needed anymore and remove this code? >=20 > I am talking about it: >=20 > /* Figure out how many bytes of space are required to store explicitly > * specified collation sequence names. > */ > for (i =3D 0; i < col_list->nExpr; i++) { > Expr *pExpr =3D col_list->a[i].pExpr; > assert(pExpr !=3D 0); > if (pExpr->op =3D=3D TK_COLLATE) { > nExtra +=3D (1 + sqlite3Strlen30(pExpr->u.zToken)); > } > } >=20 > We do not store collation names in struct Index anymore. >=20 > > @@ -3297,15 +3307,8 @@ bool > > index_is_unique_not_null(const Index *idx) > > { > > assert(idx !=3D NULL); > > - uint32_t space_id =3D SQLITE_PAGENO_TO_SPACEID(idx->tnum); > > - struct space *space =3D space_by_id(space_id); > > - assert(space !=3D NULL); > > - > > - uint32_t index_id =3D SQLITE_PAGENO_TO_INDEXID(idx->tnum); > > - struct index *index =3D space_index(space, index_id); > > - assert(index !=3D NULL); > > - return (index->def->opts.is_unique && > > - !index->def->key_def->is_nullable); > > + assert(idx->def !=3D NULL); > > + return (idx->def->key_def->is_nullable && = idx->def->opts.is_unique); >=20 > 34. This one-line function is used in a single place. Please, inline > it and remove. >=20 > > } > >=20 > > void > > @@ -3933,18 +3936,18 @@ sqlite3UniqueConstraint(Parse * pParse, /* = Parsing context */ > > ) > > { > > char *zErr; > > - int j; > > + uint32_t j; > > StrAccum errMsg; > > Table *pTab =3D pIdx->pTable; > >=20 > > 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 =3D 0; j < pIdx->nColumn; j++) { > > + for (j =3D 0; j < pIdx->def->key_def->part_count; j++) { > > char *zCol; > > - assert(pIdx->aiColumn[j] >=3D 0); > > - zCol =3D pTab->def->fields[pIdx->aiColumn[j]].name; > > + uint32_t fieldno =3D pIdx->def->key_def->parts[j].fieldno; > > + zCol =3D pTab->def->fields[fieldno].name; > > if (j) > > sqlite3StrAccumAppend(&errMsg, ", ", 2); > > sqlite3XPrintf(&errMsg, "%s.%s", pTab->def->name, zCol); > > @@ -3967,11 +3970,10 @@ static bool > > collationMatch(struct coll *coll, struct Index *index) > > { > > assert(coll !=3D NULL); > > - for (int i =3D 0; i < index->nColumn; i++) { > > - uint32_t id; > > - struct coll *idx_coll =3D sql_index_collation(index, i, &id); > > - assert(idx_coll !=3D 0 || index->aiColumn[i] < 0); > > - if (index->aiColumn[i] >=3D 0 && coll =3D=3D idx_coll) > > + for (uint32_t i =3D 0; i < index->def->key_def->part_count; i++) { > > + struct coll *idx_coll =3D index->def->key_def->parts[i].coll; > > + assert(idx_coll !=3D NULL); > > + if (coll =3D=3D idx_coll) > > return true; > > } > > return false; > > diff --git a/src/box/sql/delete.c b/src/box/sql/delete.c > > index ddad54b3e..504738cd5 100644 > > --- a/src/box/sql/delete.c > > +++ b/src/box/sql/delete.c > > @@ -252,11 +252,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 =3D 0; i < pk_len; i++) { > > - assert(pk->aiColumn[i] >=3D 0); > > sqlite3ExprCodeGetColumnOfTable(v, table->def, > > tab_cursor, > > - pk-> > > - aiColumn[i], > > + pk->def-> > > + key_def-> > > + parts[i].fieldno, >=20 > 35. Please, just save pk->def->key_def->parts above in a separate > variable, and use here only part->fieldno and ++part above. =E2=80=94 Thank you for the review. All the issues are fixed. Here is the patch: sql: add index_def to Index Now every sqlite struct Index is created with tnt struct index_def inside. This allows us to use tnt index_def in work with sqlite indexes in the same manner as with tnt index and is a step to remove sqlite Index with tnt index. Fields coll_array, coll_id_array, aiColumn, sort_order and zName are removed from Index. All usages of this fields changed to usage of corresponding index_def fields. index_is_unique(), sql_index_collation() and index_column_count() are removed with calls of index_def corresponding fields. Closes: #3369 --- Branch: = https://github.com/tarantool/tarantool/tree/sb/gh-3369-use-index-def-in-se= lect-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) =20 /* If table's PK is single column which is INTEGER, then * treat it as strict type, not affinity. */ - if (pk_idx && pk_idx->nColumn =3D=3D 1) { - int pk =3D pk_idx->aiColumn[0]; + if (pk_idx !=3D NULL && pk_idx->def->key_def->part_count =3D=3D = 1) { + int pk =3D pk_idx->def->key_def->parts[0].fieldno; if (def->fields[pk].type =3D=3D FIELD_TYPE_INTEGER) pk_forced_int =3D pk; } @@ -1552,20 +1552,19 @@ tarantoolSqlite3MakeTableOpts(Table *pTable, = const char *zSql, char *buf) */ int tarantoolSqlite3MakeIdxParts(SqliteIndex *pIndex, void *buf) { - struct space_def *def =3D pIndex->pTable->def; - assert(def !=3D NULL); + struct field_def *fields =3D pIndex->pTable->def->fields; + struct key_def *key_def =3D pIndex->def->key_def; const struct Enc *enc =3D get_enc(buf); - struct SqliteIndex *primary_index; - char *base =3D buf, *p; - int pk_forced_int =3D -1; - - primary_index =3D sqlite3PrimaryKeyIndex(pIndex->pTable); + char *base =3D buf; + uint32_t pk_forced_int =3D UINT32_MAX; + struct SqliteIndex *primary_index =3D + sqlite3PrimaryKeyIndex(pIndex->pTable); =20 /* If table's PK is single column which is INTEGER, then * treat it as strict type, not affinity. */ - if (primary_index->nColumn =3D=3D 1) { - int pk =3D primary_index->aiColumn[0]; - if (def->fields[pk].type =3D=3D FIELD_TYPE_INTEGER) + if (primary_index->def->key_def->part_count =3D=3D 1) { + int pk =3D = primary_index->def->key_def->parts[0].fieldno; + if (fields[pk].type =3D=3D FIELD_TYPE_INTEGER) pk_forced_int =3D pk; } =20 @@ -1575,46 +1574,45 @@ int tarantoolSqlite3MakeIdxParts(SqliteIndex = *pIndex, void *buf) * primary key columns. Query planner depends on this particular * data layout. */ - int i, n =3D pIndex->nColumn; - - p =3D enc->encode_array(base, n); - for (i =3D 0; i < n; i++) { - int col =3D pIndex->aiColumn[i]; - assert(def->fields[col].is_nullable =3D=3D - = action_is_nullable(def->fields[col].nullable_action)); + struct key_part *part =3D key_def->parts; + char *p =3D enc->encode_array(base, key_def->part_count); + for (uint32_t i =3D 0; i < key_def->part_count; ++i, ++part) { + uint32_t col =3D part->fieldno; + assert(fields[col].is_nullable =3D=3D + action_is_nullable(fields[col].nullable_action)); const char *t; if (pk_forced_int =3D=3D col) { t =3D "integer"; } else { - enum affinity_type affinity =3D = def->fields[col].affinity; - t =3D convertSqliteAffinity(affinity, - = def->fields[col].is_nullable); + t =3D = convertSqliteAffinity(fields[col].affinity, + = fields[col].is_nullable); } /* do not decode default collation */ - uint32_t cid =3D pIndex->coll_id_array[i]; + uint32_t cid =3D part->coll_id; p =3D enc->encode_map(p, cid =3D=3D COLL_NONE ? 5 : 6); p =3D enc->encode_str(p, "type", sizeof("type")-1); p =3D enc->encode_str(p, t, strlen(t)); p =3D enc->encode_str(p, "field", sizeof("field")-1); p =3D enc->encode_uint(p, col); if (cid !=3D COLL_NONE) { - p =3D enc->encode_str(p, "collation", = sizeof("collation")-1); + p =3D enc->encode_str(p, "collation", + sizeof("collation") - 1); p =3D enc->encode_uint(p, cid); } p =3D enc->encode_str(p, "is_nullable", 11); - p =3D enc->encode_bool(p, def->fields[col].is_nullable); + p =3D enc->encode_bool(p, fields[col].is_nullable); p =3D enc->encode_str(p, "nullable_action", 15); const char *action_str =3D - = on_conflict_action_strs[def->fields[col].nullable_action]; + = on_conflict_action_strs[fields[col].nullable_action]; p =3D enc->encode_str(p, action_str, = strlen(action_str)); =20 p =3D enc->encode_str(p, "sort_order", 10); - enum sort_order sort_order =3D pIndex->sort_order[i]; + enum sort_order sort_order =3D part->sort_order; assert(sort_order < sort_order_MAX); const char *sort_order_str =3D = sort_order_strs[sort_order]; p =3D enc->encode_str(p, sort_order_str, = strlen(sort_order_str)); } - return (int)(p - base); + return p - base; } =20 /* 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 = */ =20 if (pOnlyIdx && pOnlyIdx !=3D pIdx) continue; @@ -860,9 +859,9 @@ analyzeOneTable(Parse * pParse, /* Parser = context */ if (IsPrimaryKeyIndex(pIdx)) { zIdxName =3D pTab->def->name; } else { - zIdxName =3D pIdx->zName; + zIdxName =3D pIdx->def->name; } - nColTest =3D index_column_count(pIdx); + int nColTest =3D pIdx->def->key_def->part_count; =20 /* 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)); =20 /* Invoke the stat_init() function. The arguments are: * @@ -969,7 +968,7 @@ analyzeOneTable(Parse * pParse, /* Parser = context */ */ sqlite3VdbeAddOp0(v, OP_Goto); addrNextRow =3D sqlite3VdbeCurrentAddr(v); - if (nColTest =3D=3D 1 && index_is_unique(pIdx)) = { + if (nColTest =3D=3D 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 =3D 0; i < nColTest; i++) { - uint32_t id; - struct coll *coll =3D - sql_index_collation(pIdx, i, = &id); + struct key_part *part =3D = pIdx->def->key_def->parts; + for (i =3D 0; i < nColTest; ++i, ++part) { + struct coll *coll =3D part->coll; sqlite3VdbeAddOp2(v, OP_Integer, i, = regChng); sqlite3VdbeAddOp3(v, OP_Column, iIdxCur, - pIdx->aiColumn[i], = regTemp); + part->fieldno, = regTemp); aGotoChng[i] =3D sqlite3VdbeAddOp4(v, OP_Ne, regTemp, = 0, regPrev + i, (char = *)coll, @@ -1006,7 +1004,8 @@ analyzeOneTable(Parse * pParse, /* Parser = context */ for (i =3D 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 =3D=3D (regStat4 + 2)); Index *pPk =3D sqlite3PrimaryKeyIndex(pIdx->pTable); - int j, k, regKeyStat; - int nPkColumn =3D (int)index_column_count(pPk); - regKeyStat =3D sqlite3GetTempRange(pParse, nPkColumn); - for (j =3D 0; j < nPkColumn; j++) { - k =3D pPk->aiColumn[j]; - assert(k >=3D 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 =3D (int) pPk->def->key_def->part_count; + int regKeyStat =3D sqlite3GetTempRange(pParse, = nPkColumn); + for (int j =3D 0; j < nPkColumn; ++j) { + int k =3D pPk->def->key_def->parts[j].fieldno; + assert(k >=3D 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 =3D pParse->nTab; pParse->nTab +=3D 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 !=3D NULL) + index_def_delete(p->def); sqlite3DbFree(db, p->zColAff); sqlite3DbFree(db, p); } @@ -271,7 +273,8 @@ sqlite3UnlinkAndDeleteIndex(sqlite3 * db, Index * = pIndex) =20 struct session *user_session =3D current_session(); =20 - pIndex =3D sqlite3HashInsert(&pIndex->pTable->idxHash, = pIndex->zName, 0); + pIndex =3D sqlite3HashInsert(&pIndex->pTable->idxHash, + pIndex->def->name, 0); if (ALWAYS(pIndex)) { if (pIndex->pTable->pIndex =3D=3D pIndex) { pIndex->pTable->pIndex =3D pIndex->pNext; @@ -388,7 +391,7 @@ deleteTable(sqlite3 * db, Table * pTable) pNext =3D pIndex->pNext; assert(pIndex->pSchema =3D=3D pTable->pSchema); if ((db =3D=3D 0 || db->pnBytesFreed =3D=3D 0)) { - char *zName =3D pIndex->zName; + char *zName =3D pIndex->def->name; TESTONLY(Index * pOld =3D) = sqlite3HashInsert(&pTable->idxHash, zName, 0); @@ -1058,7 +1061,7 @@ sqlite3AddCollateType(Parse * pParse, Token * = pToken) Table *p =3D pParse->pNewTable; if (p =3D=3D NULL) return; - int i =3D p->def->field_count - 1; + uint32_t i =3D p->def->field_count - 1; sqlite3 *db =3D pParse->db; char *zColl =3D sqlite3NameFromToken(db, pToken); if (!zColl) @@ -1066,22 +1069,20 @@ sqlite3AddCollateType(Parse * pParse, Token * = pToken) uint32_t *id =3D &p->def->fields[i].coll_id; p->aCol[i].coll =3D sql_get_coll_seq(pParse, zColl, id); if (p->aCol[i].coll !=3D NULL) { - Index *pIdx; /* If the column is declared as " PRIMARY KEY = COLLATE ", * 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 =3D p->pIndex; pIdx; pIdx =3D pIdx->pNext) { - assert(pIdx->nColumn =3D=3D 1); - if (pIdx->aiColumn[0] =3D=3D i) { - id =3D &pIdx->coll_id_array[0]; - pIdx->coll_array[0] =3D + for (struct Index *pIdx =3D p->pIndex; pIdx; pIdx =3D = pIdx->pNext) { + assert(pIdx->def->key_def->part_count =3D=3D 1); + if (pIdx->def->key_def->parts[0].fieldno =3D=3D = i) { + pIdx->def->key_def->parts[0].coll_id =3D = *id; + pIdx->def->key_def->parts[0].coll =3D sql_column_collation(p->def, i, = id); } } - } else { - sqlite3DbFree(db, zColl); } + sqlite3DbFree(db, zColl); } =20 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; } =20 -struct key_def* -sql_index_key_def(struct Index *idx) -{ - uint32_t space_id =3D SQLITE_PAGENO_TO_SPACEID(idx->tnum); - uint32_t index_id =3D SQLITE_PAGENO_TO_INDEXID(idx->tnum); - struct space *space =3D space_by_id(space_id); - assert(space !=3D NULL); - struct index *index =3D space_index(space, index_id); - assert(index !=3D NULL && index->def !=3D NULL); - return index->def->key_def; -} - -struct coll * -sql_index_collation(Index *idx, uint32_t column, uint32_t *coll_id) -{ - assert(idx !=3D NULL); - uint32_t space_id =3D = SQLITE_PAGENO_TO_SPACEID(idx->pTable->tnum); - struct space *space =3D 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 =3D=3D NULL) { - assert(column < idx->nColumn); - *coll_id =3D idx->coll_id_array[column]; - return idx->coll_array[column]; - } - - struct key_def *key_def =3D sql_index_key_def(idx); - assert(key_def !=3D NULL && key_def->part_count >=3D column); - *coll_id =3D 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 !=3D NULL); - uint32_t space_id =3D = SQLITE_PAGENO_TO_SPACEID(idx->pTable->tnum); - struct space *space =3D 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 =3D=3D NULL) { - assert(column < idx->nColumn); - return idx->sort_order[column]; - } - - struct key_def *key_def =3D sql_index_key_def(idx); - assert(key_def !=3D NULL && key_def->part_count >=3D 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; } =20 -/* 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 =3D=3D *(aiCol++)) + int i =3D 0; + while (i < nCol) { + if (fieldno =3D=3D key_parts->fieldno) return 1; + key_parts++; + i++; + } return 0; } =20 @@ -1410,13 +1353,12 @@ static void convertToWithoutRowidTable(Parse * pParse, Table * pTab) { Index *pPk; - int i, j; sqlite3 *db =3D pParse->db; =20 /* Mark every PRIMARY KEY column as NOT NULL (except for = imposter tables) */ if (!db->init.imposterTable) { - for (i =3D 0; i < (int)pTab->def->field_count; i++) { + for (uint32_t i =3D 0; i < pTab->def->field_count; i++) = { if (pTab->aCol[i].is_primkey) { pTab->def->fields[i].nullable_action =3D 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 =3D j =3D 1; i < pPk->nColumn; i++) { - if (hasColumn(pPk->aiColumn, j, = pPk->aiColumn[i])) { - pPk->nColumn--; - } else { - pPk->aiColumn[j++] =3D pPk->aiColumn[i]; + + struct key_part *parts =3D pPk->def->key_def->parts; + uint32_t part_count =3D pPk->def->key_def->part_count; + uint32_t new_part_count =3D part_count; + + for (uint32_t i =3D 1; i < part_count; i++) { + if (hasColumn(parts, i, parts[i].fieldno)){ + new_part_count--; + bool is_found =3D false; + for (uint32_t j =3D i + 1; j < = part_count; j++){ + if (!(hasColumn(parts, j, + = parts[j].fieldno))) { + parts[i] =3D parts[j]; + is_found =3D true; + break; + } + } + if (!(is_found)) + break; } } - pPk->nColumn =3D j; + pPk->def->key_def->part_count =3D new_part_count; } assert(pPk !=3D 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, =20 sqlite3VdbeAddOp4(v, OP_String8, 0, iFirstCol, 0, - sqlite3DbStrDup(pParse->db, pIndex->zName), + sqlite3DbStrDup(pParse->db, = pIndex->def->name), P4_DYNAMIC); =20 if (pParse->pNewTable) { @@ -2652,14 +2608,15 @@ sqlite3RefillIndex(Parse * pParse, Index * = pIndex, int memRootPage) } else { tnum =3D pIndex->tnum; } - struct key_def *def =3D key_def_dup(sql_index_key_def(pIndex)); + struct key_def *def =3D key_def_dup(pIndex->def->key_def); if (def =3D=3D NULL) { sqlite3OomFault(db); return; } /* Open the sorter cursor if we are to use one. */ iSorter =3D 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); =20 /* 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 =3D sqlite3VdbeCurrentAddr(v); sqlite3VdbeAddOp4Int(v, OP_SorterCompare, iSorter, j2, - regRecord, pIndex->nColumn); + 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 */ =20 nByte =3D 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 =3D sqlite3DbMallocZero(db, nByte + nExtra); if (p) { char *pExtra =3D ((char *)p) + ROUND8(sizeof(Index)); - p->coll_array =3D (struct coll **)pExtra; - pExtra +=3D ROUND8(sizeof(struct coll **) * nCol); - p->coll_id_array =3D (uint32_t *) pExtra; - pExtra +=3D ROUND8(sizeof(uint32_t) * nCol); p->aiRowLogEst =3D (LogEst *) pExtra; pExtra +=3D sizeof(LogEst) * (nCol + 1); - p->aiColumn =3D (i16 *) pExtra; pExtra +=3D sizeof(i16) * nCol; - p->sort_order =3D (enum sort_order *) pExtra; - p->nColumn =3D nCol; *ppExtra =3D ((char *)p) + nByte; } return p; @@ -2836,18 +2782,133 @@ addIndexToTable(Index * pIndex, Table * pTab) } } =20 -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 !=3D NULL); - uint32_t space_id =3D SQLITE_PAGENO_TO_SPACEID(idx->tnum); - uint32_t index_id =3D SQLITE_PAGENO_TO_INDEXID(idx->tnum); - struct space *space =3D space_by_id(space_id); - assert(space !=3D NULL); - struct index *tnt_index =3D space_index(space, index_id); - assert(tnt_index !=3D NULL); + char * str_part =3D region_alloc(r, strlen(str)); + if (str_part =3D=3D NULL){ + diag_set(OutOfMemory, strlen(str), + "region_alloc", "str_part"); + parse->rc =3D SQL_TARANTOOL_ERROR; + parse->nErr++; + } + memcpy(str_part, str, strlen(str)); + *total_sql_size +=3D strlen(str); +} + +void static +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 =3D table->def; + struct index_opts opts; + index_opts_create(&opts); + opts.is_unique =3D on_error !=3D ON_CONFLICT_ACTION_NONE; + + struct key_def *key_def =3D key_def_new(expr_list->nExpr); + if (key_def =3D=3D NULL) { + parse->rc =3D SQL_TARANTOOL_ERROR; + parse->nErr++; + return; + } + + /* + * Build initial parts of SQL statement. + */ + + struct region *r =3D &fiber()->gc; + size_t total_sql_size =3D 0; + + if (idx_type =3D=3D 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 =3D 0; i < expr_list->nExpr; i++) { + Expr *expr =3D expr_list->a[i].pExpr; + sql_resolve_self_reference(parse, table, NC_IdxExpr, = expr, 0); + if (parse->nErr > 0) + return; + + Expr *column_expr =3D sqlite3ExprSkipCollate(expr); + if (column_expr->op !=3D TK_COLUMN) { + sqlite3ErrorMsg(parse, + "functional indexes aren't = supported " + "in the current version"); + return; + } + + uint32_t fieldno =3D column_expr->iColumn; + uint32_t coll_id; + struct coll *coll; + if (expr->op =3D=3D TK_COLLATE) { + coll =3D sql_get_coll_seq(parse, expr->u.zToken, + &coll_id); + + if (idx_type =3D=3D 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 =3D = expr->u.zToken; + append_string_part(r, coll_name, + &total_sql_size, = parse); + append_string_part(r, ", ", + &total_sql_size, = parse); + } + } else { + coll =3D sql_column_collation(space_def, = fieldno, + &coll_id); + if (idx_type =3D=3D 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); + } =20 - return tnt_index->def->opts.is_unique; + if (parse->nErr > 0) { + index->def =3D NULL; + return; + } + + if (idx_type =3D=3D SQLITE_IDXTYPE_APPDEF) { + memcpy(region_alloc(r, 1), "\0", 1); + total_sql_size +=3D 1; + opts.sql =3D region_join(r, total_sql_size); + + /* + * fix last ", " with ")\0" to finish the statement. + */ + opts.sql[total_sql_size - 3] =3D ')'; + opts.sql[total_sql_size - 2] =3D '\0'; + } + + struct key_def *pk_key_def; + if (idx_type =3D=3D SQLITE_IDXTYPE_APPDEF) + pk_key_def =3D table->pIndex->def->key_def; + else + pk_key_def =3D NULL; + + index->def =3D index_def_new(space_def->id, iid, name, name_len, + TREE, &opts, key_def, pk_key_def); } =20 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 =3D 0; /* Table to be indexed */ - Index *pIndex =3D 0; /* The index to be created */ - char *zName =3D 0; /* Name of the index */ - int nName; /* Number of characters in zName */ - int i, j; + Table *pTab =3D NULL; /* Table to be indexed */ + Index *pIndex =3D NULL; /* The index to be created */ + char *name =3D NULL; /* Name of the index */ + int name_len; /* Number of characters in zName */ DbFixer sFix; /* For assigning database names to = pTable */ sqlite3 *db =3D parse->db; - struct ExprList_item *col_listItem; /* For looping over = col_list */ int nExtra =3D 0; /* Space allocated for zExtra[] = */ - char *zExtra =3D 0; /* Extra space after the Index object */ + char *zExtra =3D NULL; /* Extra space after the Index object */ struct session *user_session =3D current_session(); =20 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 =3D sqlite3NameFromToken(db, token); - if (zName =3D=3D 0) + name =3D sqlite3NameFromToken(db, token); + if (name =3D=3D NULL) goto exit_create_index; assert(token->z !=3D 0); if (!db->init.busy) { - if (sqlite3HashFind(&db->pSchema->tblHash, = zName) !=3D + if (sqlite3HashFind(&db->pSchema->tblHash, name) = !=3D NULL) { sqlite3ErrorMsg(parse, "there is already a = table named %s", - zName); + name); goto exit_create_index; } } - if (sqlite3HashFind(&pTab->idxHash, zName) !=3D NULL) { + if (sqlite3HashFind(&pTab->idxHash, name) !=3D 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 =3D pTab->pIndex, n =3D 1; pLoop; pLoop =3D pLoop->pNext, n++) { } - zName =3D - sqlite3MPrintf(db, "sqlite_autoindex_%s_%d", = pTab->def->name, - n); - if (zName =3D=3D 0) { + name =3D sqlite3MPrintf(db, "sqlite_autoindex_%s_%d", + pTab->def->name, n); + if (name =3D=3D NULL) { goto exit_create_index; } } @@ -2997,31 +3055,27 @@ sql_create_index(struct Parse *parse, struct = Token *token, sqlite3ExprListCheckLength(parse, col_list, "index"); } =20 - /* Figure out how many bytes of space are required to store = explicitly - * specified collation sequence names. - */ - for (i =3D 0; i < col_list->nExpr; i++) { - Expr *pExpr =3D col_list->a[i].pExpr; - assert(pExpr !=3D 0); - if (pExpr->op =3D=3D TK_COLLATE) { - nExtra +=3D (1 + = sqlite3Strlen30(pExpr->u.zToken)); - } - } - /* * Allocate the index structure. */ - nName =3D sqlite3Strlen30(zName); + name_len =3D 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) !=3D SQLITE_OK) + goto exit_create_index; + pIndex =3D 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 =3D zExtra; - zExtra +=3D nName + 1; - memcpy(pIndex->zName, zName, nName + 1); pIndex->pTable =3D pTab; pIndex->onError =3D (u8) on_error; /* @@ -3036,7 +3090,6 @@ sql_create_index(struct Parse *parse, struct Token = *token, pIndex->idxType =3D idx_type; } pIndex->pSchema =3D db->pSchema; - pIndex->nColumn =3D 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 =3D NULL; } =20 - /* 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 =3D 0, col_listItem =3D 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 =3D sqlite3ExprSkipCollate(col_listItem->pExpr); - if (pCExpr->op !=3D TK_COLUMN) { - sqlite3ErrorMsg(parse, - "functional indexes aren't = supported " - "in the current version"); - goto exit_create_index; - } else { - j =3D pCExpr->iColumn; - assert(j <=3D 0x7fff); - if (j < 0) { - j =3D pTab->iPKey; - } - pIndex->aiColumn[i] =3D (i16) j; - } - struct coll *coll; - uint32_t id; - if (col_listItem->pExpr->op =3D=3D TK_COLLATE) { - const char *coll_name =3D = col_listItem->pExpr->u.zToken; - coll =3D sql_get_coll_seq(parse, coll_name, = &id); =20 - if (coll =3D=3D NULL && - sqlite3StrICmp(coll_name, "binary") !=3D 0) = { - goto exit_create_index; - } - } else if (j >=3D 0) { - coll =3D sql_column_collation(pTab->def, j, = &id); - } else { - id =3D COLL_NONE; - coll =3D NULL; - } - pIndex->coll_array[i] =3D coll; - pIndex->coll_id_array[i] =3D id; + uint32_t max_iid =3D 0; + for (Index *index =3D pTab->pIndex; index; index =3D = index->pNext) { + max_iid =3D max_iid > index->def->iid ? + max_iid : + index->def->iid + 1; + } =20 - /* Tarantool: DESC indexes are not supported so far. - * See gh-3016. - */ - pIndex->sort_order[i] =3D SORT_ORDER_ASC; + set_index_def(parse, pIndex, pTab, max_iid, name, name_len, = on_error, + col_list, idx_type); + + if (pIndex->def =3D=3D NULL || + !index_def_is_valid(pIndex->def, pTab->def->name)) { + goto exit_create_index; } + if (pTab =3D=3D 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 =3D pTab->pIndex; pIdx; pIdx =3D pIdx->pNext) = { - int k; + uint32_t k; assert(IsUniqueIndex(pIdx)); assert(pIdx->idxType !=3D = SQLITE_IDXTYPE_APPDEF); assert(IsUniqueIndex(pIndex)); =20 - if (pIdx->nColumn !=3D pIndex->nColumn) + if (pIdx->def->key_def->part_count !=3D + pIndex->def->key_def->part_count) { continue; - for (k =3D 0; k < pIdx->nColumn; k++) { - assert(pIdx->aiColumn[k] >=3D 0); - if (pIdx->aiColumn[k] !=3D = pIndex->aiColumn[k]) + } + for (k =3D 0; k < = pIdx->def->key_def->part_count; k++) { + if (pIdx->def->key_def->parts[k].fieldno = !=3D + = pIndex->def->key_def->parts[k].fieldno) { break; + } struct coll *coll1, *coll2; - uint32_t id; - coll1 =3D sql_index_collation(pIdx, k, = &id); - coll2 =3D sql_index_collation(pIndex, k, = &id); + coll1 =3D = pIdx->def->key_def->parts[k].coll; + coll2 =3D = pIndex->def->key_def->parts[k].coll; if (coll1 !=3D coll2) break; } - if (k =3D=3D pIdx->nColumn) { + if (k =3D=3D pIdx->def->key_def->part_count) { if (pIdx->onError !=3D 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 =3D=3D 0); if (db->init.busy) { Index *p; - p =3D sqlite3HashInsert(&pTab->idxHash, pIndex->zName, = pIndex); + p =3D sqlite3HashInsert(&pTab->idxHash, = pIndex->def->name, pIndex); if (p) { assert(p =3D=3D 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 !=3D NULL); - uint32_t space_id =3D SQLITE_PAGENO_TO_SPACEID(idx->tnum); - struct space *space =3D space_by_id(space_id); - /* It is impossible to find an ephemeral space by id. */ - if (space =3D=3D NULL) - return idx->nColumn; - - uint32_t index_id =3D SQLITE_PAGENO_TO_INDEXID(idx->tnum); - struct index *index =3D space_index(space, index_id); - assert(index !=3D 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 !=3D NULL); - uint32_t space_id =3D SQLITE_PAGENO_TO_SPACEID(idx->tnum); - struct space *space =3D space_by_id(space_id); - assert(space !=3D NULL); - - uint32_t index_id =3D SQLITE_PAGENO_TO_INDEXID(idx->tnum); - struct index *index =3D space_index(space, index_id); - assert(index !=3D NULL); - return (index->def->opts.is_unique && - !index->def->key_def->is_nullable); + sqlite3DbFree(db, name); } =20 void @@ -3938,18 +3923,19 @@ sqlite3UniqueConstraint(Parse * pParse, /* = Parsing context */ ) { char *zErr; - int j; + uint32_t j; StrAccum errMsg; Table *pTab =3D pIdx->pTable; =20 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 =3D 0; j < pIdx->nColumn; j++) { + struct key_part *part =3D pIdx->def->key_def->parts; + for (j =3D 0; j < pIdx->def->key_def->part_count; j++, = part++) { char *zCol; - assert(pIdx->aiColumn[j] >=3D 0); - zCol =3D = pTab->def->fields[pIdx->aiColumn[j]].name; + uint32_t fieldno =3D part->fieldno; + zCol =3D 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 !=3D NULL); - for (int i =3D 0; i < index->nColumn; i++) { - uint32_t id; - struct coll *idx_coll =3D sql_index_collation(index, i, = &id); - assert(idx_coll !=3D 0 || index->aiColumn[i] < 0); - if (index->aiColumn[i] >=3D 0 && coll =3D=3D idx_coll) + struct key_part *part =3D index->def->key_def->parts; + for (uint32_t i =3D 0; i < index->def->key_def->part_count; i++, = part++) { + struct coll *idx_coll =3D part->coll; + assert(idx_coll !=3D NULL); + if (coll =3D=3D 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 =3D sqlite3PrimaryKeyIndex(table); assert(pk !=3D NULL); - pk_len =3D index_column_count(pk); + pk_len =3D pk->def->key_def->part_count; parse->nMem +=3D 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, =20 /* Extract the primary key for the current row */ if (!is_view) { - for (int i =3D 0; i < pk_len; i++) { - assert(pk->aiColumn[i] >=3D 0); + struct key_part *part =3D = pk->def->key_def->parts; + for (int i =3D 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)); =20 if (one_pass =3D=3D ONEPASS_MULTI) sqlite3VdbeJumpHere(v, iAddrOnce); @@ -536,14 +535,14 @@ sql_generate_index_key(struct Parse *parse, struct = Index *index, int cursor, *part_idx_label =3D 0; } } - int col_cnt =3D index_column_count(index); + int col_cnt =3D index->def->key_def->part_count; int reg_base =3D sqlite3GetTempRange(parse, col_cnt); if (prev !=3D NULL && (reg_base !=3D reg_prev || prev->pPartIdxWhere !=3D NULL)) prev =3D NULL; for (int j =3D 0; j < col_cnt; j++) { - if (prev !=3D NULL && prev->aiColumn[j] =3D=3D = index->aiColumn[j] - && prev->aiColumn[j] !=3D XN_EXPR) { + if (prev->def->key_def->parts[j].fieldno =3D=3D + index->def->key_def->parts[j].fieldno && prev =3D=3D = 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 =3D pIdx->pNext) { Bitmask colUsed; /* Columns of the index = used */ Bitmask mCol; /* Mask for the current = column */ - if (pIdx->nColumn < nExpr) + uint32_t part_count =3D = pIdx->def->key_def-> + part_count; + struct key_part *parts =3D = 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 =3D=3D BMS - 2); - testcase(pIdx->nColumn =3D=3D BMS - 1); - if (pIdx->nColumn >=3D BMS - 1) + testcase(part_count =3D=3D BMS - 2); + testcase(>part_count =3D=3D BMS - 1); + if (part_count >=3D 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; } } =20 @@ -2449,12 +2453,13 @@ sqlite3FindInIndex(Parse * pParse, /* = Parsing context */ int j; =20 for (j =3D 0; j < nExpr; j++) { - if (pIdx->aiColumn[j] !=3D= - pRhs->iColumn) { + if ((int) = parts[j].fieldno + !=3D pRhs->iColumn) = { continue; } - struct coll *idx_coll; - idx_coll =3D = sql_index_collation(pIdx, j, &id); + + struct coll *idx_coll =3D + = parts[j].coll; if (pReq !=3D NULL && pReq !=3D 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 =3D=3D IN_INDEX_INDEX_ASC + 1); eType =3D IN_INDEX_INDEX_ASC + - = sql_index_column_sort_order(pIdx, - = 0); + parts[0].sort_order; =20 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 =3D sqlite3PrimaryKeyIndex(tab); assert(pk); =20 + uint32_t fieldno =3D pk->def->key_def->parts[0].fieldno; enum affinity_type affinity =3D - tab->def->fields[pk->aiColumn[0]].affinity; - if (pk->nColumn =3D=3D 1 + tab->def->fields[fieldno].affinity; + if (pk->def->key_def->part_count =3D=3D 1 && affinity =3D=3D AFFINITY_INTEGER - && pk->aiColumn[0] < nVector) { - int reg_pk =3D rLhs + pk->aiColumn[0]; + && (int) fieldno < nVector) { int reg_pk =3D 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 =3D pIdx->aiColumn[iIdxCol]; - if (iTabCol =3D=3D XN_EXPR) { - assert(pIdx->aColExpr); - assert(pIdx->aColExpr->nExpr > iIdxCol); - pParse->iSelfTab =3D iTabCur; - sqlite3ExprCodeCopy(pParse, = pIdx->aColExpr->a[iIdxCol].pExpr, - regOut); - } else { - sqlite3ExprCodeGetColumnOfTable(pParse->pVdbe, = pIdx->pTable->def, - iTabCur, iTabCol, = regOut); - } + i16 iTabCol =3D pIdx->def->key_def->parts[iIdxCol].fieldno; + sqlite3ExprCodeGetColumnOfTable(pParse->pVdbe, = pIdx->pTable->def, + iTabCur, iTabCol, regOut); } =20 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 */ } =20 for (pIdx =3D pParent->pIndex; pIdx; pIdx =3D pIdx->pNext) { - int nIdxCol =3D index_column_count(pIdx); - if (nIdxCol =3D=3D nCol && index_is_unique(pIdx) + int nIdxCol =3D pIdx->def->key_def->part_count; + if (nIdxCol =3D=3D nCol && pIdx->def->opts.is_unique && pIdx->pPartIdxWhere =3D=3D 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 =3D 0; i < nCol; i++) { - i16 iCol =3D pIdx->aiColumn[i]; = /* Index of column in parent tbl */ + struct key_part *part =3D + pIdx->def->key_def->parts; + for (i =3D 0; i < nCol; i++, part++) { + i16 iCol =3D (int) = part->fieldno; /* Index of column in parent tbl */ char *zIdxCol; /* Name of = indexed column */ =20 if (iCol < 0) @@ -302,9 +304,7 @@ sqlite3FkLocateIndex(Parse * pParse, /* Parse = context to store any error in */ def_coll =3D = sql_column_collation(pParent->def, = iCol, = &id); - struct coll *coll =3D - = sql_index_collation(pIdx, i, - = &id); + struct coll *coll =3D = part->coll; if (def_coll !=3D coll) break; =20 @@ -464,13 +464,15 @@ fkLookupParent(Parse * pParse, /* Parse context = */ for (i =3D 0; i < nCol; i++) { int iChild =3D aiCol[i] + 1 + = regData; int iParent =3D - pIdx->aiColumn[i] + 1 + = regData; - assert(pIdx->aiColumn[i] >=3D = 0); + (int) = pIdx->def->key_def->parts[i].fieldno + + 1 + regData; assert(aiCol[i] !=3D = pTab->iPKey); - if (pIdx->aiColumn[i] =3D=3D = pTab->iPKey) { + if ((int)pIdx->def->key_def-> + parts[i].fieldno =3D=3D = pTab->iPKey) { /* The parent key is a = composite key that includes the IPK column */ iParent =3D regData; } + sqlite3VdbeAddOp3(v, OP_Ne, = iChild, iJump, = iParent); VdbeCoverage(v); @@ -622,7 +624,7 @@ fkScanChildren(Parse * pParse, /* Parse context = */ Vdbe *v =3D sqlite3GetVdbe(pParse); =20 assert(pIdx =3D=3D 0 || pIdx->pTable =3D=3D pTab); - assert(pIdx =3D=3D 0 || (int)index_column_count(pIdx) =3D=3D = pFKey->nCol); + assert(pIdx =3D=3D 0 || (int) pIdx->def->key_def->part_count =3D=3D= pFKey->nCol); assert(pIdx !=3D 0); =20 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 = */ =20 - iCol =3D pIdx ? pIdx->aiColumn[i] : -1; + iCol =3D pIdx ? pIdx->def->key_def->parts[i].fieldno : = -1; pLeft =3D exprTableRegister(pParse, pTab, regData, = iCol); iCol =3D aiCol ? aiCol[i] : pFKey->aCol[0].iFrom; assert(iCol >=3D 0); @@ -671,10 +673,9 @@ fkScanChildren(Parse * pParse, /* Parse context = */ Expr *pEq, *pAll =3D 0; Index *pPk =3D sqlite3PrimaryKeyIndex(pTab); assert(pIdx !=3D 0); - int col_count =3D index_column_count(pPk); + int col_count =3D pPk->def->key_def->part_count; for (i =3D 0; i < col_count; i++) { - i16 iCol =3D pIdx->aiColumn[i]; - assert(iCol >=3D 0); + i16 iCol =3D (int) = pIdx->def->key_def->parts[i].fieldno; pLeft =3D exprTableRegister(pParse, pTab, = regData, iCol); pRight =3D exprTableColumn(db, pTab->def, @@ -992,7 +993,6 @@ sqlite3FkCheck(Parse * pParse, /* Parse context = */ if (aiCol[i] =3D=3D pTab->iPKey) { aiCol[i] =3D -1; } - assert(pIdx =3D=3D 0 || pIdx->aiColumn[i] >=3D = 0); } =20 pParse->nTab++; @@ -1126,10 +1126,10 @@ sqlite3FkOldmask(Parse * pParse, /* Parse = context */ Index *pIdx =3D 0; sqlite3FkLocateIndex(pParse, pTab, p, &pIdx, 0); if (pIdx) { - int nIdxCol =3D = index_column_count(pIdx); + int nIdxCol =3D = pIdx->def->key_def->part_count; for (i =3D 0; i < nIdxCol; i++) { - assert(pIdx->aiColumn[i] >=3D = 0); - mask |=3D = COLUMN_MASK(pIdx->aiColumn[i]); + mask |=3D = COLUMN_MASK(pIdx->def-> + = key_def->parts[i].fieldno); } } } @@ -1264,11 +1264,12 @@ fkActionTrigger(Parse * pParse, /* Parse context = */ || (pTab->iPKey >=3D 0 && pTab->iPKey < (int)pTab->def->field_count)); - assert(pIdx =3D=3D 0 || pIdx->aiColumn[i] >=3D = 0); + + uint32_t fieldno =3D pIdx !=3D NULL ? + = pIdx->def->key_def->parts[i].fieldno + : pTab->iPKey; 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 =3D index_column_count(pIdx); + int nColumn =3D pIdx->def->key_def->part_count; pIdx->zColAff =3D (char *)sqlite3DbMallocRaw(0, nColumn + 1); if (!pIdx->zColAff) { @@ -97,22 +97,8 @@ sqlite3IndexAffinityStr(sqlite3 * db, Index * pIdx) return 0; } for (n =3D 0; n < nColumn; n++) { - i16 x =3D pIdx->aiColumn[n]; - if (x >=3D 0) { - char affinity =3D pIdx->pTable-> - def->fields[x].affinity; - pIdx->zColAff[n] =3D affinity; - } else { - char aff; - assert(x =3D=3D XN_EXPR); - assert(pIdx->aColExpr !=3D 0); - aff =3D - = sqlite3ExprAffinity(pIdx->aColExpr->a[n]. - pExpr); - if (aff =3D=3D 0) - aff =3D AFFINITY_BLOB; - pIdx->zColAff[n] =3D aff; - } + i16 x =3D pIdx->def->key_def->parts[n].fieldno; + pIdx->zColAff[n] =3D = pIdx->pTable->def->fields[x].affinity; } pIdx->zColAff[n] =3D 0; } @@ -645,7 +631,7 @@ sqlite3Insert(Parse * pParse, /* Parser = context */ pIdx =3D pIdx->pNext, i++) { assert(pIdx); aRegIdx[i] =3D ++pParse->nMem; - pParse->nMem +=3D index_column_count(pIdx); + pParse->nMem +=3D = pIdx->def->key_def->part_count; } } =20 @@ -1088,7 +1074,7 @@ sqlite3GenerateConstraintChecks(Parse * pParse, = /* The parser context */ nCol =3D pTab->def->field_count; =20 pPk =3D sqlite3PrimaryKeyIndex(pTab); - nPkField =3D index_column_count(pPk); + nPkField =3D pPk->def->key_def->part_count; =20 /* 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 =3D aRegIdx[ix] + 1; - int nIdxCol =3D (int)index_column_count(pIdx); + int nIdxCol =3D pIdx->def->key_def->part_count; for (i =3D 0; i < nIdxCol; i++) { - int iField =3D pIdx->aiColumn[i]; + int iField =3D (int) = pIdx->def->key_def->parts[i].fieldno; int x; - if (iField =3D=3D XN_EXPR) { - pParse->ckBase =3D regNewData + 1; - sqlite3ExprCodeCopy(pParse, - = pIdx->aColExpr->a[i].pExpr, - regIdx + i); - pParse->ckBase =3D 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 =3D=3D pTab->iPKey) - x =3D regNewData; - else - x =3D iField + = regNewData + 1; - - assert(iField >=3D 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 =3D=3D pTab->iPKey) + x =3D regNewData; + else + x =3D iField + regNewData + 1; + + assert(iField >=3D 0); + sqlite3VdbeAddOp2(v, OP_SCopy, + x, regIdx + i); + VdbeComment((v, "%s", + = pTab->def->fields[iField].name)); } } =20 @@ -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 =3D=3D 1) { - reg_pk =3D regNewData + 1 + = pIdx->aiColumn[0]; - if (pTab->zColAff[pIdx->aiColumn[0]] =3D=3D= + reg_pk =3D regNewData + 1 + + = pIdx->def->key_def->parts[0].fieldno; + + int fieldno =3D = (int)pIdx->def->key_def-> + parts[0].fieldno; + if (pTab->zColAff[fieldno] =3D=3D AFFINITY_INTEGER) { int skip_if_null =3D = sqlite3VdbeMakeLabel(v); if ((pTab->tabFlags & = TF_Autoincrement) !=3D 0) { @@ -1311,8 +1290,8 @@ sqlite3GenerateConstraintChecks(Parse * pParse, = /* The parser context */ } =20 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)); } =20 /* 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); =20 @@ -1410,19 +1389,17 @@ sqlite3GenerateConstraintChecks(Parse * pParse, = /* The parser context */ = nPkField); if (isUpdate || on_error =3D=3D = ON_CONFLICT_ACTION_REPLACE) { int x; - int nPkCol =3D index_column_count(pPk); + int nPkCol =3D 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 !=3D pPk) { for (i =3D 0; i < nPkCol; i++) { - assert(pPk->aiColumn[i] >=3D 0); - x =3D pPk->aiColumn[i]; + x =3D = 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); =20 for (i =3D 0; i < nPkCol; i++) { - uint32_t id; - char *p4 =3D (char = *)sql_index_collation(pPk, i, &id); - x =3D pPk->aiColumn[i]; - assert(x >=3D 0); + char *p4 =3D (char *) = pPk->def->key_def->parts[i].coll; + x =3D = pPk->def->key_def->parts[i].fieldno; + if (pPk->tnum=3D=3D0) { + x =3D -1; + } if (i =3D=3D (nPkCol - 1)) { addrJump =3D = addrUniqueOk; op =3D 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 !=3D - ON_CONFLICT_ACTION_DEFAULT && + (pIdx->def->opts.is_unique && + pIdx->onError !=3D ON_CONFLICT_ACTION_DEFAULT && /* Condition 4.1 */ pIdx->onError !=3D 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 !=3D pSrc->pTable); - uint32_t nDestCol =3D index_column_count(pDest); - uint32_t nSrcCol =3D index_column_count(pSrc); + uint32_t nDestCol =3D pDest->def->key_def->part_count; + uint32_t nSrcCol =3D pSrc->def->key_def->part_count; if (nDestCol !=3D nSrcCol) { return 0; /* Different number of columns */ } if (pDest->onError !=3D pSrc->onError) { return 0; /* Different conflict resolution = strategies */ } - for (i =3D 0; i < nSrcCol; i++) { - if (pSrc->aiColumn[i] !=3D pDest->aiColumn[i]) { + struct key_part *src_part =3D pSrc->def->key_def->parts; + struct key_part *dest_part =3D pDest->def->key_def->parts; + for (i =3D 0; i < nSrcCol; i++, src_part++, dest_part++) { + if (src_part->fieldno !=3D dest_part->fieldno) return 0; /* Different columns indexed */ - } - if (pSrc->aiColumn[i] =3D=3D XN_EXPR) { - assert(pSrc->aColExpr !=3D 0 && pDest->aColExpr = !=3D 0); - if = (sqlite3ExprCompare(pSrc->aColExpr->a[i].pExpr, - = pDest->aColExpr->a[i].pExpr, - -1) !=3D 0) { - return 0; /* Different expressions = in the index */ - } - } - if (sql_index_column_sort_order(pSrc, i) !=3D - sql_index_column_sort_order(pDest, i)) { + if (src_part->sort_order !=3D dest_part->sort_order) return 0; /* Different sort orders */ - } - uint32_t id; - if (sql_index_collation(pSrc, i, &id) !=3D - sql_index_collation(pDest, i, &id)) { + if (src_part->coll !=3D 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 =3D pDest->pIndex; pDestIdx; pDestIdx =3D = pDestIdx->pNext) { - if (index_is_unique(pDestIdx)) { + if (pDestIdx->def->opts.is_unique) destHasUniqueIdx =3D 1; - } for (pSrcIdx =3D pSrc->pIndex; pSrcIdx; pSrcIdx =3D = pSrcIdx->pNext) { if (xferCompatibleIndex(pDestIdx, pSrcIdx)) break; } - if (pSrcIdx =3D=3D 0) { + if (pSrcIdx =3D=3D 0) return 0; /* pDestIdx has no corresponding = index in pSrc */ - } } /* Get server checks. */ ExprList *pCheck_src =3D 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 =3D 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 =3D 1; k <=3D = (int)pTab->def->field_count - && pPk->aiColumn[k = - 1] !=3D - i; k++) { + && (int) pPk->def-> + key_def-> + parts[k - = 1]. + fieldno !=3D = i; + k++) { } } bool nullable =3D @@ -430,7 +433,7 @@ sqlite3Pragma(Parse * pParse, Token * pId, /* First = part of [schema.]id field */ size_t avg_tuple_size_idx =3D = 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 =3D 3; } - mx =3D index_column_count(pIdx); + mx =3D = pIdx->def->key_def->part_count; assert(pParse->nMem <=3D pPragma->nPragCName); - for (i =3D 0; i < mx; i++) { - i16 cnum =3D = pIdx->aiColumn[i]; + struct key_part *part =3D + = pIdx->def->key_def->parts; + for (i =3D 0; i < mx; i++, = part++) { + i16 cnum =3D (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 =3D + = part->coll_id; struct coll = *coll =3D - = sql_index_collation(pIdx, i, &id); + = part->coll; if (coll !=3D = NULL) c_n =3D = coll_by_id(id)->name; else c_n =3D = "BINARY"; - enum sort_order = sort_order; - sort_order =3D = 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 =3D pFrom->u1.zIndexedBy; Index *pIdx; for (pIdx =3D pTab->pIndex; - pIdx && strcmp(pIdx->zName, zIndexedBy); + pIdx && strcmp(pIdx->def->name, zIndexedBy); pIdx =3D 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=3D=3D3 because there are - * three columns in the table. In the Index structure describing - * Ex2, nColumn=3D=3D2 since 2 of the 3 columns of Ex1 are indexed. - * The value of aiColumn is {2, 0}. aiColumn[0]=3D=3D2 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]=3D=3D0. - * * 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=3D * 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; /* =46rom 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=3D=3DUNIQUE, 2=3D=3DPRIMARY KEY, = 0=3D=3DCREATE INDEX */ + struct index_def *def; }; =20 /** @@ -2161,11 +2138,6 @@ index_field_tuple_est(struct Index *idx, uint32_t = field); #define IsUniqueIndex(X) (((X)->idxType =3D=3D = SQLITE_IDXTYPE_UNIQUE) || \ ((X)->idxType =3D=3D = SQLITE_IDXTYPE_PRIMARYKEY)) =20 -/* 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 *); =20 -/** - * 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 *); =20 /** * 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); =20 -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 =3D pTop; pSubParse->eTriggerOp =3D pTrigger->op; pSubParse->nQueryLoop =3D pParse->nQueryLoop; - struct region *region =3D &fiber()->gc; - pSubParse->region_initial_size =3D region_used(region); =20 v =3D 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 =3D 0, pIdx =3D pTab->pIndex; pIdx; pIdx =3D pIdx->pNext, = j++) { int reg; - int nIdxCol =3D index_column_count(pIdx); + int nIdxCol =3D pIdx->def->key_def->part_count; if (chngPk || hasFK || pIdx->pPartIdxWhere || pIdx =3D=3D = pPk) { reg =3D ++pParse->nMem; pParse->nMem +=3D nIdxCol; } else { reg =3D 0; for (i =3D 0; i < nIdxCol; i++) { - i16 iIdxCol =3D pIdx->aiColumn[i]; + i16 iIdxCol =3D = pIdx->def->key_def->parts[i].fieldno; if (iIdxCol < 0 || aXRef[iIdxCol] >=3D = 0) { reg =3D ++pParse->nMem; pParse->nMem +=3D nIdxCol; @@ -306,7 +306,7 @@ sqlite3Update(Parse * pParse, /* The = parser context */ nPk =3D nKey; } else { assert(pPk !=3D 0); - nPk =3D index_column_count(pPk); + nPk =3D pPk->def->key_def->part_count; } iPk =3D pParse->nMem + 1; pParse->nMem +=3D nPk; @@ -333,9 +333,9 @@ sqlite3Update(Parse * pParse, /* The = parser context */ } } else { for (i =3D 0; i < nPk; i++) { - assert(pPk->aiColumn[i] >=3D 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 =3D parse->pVdbe; assert(v !=3D NULL); assert(idx !=3D NULL); - struct key_def *def =3D key_def_dup(sql_index_key_def(idx)); + struct key_def *def =3D key_def_dup(idx->def->key_def); if (def =3D=3D 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 =3D p->pIdx; /* Index being = probed */ int nByte; /* Bytes of space to allocate */ int i; /* Counter variable */ - int nCol =3D index_column_count(pIdx); + int nCol =3D pIdx->def->key_def->part_count; =20 nByte =3D sizeof(Mem) * nCol + ROUND8(sizeof(UnpackedRecord)); @@ -1095,7 +1095,7 @@ valueNew(sqlite3 * db, struct ValueNewStat4Ctx *p) (UnpackedRecord *) sqlite3DbMallocZero(db, = nByte); if (pRec =3D=3D NULL) return NULL; - pRec->key_def =3D = key_def_dup(sql_index_key_def(pIdx)); + pRec->key_def =3D = key_def_dup(pIdx->def->key_def); if (pRec->key_def =3D=3D 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 =3D pWC->a + k; k < pWC->nTerm; k++, = pTerm++) { if (pTerm->leftCursor =3D=3D iCur && pTerm->u.leftColumn =3D=3D = iColumn - && (iColumn !=3D XN_EXPR - || = sqlite3ExprCompare(pTerm->pExpr-> - pLeft, - = pScan->pIdxExpr, - iCur) =3D=3D= 0) && (pScan->iEquiv <=3D 1 || = !ExprHasProperty(pTerm->pExpr, = EP_FromJoin)) @@ -376,19 +371,21 @@ whereScanInit(WhereScan * pScan, /* The WhereScan = object being initialized */ pScan->is_column_seen =3D false; if (pIdx) { int j =3D iColumn; - iColumn =3D pIdx->aiColumn[j]; - if (iColumn =3D=3D XN_EXPR) { - pScan->pIdxExpr =3D pIdx->aColExpr->a[j].pExpr; - } else if (iColumn >=3D 0) { + iColumn =3D pIdx->def->key_def->parts[j].fieldno; + /* + * pIdx->tnum =3D=3D 0 means that pIdx is a fake integer + * primary key index + */ + if (pIdx->tnum =3D=3D 0) + iColumn =3D -1; + + if (iColumn >=3D 0) { char affinity =3D = pIdx->pTable->def->fields[iColumn].affinity; pScan->idxaff =3D affinity; - uint32_t id; - pScan->coll =3D sql_index_collation(pIdx, j, = &id); + pScan->coll =3D = pIdx->def->key_def->parts[j].coll; pScan->is_column_seen =3D true; } - } else if (iColumn =3D=3D XN_EXPR) { - return 0; } pScan->opMask =3D opMask; pScan->k =3D 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 =3D = &pIdx->def->key_def->parts[iCol]; for (int i =3D 0; i < pList->nExpr; i++) { Expr *p =3D sqlite3ExprSkipCollate(pList->a[i].pExpr); - if (p->op =3D=3D TK_COLUMN && - p->iColumn =3D=3D pIdx->aiColumn[iCol] && - p->iTable =3D=3D iBase) { + if (p->op =3D=3D TK_COLUMN && p->iTable =3D=3D iBase && + p->iColumn =3D=3D (int) part_to_match->fieldno) { bool is_found; uint32_t id; struct coll *coll =3D sql_expr_coll(pParse, = pList->a[i].pExpr, &is_found, = &id); - if (is_found && - coll =3D=3D sql_index_collation(pIdx, iCol, = &id)) { + if (is_found && coll =3D=3D part_to_match->coll) = { return i; } } @@ -484,27 +480,6 @@ findIndexCol(Parse * pParse, /* Parse context = */ return -1; } =20 -/* - * Return TRUE if the iCol-th column of index pIdx is NOT NULL - */ -static int -indexColumnNotNull(Index * pIdx, int iCol) -{ - int j; - assert(pIdx !=3D 0); - assert(iCol >=3D 0 && iCol < (int)index_column_count(pIdx)); - j =3D pIdx->aiColumn[iCol]; - if (j >=3D 0) { - return !pIdx->pTable->def->fields[j].is_nullable; - } else if (j =3D=3D (-1)) { - return 1; - } else { - assert(j =3D=3D (-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=3DX" term are subject to a NOT NULL = constraint. */ for (pIdx =3D pTab->pIndex; pIdx; pIdx =3D pIdx->pNext) { - if (!index_is_unique(pIdx)) + if (!pIdx->def->opts.is_unique) continue; - int col_count =3D index_column_count(pIdx); + int col_count =3D pIdx->def->key_def->part_count; for (i =3D 0; i < col_count; i++) { if (0 =3D=3D 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) =3D=3D = 0) + uint32_t j =3D = pIdx->def->key_def->parts[i].fieldno; + if = (!pIdx->pTable->def->fields[j].is_nullable =3D=3D 0) break; } } - if (i =3D=3D (int)index_column_count(pIdx)) { + if (i =3D=3D (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 >=3D 0 && iCol < (int)index_column_count(pIdx)); + assert(iCol >=3D 0 && iCol < (int) = pIdx->def->key_def->part_count); if (!pIdx->zColAff) { if (sqlite3IndexAffinityStr(db, pIdx) =3D=3D 0) return AFFINITY_BLOB; @@ -1169,13 +1145,12 @@ whereRangeSkipScanEst(Parse * pParse, = /* Parsing & code generating context */ int nUpper =3D index->def->opts.stat->sample_count + 1; int rc =3D SQLITE_OK; u8 aff =3D sqlite3IndexColumnAffinity(db, p, nEq); - uint32_t id; =20 sqlite3_value *p1 =3D 0; /* Value extracted from pLower = */ sqlite3_value *p2 =3D 0; /* Value extracted from pUpper = */ sqlite3_value *pVal =3D 0; /* Value extracted from record = */ =20 - struct coll *pColl =3D sql_index_collation(p, nEq, &id); + struct coll *pColl =3D p->def->key_def->parts[nEq].coll; if (pLower) { rc =3D sqlite3Stat4ValueFromExpr(pParse, = pLower->pExpr->pRight, aff, &p1); @@ -1371,7 +1346,7 @@ whereRangeScanEst(Parse * pParse, /* Parsing & = code generating context */ || (pLower->eOperator & (WO_GT | WO_GE)) = !=3D 0); assert(pUpper =3D=3D 0 || (pUpper->eOperator & (WO_LT | WO_LE)) = !=3D 0); - if (sql_index_column_sort_order(p, nEq) !=3D + if (p->def->key_def->parts[nEq].sort_order !=3D 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; =20 assert(nEq >=3D 1); - assert(nEq <=3D (int)index_column_count(p)); + assert(nEq <=3D (int) p->def->key_def->part_count); assert(pBuilder->nRecValid < nEq); =20 /* 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 */ =20 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 =3D a[1]; =20 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 =3D p->pIndex->zName) !=3D 0) { + if (p->pIndex && (zName =3D p->pIndex->def->name) !=3D 0) { if (strncmp(zName, "sqlite_autoindex_", 17) =3D=3D 0) { int i =3D sqlite3Strlen30(zName) - 1; while (zName[i] !=3D '_') @@ -2236,7 +2211,7 @@ whereRangeVectorLen(Parse * pParse, /* = Parsing context */ int nCmp =3D sqlite3ExprVectorSize(pTerm->pExpr->pLeft); int i; =20 - nCmp =3D MIN(nCmp, (int)(index_column_count(pIdx) - nEq)); + nCmp =3D MIN(nCmp, (int)(pIdx->def->key_def->part_count - nEq)); for (i =3D 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 !=3D TK_COLUMN - || pLhs->iTable !=3D iCur - || pLhs->iColumn !=3D pIdx->aiColumn[i + nEq] - || sql_index_column_sort_order(pIdx, i + nEq) !=3D - sql_index_column_sort_order(pIdx, nEq)) { + if (pLhs->op !=3D TK_COLUMN || pLhs->iTable !=3D iCur + || pLhs->iColumn !=3D = (int)pIdx->def->key_def->parts[i + nEq].fieldno + || pIdx->def->key_def->parts[i + nEq].sort_order !=3D + pIdx->def->key_def->parts[nEq].sort_order) { break; } =20 @@ -2275,7 +2249,7 @@ whereRangeVectorLen(Parse * pParse, /* = Parsing context */ pColl =3D sql_binary_compare_coll_seq(pParse, pLhs, = pRhs, &id); if (pColl =3D=3D 0) break; - if (sql_index_collation(pIdx, i + nEq, &id) !=3D pColl) + if (pIdx->def->key_def->parts[(i + nEq)].coll !=3D = 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 =3D 0, *pBtm =3D 0; /* Top and bottom range = constraints */ - uint32_t nProbeCol =3D index_column_count(pProbe); + uint32_t nProbeCol =3D pProbe->def->key_def->part_count; =20 pNew =3D pBuilder->pNew; if (db->mallocFailed) return SQLITE_NOMEM_BKPT; WHERETRACE(0x800, ("BEGIN addBtreeIdx(%s), nEq=3D%d\n", - pProbe->zName, pNew->nEq)); + pProbe->def->name, pNew->nEq)); =20 assert((pNew->wsFlags & WHERE_TOP_LIMIT) =3D=3D 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 =3D 0; int nRecValid =3D pBuilder->nRecValid; + uint32_t j =3D = pProbe->def->key_def->parts[saved_nEq].fieldno; if ((eOp =3D=3D WO_ISNULL || (pTerm->wtFlags & = TERM_VNULL) !=3D 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 =3D pProbe->aiColumn[saved_nEq]; + int iCol =3D = pProbe->def->key_def->parts[saved_nEq].fieldno; pNew->wsFlags |=3D WHERE_COLUMN_EQ; assert(saved_nEq =3D=3D pNew->nEq); - if ((iCol > 0 && nInMul =3D=3D 0 - && saved_nEq =3D=3D nProbeCol - 1) - ) { - if (iCol >=3D 0 && - !index_is_unique_not_null(pProbe)) { + if ((iCol > 0 && nInMul =3D=3D 0 && + saved_nEq =3D=3D nProbeCol - 1)) { + bool index_is_unique_not_null =3D + = pProbe->def->key_def->is_nullable && + pProbe->def->opts.is_unique; + if (pProbe->tnum !=3D 0 && + !index_is_unique_not_null) { pNew->wsFlags |=3D = WHERE_UNQ_WANTED; } else { pNew->wsFlags |=3D WHERE_ONEROW; @@ -2514,8 +2491,7 @@ whereLoopAddBtreeIndex(WhereLoopBuilder * = pBuilder, /* The WhereLoop factory */ assert(eOp & (WO_ISNULL | WO_EQ | WO_IN)); =20 assert(pNew->nOut =3D=3D saved_nOut); - if (pTerm->truthProb <=3D 0 - && pProbe->aiColumn[saved_nEq] >=3D 0) { + if (pTerm->truthProb <=3D 0 && pProbe->tnum !=3D = 0 ) { assert((eOp & WO_IN) || nIn =3D=3D 0); testcase(eOp & WO_IN); pNew->nOut +=3D pTerm->truthProb; @@ -2671,7 +2647,7 @@ whereLoopAddBtreeIndex(WhereLoopBuilder * = pBuilder, /* The WhereLoop factory */ } =20 WHERETRACE(0x800, ("END addBtreeIdx(%s), nEq=3D%d, rc=3D%d\n", - pProbe->zName, saved_nEq, rc)); + pProbe->def->name, saved_nEq, rc)); return rc; } =20 @@ -2715,7 +2691,7 @@ indexMightHelpWithOrderBy(WhereLoopBuilder * = pBuilder, ExprList *pOB; ExprList *aColExpr; int ii, jj; - int nIdxCol =3D index_column_count(pIndex); + int nIdxCol =3D pIndex->def->key_def->part_count; if (index_is_unordered(pIndex)) return 0; if ((pOB =3D pBuilder->pWInfo->pOrderBy) =3D=3D 0) @@ -2726,13 +2702,12 @@ indexMightHelpWithOrderBy(WhereLoopBuilder * = pBuilder, if (pExpr->iColumn < 0) return 1; for (jj =3D 0; jj < nIdxCol; jj++) { - if (pExpr->iColumn =3D=3D = pIndex->aiColumn[jj]) + if (pExpr->iColumn =3D=3D (int) + = pIndex->def->key_def->parts[jj].fieldno) return 1; } } else if ((aColExpr =3D pIndex->aColExpr) !=3D 0) { for (jj =3D 0; jj < nIdxCol; jj++) { - if (pIndex->aiColumn[jj] !=3D XN_EXPR) - continue; if (sqlite3ExprCompare (pExpr, aColExpr->a[jj].pExpr, iCursor) =3D=3D 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 =3D -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 =3D 1; - sPk.aiColumn =3D &aiColumnPk; sPk.aiRowLogEst =3D aiRowEstPk; sPk.onError =3D ON_CONFLICT_ACTION_REPLACE; sPk.pTable =3D pTab; + + struct key_def *key_def =3D key_def_new(1); + if (key_def =3D=3D 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 =3D index_opts_default; + + sPk.def =3D index_def_new(pTab->def->id, 0, "primary", + sizeof("primary") - 1, TREE, = &index_opts, + key_def, NULL); + + if (sPk.def =3D=3D NULL) + return SQLITE_ERROR; + aiRowEstPk[0] =3D sql_space_tuple_log_count(pTab); aiRowEstPk[1] =3D 0; pFirst =3D pSrc->pTab->pIndex; @@ -3325,8 +3315,8 @@ wherePathSatisfiesOrderBy(WhereInfo * pWInfo, = /* The WHERE clause */ index_is_unordered(pIndex)) { return 0; } else { - nColumn =3D index_column_count(pIndex); - isOrderDistinct =3D = index_is_unique(pIndex); + nColumn =3D = pIndex->def->key_def->part_count; + isOrderDistinct =3D = pIndex->def->opts.is_unique; } =20 /* 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 =3D pIndex->aiColumn[j]; - revIdx =3D = sql_index_column_sort_order(pIndex, - = j); + iColumn =3D = pIndex->def->key_def-> + parts[j].fieldno; + revIdx =3D = pIndex->def->key_def-> + parts[j].sort_order; if (iColumn =3D=3D = pIndex->pTable->iPKey) iColumn =3D -1; } else { @@ -3442,8 +3433,7 @@ wherePathSatisfiesOrderBy(WhereInfo * pWInfo, = /* The WHERE clause */ = pOrderBy->a[i].pExpr, = &is_found, &id); struct coll *idx_coll =3D - = sql_index_collation(pIndex, - = j, &id); + = pIndex->def->key_def->parts[j].coll; if (is_found && coll !=3D idx_coll) continue; @@ -4105,13 +4095,13 @@ whereShortCut(WhereLoopBuilder * pBuilder) } else { for (pIdx =3D pTab->pIndex; pIdx; pIdx =3D pIdx->pNext) = { int opMask; - int nIdxCol =3D index_column_count(pIdx); + int nIdxCol =3D pIdx->def->key_def->part_count; assert(pLoop->aLTermSpace =3D=3D pLoop->aLTerm); - if (!index_is_unique(pIdx) + if (!pIdx->def->opts.is_unique || pIdx->pPartIdxWhere !=3D 0 - || nIdxCol > ArraySize(pLoop->aLTermSpace) - ) + || nIdxCol > ArraySize(pLoop->aLTermSpace)) = { continue; + } opMask =3D WO_EQ; for (j =3D 0; j < nIdxCol; j++) { pTerm =3D @@ -4650,7 +4640,7 @@ sqlite3WhereBegin(Parse * pParse, /* The parser = context */ wctrlFlags & WHERE_ORDERBY_MIN) = =3D=3D 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 =3D 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 =3D pIdx->aiColumn[i]; - if (i =3D=3D XN_EXPR) - return ""; + i =3D pIdx->def->key_def->parts[i].fieldno; return pIdx->pTable->def->fields[i].name; } =20 @@ -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) !=3D 0 @@ -463,7 +461,7 @@ codeEqualityTerm(Parse * pParse, /* The parsing = context */ int *aiMap =3D 0; =20 if (pLoop->pIndex !=3D 0 && - sql_index_column_sort_order(pLoop->pIndex, iEq)) { + pLoop->pIndex->def->key_def->parts[iEq].sort_order) = { testcase(iEq =3D=3D 0); testcase(bRev); bRev =3D !bRev; @@ -708,7 +706,7 @@ codeAllEqualityTerms(Parse * pParse, /* = Parsing context */ sqlite3VdbeAddOp1(v, (bRev ? OP_Last : OP_Rewind), = iIdxCur); VdbeCoverageIf(v, bRev =3D=3D 0); VdbeCoverageIf(v, bRev !=3D 0); - VdbeComment((v, "begin skip-scan on %s", pIdx->zName)); + VdbeComment((v, "begin skip-scan on %s", = pIdx->def->name)); j =3D sqlite3VdbeAddOp0(v, OP_Goto); pLevel->addrSkip =3D sqlite3VdbeAddOp4Int(v, (bRev ? OP_SeekLT : = OP_SeekGT), @@ -718,8 +716,8 @@ codeAllEqualityTerms(Parse * pParse, /* = Parsing context */ sqlite3VdbeJumpHere(v, j); for (j =3D 0; j < nSkip; j++) { sqlite3VdbeAddOp3(v, OP_Column, iIdxCur, - pIdx->aiColumn[j], regBase + = j); - testcase(pIdx->aiColumn[j] =3D=3D 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 =3D=3D 0 || pWInfo->pOrderBy->nExpr =3D=3D 1 || (pWInfo->wctrlFlags & WHERE_ORDERBY_MIN) =3D=3D = 0); - int nIdxCol =3D index_column_count(pIdx); + int nIdxCol =3D pIdx->def->key_def->part_count; if ((pWInfo->wctrlFlags & WHERE_ORDERBY_MIN) !=3D 0 && pWInfo->nOBSat > 0 && (nIdxCol > nEq)) { - j =3D pIdx->aiColumn[nEq]; + j =3D pIdx->def->key_def->parts[nEq].fieldno; /* Allow seek for column with `NOT NULL` =3D=3D = 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 >=3D 0 && = pIdx->pTable->def->fields[j].is_nullable) - || j =3D=3D XN_EXPR) { + if (pIdx->pTable->def->fields[j].is_nullable) { assert(pLoop->nSkip =3D=3D 0); bSeekPastNull =3D 1; nExtraReg =3D 1; @@ -1299,17 +1296,15 @@ sqlite3WhereCodeOneLoopStart(WhereInfo * pWInfo, = /* Complete information about t assert((bRev & ~1) =3D=3D 0); pLevel->iLikeRepCntr <<=3D 1; pLevel->iLikeRepCntr |=3D - bRev ^ = (sql_index_column_sort_order(pIdx, nEq) =3D=3D + bRev ^ (pIdx->def->key_def-> + parts[nEq].sort_order = =3D=3D SORT_ORDER_DESC); } #endif if (pRangeStart =3D=3D 0) { - j =3D pIdx->aiColumn[nEq]; - if ((j >=3D 0 && - = pIdx->pTable->def->fields[j].is_nullable)|| - j =3D=3D XN_EXPR) { + j =3D = pIdx->def->key_def->parts[nEq].fieldno; + if = (pIdx->pTable->def->fields[j].is_nullable) bSeekPastNull =3D 1; - } } } assert(pRangeEnd =3D=3D 0 @@ -1320,7 +1315,7 @@ sqlite3WhereCodeOneLoopStart(WhereInfo * pWInfo, = /* Complete information about t * start and end terms (pRangeStart and pRangeEnd). */ if ((nEq < nIdxCol && - bRev =3D=3D (sql_index_column_sort_order(pIdx, nEq) = =3D=3D + bRev =3D=3D = (pIdx->def->key_def->parts[nEq].sort_order =3D=3D SORT_ORDER_ASC)) || (bRev && nIdxCol =3D=3D nEq)) { SWAP(pRangeEnd, pRangeStart); @@ -1386,9 +1381,10 @@ sqlite3WhereCodeOneLoopStart(WhereInfo * pWInfo, = /* Complete information about t } struct Index *pk =3D = sqlite3PrimaryKeyIndex(pIdx->pTable); assert(pk); - int nPkCol =3D index_column_count(pk); + int nPkCol =3D pk->def->key_def->part_count; + uint32_t zero_fieldno =3D = pk->def->key_def->parts[0].fieldno; char affinity =3D - = pIdx->pTable->def->fields[pk->aiColumn[0]].affinity; + = pIdx->pTable->def->fields[zero_fieldno].affinity; if (nPkCol =3D=3D 1 && affinity =3D=3D 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 =3D pRangeStart =3D=3D NULL ? nEq : = nEq + 1; for (int i =3D 0; i < limit; i++) { - if (pIdx->aiColumn[i] =3D=3D = pk->aiColumn[0]) { + if (pIdx->def->key_def->parts[i].fieldno = =3D=3D + 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 !=3D iIdxCur) { Index *pPk =3D = sqlite3PrimaryKeyIndex(pIdx->pTable); - int nPkCol =3D index_column_count(pPk); + int nPkCol =3D pPk->def->key_def->part_count; int iKeyReg =3D sqlite3GetTempRange(pParse, = nPkCol); for (j =3D 0; j < nPkCol; j++) { - k =3D pPk->aiColumn[j]; + k =3D = 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) =3D=3D 0) = { Index *pPk =3D sqlite3PrimaryKeyIndex(pTab); - int nPkCol =3D index_column_count(pPk); + int nPkCol =3D pPk->def->key_def->part_count; regRowset =3D pParse->nTab++; sqlite3VdbeAddOp2(v, OP_OpenTEphemeral, regRowset, nPkCol); @@ -1718,13 +1715,16 @@ sqlite3WhereCodeOneLoopStart(WhereInfo * pWInfo, = /* Complete information about t int iSet =3D ((ii =3D=3D = pOrWc->nTerm - 1) ? -1 : ii); Index *pPk =3D = sqlite3PrimaryKeyIndex (pTab); - int nPk =3D = index_column_count(pPk); + int nPk =3D = pPk->def->key_def->part_count; int iPk; =20 /* Read the PK into an = array of temp registers. */ r =3D = sqlite3GetTempRange(pParse, nPk); for (iPk =3D 0; iPk < = nPk; iPk++) { - int iCol =3D = pPk->aiColumn[iPk]; + int iCol =3D = 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; =20 @@ -930,20 +929,6 @@ exprMightBeIndexed(SrcList * pFrom, /* The = FROM clause */ for (i =3D 0; mPrereq > 1; i++, mPrereq >>=3D 1) { } iCur =3D pFrom->a[i].iCursor; - for (pIdx =3D pFrom->a[i].pTab->pIndex; pIdx; pIdx =3D = pIdx->pNext) { - if (pIdx->aColExpr =3D=3D 0) - continue; - for (i =3D 0; i < pIdx->nColumn; i++) { - if (pIdx->aiColumn[i] !=3D XN_EXPR) - continue; - if (sqlite3ExprCompare - (pExpr, pIdx->aColExpr->a[i].pExpr, iCur) =3D=3D= 0) { - *piCur =3D iCur; - *piColumn =3D XN_EXPR; - return 1; - } - } - } return 0; } =20 --=20= --Apple-Mail=_ECB47D6D-69B1-4BF0-94FD-048F95FFBE64 Content-Transfer-Encoding: quoted-printable Content-Type: text/html; charset=utf-8


1. Please, do not paste SMTP = headers in the body.

2. Looks like in the diff below all tabs are turned into = 4 spaces. Please
cope with it. Maybe, your = IDE made it.


> Now every sqlite struct = Index is created with tnt struct
> index_def = inside. This allows us to use tnt index_def
> in work with sqlite indexes in the same manner as = with
> tnt index and is a step to remove sqlite Index = with
> tnt index.
> Fields = coll_array, coll_id_array, aiColumn, sort_order
> and zName are removed from Index. All usages of = this
> fields changed to usage of corresponding = index_def
> fields.
> index_is_unique(), sql_index_collation() = and
> index_column_count() are removed with calls = of
> index_def corresponding fields.

> Closes: = #3369
> Github branch: https://github.com/tarantool/tarantool/tree/sb/gh-3369-use-inde= x-def-in-select-and-where

3. Please, put branch link below ---.

> = ---

Here. And do not = forget about link to the issue.

     Branch: <link>
     Issue: = <link>

> src/box/sql.c | 18 +--
> = src/box/sql/analyze.c | 24 +--
> = src/box/sql/build.c | 398 = ++++++++++++++++++++++++------------------------
> src/box/sql/delete.c | 16 +-
> src/box/sql/expr.c | 59 ++++---
> src/box/sql/fkey.c | 41 +++--
> src/box/sql/insert.c | 134 = +++++++---------
> src/box/sql/pragma.c | = 19 ++-
> src/box/sql/select.c | = 2 +-
> src/box/sql/sqliteInt.h | 25 +--
> src/box/sql/trigger.c | 2 -
> src/box/sql/update.c | 10 +-
> src/box/sql/vdbemem.c | 2 +-
> src/box/sql/where.c | 140 = ++++++++---------
> src/box/sql/wherecode.c = | 43 +++---
> src/box/sql/whereexpr.c = | 15 --
> 16 files changed, 433 = insertions(+), 515 deletions(-)
>> diff = --git a/src/box/sql/build.c b/src/box/sql/build.c
> index 28e4d7a4d..74fb66565 100644
> --- a/src/box/sql/build.c
> +++ b/src/box/sql/build.c
> @@ -1072,11 +1075,9 @@ sqlite3AddCollateType(Parse * = pParse, Token * pToken)
> * collation = type was added. Correct this if it is the case.
> */
> for (pIdx =3D = p->pIndex; pIdx; pIdx =3D pIdx->pNext) {
> - assert(pIdx->nColumn =3D=3D 1);
> - if (pIdx->aiColumn[0] =3D=3D i) {
> - id =3D &pIdx->coll_id_array[0];
> - pIdx->coll_array[0] =3D
> - sql_column_collation(p->def, i, id);
> + assert(pIdx->def->key_def->part_count =3D=3D= 1);
> + if = ((int)pIdx->def->key_def->parts[0].fieldno =3D=3D i) = {
> + id =3D = &pIdx->def->key_def->parts[0].coll_id;

4. I have just = noticed the zColl leaks here. It is not deleted if
coll !=3D NULL, but must.

5. Here you have removed = coll * initialization.
sql_column_collation = function here was used to set coll id and collation,
by column number. But now this whole cycle does nothing = as you can see.
Please, return this = initialization. You sill must init
key_def->parts[0].coll_id and coll.

> }
> }
> } else {
> @@ -1123,52 +1124,10 @@ sql_index_key_def(struct = Index *idx)
> enum = sort_order
> = sql_index_column_sort_order(Index *idx, uint32_t column)

6. Now this = function is useless one line wrapper. Please,
remove it too.

> {
> - assert(idx !=3D NULL);
> - uint32_t space_id =3D = SQLITE_PAGENO_TO_SPACEID(idx->pTable->tnum);
> - struct space *space =3D = 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 = =3D=3D NULL) {
> - assert(column < = idx->nColumn);
> - return = idx->sort_order[column];
> - = }
> -
> - struct key_def = *key_def =3D sql_index_key_def(idx);
> - = assert(key_def !=3D NULL && key_def->part_count >=3D = column);
> - return = key_def->parts[column].sort_order;
> + return = idx->def->key_def->parts[column].sort_order;
> }

> /**
> @@ -1383,14 = +1342,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, const struct key_part key_part)

7. Passing a struct by value = is very bad idea. Please, don't do it ever.
And looks like here fieldno is enough.

> {
> - while (nCol-- > 0)
> - if (x =3D=3D *(aiCol++))
> + int i =3D 0;
> + while (i = < nCol) {
> + if (key_part.fieldno = =3D=3D key_parts->fieldno)
> return = 1;
> + key_parts++;
> + = i++;
> + }
> return = 0;
> }

> @@ -1410,13 +1371,13 @@ static void
> convertToWithoutRowidTable(Parse * pParse, Table * = pTab)
> {
> Index *pPk;
> - int i, j;
> + uint32_t = i, j;

8. Please, do = not predeclare cycle iterators when possible. It is
SQLite code style, not Tarantool. When you change the = SQLite code,
it must turn into Tarantool = style.

> @@ -1454,14 +1415,17 @@ = 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 =3D j =3D 1; i < pPk->nColumn; i++) = {
> - if (hasColumn(pPk->aiColumn, j, = pPk->aiColumn[i])) {
> - = pPk->nColumn--;
> + for (i =3D j =3D 1; i = < pPk->def->key_def->part_count; i++) {
> + if (hasColumn(pPk->def->key_def->parts, = j,
> + pPk->def->key_def->parts[i])) {
> + = pPk->def->key_def->part_count--;
> } else = {
> - pPk->aiColumn[j++] =3D = pPk->aiColumn[i];
> + = pPk->def->key_def->parts[j++] =3D
> + = pPk->def->key_def->parts[i];
> }

9. Wrong = alignments almost on all new lines. And please, save a
key_def->parts in a separate variable and use it = instead of the full
path = pPk->def->key_def->parts. It is too long to be used 4 times = on
6 lines.

10. I see that cycle = iterates until pPk->def->key_def->part_count,
but this variable is decremented inside the cycle. So the = last
columns are not checked. This value must be saved in a = separate
variable before usage as a = 'for' guard.

> }
> - pPk->nColumn =3D = j;
> +
> + = pPk->def->key_def->part_count =3D j;

11. This line = makes no sense. You have already updated part_count
inside the cycle. Either you update here, or in the = cycle.

> }
> assert(pPk !=3D = 0);
> }
> @@ -2654,8 +2618,9 @@ = sqlite3RefillIndex(Parse * pParse, Index * pIndex, int = memRootPage)
> }
> /* Open the sorter cursor if we are to use one. = */
> iSorter =3D pParse->nTab++;
> - sqlite3VdbeAddOp4(v, OP_SorterOpen, iSorter, 0, = pIndex->nColumn,
> - (char *)def, = P4_KEYDEF);
> + sqlite3VdbeAddOp4(v, = OP_SorterOpen, iSorter, 0,
> + = pIndex->def->key_def->part_count,
> + (char = *)def, P4_KEYDEF);

12. Wrong alignment.


> /* Open the table. Loop through all rows of the = table, inserting index
> * records into the = sorter.
> @@ -2687,7 +2652,7 @@ = sqlite3RefillIndex(Parse * pParse, Index * pIndex, int = memRootPage)
> sqlite3VdbeGoto(v, = j2);
> addr2 =3D sqlite3VdbeCurrentAddr(v);
> sqlite3VdbeAddOp4Int(v, OP_SorterCompare, iSorter, = j2,
> - regRecord, pIndex->nColumn);
> + regRecord, = pIndex->def->key_def->part_count);

13. = Same.

> = VdbeCoverage(v);
> = sqlite3UniqueConstraint(pParse, ON_CONFLICT_ACTION_ABORT,
> pIndex);
> @@ -2733,16 = +2698,11 @@ sqlite3AllocateIndexObject(sqlite3 * db, /* Database = connection */
> p =3D = sqlite3DbMallocZero(db, nByte + nExtra);
> if (p) = {
> char *pExtra =3D ((char *)p) + = ROUND8(sizeof(Index));
> - p->coll_array =3D = (struct coll **)pExtra;

14. I still see coll_array = in build.c in comments.

> pExtra +=3D = ROUND8(sizeof(struct coll **) * nCol);
> - = p->coll_id_array =3D (uint32_t *) pExtra;

15. = Same.

> pExtra +=3D = ROUND8(sizeof(uint32_t) * nCol);
> = p->aiRowLogEst =3D (LogEst *) pExtra;
> pExtra +=3D = sizeof(LogEst) * (nCol + 1);
> - = p->aiColumn =3D (i16 *) pExtra;

16. Same. And in very many = places.

> pExtra +=3D sizeof(i16) * nCol;
> - p->sort_order =3D (enum sort_order *) = pExtra;
> - p->nColumn =3D = nCol;
> *ppExtra =3D ((char *)p) + nByte;

17. You have = removed the fields, but did not update
sqlite3AllocateIndexObject that still allocates memory for = them.

> }
> return p;
> @@ -2831,18 = +2791,119 @@ addIndexToTable(Index * pIndex, Table * pTab)
> }
> }

> = -bool
> -index_is_unique(Index *idx)
> +void
> = +append(struct region *r, const char *str, size_t = *total_sql_size)
> {
> - assert(idx !=3D NULL);
> - uint32_t space_id =3D = SQLITE_PAGENO_TO_SPACEID(idx->tnum);
> - uint32_t = index_id =3D SQLITE_PAGENO_TO_INDEXID(idx->tnum);
> - struct space *space =3D = space_by_id(space_id);
> - assert(space !=3D = NULL);
> - struct index = *tnt_index =3D space_index(space, index_id);
> - assert(tnt_index !=3D NULL);
> + memcpy(region_alloc(r, strlen(str)), str, = strlen(str));
> + *total_sql_size +=3D = strlen(str);
> +}
18. Please, rename append function. This name is too = common. And
make it static = inline.

19. You do not check region_alloc fails.

> +
> +char *
> = +create_sql(const char *idx_name, struct space_def *space_def, ExprList = *expr_list)

20. Same as 18.

21. Out of 80 = symbols.

22. Please, use struct ExprList, not ExprList.

23. I still can = not understand why can not you merge this function into
index_def building. It is needed in a single place, and = makes redundant
scan of ExprList.

> = +{
> + struct region *r =3D = &fiber()->gc;
> + size_t total_sql_size = =3D 0;
> + append(r, "CREATE = INDEX ", &total_sql_size);
> + append(r, = idx_name, &total_sql_size);
> + append(r, = " ON ", &total_sql_size);
> + append(r, = space_def->name, &total_sql_size);
> + append(r, = " (", &total_sql_size);
> +
> + for (int i =3D 0; i < expr_list->nExpr; = i++){
> + Expr *expr =3D expr_list->a[i].pExpr;
> + assert(expr->op =3D=3D TK_COLLATE || = expr->op =3D=3D TK_COLUMN);
> + Expr = *column_expr =3D sqlite3ExprSkipCollate(expr);
> + const char *name =3D = space_def->fields[column_expr->iColumn].name;
> +
> + if (expr->op =3D=3D = TK_COLLATE){
> + append(r, name, = &total_sql_size);
> + append(r, " COLLATE = ", &total_sql_size);
> + const = char *coll_name =3D expr->u.zToken;
> + append(r, = coll_name, &total_sql_size);
> + append(r, = ", ", &total_sql_size);
> + } else = {
> + append(r, name, &total_sql_size);
> + append(r, ", ", &total_sql_size);
> + }
> + = }
> +
> + = memcpy(region_alloc(r, 1), "\0", 1);
> + = total_sql_size +=3D 1;
> + char *res =3D = region_join(r, total_sql_size);
> +
> + /*
> + * fix = last ", " with ")\0"
> + */
> + res[strlen(res) - 2] =3D ')';
> + res[strlen(res) - 1] =3D '\0';

24. strlen has = O(N) complexity. And here you already know the
result: total_sql_size.

> + return res;
> +}
> +
> +void
> = +set_index_def(Parse *parse, Index *index, Table *table, uint32_t = iid,
> + const char *name, uint32_t name_len, int = on_error,
> + ExprList *expr_list, = u8 idx_type)

25. Still bad alignment. And make this function be = static.


> = +{
> + struct space_def *space_def =3D = table->def;
> + struct index_opts = opts;
> + index_opts_create(&opts);
> + opts.is_unique =3D on_error !=3D = ON_CONFLICT_ACTION_NONE;
> +
> + struct key_def *key_def =3D = key_def_new(expr_list->nExpr);
> + if = (key_def =3D=3D NULL)

26. If key_def_new fails, you should inform the parser = or
db: either set Parse.nErr and Parser.rc, or = sqlite3OomFaul(). Same
about all other errors got = from Tarantool functions.

> + return;
> +
> + for (int i =3D 0; i = < expr_list->nExpr; i++) {
> + Expr = *expr =3D expr_list->a[i].pExpr;
> + = sql_resolve_self_reference(parse, table, NC_IdxExpr,
> + expr, 0);

27. Bad alignment. And as I = can see it fits in one line.

Please check alignment = during another self-review iteration in
the whole patch. = I will not mention it below again.

> + if (parse->nErr = > 0)
> + return;
> +
> + Expr *column_expr =3D = sqlite3ExprSkipCollate(expr);
> + if = (column_expr->op !=3D TK_COLUMN) {
> + = sqlite3ErrorMsg(parse,
> + "functional indexes = aren't supported "
> + "in the current = version");
> + return;
> + }
> +
> + uint32_t fieldno =3D = column_expr->iColumn;
> +
> + uint32_t coll_id;
> + struct = coll *coll;
> + if (expr->op =3D=3D = TK_COLLATE)
> + coll =3D = sql_get_coll_seq(parse, expr->u.zToken,
> + = &coll_id);
> + else
> + coll =3D sql_column_collation(space_def, = fieldno,
> + = &coll_id);

28. Please, use {}, when 'if' body consists of multiple = lines.

> +
> + if = (sqlite3StrICmp(expr->u.zToken, "binary") !=3D 0 &&
> + coll =3D=3D NULL && expr->op =3D=3D = TK_COLLATE)

29. This check is needed in 'if (expr->op =3D=3D = TK_COLLATE)' above.
And why do you do this = youself? sql_get_coll_seq already sets
the nErr in = parser for this error.

> + return;
> +
> + /* Tarantool: DESC indexes are not supported so = far.
> + * See gh-3016.

30. Please, obey Tarantool = comment style:
/*
  * My comment inside the function.
  * Second line.
  */

And wrap the comment line on = 66 ruler.

> void
> @@ -2853,12 = +2914,11 @@ sql_create_index(struct Parse *parse, struct Token = *token,
> {
> Table *pTab =3D 0; /* Table to be indexed = */
> Index *pIndex =3D 0; /* The index to be created = */
> - char *zName =3D 0; /* Name of the index = */
> + char *name =3D 0; /* Name of the index = */

31. Please, use = explicit NULL.

> int nName; /* Number of characters in zName = */
> - int i, j;
> + int = i;
> DbFixer sFix; /* For assigning database names to = pTable */
> sqlite3 *db =3D = parse->db;
> - struct ExprList_item = *col_listItem; /* For looping over col_list */
> int nExtra =3D 0; /* Space allocated for zExtra[] = */
> char *zExtra =3D 0; /* Extra space after the Index = object */
> struct session = *user_session =3D current_session();
> @@ -3159,6 = +3189,7 @@ sql_create_index(struct Parse *parse, struct Token = *token,
> }
> if (idx_type =3D=3D = SQLITE_IDXTYPE_PRIMARYKEY)
> = pIdx->idxType =3D idx_type;
> +
> goto exit_create_index;

32. Garbage = diff. Please, find other garbage diffs and remove them.

> }
> }
> @@ -3268,28 +3299,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);

33. I have = noticed than on line 3056 collation names add extra bytes
to struct Index object. Can you please investigate if = they are not
needed anymore and remove = this code?

I am talking about it:

/* Figure out how many bytes = of space are required to store explicitly
* specified = collation sequence names.
*/
for (i =3D 0; i < col_list->nExpr; i++) {
Expr *pExpr =3D col_list->a[i].pExpr;
assert(pExpr !=3D 0);
if (pExpr->op = =3D=3D TK_COLLATE) {
nExtra +=3D (1 + = sqlite3Strlen30(pExpr->u.zToken));
}
}

We do not store collation names in struct Index = anymore.

> @@ -3297,15 +3307,8 @@ bool
> index_is_unique_not_null(const Index *idx)
> {
> assert(idx !=3D = NULL);
> - uint32_t space_id =3D = SQLITE_PAGENO_TO_SPACEID(idx->tnum);
> - struct = space *space =3D space_by_id(space_id);
> - = assert(space !=3D NULL);
> -
> - uint32_t index_id =3D = SQLITE_PAGENO_TO_INDEXID(idx->tnum);
> - struct = index *index =3D space_index(space, index_id);
> - assert(index !=3D NULL);
> - return (index->def->opts.is_unique = &&
> - = !index->def->key_def->is_nullable);
> + assert(idx->def !=3D NULL);
> + return (idx->def->key_def->is_nullable = && idx->def->opts.is_unique);

34. This = one-line function is used in a single place. Please, inline
it and remove.

> }

> = void
> @@ -3933,18 +3936,18 @@ = sqlite3UniqueConstraint(Parse * pParse, /* Parsing context */
> )
> {
> char *zErr;
> - int = j;
> + uint32_t j;
> StrAccum = errMsg;
> Table *pTab =3D = 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 =3D 0; j < pIdx->nColumn; j++) = {
> + for (j =3D 0; j < = pIdx->def->key_def->part_count; j++) {
> char *zCol;
> - = assert(pIdx->aiColumn[j] >=3D 0);
> - zCol =3D = pTab->def->fields[pIdx->aiColumn[j]].name;
> + uint32_t fieldno =3D = pIdx->def->key_def->parts[j].fieldno;
> + zCol =3D = pTab->def->fields[fieldno].name;
> if = (j)
> sqlite3StrAccumAppend(&errMsg, ", ", = 2);
> sqlite3XPrintf(&errMsg, "%s.%s", = pTab->def->name, zCol);
> @@ -3967,11 = +3970,10 @@ static bool
> = collationMatch(struct coll *coll, struct Index *index)
> {
> assert(coll !=3D = NULL);
> - for (int i =3D 0; i = < index->nColumn; i++) {
> - uint32_t = id;
> - struct coll *idx_coll =3D = sql_index_collation(index, i, &id);
> - = assert(idx_coll !=3D 0 || index->aiColumn[i] < 0);
> - if (index->aiColumn[i] >=3D 0 && = coll =3D=3D idx_coll)
> + for (uint32_t i =3D = 0; i < index->def->key_def->part_count; i++) {
> + struct coll *idx_coll =3D = index->def->key_def->parts[i].coll;
> + assert(idx_coll !=3D NULL);
> + if (coll =3D=3D idx_coll)
> return true;
> }
> return false;
> diff --git = a/src/box/sql/delete.c b/src/box/sql/delete.c
> index ddad54b3e..504738cd5 100644
> --- a/src/box/sql/delete.c
> +++ b/src/box/sql/delete.c
> @@ -252,11 +252,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 =3D 0; i = < pk_len; i++) {
> - = assert(pk->aiColumn[i] >=3D 0);
> = sqlite3ExprCodeGetColumnOfTable(v, table->def,
> tab_cursor,
> - = pk->
> - = aiColumn[i],
> + = pk->def->
> + = key_def->
> + = parts[i].fieldno,

35. Please, just save pk->def->key_def->parts = above in a separate
variable, and use here only = part->fieldno and ++part above.
=E2=80=94
Thank you for the review. All the issues are fixed. Here is =
the patch:
sql: add =
index_def to Index

Now every sqlite struct Index is created with tnt struct
index_def inside. This allows us to use tnt index_def
in work with sqlite indexes in the same manner as with
tnt index and is a step to remove sqlite Index with
tnt index.
Fields coll_array, coll_id_array, aiColumn, sort_order
and zName are removed from Index. All usages of this
fields changed to usage of corresponding index_def
fields.
index_is_unique(), sql_index_collation() and
index_column_count() are removed with calls of
index_def corresponding fields.

Closes: #3369
---
Branch: https://github.com/tarantool/tarantool/tree/sb/gh-3369-use-inde=
x-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)
=20
 	/* If table's PK is single column which is INTEGER, then
 	 * treat it as strict type, not affinity.  */
-	if (pk_idx && pk_idx->nColumn =3D=3D 1) {
-		int pk =3D pk_idx->aiColumn[0];
+	if (pk_idx !=3D NULL && =
pk_idx->def->key_def->part_count =3D=3D 1) {
+		int pk =3D =
pk_idx->def->key_def->parts[0].fieldno;
 		if (def->fields[pk].type =3D=3D FIELD_TYPE_INTEGER)
 			pk_forced_int =3D pk;
 	}
@@ -1552,20 +1552,19 @@ tarantoolSqlite3MakeTableOpts(Table *pTable, =
const char *zSql, char *buf)
  */
 int tarantoolSqlite3MakeIdxParts(SqliteIndex *pIndex, void *buf)
 {
-	struct space_def *def =3D pIndex->pTable->def;
-	assert(def !=3D NULL);
+	struct field_def *fields =3D =
pIndex->pTable->def->fields;
+	struct key_def *key_def =3D pIndex->def->key_def;
 	const struct Enc *enc =3D get_enc(buf);
-	struct SqliteIndex *primary_index;
-	char *base =3D buf, *p;
-	int pk_forced_int =3D -1;
-
-	primary_index =3D sqlite3PrimaryKeyIndex(pIndex->pTable);
+	char *base =3D buf;
+	uint32_t pk_forced_int =3D UINT32_MAX;
+	struct SqliteIndex *primary_index =3D
+		sqlite3PrimaryKeyIndex(pIndex->pTable);
=20
 	/* If table's PK is single column which is INTEGER, then
 	 * treat it as strict type, not affinity.  */
-	if (primary_index->nColumn =3D=3D 1) {
-		int pk =3D primary_index->aiColumn[0];
-		if (def->fields[pk].type =3D=3D FIELD_TYPE_INTEGER)
+	if (primary_index->def->key_def->part_count =3D=3D 1) {
+		int pk =3D =
primary_index->def->key_def->parts[0].fieldno;
+		if (fields[pk].type =3D=3D FIELD_TYPE_INTEGER)
 			pk_forced_int =3D pk;
 	}
=20
@@ -1575,46 +1574,45 @@ int tarantoolSqlite3MakeIdxParts(SqliteIndex =
*pIndex, void *buf)
 	 * primary key columns. Query planner depends on this particular
 	 * data layout.
 	 */
-	int i, n =3D pIndex->nColumn;
-
-	p =3D enc->encode_array(base, n);
-	for (i =3D 0; i < n; i++) {
-		int col =3D pIndex->aiColumn[i];
-		assert(def->fields[col].is_nullable =3D=3D
-		       =
action_is_nullable(def->fields[col].nullable_action));
+	struct key_part *part =3D key_def->parts;
+	char *p =3D enc->encode_array(base, key_def->part_count);
+	for (uint32_t i =3D 0; i < key_def->part_count; ++i, =
++part) {
+		uint32_t col =3D part->fieldno;
+		assert(fields[col].is_nullable =3D=3D
+		       action_is_nullable(fields[col].nullable_action));
 		const char *t;
 		if (pk_forced_int =3D=3D col) {
 			t =3D "integer";
 		} else {
-			enum affinity_type affinity =3D =
def->fields[col].affinity;
-			t =3D convertSqliteAffinity(affinity,
-						  =
def->fields[col].is_nullable);
+			t =3D =
convertSqliteAffinity(fields[col].affinity,
+						  =
fields[col].is_nullable);
 		}
 		/* do not decode default collation */
-		uint32_t cid =3D pIndex->coll_id_array[i];
+		uint32_t cid =3D part->coll_id;
 		p =3D enc->encode_map(p, cid =3D=3D COLL_NONE ? 5 : =
6);
 		p =3D enc->encode_str(p, "type", sizeof("type")-1);
 		p =3D enc->encode_str(p, t, strlen(t));
 		p =3D enc->encode_str(p, "field", sizeof("field")-1);
 		p =3D enc->encode_uint(p, col);
 		if (cid !=3D COLL_NONE) {
-			p =3D enc->encode_str(p, "collation", =
sizeof("collation")-1);
+			p =3D enc->encode_str(p, "collation",
+					    sizeof("collation") - 1);
 			p =3D enc->encode_uint(p, cid);
 		}
 		p =3D enc->encode_str(p, "is_nullable", 11);
-		p =3D enc->encode_bool(p, =
def->fields[col].is_nullable);
+		p =3D enc->encode_bool(p, fields[col].is_nullable);
 		p =3D enc->encode_str(p, "nullable_action", 15);
 		const char *action_str =3D
-			=
on_conflict_action_strs[def->fields[col].nullable_action];
+			=
on_conflict_action_strs[fields[col].nullable_action];
 		p =3D enc->encode_str(p, action_str, =
strlen(action_str));
=20
 		p =3D enc->encode_str(p, "sort_order", 10);
-		enum sort_order sort_order =3D pIndex->sort_order[i];
+		enum sort_order sort_order =3D part->sort_order;
 		assert(sort_order < sort_order_MAX);
 		const char *sort_order_str =3D =
sort_order_strs[sort_order];
 		p =3D enc->encode_str(p, sort_order_str, =
strlen(sort_order_str));
 	}
-	return (int)(p - base);
+	return p - base;
 }
=20
 /*
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 =
*/
=20
 		if (pOnlyIdx && pOnlyIdx !=3D pIdx)
 			continue;
@@ -860,9 +859,9 @@ analyzeOneTable(Parse * pParse,	/* Parser =
context */
 		if (IsPrimaryKeyIndex(pIdx)) {
 			zIdxName =3D pTab->def->name;
 		} else {
-			zIdxName =3D pIdx->zName;
+			zIdxName =3D pIdx->def->name;
 		}
-		nColTest =3D index_column_count(pIdx);
+		int nColTest =3D =
pIdx->def->key_def->part_count;
=20
 		/* 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));
=20
 		/* Invoke the stat_init() function. The arguments are:
 		 *
@@ -969,7 +968,7 @@ analyzeOneTable(Parse * pParse,	/* Parser =
context */
 			 */
 			sqlite3VdbeAddOp0(v, OP_Goto);
 			addrNextRow =3D sqlite3VdbeCurrentAddr(v);
-			if (nColTest =3D=3D 1 && =
index_is_unique(pIdx)) {
+			if (nColTest =3D=3D 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 =3D 0; i < nColTest; i++) {
-				uint32_t id;
-				struct coll *coll =3D
-					sql_index_collation(pIdx, i, =
&id);
+			struct key_part *part =3D =
pIdx->def->key_def->parts;
+			for (i =3D 0; i < nColTest; ++i, ++part) {
+				struct coll *coll =3D part->coll;
 				sqlite3VdbeAddOp2(v, OP_Integer, i, =
regChng);
 				sqlite3VdbeAddOp3(v, OP_Column, iIdxCur,
-						  pIdx->aiColumn[i], =
regTemp);
+						  part->fieldno, =
regTemp);
 				aGotoChng[i] =3D
 				    sqlite3VdbeAddOp4(v, OP_Ne, regTemp, =
0,
 						      regPrev + i, (char =
*)coll,
@@ -1006,7 +1004,8 @@ analyzeOneTable(Parse * pParse,	/* Parser =
context */
 			for (i =3D 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 =3D=3D (regStat4 + 2));
 		Index *pPk =3D sqlite3PrimaryKeyIndex(pIdx->pTable);
-		int j, k, regKeyStat;
-		int nPkColumn =3D (int)index_column_count(pPk);
-		regKeyStat =3D sqlite3GetTempRange(pParse, nPkColumn);
-		for (j =3D 0; j < nPkColumn; j++) {
-			k =3D pPk->aiColumn[j];
-			assert(k >=3D 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 =3D (int) =
pPk->def->key_def->part_count;
+		int regKeyStat =3D sqlite3GetTempRange(pParse, =
nPkColumn);
+		for (int j =3D 0; j < nPkColumn; ++j) {
+			int k =3D =
pPk->def->key_def->parts[j].fieldno;
+			assert(k >=3D 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 =3D pParse->nTab;
 	pParse->nTab +=3D 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 !=3D NULL)
+		index_def_delete(p->def);
 	sqlite3DbFree(db, p->zColAff);
 	sqlite3DbFree(db, p);
 }
@@ -271,7 +273,8 @@ sqlite3UnlinkAndDeleteIndex(sqlite3 * db, Index * =
pIndex)
=20
 	struct session *user_session =3D current_session();
=20
-	pIndex =3D sqlite3HashInsert(&pIndex->pTable->idxHash, =
pIndex->zName, 0);
+	pIndex =3D sqlite3HashInsert(&pIndex->pTable->idxHash,
+				   pIndex->def->name, 0);
 	if (ALWAYS(pIndex)) {
 		if (pIndex->pTable->pIndex =3D=3D pIndex) {
 			pIndex->pTable->pIndex =3D =
pIndex->pNext;
@@ -388,7 +391,7 @@ deleteTable(sqlite3 * db, Table * pTable)
 		pNext =3D pIndex->pNext;
 		assert(pIndex->pSchema =3D=3D pTable->pSchema);
 		if ((db =3D=3D 0 || db->pnBytesFreed =3D=3D 0)) {
-			char *zName =3D pIndex->zName;
+			char *zName =3D pIndex->def->name;
 			TESTONLY(Index *
 				 pOld =3D) =
sqlite3HashInsert(&pTable->idxHash,
 							   zName, 0);
@@ -1058,7 +1061,7 @@ sqlite3AddCollateType(Parse * pParse, Token * =
pToken)
 	Table *p =3D pParse->pNewTable;
 	if (p =3D=3D NULL)
 		return;
-	int i =3D p->def->field_count - 1;
+	uint32_t i =3D p->def->field_count - 1;
 	sqlite3 *db =3D pParse->db;
 	char *zColl =3D sqlite3NameFromToken(db, pToken);
 	if (!zColl)
@@ -1066,22 +1069,20 @@ sqlite3AddCollateType(Parse * pParse, Token * =
pToken)
 	uint32_t *id =3D &p->def->fields[i].coll_id;
 	p->aCol[i].coll =3D sql_get_coll_seq(pParse, zColl, id);
 	if (p->aCol[i].coll !=3D 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 =3D p->pIndex; pIdx; pIdx =3D =
pIdx->pNext) {
-			assert(pIdx->nColumn =3D=3D 1);
-			if (pIdx->aiColumn[0] =3D=3D i) {
-				id =3D &pIdx->coll_id_array[0];
-				pIdx->coll_array[0] =3D
+		for (struct Index *pIdx =3D p->pIndex; pIdx; pIdx =3D =
pIdx->pNext) {
+			assert(pIdx->def->key_def->part_count =
=3D=3D 1);
+			if =
(pIdx->def->key_def->parts[0].fieldno =3D=3D i) {
+				=
pIdx->def->key_def->parts[0].coll_id =3D *id;
+				=
pIdx->def->key_def->parts[0].coll =3D
 					sql_column_collation(p->def, =
i, id);
 			}
 		}
-	} else {
-		sqlite3DbFree(db, zColl);
 	}
+	sqlite3DbFree(db, zColl);
 }
=20
 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;
 }
=20
-struct key_def*
-sql_index_key_def(struct Index *idx)
-{
-	uint32_t space_id =3D SQLITE_PAGENO_TO_SPACEID(idx->tnum);
-	uint32_t index_id =3D SQLITE_PAGENO_TO_INDEXID(idx->tnum);
-	struct space *space =3D space_by_id(space_id);
-	assert(space !=3D NULL);
-	struct index *index =3D space_index(space, index_id);
-	assert(index !=3D NULL && index->def !=3D NULL);
-	return index->def->key_def;
-}
-
-struct coll *
-sql_index_collation(Index *idx, uint32_t column, uint32_t *coll_id)
-{
-	assert(idx !=3D NULL);
-	uint32_t space_id =3D =
SQLITE_PAGENO_TO_SPACEID(idx->pTable->tnum);
-	struct space *space =3D 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 =3D=3D NULL) {
-		assert(column < idx->nColumn);
-		*coll_id =3D idx->coll_id_array[column];
-		return idx->coll_array[column];
-	}
-
-	struct key_def *key_def =3D sql_index_key_def(idx);
-	assert(key_def !=3D NULL && key_def->part_count >=3D=
 column);
-	*coll_id =3D 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 !=3D NULL);
-	uint32_t space_id =3D =
SQLITE_PAGENO_TO_SPACEID(idx->pTable->tnum);
-	struct space *space =3D 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 =3D=3D NULL) {
-		assert(column < idx->nColumn);
-		return idx->sort_order[column];
-	}
-
-	struct key_def *key_def =3D sql_index_key_def(idx);
-	assert(key_def !=3D NULL && key_def->part_count >=3D=
 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;
 }
=20
-/* 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 =3D=3D *(aiCol++))
+	int i =3D 0;
+	while (i < nCol) {
+		if (fieldno =3D=3D key_parts->fieldno)
 			return 1;
+		key_parts++;
+		i++;
+	}
 	return 0;
 }
=20
@@ -1410,13 +1353,12 @@ static void
 convertToWithoutRowidTable(Parse * pParse, Table * pTab)
 {
 	Index *pPk;
-	int i, j;
 	sqlite3 *db =3D pParse->db;
=20
 	/* Mark every PRIMARY KEY column as NOT NULL (except for =
imposter tables)
 	 */
 	if (!db->init.imposterTable) {
-		for (i =3D 0; i < (int)pTab->def->field_count; =
i++) {
+		for (uint32_t i =3D 0; i < =
pTab->def->field_count; i++) {
 			if (pTab->aCol[i].is_primkey) {
 				=
pTab->def->fields[i].nullable_action
 					=3D 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 =3D j =3D 1; i < pPk->nColumn; i++) {
-			if (hasColumn(pPk->aiColumn, j, =
pPk->aiColumn[i])) {
-				pPk->nColumn--;
-			} else {
-				pPk->aiColumn[j++] =3D =
pPk->aiColumn[i];
+
+		struct key_part *parts =3D =
pPk->def->key_def->parts;
+		uint32_t part_count =3D =
pPk->def->key_def->part_count;
+		uint32_t new_part_count =3D part_count;
+
+		for (uint32_t i =3D 1; i < part_count; i++) {
+			if (hasColumn(parts, i, parts[i].fieldno)){
+				new_part_count--;
+				bool is_found =3D false;
+				for (uint32_t j =3D i + 1; j < =
part_count; j++){
+					if (!(hasColumn(parts, j,
+							=
parts[j].fieldno))) {
+						parts[i] =3D parts[j];
+						is_found =3D true;
+						break;
+					}
+				}
+				if (!(is_found))
+					break;
 			}
 		}
-		pPk->nColumn =3D j;
+		pPk->def->key_def->part_count =3D =
new_part_count;
 	}
 	assert(pPk !=3D 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,
=20
 	sqlite3VdbeAddOp4(v,
 			  OP_String8, 0, iFirstCol, 0,
-			  sqlite3DbStrDup(pParse->db, =
pIndex->zName),
+			  sqlite3DbStrDup(pParse->db, =
pIndex->def->name),
 			  P4_DYNAMIC);
=20
 	if (pParse->pNewTable) {
@@ -2652,14 +2608,15 @@ sqlite3RefillIndex(Parse * pParse, Index * =
pIndex, int memRootPage)
 	} else {
 		tnum =3D pIndex->tnum;
 	}
-	struct key_def *def =3D key_def_dup(sql_index_key_def(pIndex));
+	struct key_def *def =3D key_def_dup(pIndex->def->key_def);
 	if (def =3D=3D NULL) {
 		sqlite3OomFault(db);
 		return;
 	}
 	/* Open the sorter cursor if we are to use one. */
 	iSorter =3D 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);
=20
 	/* 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 =3D sqlite3VdbeCurrentAddr(v);
 		sqlite3VdbeAddOp4Int(v, OP_SorterCompare, iSorter, j2,
-				     regRecord, pIndex->nColumn);
+				     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 */
=20
 	nByte =3D 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 =3D sqlite3DbMallocZero(db, nByte + nExtra);
 	if (p) {
 		char *pExtra =3D ((char *)p) + ROUND8(sizeof(Index));
-		p->coll_array =3D (struct coll **)pExtra;
-		pExtra +=3D ROUND8(sizeof(struct coll **) * nCol);
-		p->coll_id_array =3D (uint32_t *) pExtra;
-		pExtra +=3D ROUND8(sizeof(uint32_t) * nCol);
 		p->aiRowLogEst =3D (LogEst *) pExtra;
 		pExtra +=3D sizeof(LogEst) * (nCol + 1);
-		p->aiColumn =3D (i16 *) pExtra;
 		pExtra +=3D sizeof(i16) * nCol;
-		p->sort_order =3D (enum sort_order *) pExtra;
-		p->nColumn =3D nCol;
 		*ppExtra =3D ((char *)p) + nByte;
 	}
 	return p;
@@ -2836,18 +2782,133 @@ addIndexToTable(Index * pIndex, Table * pTab)
 	}
 }
=20
-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 !=3D NULL);
-	uint32_t space_id =3D SQLITE_PAGENO_TO_SPACEID(idx->tnum);
-	uint32_t index_id =3D SQLITE_PAGENO_TO_INDEXID(idx->tnum);
-	struct space *space =3D space_by_id(space_id);
-	assert(space !=3D NULL);
-	struct index *tnt_index =3D space_index(space, index_id);
-	assert(tnt_index !=3D NULL);
+	char * str_part =3D region_alloc(r, strlen(str));
+	if (str_part =3D=3D NULL){
+		diag_set(OutOfMemory, strlen(str),
+			 "region_alloc", "str_part");
+		parse->rc =3D SQL_TARANTOOL_ERROR;
+		parse->nErr++;
+	}
+	memcpy(str_part, str, strlen(str));
+	*total_sql_size +=3D strlen(str);
+}
+
+void static
+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 =3D table->def;
+	struct index_opts opts;
+	index_opts_create(&opts);
+	opts.is_unique =3D on_error !=3D ON_CONFLICT_ACTION_NONE;
+
+	struct key_def *key_def =3D key_def_new(expr_list->nExpr);
+	if (key_def =3D=3D NULL) {
+		parse->rc =3D SQL_TARANTOOL_ERROR;
+		parse->nErr++;
+		return;
+	}
+
+	/*
+	 * Build initial parts of SQL statement.
+	 */
+
+	struct region *r =3D &fiber()->gc;
+	size_t total_sql_size =3D 0;
+
+	if (idx_type =3D=3D 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 =3D 0; i < expr_list->nExpr; i++) {
+		Expr *expr =3D expr_list->a[i].pExpr;
+		sql_resolve_self_reference(parse, table, NC_IdxExpr, =
expr, 0);
+		if (parse->nErr > 0)
+			return;
+
+		Expr *column_expr =3D sqlite3ExprSkipCollate(expr);
+		if (column_expr->op !=3D TK_COLUMN) {
+			sqlite3ErrorMsg(parse,
+					"functional indexes aren't =
supported "
+					"in the current version");
+			return;
+		}
+
+		uint32_t fieldno =3D column_expr->iColumn;
+		uint32_t coll_id;
+		struct coll *coll;
+		if (expr->op =3D=3D TK_COLLATE) {
+			coll =3D sql_get_coll_seq(parse, =
expr->u.zToken,
+						&coll_id);
+
+			if (idx_type =3D=3D 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 =3D =
expr->u.zToken;
+				append_string_part(r, coll_name,
+						   &total_sql_size, =
parse);
+				append_string_part(r, ", ",
+						   &total_sql_size, =
parse);
+			}
+		} else {
+			coll =3D sql_column_collation(space_def, =
fieldno,
+						    &coll_id);
+			if (idx_type =3D=3D 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);
+	}
=20
-	return tnt_index->def->opts.is_unique;
+	if (parse->nErr > 0) {
+		index->def =3D NULL;
+		return;
+	}
+
+	if (idx_type =3D=3D SQLITE_IDXTYPE_APPDEF) {
+		memcpy(region_alloc(r, 1), "\0", 1);
+		total_sql_size +=3D 1;
+		opts.sql =3D region_join(r, total_sql_size);
+
+		/*
+		 * fix last ", " with ")\0" to finish the statement.
+		 */
+		opts.sql[total_sql_size - 3] =3D ')';
+		opts.sql[total_sql_size - 2] =3D '\0';
+	}
+
+	struct key_def *pk_key_def;
+	if (idx_type =3D=3D SQLITE_IDXTYPE_APPDEF)
+		pk_key_def =3D table->pIndex->def->key_def;
+	else
+		pk_key_def =3D NULL;
+
+	index->def =3D index_def_new(space_def->id, iid, name, =
name_len,
+				   TREE, &opts, key_def, =
pk_key_def);
 }
=20
 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 =3D 0;	/* Table to be indexed */
-	Index *pIndex =3D 0;	/* The index to be created */
-	char *zName =3D 0;	/* Name of the index */
-	int nName;		/* Number of characters in zName */
-	int i, j;
+	Table *pTab =3D NULL;	/* Table to be indexed */
+	Index *pIndex =3D NULL;	/* The index to be created */
+	char *name =3D NULL;	/* Name of the index */
+	int name_len;		/* Number of characters in zName */
 	DbFixer sFix;		/* For assigning database names to =
pTable */
 	sqlite3 *db =3D parse->db;
-	struct ExprList_item *col_listItem;	/* For looping over =
col_list */
 	int nExtra =3D 0;		/* Space allocated for zExtra[] =
*/
-	char *zExtra =3D 0;	/* Extra space after the Index object */
+	char *zExtra =3D NULL;	/* Extra space after the Index object */
 	struct session *user_session =3D current_session();
=20
 	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 =3D sqlite3NameFromToken(db, token);
-		if (zName =3D=3D 0)
+		name =3D sqlite3NameFromToken(db, token);
+		if (name =3D=3D NULL)
 			goto exit_create_index;
 		assert(token->z !=3D 0);
 		if (!db->init.busy) {
-			if =
(sqlite3HashFind(&db->pSchema->tblHash, zName) !=3D
+			if =
(sqlite3HashFind(&db->pSchema->tblHash, name) !=3D
 			    NULL) {
 				sqlite3ErrorMsg(parse,
 						"there is already a =
table named %s",
-						zName);
+						name);
 				goto exit_create_index;
 			}
 		}
-		if (sqlite3HashFind(&pTab->idxHash, zName) !=3D =
NULL) {
+		if (sqlite3HashFind(&pTab->idxHash, name) !=3D =
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 =3D pTab->pIndex, n =3D 1; pLoop;
 		     pLoop =3D pLoop->pNext, n++) {
 		}
-		zName =3D
-		    sqlite3MPrintf(db, "sqlite_autoindex_%s_%d", =
pTab->def->name,
-				   n);
-		if (zName =3D=3D 0) {
+		name =3D sqlite3MPrintf(db, "sqlite_autoindex_%s_%d",
+				      pTab->def->name, n);
+		if (name =3D=3D NULL) {
 			goto exit_create_index;
 		}
 	}
@@ -2997,31 +3055,27 @@ sql_create_index(struct Parse *parse, struct =
Token *token,
 		sqlite3ExprListCheckLength(parse, col_list, "index");
 	}
=20
-	/* Figure out how many bytes of space are required to store =
explicitly
-	 * specified collation sequence names.
-	 */
-	for (i =3D 0; i < col_list->nExpr; i++) {
-		Expr *pExpr =3D col_list->a[i].pExpr;
-		assert(pExpr !=3D 0);
-		if (pExpr->op =3D=3D TK_COLLATE) {
-			nExtra +=3D (1 + =
sqlite3Strlen30(pExpr->u.zToken));
-		}
-	}
-
 	/*
 	 * Allocate the index structure.
 	 */
-	nName =3D sqlite3Strlen30(zName);
+	name_len =3D 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) !=3D SQLITE_OK)
+		goto exit_create_index;
+
 	pIndex =3D 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 =3D zExtra;
-	zExtra +=3D nName + 1;
-	memcpy(pIndex->zName, zName, nName + 1);
 	pIndex->pTable =3D pTab;
 	pIndex->onError =3D (u8) on_error;
 	/*
@@ -3036,7 +3090,6 @@ sql_create_index(struct Parse *parse, struct Token =
*token,
 		pIndex->idxType =3D idx_type;
 	}
 	pIndex->pSchema =3D db->pSchema;
-	pIndex->nColumn =3D 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 =3D NULL;
 	}
=20
-	/* 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 =3D 0, col_listItem =3D 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 =3D =
sqlite3ExprSkipCollate(col_listItem->pExpr);
-		if (pCExpr->op !=3D TK_COLUMN) {
-			sqlite3ErrorMsg(parse,
-					"functional indexes aren't =
supported "
-					"in the current version");
-			goto exit_create_index;
-		} else {
-			j =3D pCExpr->iColumn;
-			assert(j <=3D 0x7fff);
-			if (j < 0) {
-				j =3D pTab->iPKey;
-			}
-			pIndex->aiColumn[i] =3D (i16) j;
-		}
-		struct coll *coll;
-		uint32_t id;
-		if (col_listItem->pExpr->op =3D=3D TK_COLLATE) {
-			const char *coll_name =3D =
col_listItem->pExpr->u.zToken;
-			coll =3D sql_get_coll_seq(parse, coll_name, =
&id);
=20
-			if (coll =3D=3D NULL &&
-			    sqlite3StrICmp(coll_name, "binary") !=3D 0) =
{
-				goto exit_create_index;
-			}
-		} else if (j >=3D 0) {
-			coll =3D sql_column_collation(pTab->def, j, =
&id);
-		} else {
-			id =3D COLL_NONE;
-			coll =3D NULL;
-		}
-		pIndex->coll_array[i] =3D coll;
-		pIndex->coll_id_array[i] =3D id;
+	uint32_t max_iid =3D 0;
+	for (Index *index =3D pTab->pIndex; index; index =3D =
index->pNext) {
+		max_iid =3D max_iid > index->def->iid ?
+			  max_iid :
+			  index->def->iid + 1;
+	}
=20
-		/* Tarantool: DESC indexes are not supported so far.
-		 * See gh-3016.
-		 */
-		pIndex->sort_order[i] =3D SORT_ORDER_ASC;
+	set_index_def(parse, pIndex, pTab, max_iid, name, name_len, =
on_error,
+		      col_list, idx_type);
+
+	if (pIndex->def =3D=3D NULL ||
+	    !index_def_is_valid(pIndex->def, pTab->def->name)) =
{
+		goto exit_create_index;
 	}
+
 	if (pTab =3D=3D 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 =3D pTab->pIndex; pIdx; pIdx =3D =
pIdx->pNext) {
-			int k;
+			uint32_t k;
 			assert(IsUniqueIndex(pIdx));
 			assert(pIdx->idxType !=3D =
SQLITE_IDXTYPE_APPDEF);
 			assert(IsUniqueIndex(pIndex));
=20
-			if (pIdx->nColumn !=3D pIndex->nColumn)
+			if (pIdx->def->key_def->part_count !=3D
+			    pIndex->def->key_def->part_count) {
 				continue;
-			for (k =3D 0; k < pIdx->nColumn; k++) {
-				assert(pIdx->aiColumn[k] >=3D 0);
-				if (pIdx->aiColumn[k] !=3D =
pIndex->aiColumn[k])
+			}
+			for (k =3D 0; k < =
pIdx->def->key_def->part_count; k++) {
+				if =
(pIdx->def->key_def->parts[k].fieldno !=3D
+				    =
pIndex->def->key_def->parts[k].fieldno) {
 					break;
+				}
 				struct coll *coll1, *coll2;
-				uint32_t id;
-				coll1 =3D sql_index_collation(pIdx, k, =
&id);
-				coll2 =3D sql_index_collation(pIndex, k, =
&id);
+				coll1 =3D =
pIdx->def->key_def->parts[k].coll;
+				coll2 =3D =
pIndex->def->key_def->parts[k].coll;
 				if (coll1 !=3D coll2)
 					break;
 			}
-			if (k =3D=3D pIdx->nColumn) {
+			if (k =3D=3D =
pIdx->def->key_def->part_count) {
 				if (pIdx->onError !=3D =
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 =3D=3D 0);
 	if (db->init.busy) {
 		Index *p;
-		p =3D sqlite3HashInsert(&pTab->idxHash, =
pIndex->zName, pIndex);
+		p =3D sqlite3HashInsert(&pTab->idxHash, =
pIndex->def->name, pIndex);
 		if (p) {
 			assert(p =3D=3D 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 !=3D NULL);
-	uint32_t space_id =3D SQLITE_PAGENO_TO_SPACEID(idx->tnum);
-	struct space *space =3D space_by_id(space_id);
-	/* It is impossible to find an ephemeral space by id. */
-	if (space =3D=3D NULL)
-		return idx->nColumn;
-
-	uint32_t index_id =3D SQLITE_PAGENO_TO_INDEXID(idx->tnum);
-	struct index *index =3D space_index(space, index_id);
-	assert(index !=3D 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 !=3D NULL);
-	uint32_t space_id =3D SQLITE_PAGENO_TO_SPACEID(idx->tnum);
-	struct space *space =3D space_by_id(space_id);
-	assert(space !=3D NULL);
-
-	uint32_t index_id =3D SQLITE_PAGENO_TO_INDEXID(idx->tnum);
-	struct index *index =3D space_index(space, index_id);
-	assert(index !=3D NULL);
-	return (index->def->opts.is_unique &&
-		!index->def->key_def->is_nullable);
+	sqlite3DbFree(db, name);
 }
=20
 void
@@ -3938,18 +3923,19 @@ sqlite3UniqueConstraint(Parse * pParse,	/* =
Parsing context */
     )
 {
 	char *zErr;
-	int j;
+	uint32_t j;
 	StrAccum errMsg;
 	Table *pTab =3D pIdx->pTable;
=20
 	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 =3D 0; j < pIdx->nColumn; j++) {
+		struct key_part *part =3D =
pIdx->def->key_def->parts;
+		for (j =3D 0; j < =
pIdx->def->key_def->part_count; j++, part++) {
 			char *zCol;
-			assert(pIdx->aiColumn[j] >=3D 0);
-			zCol =3D =
pTab->def->fields[pIdx->aiColumn[j]].name;
+			uint32_t fieldno =3D part->fieldno;
+			zCol =3D 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 !=3D NULL);
-	for (int i =3D 0; i < index->nColumn; i++) {
-		uint32_t id;
-		struct coll *idx_coll =3D sql_index_collation(index, i, =
&id);
-		assert(idx_coll !=3D 0 || index->aiColumn[i] < 0);
-		if (index->aiColumn[i] >=3D 0 && coll =3D=3D=
 idx_coll)
+	struct key_part *part =3D index->def->key_def->parts;
+	for (uint32_t i =3D 0; i < =
index->def->key_def->part_count; i++, part++) {
+		struct coll *idx_coll =3D part->coll;
+		assert(idx_coll !=3D NULL);
+		if (coll =3D=3D 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 =3D sqlite3PrimaryKeyIndex(table);
 			assert(pk !=3D NULL);
-			pk_len =3D index_column_count(pk);
+			pk_len =3D =
pk->def->key_def->part_count;
 			parse->nMem +=3D 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,
=20
 		/* Extract the primary key for the current row */
 		if (!is_view) {
-			for (int i =3D 0; i < pk_len; i++) {
-				assert(pk->aiColumn[i] >=3D 0);
+			struct key_part *part =3D =
pk->def->key_def->parts;
+			for (int i =3D 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));
=20
 			if (one_pass =3D=3D ONEPASS_MULTI)
 				sqlite3VdbeJumpHere(v, iAddrOnce);
@@ -536,14 +535,14 @@ sql_generate_index_key(struct Parse *parse, struct =
Index *index, int cursor,
 			*part_idx_label =3D 0;
 		}
 	}
-	int col_cnt =3D index_column_count(index);
+	int col_cnt =3D index->def->key_def->part_count;
 	int reg_base =3D sqlite3GetTempRange(parse, col_cnt);
 	if (prev !=3D NULL && (reg_base !=3D reg_prev ||
 			     prev->pPartIdxWhere !=3D NULL))
 		prev =3D NULL;
 	for (int j =3D 0; j < col_cnt; j++) {
-		if (prev !=3D NULL && prev->aiColumn[j] =3D=3D =
index->aiColumn[j]
-		    && prev->aiColumn[j] !=3D XN_EXPR) {
+		if (prev->def->key_def->parts[j].fieldno =3D=3D
+		    index->def->key_def->parts[j].fieldno =
&& prev =3D=3D 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 =3D pIdx->pNext) {
 				Bitmask colUsed; /* Columns of the index =
used */
 				Bitmask mCol;	/* Mask for the current =
column */
-				if (pIdx->nColumn < nExpr)
+				uint32_t part_count =3D =
pIdx->def->key_def->
+					part_count;
+				struct key_part *parts =3D =
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 =3D=3D BMS - =
2);
-				testcase(pIdx->nColumn =3D=3D BMS - =
1);
-				if (pIdx->nColumn >=3D BMS - 1)
+				testcase(part_count =3D=3D BMS - 2);
+				testcase(>part_count =3D=3D BMS - 1);
+				if (part_count >=3D 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;
 					}
 				}
=20
@@ -2449,12 +2453,13 @@ sqlite3FindInIndex(Parse * pParse,	/* =
Parsing context */
 					int j;
=20
 					for (j =3D 0; j < nExpr; j++) =
{
-						if (pIdx->aiColumn[j] =
!=3D
-						    pRhs->iColumn) {
+						if ((int) =
parts[j].fieldno
+						    !=3D =
pRhs->iColumn) {
 							continue;
 						}
-						struct coll *idx_coll;
-						idx_coll =3D =
sql_index_collation(pIdx, j, &id);
+
+						struct coll *idx_coll =3D
+							     =
parts[j].coll;
 						if (pReq !=3D NULL =
&&
 						    pReq !=3D 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 =3D=3D
 					       IN_INDEX_INDEX_ASC + 1);
 					eType =3D IN_INDEX_INDEX_ASC +
-						=
sql_index_column_sort_order(pIdx,
-									 =
   0);
+						parts[0].sort_order;
=20
 					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 =3D sqlite3PrimaryKeyIndex(tab);
 		assert(pk);
=20
+		uint32_t fieldno =3D =
pk->def->key_def->parts[0].fieldno;
 		enum affinity_type affinity =3D
-			=
tab->def->fields[pk->aiColumn[0]].affinity;
-		if (pk->nColumn =3D=3D 1
+			tab->def->fields[fieldno].affinity;
+		if (pk->def->key_def->part_count =3D=3D 1
 		    && affinity =3D=3D AFFINITY_INTEGER
-		    && pk->aiColumn[0] < nVector) {
-			int reg_pk =3D rLhs + pk->aiColumn[0];
+		    && (int) fieldno < nVector) { int reg_pk =
=3D 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 =3D pIdx->aiColumn[iIdxCol];
-	if (iTabCol =3D=3D XN_EXPR) {
-		assert(pIdx->aColExpr);
-		assert(pIdx->aColExpr->nExpr > iIdxCol);
-		pParse->iSelfTab =3D iTabCur;
-		sqlite3ExprCodeCopy(pParse, =
pIdx->aColExpr->a[iIdxCol].pExpr,
-				    regOut);
-	} else {
-		sqlite3ExprCodeGetColumnOfTable(pParse->pVdbe, =
pIdx->pTable->def,
-						iTabCur, iTabCol, =
regOut);
-	}
+	i16 iTabCol =3D =
pIdx->def->key_def->parts[iIdxCol].fieldno;
+	sqlite3ExprCodeGetColumnOfTable(pParse->pVdbe, =
pIdx->pTable->def,
+					iTabCur, iTabCol, regOut);
 }
=20
 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 */
 	}
=20
 	for (pIdx =3D pParent->pIndex; pIdx; pIdx =3D pIdx->pNext) =
{
-		int nIdxCol =3D index_column_count(pIdx);
-		if (nIdxCol =3D=3D nCol && index_is_unique(pIdx)
+		int nIdxCol =3D pIdx->def->key_def->part_count;
+		if (nIdxCol =3D=3D nCol && =
pIdx->def->opts.is_unique
 		    && pIdx->pPartIdxWhere =3D=3D 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 =3D 0; i < nCol; i++) {
-					i16 iCol =3D =
pIdx->aiColumn[i];	/* Index of column in parent tbl */
+				struct key_part *part =3D
+					=
pIdx->def->key_def->parts;
+				for (i =3D 0; i < nCol; i++, part++) =
{
+					i16 iCol =3D (int) =
part->fieldno;	/* Index of column in parent tbl */
 					char *zIdxCol;	/* Name of =
indexed column */
=20
 					if (iCol < 0)
@@ -302,9 +304,7 @@ sqlite3FkLocateIndex(Parse * pParse,	/* Parse =
context to store any error in */
 					def_coll =3D =
sql_column_collation(pParent->def,
 									=
iCol,
 									=
&id);
-					struct coll *coll =3D
-						=
sql_index_collation(pIdx, i,
-								    =
&id);
+					struct coll *coll =3D =
part->coll;
 					if (def_coll !=3D coll)
 						break;
=20
@@ -464,13 +464,15 @@ fkLookupParent(Parse * pParse,	/* Parse context =
*/
 				for (i =3D 0; i < nCol; i++) {
 					int iChild =3D aiCol[i] + 1 + =
regData;
 					int iParent =3D
-					    pIdx->aiColumn[i] + 1 + =
regData;
-					assert(pIdx->aiColumn[i] =
>=3D 0);
+						(int) =
pIdx->def->key_def->parts[i].fieldno
+						+ 1 + regData;
 					assert(aiCol[i] !=3D =
pTab->iPKey);
-					if (pIdx->aiColumn[i] =3D=3D =
pTab->iPKey) {
+					if =
((int)pIdx->def->key_def->
+						parts[i].fieldno =3D=3D =
pTab->iPKey) {
 						/* The parent key is a =
composite key that includes the IPK column */
 						iParent =3D regData;
 					}
+
 					sqlite3VdbeAddOp3(v, OP_Ne, =
iChild,
 							  iJump, =
iParent);
 					VdbeCoverage(v);
@@ -622,7 +624,7 @@ fkScanChildren(Parse * pParse,	/* Parse context =
*/
 	Vdbe *v =3D sqlite3GetVdbe(pParse);
=20
 	assert(pIdx =3D=3D 0 || pIdx->pTable =3D=3D pTab);
-	assert(pIdx =3D=3D 0 || (int)index_column_count(pIdx) =3D=3D =
pFKey->nCol);
+	assert(pIdx =3D=3D 0 || (int) =
pIdx->def->key_def->part_count =3D=3D pFKey->nCol);
 	assert(pIdx !=3D 0);
=20
 	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 =
*/
=20
-		iCol =3D pIdx ? pIdx->aiColumn[i] : -1;
+		iCol =3D pIdx ? =
pIdx->def->key_def->parts[i].fieldno : -1;
 		pLeft =3D exprTableRegister(pParse, pTab, regData, =
iCol);
 		iCol =3D aiCol ? aiCol[i] : pFKey->aCol[0].iFrom;
 		assert(iCol >=3D 0);
@@ -671,10 +673,9 @@ fkScanChildren(Parse * pParse,	/* Parse context =
*/
 		Expr *pEq, *pAll =3D 0;
 		Index *pPk =3D sqlite3PrimaryKeyIndex(pTab);
 		assert(pIdx !=3D 0);
-		int col_count =3D index_column_count(pPk);
+		int col_count =3D =
pPk->def->key_def->part_count;
 		for (i =3D 0; i < col_count; i++) {
-			i16 iCol =3D pIdx->aiColumn[i];
-			assert(iCol >=3D 0);
+			i16 iCol =3D (int) =
pIdx->def->key_def->parts[i].fieldno;
 			pLeft =3D exprTableRegister(pParse, pTab, =
regData, iCol);
 			pRight =3D
 				exprTableColumn(db, pTab->def,
@@ -992,7 +993,6 @@ sqlite3FkCheck(Parse * pParse,	/* Parse context =
*/
 			if (aiCol[i] =3D=3D pTab->iPKey) {
 				aiCol[i] =3D -1;
 			}
-			assert(pIdx =3D=3D 0 || pIdx->aiColumn[i] =
>=3D 0);
 		}
=20
 		pParse->nTab++;
@@ -1126,10 +1126,10 @@ sqlite3FkOldmask(Parse * pParse,	/* Parse =
context */
 			Index *pIdx =3D 0;
 			sqlite3FkLocateIndex(pParse, pTab, p, &pIdx, =
0);
 			if (pIdx) {
-				int nIdxCol =3D =
index_column_count(pIdx);
+				int nIdxCol =3D =
pIdx->def->key_def->part_count;
 				for (i =3D 0; i < nIdxCol; i++) {
-					assert(pIdx->aiColumn[i] =
>=3D 0);
-					mask |=3D =
COLUMN_MASK(pIdx->aiColumn[i]);
+					mask |=3D =
COLUMN_MASK(pIdx->def->
+						=
key_def->parts[i].fieldno);
 				}
 			}
 		}
@@ -1264,11 +1264,12 @@ fkActionTrigger(Parse * pParse,	/* Parse context =
*/
 			       || (pTab->iPKey >=3D 0
 				   && pTab->iPKey <
 				      =
(int)pTab->def->field_count));
-			assert(pIdx =3D=3D 0 || pIdx->aiColumn[i] =
>=3D 0);
+
+			uint32_t fieldno =3D pIdx !=3D NULL ?
+					     =
pIdx->def->key_def->parts[i].fieldno
+					   : pTab->iPKey;
 			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 =3D index_column_count(pIdx);
+		int nColumn =3D pIdx->def->key_def->part_count;
 		pIdx->zColAff =3D
 		    (char *)sqlite3DbMallocRaw(0, nColumn + 1);
 		if (!pIdx->zColAff) {
@@ -97,22 +97,8 @@ sqlite3IndexAffinityStr(sqlite3 * db, Index * pIdx)
 			return 0;
 		}
 		for (n =3D 0; n < nColumn; n++) {
-			i16 x =3D pIdx->aiColumn[n];
-			if (x >=3D 0) {
-				char affinity =3D pIdx->pTable->
-					def->fields[x].affinity;
-				pIdx->zColAff[n] =3D affinity;
-			} else {
-				char aff;
-				assert(x =3D=3D XN_EXPR);
-				assert(pIdx->aColExpr !=3D 0);
-				aff =3D
-				    =
sqlite3ExprAffinity(pIdx->aColExpr->a[n].
-							pExpr);
-				if (aff =3D=3D 0)
-					aff =3D AFFINITY_BLOB;
-				pIdx->zColAff[n] =3D aff;
-			}
+			i16 x =3D =
pIdx->def->key_def->parts[n].fieldno;
+			pIdx->zColAff[n] =3D =
pIdx->pTable->def->fields[x].affinity;
 		}
 		pIdx->zColAff[n] =3D 0;
 	}
@@ -645,7 +631,7 @@ sqlite3Insert(Parse * pParse,	/* Parser =
context */
 		     pIdx =3D pIdx->pNext, i++) {
 			assert(pIdx);
 			aRegIdx[i] =3D ++pParse->nMem;
-			pParse->nMem +=3D index_column_count(pIdx);
+			pParse->nMem +=3D =
pIdx->def->key_def->part_count;
 		}
 	}
=20
@@ -1088,7 +1074,7 @@ sqlite3GenerateConstraintChecks(Parse * pParse,		=
/* The parser context */
 	nCol =3D pTab->def->field_count;
=20
 	pPk =3D sqlite3PrimaryKeyIndex(pTab);
-	nPkField =3D index_column_count(pPk);
+	nPkField =3D pPk->def->key_def->part_count;
=20
 	/* 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 =3D aRegIdx[ix] + 1;
-		int nIdxCol =3D (int)index_column_count(pIdx);
+		int nIdxCol =3D pIdx->def->key_def->part_count;
 		for (i =3D 0; i < nIdxCol; i++) {
-			int iField =3D pIdx->aiColumn[i];
+			int iField =3D (int) =
pIdx->def->key_def->parts[i].fieldno;
 			int x;
-			if (iField =3D=3D XN_EXPR) {
-				pParse->ckBase =3D regNewData + 1;
-				sqlite3ExprCodeCopy(pParse,
-						    =
pIdx->aColExpr->a[i].pExpr,
-						    regIdx + i);
-				pParse->ckBase =3D 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 =3D=3D =
pTab->iPKey)
-						x =3D regNewData;
-					else
-						x =3D iField + =
regNewData + 1;
-
-					assert(iField >=3D 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 =3D=3D pTab->iPKey)
+					x =3D regNewData;
+				else
+					x =3D iField + regNewData + 1;
+
+				assert(iField >=3D 0);
+				sqlite3VdbeAddOp2(v, OP_SCopy,
+						  x, regIdx + i);
+				VdbeComment((v, "%s",
+					     =
pTab->def->fields[iField].name));
 			}
 		}
=20
@@ -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 =3D=3D 1) {
-				reg_pk =3D regNewData + 1 + =
pIdx->aiColumn[0];
-				if =
(pTab->zColAff[pIdx->aiColumn[0]] =3D=3D
+				reg_pk =3D regNewData + 1 +
+					=
pIdx->def->key_def->parts[0].fieldno;
+
+				int fieldno =3D =
(int)pIdx->def->key_def->
+					parts[0].fieldno;
+				if (pTab->zColAff[fieldno] =3D=3D
 				    AFFINITY_INTEGER) {
 					int skip_if_null =3D =
sqlite3VdbeMakeLabel(v);
 					if ((pTab->tabFlags & =
TF_Autoincrement) !=3D 0) {
@@ -1311,8 +1290,8 @@ sqlite3GenerateConstraintChecks(Parse * pParse,		=
/* The parser context */
 			}
=20
 			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));
 		}
=20
 		/* 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);
=20
@@ -1410,19 +1389,17 @@ sqlite3GenerateConstraintChecks(Parse * pParse,		=
/* The parser context */
 								 =
nPkField);
 		if (isUpdate || on_error =3D=3D =
ON_CONFLICT_ACTION_REPLACE) {
 			int x;
-			int nPkCol =3D index_column_count(pPk);
+			int nPkCol =3D =
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 !=3D pPk) {
 				for (i =3D 0; i < nPkCol; i++) {
-					assert(pPk->aiColumn[i] >=3D=
 0);
-					x =3D pPk->aiColumn[i];
+					x =3D =
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);
=20
 				for (i =3D 0; i < nPkCol; i++) {
-					uint32_t id;
-					char *p4 =3D (char =
*)sql_index_collation(pPk, i, &id);
-					x =3D pPk->aiColumn[i];
-					assert(x >=3D 0);
+					char *p4 =3D (char *) =
pPk->def->key_def->parts[i].coll;
+					x =3D =
pPk->def->key_def->parts[i].fieldno;
+					if (pPk->tnum=3D=3D0) {
+						x =3D -1;
+					}
 					if (i =3D=3D (nPkCol - 1)) {
 						addrJump =3D =
addrUniqueOk;
 						op =3D 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 =
!=3D
-		     ON_CONFLICT_ACTION_DEFAULT &&
+		    (pIdx->def->opts.is_unique &&
+		     pIdx->onError !=3D ON_CONFLICT_ACTION_DEFAULT =
&&
 		     /* Condition 4.1 */
 		     pIdx->onError !=3D 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 !=3D pSrc->pTable);
-	uint32_t nDestCol =3D index_column_count(pDest);
-	uint32_t nSrcCol =3D index_column_count(pSrc);
+	uint32_t nDestCol =3D pDest->def->key_def->part_count;
+	uint32_t nSrcCol =3D pSrc->def->key_def->part_count;
 	if (nDestCol !=3D nSrcCol) {
 		return 0;	/* Different number of columns */
 	}
 	if (pDest->onError !=3D pSrc->onError) {
 		return 0;	/* Different conflict resolution =
strategies */
 	}
-	for (i =3D 0; i < nSrcCol; i++) {
-		if (pSrc->aiColumn[i] !=3D pDest->aiColumn[i]) {
+	struct key_part *src_part =3D =
pSrc->def->key_def->parts;
+	struct key_part *dest_part =3D =
pDest->def->key_def->parts;
+	for (i =3D 0; i < nSrcCol; i++, src_part++, dest_part++) {
+		if (src_part->fieldno !=3D dest_part->fieldno)
 			return 0;	/* Different columns indexed */
-		}
-		if (pSrc->aiColumn[i] =3D=3D XN_EXPR) {
-			assert(pSrc->aColExpr !=3D 0 && =
pDest->aColExpr !=3D 0);
-			if =
(sqlite3ExprCompare(pSrc->aColExpr->a[i].pExpr,
-					       =
pDest->aColExpr->a[i].pExpr,
-					       -1) !=3D 0) {
-				return 0;	/* Different expressions =
in the index */
-			}
-		}
-		if (sql_index_column_sort_order(pSrc, i) !=3D
-		    sql_index_column_sort_order(pDest, i)) {
+		if (src_part->sort_order !=3D =
dest_part->sort_order)
 			return 0;	/* Different sort orders */
-		}
-		uint32_t id;
-		if (sql_index_collation(pSrc, i, &id) !=3D
-		    sql_index_collation(pDest, i, &id)) {
+		if (src_part->coll !=3D 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 =3D pDest->pIndex; pDestIdx; pDestIdx =3D =
pDestIdx->pNext) {
-		if (index_is_unique(pDestIdx)) {
+		if (pDestIdx->def->opts.is_unique)
 			destHasUniqueIdx =3D 1;
-		}
 		for (pSrcIdx =3D pSrc->pIndex; pSrcIdx; pSrcIdx =3D =
pSrcIdx->pNext) {
 			if (xferCompatibleIndex(pDestIdx, pSrcIdx))
 				break;
 		}
-		if (pSrcIdx =3D=3D 0) {
+		if (pSrcIdx =3D=3D 0)
 			return 0;	/* pDestIdx has no corresponding =
index in pSrc */
-		}
 	}
 	/* Get server checks. */
 	ExprList *pCheck_src =3D 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 =3D 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 =3D 1;
 						     k <=3D
 						     =
(int)pTab->def->field_count
-						     && =
pPk->aiColumn[k - 1] !=3D
-						     i; k++) {
+						     && (int) =
pPk->def->
+							     =
key_def->
+							     parts[k - =
1].
+							     fieldno !=3D =
i;
+						     k++) {
 						}
 					}
 					bool nullable =3D
@@ -430,7 +433,7 @@ sqlite3Pragma(Parse * pParse, Token * pId,	/* First =
part of [schema.]id field */
 					size_t avg_tuple_size_idx =3D
 						=
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 =3D 3;
 					}
-					mx =3D index_column_count(pIdx);
+					mx =3D =
pIdx->def->key_def->part_count;
 					assert(pParse->nMem <=3D
 					       pPragma->nPragCName);
-					for (i =3D 0; i < mx; i++) {
-						i16 cnum =3D =
pIdx->aiColumn[i];
+					struct key_part *part =3D
+						=
pIdx->def->key_def->parts;
+					for (i =3D 0; i < mx; i++, =
part++) {
+						i16 cnum =3D (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 =3D
+								=
part->coll_id;
 							struct coll =
*coll =3D
-								=
sql_index_collation(pIdx, i, &id);
+								=
part->coll;
 							if (coll !=3D =
NULL)
 								c_n =3D =
coll_by_id(id)->name;
 							else
 								c_n =3D =
"BINARY";
-							enum sort_order =
sort_order;
-							sort_order =3D =
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 =3D pFrom->u1.zIndexedBy;
 		Index *pIdx;
 		for (pIdx =3D pTab->pIndex;
-		     pIdx && strcmp(pIdx->zName, zIndexedBy);
+		     pIdx && strcmp(pIdx->def->name, =
zIndexedBy);
 		     pIdx =3D 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=3D=3D3 because there are
- * three columns in the table.  In the Index structure describing
- * Ex2, nColumn=3D=3D2 since 2 of the 3 columns of Ex1 are indexed.
- * The value of aiColumn is {2, 0}.  aiColumn[0]=3D=3D2 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]=3D=3D0.
- *
  * 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=3D
  * 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;	/* =46rom 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=3D=3DUNIQUE, 2=3D=3DPRIMARY KEY, =
0=3D=3DCREATE INDEX */
+	struct index_def *def;
 };
=20
 /**
@@ -2161,11 +2138,6 @@ index_field_tuple_est(struct Index *idx, uint32_t =
field);
 #define IsUniqueIndex(X)      (((X)->idxType =3D=3D =
SQLITE_IDXTYPE_UNIQUE) || \
 				((X)->idxType =3D=3D =
SQLITE_IDXTYPE_PRIMARYKEY))
=20
-/* 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 *);
=20
-/**
- * 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 *);
=20
 /**
  * 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);
=20
-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 =3D pTop;
 	pSubParse->eTriggerOp =3D pTrigger->op;
 	pSubParse->nQueryLoop =3D pParse->nQueryLoop;
-	struct region *region =3D &fiber()->gc;
-	pSubParse->region_initial_size =3D region_used(region);
=20
 	v =3D 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 =3D 0, pIdx =3D pTab->pIndex; pIdx; pIdx =3D =
pIdx->pNext, j++) {
 		int reg;
-		int nIdxCol =3D index_column_count(pIdx);
+		int nIdxCol =3D pIdx->def->key_def->part_count;
 		if (chngPk || hasFK || pIdx->pPartIdxWhere || pIdx =3D=3D=
 pPk) {
 			reg =3D ++pParse->nMem;
 			pParse->nMem +=3D nIdxCol;
 		} else {
 			reg =3D 0;
 			for (i =3D 0; i < nIdxCol; i++) {
-				i16 iIdxCol =3D pIdx->aiColumn[i];
+				i16 iIdxCol =3D =
pIdx->def->key_def->parts[i].fieldno;
 				if (iIdxCol < 0 || aXRef[iIdxCol] =
>=3D 0) {
 					reg =3D ++pParse->nMem;
 					pParse->nMem +=3D nIdxCol;
@@ -306,7 +306,7 @@ sqlite3Update(Parse * pParse,		/* The =
parser context */
 		nPk =3D nKey;
 	} else {
 		assert(pPk !=3D 0);
-		nPk =3D index_column_count(pPk);
+		nPk =3D pPk->def->key_def->part_count;
 	}
 	iPk =3D pParse->nMem + 1;
 	pParse->nMem +=3D nPk;
@@ -333,9 +333,9 @@ sqlite3Update(Parse * pParse,		/* The =
parser context */
 		}
 	} else {
 		for (i =3D 0; i < nPk; i++) {
-			assert(pPk->aiColumn[i] >=3D 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 =3D parse->pVdbe;
 	assert(v !=3D NULL);
 	assert(idx !=3D NULL);
-	struct key_def *def =3D key_def_dup(sql_index_key_def(idx));
+	struct key_def *def =3D key_def_dup(idx->def->key_def);
 	if (def =3D=3D 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 =3D p->pIdx;	/* Index being =
probed */
 			int nByte;	/* Bytes of space to allocate */
 			int i;	/* Counter variable */
-			int nCol =3D index_column_count(pIdx);
+			int nCol =3D =
pIdx->def->key_def->part_count;
=20
 			nByte =3D sizeof(Mem) * nCol +
 				ROUND8(sizeof(UnpackedRecord));
@@ -1095,7 +1095,7 @@ valueNew(sqlite3 * db, struct ValueNewStat4Ctx *p)
 			    (UnpackedRecord *) sqlite3DbMallocZero(db, =
nByte);
 			if (pRec =3D=3D NULL)
 				return NULL;
-			pRec->key_def =3D =
key_def_dup(sql_index_key_def(pIdx));
+			pRec->key_def =3D =
key_def_dup(pIdx->def->key_def);
 			if (pRec->key_def =3D=3D 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 =3D pWC->a + k; k < =
pWC->nTerm; k++, pTerm++) {
 				if (pTerm->leftCursor =3D=3D iCur
 				    && pTerm->u.leftColumn =3D=3D=
 iColumn
-				    && (iColumn !=3D XN_EXPR
-					|| =
sqlite3ExprCompare(pTerm->pExpr->
-							      pLeft,
-							      =
pScan->pIdxExpr,
-							      iCur) =3D=3D=
 0)
 				    && (pScan->iEquiv <=3D =
1
 					|| =
!ExprHasProperty(pTerm->pExpr,
 							    =
EP_FromJoin))
@@ -376,19 +371,21 @@ whereScanInit(WhereScan * pScan,	/* The WhereScan =
object being initialized */
 	pScan->is_column_seen =3D false;
 	if (pIdx) {
 		int j =3D iColumn;
-		iColumn =3D pIdx->aiColumn[j];
-		if (iColumn =3D=3D XN_EXPR) {
-			pScan->pIdxExpr =3D =
pIdx->aColExpr->a[j].pExpr;
-		} else if (iColumn >=3D 0) {
+		iColumn =3D =
pIdx->def->key_def->parts[j].fieldno;
+		/*
+		 * pIdx->tnum =3D=3D 0 means that pIdx is a fake =
integer
+		 * primary key index
+		 */
+		if (pIdx->tnum =3D=3D 0)
+			iColumn =3D -1;
+
+		if (iColumn >=3D 0) {
 			char affinity =3D
 				=
pIdx->pTable->def->fields[iColumn].affinity;
 			pScan->idxaff =3D affinity;
-			uint32_t id;
-			pScan->coll =3D sql_index_collation(pIdx, j, =
&id);
+			pScan->coll =3D =
pIdx->def->key_def->parts[j].coll;
 			pScan->is_column_seen =3D true;
 		}
-	} else if (iColumn =3D=3D XN_EXPR) {
-		return 0;
 	}
 	pScan->opMask =3D opMask;
 	pScan->k =3D 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 =3D =
&pIdx->def->key_def->parts[iCol];
 	for (int i =3D 0; i < pList->nExpr; i++) {
 		Expr *p =3D =
sqlite3ExprSkipCollate(pList->a[i].pExpr);
-		if (p->op =3D=3D TK_COLUMN &&
-		    p->iColumn =3D=3D pIdx->aiColumn[iCol] =
&&
-		    p->iTable =3D=3D iBase) {
+		if (p->op =3D=3D TK_COLUMN && p->iTable =3D=3D=
 iBase &&
+		    p->iColumn =3D=3D (int) =
part_to_match->fieldno) {
 			bool is_found;
 			uint32_t id;
 			struct coll *coll =3D sql_expr_coll(pParse,
 							  =
pList->a[i].pExpr,
 							  &is_found, =
&id);
-			if (is_found &&
-			    coll =3D=3D sql_index_collation(pIdx, iCol, =
&id)) {
+			if (is_found && coll =3D=3D =
part_to_match->coll) {
 				return i;
 			}
 		}
@@ -484,27 +480,6 @@ findIndexCol(Parse * pParse,	/* Parse context =
*/
 	return -1;
 }
=20
-/*
- * Return TRUE if the iCol-th column of index pIdx is NOT NULL
- */
-static int
-indexColumnNotNull(Index * pIdx, int iCol)
-{
-	int j;
-	assert(pIdx !=3D 0);
-	assert(iCol >=3D 0 && iCol < =
(int)index_column_count(pIdx));
-	j =3D pIdx->aiColumn[iCol];
-	if (j >=3D 0) {
-		return =
!pIdx->pTable->def->fields[j].is_nullable;
-	} else if (j =3D=3D (-1)) {
-		return 1;
-	} else {
-		assert(j =3D=3D (-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=3DX" term are subject to a NOT NULL =
constraint.
 	 */
 	for (pIdx =3D pTab->pIndex; pIdx; pIdx =3D pIdx->pNext) {
-		if (!index_is_unique(pIdx))
+		if (!pIdx->def->opts.is_unique)
 			continue;
-		int col_count =3D index_column_count(pIdx);
+		int col_count =3D =
pIdx->def->key_def->part_count;
 		for (i =3D 0; i < col_count; i++) {
 			if (0 =3D=3D
 			    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) =3D=3D =
0)
+				uint32_t j =3D =
pIdx->def->key_def->parts[i].fieldno;
+				if =
(!pIdx->pTable->def->fields[j].is_nullable =3D=3D 0)
 					break;
 			}
 		}
-		if (i =3D=3D (int)index_column_count(pIdx)) {
+		if (i =3D=3D (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 >=3D 0 && iCol < =
(int)index_column_count(pIdx));
+	assert(iCol >=3D 0 && iCol < (int) =
pIdx->def->key_def->part_count);
 	if (!pIdx->zColAff) {
 		if (sqlite3IndexAffinityStr(db, pIdx) =3D=3D 0)
 			return AFFINITY_BLOB;
@@ -1169,13 +1145,12 @@ whereRangeSkipScanEst(Parse * pParse,		=
/* Parsing & code generating context */
 	int nUpper =3D index->def->opts.stat->sample_count + 1;
 	int rc =3D SQLITE_OK;
 	u8 aff =3D sqlite3IndexColumnAffinity(db, p, nEq);
-	uint32_t id;
=20
 	sqlite3_value *p1 =3D 0;	/* Value extracted from pLower =
*/
 	sqlite3_value *p2 =3D 0;	/* Value extracted from pUpper =
*/
 	sqlite3_value *pVal =3D 0;	/* Value extracted from record =
*/
=20
-	struct coll *pColl =3D sql_index_collation(p, nEq, &id);
+	struct coll *pColl =3D =
p->def->key_def->parts[nEq].coll;
 	if (pLower) {
 		rc =3D sqlite3Stat4ValueFromExpr(pParse, =
pLower->pExpr->pRight,
 					       aff, &p1);
@@ -1371,7 +1346,7 @@ whereRangeScanEst(Parse * pParse,	/* Parsing & =
code generating context */
 			       || (pLower->eOperator & (WO_GT | =
WO_GE)) !=3D 0);
 			assert(pUpper =3D=3D 0
 			       || (pUpper->eOperator & (WO_LT | =
WO_LE)) !=3D 0);
-			if (sql_index_column_sort_order(p, nEq) !=3D
+			if =
(p->def->key_def->parts[nEq].sort_order !=3D
 			    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;
=20
 	assert(nEq >=3D 1);
-	assert(nEq <=3D (int)index_column_count(p));
+	assert(nEq <=3D (int) p->def->key_def->part_count);
 	assert(pBuilder->nRecValid < nEq);
=20
 	/* 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 */
=20
 	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 =3D a[1];
=20
 	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 =3D p->pIndex->zName) =
!=3D 0) {
+	if (p->pIndex && (zName =3D =
p->pIndex->def->name) !=3D 0) {
 		if (strncmp(zName, "sqlite_autoindex_", 17) =3D=3D 0) {
 			int i =3D sqlite3Strlen30(zName) - 1;
 			while (zName[i] !=3D '_')
@@ -2236,7 +2211,7 @@ whereRangeVectorLen(Parse * pParse,	/* =
Parsing context */
 	int nCmp =3D sqlite3ExprVectorSize(pTerm->pExpr->pLeft);
 	int i;
=20
-	nCmp =3D MIN(nCmp, (int)(index_column_count(pIdx) - nEq));
+	nCmp =3D MIN(nCmp, (int)(pIdx->def->key_def->part_count =
- nEq));
 	for (i =3D 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 !=3D TK_COLUMN
-		    || pLhs->iTable !=3D iCur
-		    || pLhs->iColumn !=3D pIdx->aiColumn[i + nEq]
-		    || sql_index_column_sort_order(pIdx, i + nEq) !=3D
-		       sql_index_column_sort_order(pIdx, nEq)) {
+		if (pLhs->op !=3D TK_COLUMN || pLhs->iTable !=3D =
iCur
+		    || pLhs->iColumn !=3D =
(int)pIdx->def->key_def->parts[i + nEq].fieldno
+		    || pIdx->def->key_def->parts[i + =
nEq].sort_order !=3D
+		       =
pIdx->def->key_def->parts[nEq].sort_order) {
 			break;
 		}
=20
@@ -2275,7 +2249,7 @@ whereRangeVectorLen(Parse * pParse,	/* =
Parsing context */
 		pColl =3D sql_binary_compare_coll_seq(pParse, pLhs, =
pRhs, &id);
 		if (pColl =3D=3D 0)
 			break;
-	        if (sql_index_collation(pIdx, i + nEq, &id) !=3D =
pColl)
+		if (pIdx->def->key_def->parts[(i + nEq)].coll =
!=3D 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 =3D 0, *pBtm =3D 0;	/* Top and bottom range =
constraints */
-	uint32_t nProbeCol =3D index_column_count(pProbe);
+	uint32_t nProbeCol =3D =
pProbe->def->key_def->part_count;
=20
 	pNew =3D pBuilder->pNew;
 	if (db->mallocFailed)
 		return SQLITE_NOMEM_BKPT;
 	WHERETRACE(0x800, ("BEGIN addBtreeIdx(%s), nEq=3D%d\n",
-			   pProbe->zName, pNew->nEq));
+			   pProbe->def->name, pNew->nEq));
=20
 	assert((pNew->wsFlags & WHERE_TOP_LIMIT) =3D=3D 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 =3D 0;
 		int nRecValid =3D pBuilder->nRecValid;
+		uint32_t j =3D =
pProbe->def->key_def->parts[saved_nEq].fieldno;
 		if ((eOp =3D=3D WO_ISNULL || (pTerm->wtFlags & =
TERM_VNULL) !=3D 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 =3D pProbe->aiColumn[saved_nEq];
+			int iCol =3D =
pProbe->def->key_def->parts[saved_nEq].fieldno;
 			pNew->wsFlags |=3D WHERE_COLUMN_EQ;
 			assert(saved_nEq =3D=3D pNew->nEq);
-			if ((iCol > 0 && nInMul =3D=3D 0
-				&& saved_nEq =3D=3D nProbeCol - =
1)
-			    ) {
-				if (iCol >=3D 0 &&
-				    !index_is_unique_not_null(pProbe)) {
+			if ((iCol > 0 && nInMul =3D=3D 0 =
&&
+			     saved_nEq =3D=3D nProbeCol - 1)) {
+				bool index_is_unique_not_null =3D
+					=
pProbe->def->key_def->is_nullable &&
+					=
pProbe->def->opts.is_unique;
+				if (pProbe->tnum !=3D 0 &&
+				    !index_is_unique_not_null) {
 					pNew->wsFlags |=3D =
WHERE_UNQ_WANTED;
 				} else {
 					pNew->wsFlags |=3D =
WHERE_ONEROW;
@@ -2514,8 +2491,7 @@ whereLoopAddBtreeIndex(WhereLoopBuilder * =
pBuilder,	/* The WhereLoop factory */
 			assert(eOp & (WO_ISNULL | WO_EQ | WO_IN));
=20
 			assert(pNew->nOut =3D=3D saved_nOut);
-			if (pTerm->truthProb <=3D 0
-			    && pProbe->aiColumn[saved_nEq] =
>=3D 0) {
+			if (pTerm->truthProb <=3D 0 && =
pProbe->tnum !=3D 0 ) {
 				assert((eOp & WO_IN) || nIn =3D=3D =
0);
 				testcase(eOp & WO_IN);
 				pNew->nOut +=3D pTerm->truthProb;
@@ -2671,7 +2647,7 @@ whereLoopAddBtreeIndex(WhereLoopBuilder * =
pBuilder,	/* The WhereLoop factory */
 	}
=20
 	WHERETRACE(0x800, ("END addBtreeIdx(%s), nEq=3D%d, rc=3D%d\n",
-			   pProbe->zName, saved_nEq, rc));
+			   pProbe->def->name, saved_nEq, rc));
 	return rc;
 }
=20
@@ -2715,7 +2691,7 @@ indexMightHelpWithOrderBy(WhereLoopBuilder * =
pBuilder,
 	ExprList *pOB;
 	ExprList *aColExpr;
 	int ii, jj;
-	int nIdxCol =3D index_column_count(pIndex);
+	int nIdxCol =3D pIndex->def->key_def->part_count;
 	if (index_is_unordered(pIndex))
 		return 0;
 	if ((pOB =3D pBuilder->pWInfo->pOrderBy) =3D=3D 0)
@@ -2726,13 +2702,12 @@ indexMightHelpWithOrderBy(WhereLoopBuilder * =
pBuilder,
 			if (pExpr->iColumn < 0)
 				return 1;
 			for (jj =3D 0; jj < nIdxCol; jj++) {
-				if (pExpr->iColumn =3D=3D =
pIndex->aiColumn[jj])
+				if (pExpr->iColumn =3D=3D (int)
+				    =
pIndex->def->key_def->parts[jj].fieldno)
 					return 1;
 			}
 		} else if ((aColExpr =3D pIndex->aColExpr) !=3D 0) {
 			for (jj =3D 0; jj < nIdxCol; jj++) {
-				if (pIndex->aiColumn[jj] !=3D =
XN_EXPR)
-					continue;
 				if (sqlite3ExprCompare
 				    (pExpr, aColExpr->a[jj].pExpr,
 				     iCursor) =3D=3D 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 =3D -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 =3D 1;
-		sPk.aiColumn =3D &aiColumnPk;
 		sPk.aiRowLogEst =3D aiRowEstPk;
 		sPk.onError =3D ON_CONFLICT_ACTION_REPLACE;
 		sPk.pTable =3D pTab;
+
+		struct key_def *key_def =3D key_def_new(1);
+		if (key_def =3D=3D 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 =3D index_opts_default;
+
+		sPk.def =3D index_def_new(pTab->def->id, 0, =
"primary",
+					sizeof("primary") - 1, TREE, =
&index_opts,
+					key_def, NULL);
+
+		if (sPk.def =3D=3D NULL)
+			return SQLITE_ERROR;
+
 		aiRowEstPk[0] =3D sql_space_tuple_log_count(pTab);
 		aiRowEstPk[1] =3D 0;
 		pFirst =3D pSrc->pTab->pIndex;
@@ -3325,8 +3315,8 @@ wherePathSatisfiesOrderBy(WhereInfo * pWInfo,	=
/* The WHERE clause */
 				   index_is_unordered(pIndex)) {
 				return 0;
 			} else {
-				nColumn =3D index_column_count(pIndex);
-				isOrderDistinct =3D =
index_is_unique(pIndex);
+				nColumn =3D =
pIndex->def->key_def->part_count;
+				isOrderDistinct =3D =
pIndex->def->opts.is_unique;
 			}
=20
 			/* 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 =3D =
pIndex->aiColumn[j];
-					revIdx =3D =
sql_index_column_sort_order(pIndex,
-									 =
    j);
+					iColumn =3D =
pIndex->def->key_def->
+						parts[j].fieldno;
+					revIdx =3D =
pIndex->def->key_def->
+						parts[j].sort_order;
 					if (iColumn =3D=3D =
pIndex->pTable->iPKey)
 						iColumn =3D -1;
 				} else {
@@ -3442,8 +3433,7 @@ wherePathSatisfiesOrderBy(WhereInfo * pWInfo,	=
/* The WHERE clause */
 								      =
pOrderBy->a[i].pExpr,
 								      =
&is_found, &id);
 						struct coll *idx_coll =3D
-							=
sql_index_collation(pIndex,
-									 =
   j, &id);
+							=
pIndex->def->key_def->parts[j].coll;
 						if (is_found &&
 						    coll !=3D idx_coll)
 							continue;
@@ -4105,13 +4095,13 @@ whereShortCut(WhereLoopBuilder * pBuilder)
 	} else {
 		for (pIdx =3D pTab->pIndex; pIdx; pIdx =3D =
pIdx->pNext) {
 			int opMask;
-			int nIdxCol =3D index_column_count(pIdx);
+			int nIdxCol =3D =
pIdx->def->key_def->part_count;
 			assert(pLoop->aLTermSpace =3D=3D =
pLoop->aLTerm);
-			if (!index_is_unique(pIdx)
+			if (!pIdx->def->opts.is_unique
 			    || pIdx->pPartIdxWhere !=3D 0
-			    || nIdxCol > =
ArraySize(pLoop->aLTermSpace)
-			    )
+			    || nIdxCol > =
ArraySize(pLoop->aLTermSpace)) {
 				continue;
+			}
 			opMask =3D WO_EQ;
 			for (j =3D 0; j < nIdxCol; j++) {
 				pTerm =3D
@@ -4650,7 +4640,7 @@ sqlite3WhereBegin(Parse * pParse,	/* The parser =
context */
 					wctrlFlags & =
WHERE_ORDERBY_MIN) =3D=3D 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 =3D 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 =3D pIdx->aiColumn[i];
-	if (i =3D=3D XN_EXPR)
-		return "<expr>";
+	i =3D pIdx->def->key_def->parts[i].fieldno;
 	return pIdx->pTable->def->fields[i].name;
 }
=20
@@ -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) !=3D 0
@@ -463,7 +461,7 @@ codeEqualityTerm(Parse * pParse,	/* The parsing =
context */
 		int *aiMap =3D 0;
=20
 		if (pLoop->pIndex !=3D 0 &&
-		    sql_index_column_sort_order(pLoop->pIndex, iEq)) =
{
+		    =
pLoop->pIndex->def->key_def->parts[iEq].sort_order) {
 			testcase(iEq =3D=3D 0);
 			testcase(bRev);
 			bRev =3D !bRev;
@@ -708,7 +706,7 @@ codeAllEqualityTerms(Parse * pParse,	/* =
Parsing context */
 		sqlite3VdbeAddOp1(v, (bRev ? OP_Last : OP_Rewind), =
iIdxCur);
 		VdbeCoverageIf(v, bRev =3D=3D 0);
 		VdbeCoverageIf(v, bRev !=3D 0);
-		VdbeComment((v, "begin skip-scan on %s", =
pIdx->zName));
+		VdbeComment((v, "begin skip-scan on %s", =
pIdx->def->name));
 		j =3D sqlite3VdbeAddOp0(v, OP_Goto);
 		pLevel->addrSkip =3D
 		    sqlite3VdbeAddOp4Int(v, (bRev ? OP_SeekLT : =
OP_SeekGT),
@@ -718,8 +716,8 @@ codeAllEqualityTerms(Parse * pParse,	/* =
Parsing context */
 		sqlite3VdbeJumpHere(v, j);
 		for (j =3D 0; j < nSkip; j++) {
 			sqlite3VdbeAddOp3(v, OP_Column, iIdxCur,
-					  pIdx->aiColumn[j], regBase =
+ j);
-			testcase(pIdx->aiColumn[j] =3D=3D 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 =3D=3D 0
 		       || pWInfo->pOrderBy->nExpr =3D=3D 1
 		       || (pWInfo->wctrlFlags & =
WHERE_ORDERBY_MIN) =3D=3D 0);
-		int nIdxCol =3D index_column_count(pIdx);
+		int nIdxCol =3D pIdx->def->key_def->part_count;
 		if ((pWInfo->wctrlFlags & WHERE_ORDERBY_MIN) !=3D =
0
 		    && pWInfo->nOBSat > 0 && =
(nIdxCol > nEq)) {
-			j =3D pIdx->aiColumn[nEq];
+			j =3D =
pIdx->def->key_def->parts[nEq].fieldno;
 			/* Allow seek for column with `NOT NULL` =3D=3D =
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 >=3D 0 && =
pIdx->pTable->def->fields[j].is_nullable)
-			    || j =3D=3D XN_EXPR) {
+			if =
(pIdx->pTable->def->fields[j].is_nullable) {
 				assert(pLoop->nSkip =3D=3D 0);
 				bSeekPastNull =3D 1;
 				nExtraReg =3D 1;
@@ -1299,17 +1296,15 @@ sqlite3WhereCodeOneLoopStart(WhereInfo * pWInfo,	=
/* Complete information about t
 				assert((bRev & ~1) =3D=3D 0);
 				pLevel->iLikeRepCntr <<=3D 1;
 				pLevel->iLikeRepCntr |=3D
-					bRev ^ =
(sql_index_column_sort_order(pIdx, nEq) =3D=3D
+					bRev ^ =
(pIdx->def->key_def->
+						  parts[nEq].sort_order =
=3D=3D
 						SORT_ORDER_DESC);
 			}
 #endif
 			if (pRangeStart =3D=3D 0) {
-				j =3D pIdx->aiColumn[nEq];
-				if ((j >=3D 0 &&
-				     =
pIdx->pTable->def->fields[j].is_nullable)||
-				    j =3D=3D XN_EXPR) {
+				j =3D =
pIdx->def->key_def->parts[nEq].fieldno;
+				if =
(pIdx->pTable->def->fields[j].is_nullable)
 					bSeekPastNull =3D 1;
-				}
 			}
 		}
 		assert(pRangeEnd =3D=3D 0
@@ -1320,7 +1315,7 @@ sqlite3WhereCodeOneLoopStart(WhereInfo * pWInfo,	=
/* Complete information about t
 		 * start and end terms (pRangeStart and pRangeEnd).
 		 */
 		if ((nEq < nIdxCol &&
-		     bRev =3D=3D (sql_index_column_sort_order(pIdx, nEq) =
=3D=3D
+		     bRev =3D=3D =
(pIdx->def->key_def->parts[nEq].sort_order =3D=3D
 			      SORT_ORDER_ASC)) ||
 		    (bRev && nIdxCol =3D=3D nEq)) {
 			SWAP(pRangeEnd, pRangeStart);
@@ -1386,9 +1381,10 @@ sqlite3WhereCodeOneLoopStart(WhereInfo * pWInfo,	=
/* Complete information about t
 		}
 		struct Index *pk =3D =
sqlite3PrimaryKeyIndex(pIdx->pTable);
 		assert(pk);
-		int nPkCol =3D index_column_count(pk);
+		int nPkCol =3D pk->def->key_def->part_count;
+		uint32_t zero_fieldno =3D =
pk->def->key_def->parts[0].fieldno;
 		char affinity =3D
-			=
pIdx->pTable->def->fields[pk->aiColumn[0]].affinity;
+			=
pIdx->pTable->def->fields[zero_fieldno].affinity;
 		if (nPkCol =3D=3D 1 && affinity =3D=3D =
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 =3D pRangeStart =3D=3D NULL ? nEq : =
nEq + 1;
 			for (int i =3D 0; i < limit; i++) {
-				if (pIdx->aiColumn[i] =3D=3D =
pk->aiColumn[0]) {
+				if =
(pIdx->def->key_def->parts[i].fieldno =3D=3D
+				    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 !=3D iIdxCur) {
 			Index *pPk =3D =
sqlite3PrimaryKeyIndex(pIdx->pTable);
-			int nPkCol =3D index_column_count(pPk);
+			int nPkCol =3D =
pPk->def->key_def->part_count;
 			int iKeyReg =3D sqlite3GetTempRange(pParse, =
nPkCol);
 			for (j =3D 0; j < nPkCol; j++) {
-				k =3D pPk->aiColumn[j];
+				k =3D =
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) =3D=3D=
 0) {
 			Index *pPk =3D sqlite3PrimaryKeyIndex(pTab);
-			int nPkCol =3D index_column_count(pPk);
+			int nPkCol =3D =
pPk->def->key_def->part_count;
 			regRowset =3D pParse->nTab++;
 			sqlite3VdbeAddOp2(v, OP_OpenTEphemeral,
 					  regRowset, nPkCol);
@@ -1718,13 +1715,16 @@ sqlite3WhereCodeOneLoopStart(WhereInfo * pWInfo,	=
/* Complete information about t
 						int iSet =3D
 						    ((ii =3D=3D =
pOrWc->nTerm - 1) ? -1 : ii);
 						Index *pPk =3D =
sqlite3PrimaryKeyIndex (pTab);
-						int nPk =3D =
index_column_count(pPk);
+						int nPk =3D =
pPk->def->key_def->part_count;
 						int iPk;
=20
 						/* Read the PK into an =
array of temp registers. */
 						r =3D =
sqlite3GetTempRange(pParse, nPk);
 						for (iPk =3D 0; iPk < =
nPk; iPk++) {
-							int iCol =3D =
pPk->aiColumn[iPk];
+							int iCol =3D =
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;
=20
@@ -930,20 +929,6 @@ exprMightBeIndexed(SrcList * pFrom,	/* The =
FROM clause */
 	for (i =3D 0; mPrereq > 1; i++, mPrereq >>=3D 1) {
 	}
 	iCur =3D pFrom->a[i].iCursor;
-	for (pIdx =3D pFrom->a[i].pTab->pIndex; pIdx; pIdx =3D =
pIdx->pNext) {
-		if (pIdx->aColExpr =3D=3D 0)
-			continue;
-		for (i =3D 0; i < pIdx->nColumn; i++) {
-			if (pIdx->aiColumn[i] !=3D XN_EXPR)
-				continue;
-			if (sqlite3ExprCompare
-			    (pExpr, pIdx->aColExpr->a[i].pExpr, =
iCur) =3D=3D 0) {
-				*piCur =3D iCur;
-				*piColumn =3D XN_EXPR;
-				return 1;
-			}
-		}
-	}
 	return 0;
 }
=20
-- 
= --Apple-Mail=_ECB47D6D-69B1-4BF0-94FD-048F95FFBE64--