<HTML><BODY><div class="js-helper js-readmsg-msg">
        <style type="text/css"></style>
        <div>
                <base target="_self" href="https://e.mail.ru/">
                
            <div id="style_15276831760000000613_BODY"><div class="class_1528225496">
<blockquote style="border-left:1px solid #0857A6;margin:10px;padding:0 0 0 10px;"><div id=""><div class="js-helper_mailru_css_attribute_postfix js-readmsg-msg_mailru_css_attribute_postfix"><div><div id="style_15275094580000000439_BODY_mailru_css_attribute_postfix">1. The patch is not so big to do not fit in a letter. Please, send a patch as a file only when<br>
it is really huge.</div></div></div></div></blockquote>Ok.<br><blockquote style="border-left:1px solid #0857A6;margin:10px;padding:0 0 0 10px;"><div id=""><div class="js-helper_mailru_css_attribute_postfix js-readmsg-msg_mailru_css_attribute_postfix"><div><div id="style_15275094580000000439_BODY_mailru_css_attribute_postfix">2. There is a problem with your GibHub account. Your profile name in commits on github must<br>
be a link to the profile. But your name is just a text. Please, cope with the problem.</div></div></div></div></blockquote>Fixed.<br><blockquote style="border-left:1px solid #0857A6;margin:10px;padding:0 0 0 10px;"><div id=""><div class="js-helper_mailru_css_attribute_postfix js-readmsg-msg_mailru_css_attribute_postfix"><div><div id="style_15275094580000000439_BODY_mailru_css_attribute_postfix">3. Now you can remove sql_index_collation() function. It is useless two line wrapper. And<br>
its second out parameter is not needed in some places.</div></div></div></div></blockquote>Ok, fixed in all the places.<blockquote style="border-left:1px solid #0857A6;margin:10px;padding:0 0 0 10px;"><div id=""><div class="js-helper_mailru_css_attribute_postfix js-readmsg-msg_mailru_css_attribute_postfix"><div><div id="style_15275094580000000439_BODY_mailru_css_attribute_postfix">
4. Same about index_is_unique().</div></div></div></div></blockquote>Fixed.<br><blockquote style="border-left:1px solid #0857A6;margin:10px;padding:0 0 0 10px;"><div id=""><div class="js-helper_mailru_css_attribute_postfix js-readmsg-msg_mailru_css_attribute_postfix"><div><div id="style_15275094580000000439_BODY_mailru_css_attribute_postfix"><br>
5. get_new_iid() is unused.</div></div></div></div></blockquote>Removed.<br><blockquote style="border-left:1px solid #0857A6;margin:10px;padding:0 0 0 10px;"><div id=""><div class="js-helper_mailru_css_attribute_postfix js-readmsg-msg_mailru_css_attribute_postfix"><div><div id="style_15275094580000000439_BODY_mailru_css_attribute_postfix"><br>
                                 > +void<br>
> +append(struct region *r, const char *str, char **sql_arr, int idx, int total_sql_str_size)<br>
> +{<br>
> +  sql_arr[idx] = region_alloc(r, strlen(str));<br>
> +  strcpy(sql_arr[idx], str);<br>
> +  total_sql_str_size += strlen(str);<br>
> +}<br><br>
6. How does it work? total_sql_str_size is not out parameter here. In the caller<br>
context it is still unchanged.</div></div></div></div></blockquote>Refactoring mistake. Made total_sql_str_size output parameter.<br><blockquote style="border-left:1px solid #0857A6;margin:10px;padding:0 0 0 10px;"><div id=""><div class="js-helper_mailru_css_attribute_postfix js-readmsg-msg_mailru_css_attribute_postfix"><div><div id="style_15275094580000000439_BODY_mailru_css_attribute_postfix"><br>
> +<br>
> +char *<br>
> +create_sql(const char *idx_name, struct space_def *space_def, ExprList *expr_list)<br>
> +{<br>
> +  struct region *r = &fiber()->gc;<br>
> +  /*<br>
> +   * CREATE_INDEX_ + INDEX_NAME + _ON_ + TABLE_NAME + _(<br>
> +   */<br>
> +  uint32_t sql_parts_count = 5;<br><br>
7. You do not need sql parts array, part count. You just do region_alloc + memcpy and<br>
region_join at the end. It is not required to store each part anywhere.</div></div></div></div></blockquote>Ok, fixed.<blockquote style="border-left:1px solid #0857A6;margin:10px;padding:0 0 0 10px;"><div id=""><div class="js-helper_mailru_css_attribute_postfix js-readmsg-msg_mailru_css_attribute_postfix"><div><div id="style_15275094580000000439_BODY_mailru_css_attribute_postfix">
> +  Expr *column_expr;<br>
> +  for (uint32_t i = 0; i < (uint32_t) expr_list->nExpr; i++){<br>
> +          column_expr = sqlite3ExprSkipCollate(expr_list->a[i].pExpr);<br>
> +          sql_parts_count += expr_list->a[i].pExpr == column_expr ? 2 : 4;<br>
> +  }<br>
> +<br>
> +  size_t total_sql_str_size = 0;<br>
> +<br>
> +  char **sql = region_alloc(r, sizeof(char*) * sql_parts_count);<br>
> +<br>
> +  char *CREATE_INDEX = "CREATE INDEX ";<br><br>
8. Do not store const string as non-const.</div></div></div></div></blockquote>Fxd.<blockquote style="border-left:1px solid #0857A6;margin:10px;padding:0 0 0 10px;"><div id=""><div class="js-helper_mailru_css_attribute_postfix js-readmsg-msg_mailru_css_attribute_postfix"><div><div id="style_15275094580000000439_BODY_mailru_css_attribute_postfix">
9. Why do you need this variable? Why can not you just<br>
inline it one line below?.</div></div></div></div></blockquote>Made it inline.<blockquote style="border-left:1px solid #0857A6;margin:10px;padding:0 0 0 10px;"><div id=""><div class="js-helper_mailru_css_attribute_postfix js-readmsg-msg_mailru_css_attribute_postfix"><div><div id="style_15275094580000000439_BODY_mailru_css_attribute_postfix">
> +  append(r, CREATE_INDEX, sql, 0, total_sql_str_size);<br>
> +  append(r, idx_name, sql, 1, total_sql_str_size);<br>
> +<br>
> +  char *ON = " ON ";</div></div></div></div></blockquote><blockquote style="border-left:1px solid #0857A6;margin:10px;padding:0 0 0 10px;"><div id=""><div class="js-helper_mailru_css_attribute_postfix js-readmsg-msg_mailru_css_attribute_postfix"><div><div id="style_15275094580000000439_BODY_mailru_css_attribute_postfix">
10. Same.</div></div></div></div></blockquote>Fxd.<br><blockquote style="border-left:1px solid #0857A6;margin:10px;padding:0 0 0 10px;"><div id=""><div class="js-helper_mailru_css_attribute_postfix js-readmsg-msg_mailru_css_attribute_postfix"><div><div id="style_15275094580000000439_BODY_mailru_css_attribute_postfix"><br>
> +  append(r, ON, sql, 2, total_sql_str_size);<br>
> +<br>
> +  char *name = space_def->name;<br>
> +  append(r, name, sql, 3, total_sql_str_size);<br>
> +  append(r, " (", sql, 4, total_sql_str_size);<br>
> +<br>
> +  Expr *collation_expr;<br>
> +  uint32_t sql_idx = 5;<br>
> +  for (int i = 0; i < expr_list->nExpr; i++){<br>
> +          column_expr = sqlite3ExprSkipCollate(expr_list->a[i].pExpr);<br>
> +          collation_expr = expr_list->a[i].pExpr == column_expr ?<br>
> +                           NULL : expr_list->a[i].pExpr;<br><br>
11. Please, do like sql_create_index(): use pExpr->op == TK_COLLATE to detect that<br>
it is a collated column.</div></div></div></div></blockquote>Ok. Fixed.<br><blockquote style="border-left:1px solid #0857A6;margin:10px;padding:0 0 0 10px;"><div id=""><div class="js-helper_mailru_css_attribute_postfix js-readmsg-msg_mailru_css_attribute_postfix"><div><div id="style_15275094580000000439_BODY_mailru_css_attribute_postfix">12. Why did not you call sqlite3ResolveSelfReference() ?</div></div></div></div></blockquote>Because it is called in create_index before set_index_def invocation. Anyway I got your point, add call in set_index_def<blockquote style="border-left:1px solid #0857A6;margin:10px;padding:0 0 0 10px;"><div id=""><div class="js-helper_mailru_css_attribute_postfix js-readmsg-msg_mailru_css_attribute_postfix"><div><div id="style_15275094580000000439_BODY_mailru_css_attribute_postfix">
13. Why your patch did not delete Index.coll_array, Index.coll_id_array, Index.aiColumn?<br>
Your patch must remove them. Index_def initialization must replace code in build.c<br>
on lines 3158:3202.</div></div></div></div></blockquote>Okay, I removed coll_array, coll_id_array and aiColumn.<blockquote style="border-left:1px solid #0857A6;margin:10px;padding:0 0 0 10px;"><div id=""><div class="js-helper_mailru_css_attribute_postfix js-readmsg-msg_mailru_css_attribute_postfix"><div><div id="style_15275094580000000439_BODY_mailru_css_attribute_postfix">
> +  // fix last ", " with ")\0"<br>
> +  strcpy(sql[sql_idx - 1], ")\0");<br>
> +  char *res = region_join(r, total_sql_str_size);<br>
> +  return res;<br><br>
14. Do not use // comments.</div></div></div></div></blockquote>Ok, fixed.<blockquote style="border-left:1px solid #0857A6;margin:10px;padding:0 0 0 10px;"><div id=""><div class="js-helper_mailru_css_attribute_postfix js-readmsg-msg_mailru_css_attribute_postfix"><div><div id="style_15275094580000000439_BODY_mailru_css_attribute_postfix">
15. Why do you need separate create_sql() function? Can you generate an expression<br>
during parts initialization?</div></div></div></div></blockquote>Create_sql() function seems to be separate piece of string building logic. SQL expression<br>can be generated during part initialising, but IMHO it will mess the code.<blockquote style="border-left:1px solid #0857A6;margin:10px;padding:0 0 0 10px;"><div id=""><div class="js-helper_mailru_css_attribute_postfix js-readmsg-msg_mailru_css_attribute_postfix"><div><div id="style_15275094580000000439_BODY_mailru_css_attribute_postfix">
> +void<br>
> +set_index_def(Parse *parse, Index *index, Table *table,<br>
> +                             const char *name,<br>
> +                             uint32_t name_len, int on_error,<br>
> +                             ExprList *expr_list, u8 idx_type)<br><br>
16. Big problems with indentation.</div></div></div></div></blockquote>Fixed.<blockquote style="border-left:1px solid #0857A6;margin:10px;padding:0 0 0 10px;"><div id=""><div class="js-helper_mailru_css_attribute_postfix js-readmsg-msg_mailru_css_attribute_postfix"><div><div id="style_15275094580000000439_BODY_mailru_css_attribute_postfix">
> +{<br>
> +  struct space_def *space_def = table->def;<br>
> +  uint32_t iid = SQLITE_PAGENO_TO_INDEXID(index->tnum);<br><br>
17. You do not know iid here. The parser just parses and does not<br>
generate identifiers. I checked this code in a debugger, and in this example</div></div></div></div></blockquote><blockquote style="border-left:1px solid #0857A6;margin:10px;padding:0 0 0 10px;"><div id=""><div class="js-helper_mailru_css_attribute_postfix js-readmsg-msg_mailru_css_attribute_postfix"><div><div id="style_15275094580000000439_BODY_mailru_css_attribute_postfix"><br>
box.sql.execute("CREATE TABLE t1(x integer primary key, a integer unique);")<br><br>
It creates two struct Index with the same def->iid == 0. It works only thanks<br>
to that Index.def->iid is not used anywhere now.</div></div></div></div></blockquote>Ok, fixed. Now is retrieved after looping over all existing indexes metadata.<blockquote style="border-left:1px solid #0857A6;margin:10px;padding:0 0 0 10px;"><div id=""><div class="js-helper_mailru_css_attribute_postfix js-readmsg-msg_mailru_css_attribute_postfix"><div><div id="style_15275094580000000439_BODY_mailru_css_attribute_postfix"><br>
> +  struct index_opts opts;<br>
> +  index_opts_create(&opts);<br>
> +  opts.stat = NULL;<br><br>
18. Already done by index_opts_create.</div></div></div></div></blockquote>Removed.<blockquote style="border-left:1px solid #0857A6;margin:10px;padding:0 0 0 10px;"><div id=""><div class="js-helper_mailru_css_attribute_postfix js-readmsg-msg_mailru_css_attribute_postfix"><div><div id="style_15275094580000000439_BODY_mailru_css_attribute_postfix">
> +  opts.is_unique = on_error != ON_CONFLICT_ACTION_NONE;<br>
> +<br>
> +  struct key_def *key_def = key_def_new(expr_list->nExpr);<br><br>
19. Please, check each function result on error. Key_def_new can<br>
fail.</div></div></div></div></blockquote>Added check.<blockquote style="border-left:1px solid #0857A6;margin:10px;padding:0 0 0 10px;"><div id=""><div class="js-helper_mailru_css_attribute_postfix js-readmsg-msg_mailru_css_attribute_postfix"><div><div id="style_15275094580000000439_BODY_mailru_css_attribute_postfix">
> +<br>
> +  if (idx_type == SQLITE_IDXTYPE_APPDEF) {<br>
> +          opts.sql = create_sql(name, table->def, expr_list);<br>
> +  }<br><br>
20. Please, do not put 'if' body in {} when it consists of a single line.</div></div></div></div></blockquote>Fixed.<br><blockquote style="border-left:1px solid #0857A6;margin:10px;padding:0 0 0 10px;"><div id=""><div class="js-helper_mailru_css_attribute_postfix js-readmsg-msg_mailru_css_attribute_postfix"><div><div id="style_15275094580000000439_BODY_mailru_css_attribute_postfix"><br>
> +<br>
> +  int i = 0;<br><br>
21. Why can not you put 'int i = 0' into 'for (...'?</div></div></div></div></blockquote>Fixed.<br><blockquote style="border-left:1px solid #0857A6;margin:10px;padding:0 0 0 10px;"><div id=""><div class="js-helper_mailru_css_attribute_postfix js-readmsg-msg_mailru_css_attribute_postfix"><div><div id="style_15275094580000000439_BODY_mailru_css_attribute_postfix"><br><br>
> +  Expr *column_expr;<br>
> +  Expr *collation_expr;<br><br>
22. Same. These variables are unused out of 'for'. And can be declared +<br>
initialized inside.</div></div></div></div></blockquote>Fixed.<br><blockquote style="border-left:1px solid #0857A6;margin:10px;padding:0 0 0 10px;"><div id=""><div class="js-helper_mailru_css_attribute_postfix js-readmsg-msg_mailru_css_attribute_postfix"><div><div id="style_15275094580000000439_BODY_mailru_css_attribute_postfix"><br><br>
> +  for (i = 0; i < expr_list->nExpr; i++) {<br>
> +          column_expr = sqlite3ExprSkipCollate(expr_list->a[i].pExpr);<br>
> +          collation_expr = expr_list->a[i].pExpr == column_expr ?<br>
> +                           NULL : expr_list->a[i].pExpr;<br><br>
23. Same as 11.</div></div></div></div></blockquote>Fixed.<blockquote style="border-left:1px solid #0857A6;margin:10px;padding:0 0 0 10px;"><div id=""><div class="js-helper_mailru_css_attribute_postfix js-readmsg-msg_mailru_css_attribute_postfix"><div><div id="style_15275094580000000439_BODY_mailru_css_attribute_postfix">
> +<br>
> +          uint32_t fieldno = column_expr->iColumn;<br>
> +<br>
> +          uint32_t coll_id;<br>
> +          struct coll *coll;<br>
> +          if (collation_expr != NULL)<br>
> +                  coll = sql_get_coll_seq(parse, collation_expr->u.zToken,&coll_id);<br>
> +          else<br>
> +                  coll = sql_column_collation(space_def, fieldno, &coll_id);<br>
> +<br>
> +          key_def_set_part(key_def, i, fieldno,<br>
> +                          space_def->fields[fieldno].type,<br>
> +                          space_def->fields[fieldno].nullable_action,<br>
> +                          coll, coll_id, SORT_ORDER_ASC);<br>
> +  }<br>
> +<br>
> +  struct key_def *pk_key_def;<br>
> +  if (idx_type == SQLITE_IDXTYPE_APPDEF) {<br>
> +          pk_key_def = table->pIndex->def->key_def;<br>
> +  } else {<br>
> +          pk_key_def = NULL;<br>
> +  }<br>
> +<br>
> +  index->def = index_def_new(space_def->id, iid, name, name_len,<br>
> +                       TREE, &opts, key_def, pk_key_def);<br><br>
24. Same as 16.</div></div></div></div></blockquote>Fixed. There was a problem with indents in my IDE.<br><blockquote style="border-left:1px solid #0857A6;margin:10px;padding:0 0 0 10px;"><div id=""><div class="js-helper_mailru_css_attribute_postfix js-readmsg-msg_mailru_css_attribute_postfix"><div><div id="style_15275094580000000439_BODY_mailru_css_attribute_postfix"><br>
> @@ -3068,6 +3200,11 @@ sql_create_index(struct Parse *parse, struct Token *token,<br>
>             */<br>
>            pIndex->sort_order[i] = SORT_ORDER_ASC;<br>
>    }<br>
> +<br>
> +  set_index_def(parse, pIndex, pTab, zName,<br>
> +                nName, on_error, col_list,<br>
> +                idx_type);<br>
> +<br><br>
25. It fits in 2 lines.</div></div></div></div></blockquote>Fitted in 2 lines.<br><blockquote style="border-left:1px solid #0857A6;margin:10px;padding:0 0 0 10px;"><div id=""><div class="js-helper_mailru_css_attribute_postfix js-readmsg-msg_mailru_css_attribute_postfix"><div><div id="style_15275094580000000439_BODY_mailru_css_attribute_postfix"><br><br>
> @@ -3133,6 +3270,7 @@ sql_create_index(struct Parse *parse, struct Token *token,<br>
>                            }<br>
>                            if (idx_type == SQLITE_IDXTYPE_PRIMARYKEY)<br>
>                                    pIdx->idxType = idx_type;<br>
> +<br>
>                            goto exit_create_index;<br>
>                    }<br>
>            }<br><br>
26. Garbage diff.</div></div></div></div></blockquote>Removed.<br><blockquote style="border-left:1px solid #0857A6;margin:10px;padding:0 0 0 10px;"><div id=""><div class="js-helper_mailru_css_attribute_postfix js-readmsg-msg_mailru_css_attribute_postfix"><div><div id="style_15275094580000000439_BODY_mailru_css_attribute_postfix"><br><br>
> @@ -3141,6 +3279,7 @@ sql_create_index(struct Parse *parse, struct Token *token,<br>
>    /* Link the new Index structure to its table and to the other<br>
>     * in-memory database structures.<br>
>     */<br>
> +<br>
>    assert(parse->nErr == 0);<br>
>    if (db->init.busy) {<br>
>            Index *p;<br><br>
27. Same.</div></div></div></div></blockquote>Removed.<blockquote style="border-left:1px solid #0857A6;margin:10px;padding:0 0 0 10px;"><div id=""><div class="js-helper_mailru_css_attribute_postfix js-readmsg-msg_mailru_css_attribute_postfix"><div><div id="style_15275094580000000439_BODY_mailru_css_attribute_postfix">
28. index_column_count() is useless now.</div></div></div></div></blockquote>Removed.<br><blockquote style="border-left:1px solid #0857A6;margin:10px;padding:0 0 0 10px;"><div id=""><div class="js-helper_mailru_css_attribute_postfix js-readmsg-msg_mailru_css_attribute_postfix"><div><div id="style_15275094580000000439_BODY_mailru_css_attribute_postfix"><br><br>
29. index_is_unique_not_null() - same.</div></div></div></div></blockquote>Removed.<br><blockquote style="border-left:1px solid #0857A6;margin:10px;padding:0 0 0 10px;"><div id=""><div class="js-helper_mailru_css_attribute_postfix js-readmsg-msg_mailru_css_attribute_postfix"><div><div id="style_15275094580000000439_BODY_mailru_css_attribute_postfix"><br><br>
> diff --git a/src/box/sql/select.c b/src/box/sql/select.c<br>
> index 5fbcbaffc..34ac846cf 100644<br>
> --- a/src/box/sql/select.c<br>
> +++ b/src/box/sql/select.c<br>
> @@ -4298,7 +4298,7 @@ sqlite3IndexedByLookup(Parse * pParse, struct SrcList_item *pFrom)<br>
>                 char *zIndexedBy = pFrom->u1.zIndexedBy;<br>
>                 Index *pIdx;<br>
>                 for (pIdx = pTab->pIndex;<br>
> -                    pIdx && strcmp(pIdx->zName, zIndexedBy);<br>
> +                    pIdx && strcmp(pIdx->def->name, zIndexedBy);<br>
>                      pIdx = pIdx->pNext) ;<br>
>                 if (!pIdx) {<br>
>                         sqlite3ErrorMsg(pParse, "no such index: %s", zIndexedBy,<br><br>
30. If you use def->name instead of zName, then remove zName from struct Index.</div></div></div></div></blockquote>Removed.<br><blockquote style="border-left:1px solid #0857A6;margin:10px;padding:0 0 0 10px;"><div id=""><div class="js-helper_mailru_css_attribute_postfix js-readmsg-msg_mailru_css_attribute_postfix"><div><div id="style_15275094580000000439_BODY_mailru_css_attribute_postfix"><br><br>
> diff --git a/src/box/sql/sqliteInt.h b/src/box/sql/sqliteInt.h<br>
> index 950409d93..fe61dacfe 100644<br>
> --- a/src/box/sql/sqliteInt.h<br>
> +++ b/src/box/sql/sqliteInt.h<br>
> @@ -2124,6 +2124,7 @@ struct Index {<br>
>                             * or _NONE<br>
>                             */<br>
>    unsigned idxType:2;     /* 1==UNIQUE, 2==PRIMARY KEY, 0==CREATE INDEX */<br>
> +          struct index_def *def;<br>
>  };<br><br>
31. 4 spaces + 1 tab is bad indentation. Here 1 tab is enough</div></div></div></div></blockquote>Fixed.<br><blockquote style="border-left:1px solid #0857A6;margin:10px;padding:0 0 0 10px;"><div id=""><div class="js-helper_mailru_css_attribute_postfix js-readmsg-msg_mailru_css_attribute_postfix"><div><div id="style_15275094580000000439_BODY_mailru_css_attribute_postfix">.<br><br>
> diff --git a/src/box/sql/where.c b/src/box/sql/where.c<br>
> index 496b41725..6d95185b3 100644<br>
> --- a/src/box/sql/where.c<br>
> +++ b/src/box/sql/where.c<br>
> @@ -470,7 +470,7 @@ findIndexCol(Parse * pParse,   /* Parse context */<br>
>    for (int i = 0; i < pList->nExpr; i++) {<br>
>            Expr *p = sqlite3ExprSkipCollate(pList->a[i].pExpr);<br>
>            if (p->op == TK_COLUMN &&<br>
> -              p->iColumn == pIdx->aiColumn[iCol] &&<br>
> +              p->iColumn == (int) pIdx->def->key_def->parts[iCol].fieldno &&<br>
>                p->iTable == iBase) {<br>
>                    bool is_found;<br>
>                    uint32_t id;<br><br>
32. Out of 80.</div></div></div></div></blockquote>Fixed.<br><blockquote style="border-left:1px solid #0857A6;margin:10px;padding:0 0 0 10px;"><div id=""><div class="js-helper_mailru_css_attribute_postfix js-readmsg-msg_mailru_css_attribute_postfix"><div><div id="style_15275094580000000439_BODY_mailru_css_attribute_postfix"><br><br>
> @@ -2859,6 +2859,19 @@ whereLoopAddBtree(WhereLoopBuilder * pBuilder,      /* WHERE clause information */<br>
>            sPk.aiRowLogEst = aiRowEstPk;<br>
>            sPk.onError = ON_CONFLICT_ACTION_REPLACE;<br>
>            sPk.pTable = pTab;<br>
> +<br>
> +          struct key_def *key_def = key_def_new(1);<br><br>
33. Check on error.</div></div></div></div></blockquote>Added check.<br><blockquote style="border-left:1px solid #0857A6;margin:10px;padding:0 0 0 10px;"><div id=""><div class="js-helper_mailru_css_attribute_postfix js-readmsg-msg_mailru_css_attribute_postfix"><div><div id="style_15275094580000000439_BODY_mailru_css_attribute_postfix"><br><br>
> +          key_def_set_part(key_def, 0, 0, pTab->def->fields[0].type,<br>
> +                           ON_CONFLICT_ACTION_ABORT,<br>
> +                           NULL, COLL_NONE, SORT_ORDER_ASC);<br>
> +<br>
> +          struct index_opts index_opts = index_opts_default;<br>
> +<br>
> +          sPk.def =<br>
> +                  index_def_new(pTab->def->id, 0, "primary",<br>
> +                                sizeof("primary") - 1, TREE,<br>
> +                                &index_opts, key_def, NULL);<br><br>
34. Same as 33. And it fits in 3 lines.</div></div></div></div></blockquote>Fixed.<br><blockquote style="border-left:1px solid #0857A6;margin:10px;padding:0 0 0 10px;"><div id=""><div class="js-helper_mailru_css_attribute_postfix js-readmsg-msg_mailru_css_attribute_postfix"><div><div id="style_15275094580000000439_BODY_mailru_css_attribute_postfix"><br><br>
> diff --git a/test/box/suite.ini b/test/box/suite.ini<br>
> index ddc71326b..94bc39c57 100644<br>
> --- a/test/box/suite.ini<br>
> +++ b/test/box/suite.ini<br>
> @@ -2,7 +2,7 @@<br>
>  core = tarantool<br>
>  description = Database tests<br>
>  script = box.lua<br>
> -disabled = rtree_errinj.test.lua tuple_bench.test.lua<br>
> +disabled = rtree_errinj.test.lua tuple_bench.test.lua errinj.test.lua<br>
>  release_disabled = errinj.test.lua errinj_index.test.lua rtree_errinj.test.lua upsert_errinj.test.lua iproto_stress.test.lua<br>
>  lua_libs = lua/fifo.lua lua/utils.lua lua/bitset.lua lua/index_random_test.lua lua/push.lua lua/identifier.lua<br>
>  use_unix_sockets = True<br><br>
35. Why?</div></div></div></div></blockquote>It is not needed in the current master version. Removed<br>
<br>Patch:<br><br><pre style="font-family: Menlo; font-size: 9pt;" data-mce-style="font-family: Menlo; font-size: 9pt;">From 9fe52af99c305aaf381de3b5414ab5fb03f65817 Mon Sep 17 00:00:00 2001<br>From: Ivan Koptelov <ivan.koptelov@tarantool.org><br>Date: Thu, 7 Jun 2018 19:42:16 +0300<br>Subject: [PATCH] sql: add index_def to Index<br><br>Now every sqlite struct Index is created with tnt struct<br>index_def inside. This allows us to use tnt index_def<br>in work with sqlite indexes in the same manner as with<br>tnt index and is a step to remove sqlite Index with<br>tnt index.<br>Fields coll_array, coll_id_array, aiColumn, sort_order<br>and zName are removed from Index. All usages of this<br>fields changed to usage of corresponding index_def<br>fields.<br>index_is_unique(), sql_index_collation() and<br>index_column_count() are removed with calls of<br>index_def corresponding fields.<br><br>Closes: #3369<br>Github branch: https://github.com/tarantool/tarantool/tree/sb/gh-3369-use-index-def-in-select-and-where<br>---<br> src/box/sql.c           |  18 +--<br> src/box/sql/analyze.c   |  24 +--<br> src/box/sql/build.c     | 398 ++++++++++++++++++++++++------------------------<br> src/box/sql/delete.c    |  16 +-<br> src/box/sql/expr.c      |  59 ++++---<br> src/box/sql/fkey.c      |  41 +++--<br> src/box/sql/insert.c    | 134 +++++++---------<br> src/box/sql/pragma.c    |  19 ++-<br> src/box/sql/select.c    |   2 +-<br> src/box/sql/sqliteInt.h |  25 +--<br> src/box/sql/trigger.c   |   2 -<br> src/box/sql/update.c    |  10 +-<br> src/box/sql/vdbemem.c   |   2 +-<br> src/box/sql/where.c     | 140 ++++++++---------<br> src/box/sql/wherecode.c |  43 +++---<br> src/box/sql/whereexpr.c |  15 --<br> 16 files changed, 433 insertions(+), 515 deletions(-)<br><br>diff --git a/src/box/sql.c b/src/box/sql.c<br>index 7379cb418..c5ff8dcbc 100644<br>--- a/src/box/sql.c<br>+++ b/src/box/sql.c<br>@@ -1442,8 +1442,8 @@ int tarantoolSqlite3MakeTableFormat(Table *pTable, void *buf)<br> <br>   /* If table's PK is single column which is INTEGER, then<br>    * treat it as strict type, not affinity.  */<br>-  if (pk_idx && pk_idx->nColumn == 1) {<br>-     int pk = pk_idx->aiColumn[0];<br>+  if (pk_idx && pk_idx->def->key_def->part_count == 1) {<br>+     int pk = pk_idx->def->key_def->parts[0].fieldno;<br>      if (def->fields[pk].type == FIELD_TYPE_INTEGER)<br>         pk_forced_int = pk;<br>   }<br>@@ -1563,8 +1563,8 @@ int tarantoolSqlite3MakeIdxParts(SqliteIndex *pIndex, void *buf)<br> <br>   /* If table's PK is single column which is INTEGER, then<br>    * treat it as strict type, not affinity.  */<br>-  if (primary_index->nColumn == 1) {<br>-     int pk = primary_index->aiColumn[0];<br>+  if (primary_index->def->key_def->part_count == 1) {<br>+     int pk = primary_index->def->key_def->parts[0].fieldno;<br>      if (def->fields[pk].type == FIELD_TYPE_INTEGER)<br>         pk_forced_int = pk;<br>   }<br>@@ -1575,13 +1575,13 @@ int tarantoolSqlite3MakeIdxParts(SqliteIndex *pIndex, void *buf)<br>    * primary key columns. Query planner depends on this particular<br>    * data layout.<br>    */<br>-  int i, n = pIndex->nColumn;<br>+  int i, n = pIndex->def->key_def->part_count;<br> <br>   p = enc->encode_array(base, n);<br>   for (i = 0; i < n; i++) {<br>-     int col = pIndex->aiColumn[i];<br>+     int col = pIndex->def->key_def->parts[i].fieldno;<br>      assert(def->fields[col].is_nullable ==<br>-            action_is_nullable(def->fields[col].nullable_action));<br>+        action_is_nullable(def->fields[col].nullable_action));<br>      const char *t;<br>      if (pk_forced_int == col) {<br>         t = "integer";<br>@@ -1591,7 +1591,7 @@ int tarantoolSqlite3MakeIdxParts(SqliteIndex *pIndex, void *buf)<br>                    def->fields[col].is_nullable);<br>      }<br>      /* do not decode default collation */<br>-     uint32_t cid = pIndex->coll_id_array[i];<br>+     uint32_t cid = pIndex->def->key_def->parts[i].coll_id;<br>      p = enc->encode_map(p, cid == COLL_NONE ? 5 : 6);<br>      p = enc->encode_str(p, "type", sizeof("type")-1);<br>      p = enc->encode_str(p, t, strlen(t));<br>@@ -1609,7 +1609,7 @@ int tarantoolSqlite3MakeIdxParts(SqliteIndex *pIndex, void *buf)<br>      p = enc->encode_str(p, action_str, strlen(action_str));<br> <br>      p = enc->encode_str(p, "sort_order", 10);<br>-     enum sort_order sort_order = pIndex->sort_order[i];<br>+     enum sort_order sort_order = pIndex->def->key_def->parts[i].sort_order;<br>      assert(sort_order < sort_order_MAX);<br>      const char *sort_order_str = sort_order_strs[sort_order];<br>      p = enc->encode_str(p, sort_order_str, strlen(sort_order_str));<br>diff --git a/src/box/sql/analyze.c b/src/box/sql/analyze.c<br>index afc824a1a..a90a89d2b 100644<br>--- a/src/box/sql/analyze.c<br>+++ b/src/box/sql/analyze.c<br>@@ -860,9 +860,9 @@ analyzeOneTable(Parse * pParse,    /* Parser context */<br>      if (IsPrimaryKeyIndex(pIdx)) {<br>         zIdxName = pTab->def->name;<br>      } else {<br>-        zIdxName = pIdx->zName;<br>+        zIdxName = pIdx->def->name;<br>      }<br>-     nColTest = index_column_count(pIdx);<br>+     nColTest = pIdx->def->key_def->part_count;<br> <br>      /* Populate the register containing the index name. */<br>      sqlite3VdbeLoadString(v, regIdxname, zIdxName);<br>@@ -917,7 +917,7 @@ analyzeOneTable(Parse * pParse,    /* Parser context */<br>      sqlite3VdbeAddOp3(v, OP_OpenRead, iIdxCur, pIdx->tnum,<br>              space_ptr_reg);<br>      sql_vdbe_set_p4_key_def(pParse, pIdx);<br>-     VdbeComment((v, "%s", pIdx->zName));<br>+     VdbeComment((v, "%s", pIdx->def->name));<br> <br>      /* Invoke the stat_init() function. The arguments are:<br>       *<br>@@ -969,7 +969,7 @@ analyzeOneTable(Parse * pParse,    /* Parser context */<br>          */<br>         sqlite3VdbeAddOp0(v, OP_Goto);<br>         addrNextRow = sqlite3VdbeCurrentAddr(v);<br>-        if (nColTest == 1 && index_is_unique(pIdx)) {<br>+        if (nColTest == 1 && pIdx->def->opts.is_unique) {<br>            /* For a single-column UNIQUE index, once we have found a non-NULL<br>             * row, we know that all the rest will be distinct, so skip<br>             * subsequent distinctness tests.<br>@@ -979,12 +979,12 @@ analyzeOneTable(Parse * pParse,  /* Parser context */<br>            VdbeCoverage(v);<br>         }<br>         for (i = 0; i < nColTest; i++) {<br>-           uint32_t id;<br>            struct coll *coll =<br>-              sql_index_collation(pIdx, i, &id);<br>+              pIdx->def->key_def->parts[i].coll;<br>            sqlite3VdbeAddOp2(v, OP_Integer, i, regChng);<br>            sqlite3VdbeAddOp3(v, OP_Column, iIdxCur,<br>-                   pIdx->aiColumn[i], regTemp);<br>+                   pIdx->def->key_def->parts[i].fieldno,<br>+                   regTemp);<br>            aGotoChng[i] =<br>                sqlite3VdbeAddOp4(v, OP_Ne, regTemp, 0,<br>                        regPrev + i, (char *)coll,<br>@@ -1006,7 +1006,7 @@ analyzeOneTable(Parse * pParse,  /* Parser context */<br>         for (i = 0; i < nColTest; i++) {<br>            sqlite3VdbeJumpHere(v, aGotoChng[i]);<br>            sqlite3VdbeAddOp3(v, OP_Column, iIdxCur,<br>-                   pIdx->aiColumn[i],<br>+                   pIdx->def->key_def->parts[i].fieldno,<br>                    regPrev + i);<br>         }<br>         sqlite3VdbeResolveLabel(v, endDistinctTest);<br>@@ -1023,14 +1023,14 @@ analyzeOneTable(Parse * pParse,    /* Parser context */<br>      assert(regKey == (regStat4 + 2));<br>      Index *pPk = sqlite3PrimaryKeyIndex(pIdx->pTable);<br>      int j, k, regKeyStat;<br>-     int nPkColumn = (int)index_column_count(pPk);<br>+     int nPkColumn = (int) pPk->def->key_def->part_count;<br>      regKeyStat = sqlite3GetTempRange(pParse, nPkColumn);<br>      for (j = 0; j < nPkColumn; j++) {<br>-        k = pPk->aiColumn[j];<br>+        k = pPk->def->key_def->parts[j].fieldno;<br>         assert(k >= 0 && k < (int)pTab->def->field_count);<br>         sqlite3VdbeAddOp3(v, OP_Column, iIdxCur, k, regKeyStat + j);<br>         VdbeComment((v, "%s",<br>-           pTab->def->fields[pPk->aiColumn[j]].name));<br>+           pTab->def->fields[k].name));<br>      }<br>      sqlite3VdbeAddOp3(v, OP_MakeRecord, regKeyStat,<br>              nPkColumn, regKey);<br>@@ -1150,7 +1150,7 @@ analyzeTable(Parse * pParse, Table * pTab, Index * pOnlyIdx)<br>   iStatCur = pParse->nTab;<br>   pParse->nTab += 3;<br>   if (pOnlyIdx) {<br>-     openStatTable(pParse, iStatCur, pOnlyIdx->zName, "idx");<br>+     openStatTable(pParse, iStatCur, pOnlyIdx->def->name, "idx");<br>   } else {<br>      openStatTable(pParse, iStatCur, pTab->def->name, "tbl");<br>   }<br>diff --git a/src/box/sql/build.c b/src/box/sql/build.c<br>index 28e4d7a4d..74fb66565 100644<br>--- a/src/box/sql/build.c<br>+++ b/src/box/sql/build.c<br>@@ -253,6 +253,8 @@ freeIndex(sqlite3 * db, Index * p)<br> {<br>   sql_expr_delete(db, p->pPartIdxWhere, false);<br>   sql_expr_list_delete(db, p->aColExpr);<br>+  if (p->def != NULL)<br>+     index_def_delete(p->def);<br>   sqlite3DbFree(db, p->zColAff);<br>   sqlite3DbFree(db, p);<br> }<br>@@ -271,7 +273,8 @@ sqlite3UnlinkAndDeleteIndex(sqlite3 * db, Index * pIndex)<br> <br>   struct session *user_session = current_session();<br> <br>-  pIndex = sqlite3HashInsert(&pIndex->pTable->idxHash, pIndex->zName, 0);<br>+  pIndex = sqlite3HashInsert(&pIndex->pTable->idxHash,<br>+        pIndex->def->name, 0);<br>   if (ALWAYS(pIndex)) {<br>      if (pIndex->pTable->pIndex == pIndex) {<br>         pIndex->pTable->pIndex = pIndex->pNext;<br>@@ -388,7 +391,7 @@ deleteTable(sqlite3 * db, Table * pTable)<br>      pNext = pIndex->pNext;<br>      assert(pIndex->pSchema == pTable->pSchema);<br>      if ((db == 0 || db->pnBytesFreed == 0)) {<br>-        char *zName = pIndex->zName;<br>+        char *zName = pIndex->def->name;<br>         TESTONLY(Index *<br>             pOld =) sqlite3HashInsert(&pTable->idxHash,<br>                        zName, 0);<br>@@ -1072,11 +1075,9 @@ sqlite3AddCollateType(Parse * pParse, Token * pToken)<br>       * collation type was added. Correct this if it is the case.<br>       */<br>      for (pIdx = p->pIndex; pIdx; pIdx = pIdx->pNext) {<br>-        assert(pIdx->nColumn == 1);<br>-        if (pIdx->aiColumn[0] == i) {<br>-           id = &pIdx->coll_id_array[0];<br>-           pIdx->coll_array[0] =<br>-              sql_column_collation(p->def, i, id);<br>+        assert(pIdx->def->key_def->part_count == 1);<br>+        if ((int)pIdx->def->key_def->parts[0].fieldno == i) {<br>+           id = &pIdx->def->key_def->parts[0].coll_id;<br>         }<br>      }<br>   } else {<br>@@ -1123,52 +1124,10 @@ sql_index_key_def(struct Index *idx)<br>   return index->def->key_def;<br> }<br> <br>-struct coll *<br>-sql_index_collation(Index *idx, uint32_t column, uint32_t *coll_id)<br>-{<br>-  assert(idx != NULL);<br>-  uint32_t space_id = SQLITE_PAGENO_TO_SPACEID(idx->pTable->tnum);<br>-  struct space *space = space_by_id(space_id);<br>-<br>-  assert(column < idx->nColumn);<br>-  /*<br>-   * If space is still under construction, or it is<br>-   * an ephemeral space, then fetch collation from<br>-   * SQL internal structure.<br>-   */<br>-  if (space == NULL) {<br>-     assert(column < idx->nColumn);<br>-     *coll_id = idx->coll_id_array[column];<br>-     return idx->coll_array[column];<br>-  }<br>-<br>-  struct key_def *key_def = sql_index_key_def(idx);<br>-  assert(key_def != NULL && key_def->part_count >= column);<br>-  *coll_id = key_def->parts[column].coll_id;<br>-  return key_def->parts[column].coll;<br>-}<br>-<br> enum sort_order<br> sql_index_column_sort_order(Index *idx, uint32_t column)<br> {<br>-  assert(idx != NULL);<br>-  uint32_t space_id = SQLITE_PAGENO_TO_SPACEID(idx->pTable->tnum);<br>-  struct space *space = space_by_id(space_id);<br>-<br>-  assert(column < idx->nColumn);<br>-  /*<br>-   * If space is still under construction, or it is<br>-   * an ephemeral space, then fetch collation from<br>-   * SQL internal structure.<br>-   */<br>-  if (space == NULL) {<br>-     assert(column < idx->nColumn);<br>-     return idx->sort_order[column];<br>-  }<br>-<br>-  struct key_def *key_def = sql_index_key_def(idx);<br>-  assert(key_def != NULL && key_def->part_count >= column);<br>-  return key_def->parts[column].sort_order;<br>+  return idx->def->key_def->parts[column].sort_order;<br> }<br> <br> /**<br>@@ -1383,14 +1342,16 @@ createTableStmt(sqlite3 * db, Table * p)<br>   return zStmt;<br> }<br> <br>-/* Return true if value x is found any of the first nCol entries of aiCol[]<br>- */<br> static int<br>-hasColumn(const i16 * aiCol, int nCol, int x)<br>+hasColumn(const struct key_part *key_parts, int nCol, const struct key_part key_part)<br> {<br>-  while (nCol-- > 0)<br>-     if (x == *(aiCol++))<br>+  int i = 0;<br>+  while (i < nCol) {<br>+     if (key_part.fieldno == key_parts->fieldno)<br>         return 1;<br>+     key_parts++;<br>+     i++;<br>+  }<br>   return 0;<br> }<br> <br>@@ -1410,13 +1371,13 @@ static void<br> convertToWithoutRowidTable(Parse * pParse, Table * pTab)<br> {<br>   Index *pPk;<br>-  int i, j;<br>+  uint32_t i, j;<br>   sqlite3 *db = pParse->db;<br> <br>   /* Mark every PRIMARY KEY column as NOT NULL (except for imposter tables)<br>    */<br>   if (!db->init.imposterTable) {<br>-     for (i = 0; i < (int)pTab->def->field_count; i++) {<br>+     for (i = 0; i < pTab->def->field_count; i++) {<br>         if (pTab->aCol[i].is_primkey) {<br>            pTab->def->fields[i].nullable_action<br>               = ON_CONFLICT_ACTION_ABORT;<br>@@ -1454,14 +1415,17 @@ convertToWithoutRowidTable(Parse * pParse, Table * pTab)<br>       * "PRIMARY KEY(a,b,a,b,c,b,c,d)" into just "PRIMARY KEY(a,b,c,d)".  Later<br>       * code assumes the PRIMARY KEY contains no repeated columns.<br>       */<br>-     for (i = j = 1; i < pPk->nColumn; i++) {<br>-        if (hasColumn(pPk->aiColumn, j, pPk->aiColumn[i])) {<br>-           pPk->nColumn--;<br>+     for (i = j = 1; i < pPk->def->key_def->part_count; i++) {<br>+        if (hasColumn(pPk->def->key_def->parts, j,<br>+              pPk->def->key_def->parts[i])) {<br>+           pPk->def->key_def->part_count--;<br>         } else {<br>-           pPk->aiColumn[j++] = pPk->aiColumn[i];<br>+           pPk->def->key_def->parts[j++] =<br>+                 pPk->def->key_def->parts[i];<br>         }<br>      }<br>-     pPk->nColumn = j;<br>+<br>+     pPk->def->key_def->part_count = j;<br>   }<br>   assert(pPk != 0);<br> }<br>@@ -1543,7 +1507,7 @@ createIndex(Parse * pParse, Index * pIndex, int iSpaceId, int iIndexId,<br>   }<br>   sqlite3VdbeAddOp4(v,<br>           OP_String8, 0, iFirstCol + 2, 0,<br>-          sqlite3DbStrDup(pParse->db, pIndex->zName),<br>+          sqlite3DbStrDup(pParse->db, pIndex->def->name),<br>           P4_DYNAMIC);<br>   sqlite3VdbeAddOp4(v, OP_String8, 0, iFirstCol + 3, 0, "tree",<br>           P4_STATIC);<br>@@ -1580,7 +1544,7 @@ makeIndexSchemaRecord(Parse * pParse,<br> <br>   sqlite3VdbeAddOp4(v,<br>           OP_String8, 0, iFirstCol, 0,<br>-          sqlite3DbStrDup(pParse->db, pIndex->zName),<br>+          sqlite3DbStrDup(pParse->db, pIndex->def->name),<br>           P4_DYNAMIC);<br> <br>   if (pParse->pNewTable) {<br>@@ -2654,8 +2618,9 @@ sqlite3RefillIndex(Parse * pParse, Index * pIndex, int memRootPage)<br>   }<br>   /* Open the sorter cursor if we are to use one. */<br>   iSorter = pParse->nTab++;<br>-  sqlite3VdbeAddOp4(v, OP_SorterOpen, iSorter, 0, pIndex->nColumn,<br>-          (char *)def, P4_KEYDEF);<br>+  sqlite3VdbeAddOp4(v, OP_SorterOpen, iSorter, 0,<br>+        pIndex->def->key_def->part_count,<br>+        (char *)def, P4_KEYDEF);<br> <br>   /* Open the table. Loop through all rows of the table, inserting index<br>    * records into the sorter.<br>@@ -2687,7 +2652,7 @@ sqlite3RefillIndex(Parse * pParse, Index * pIndex, int memRootPage)<br>      sqlite3VdbeGoto(v, j2);<br>      addr2 = sqlite3VdbeCurrentAddr(v);<br>      sqlite3VdbeAddOp4Int(v, OP_SorterCompare, iSorter, j2,<br>-                regRecord, pIndex->nColumn);<br>+           regRecord, pIndex->def->key_def->part_count);<br>      VdbeCoverage(v);<br>      sqlite3UniqueConstraint(pParse, ON_CONFLICT_ACTION_ABORT,<br>               pIndex);<br>@@ -2733,16 +2698,11 @@ sqlite3AllocateIndexObject(sqlite3 * db,   /* Database connection */<br>   p = sqlite3DbMallocZero(db, nByte + nExtra);<br>   if (p) {<br>      char *pExtra = ((char *)p) + ROUND8(sizeof(Index));<br>-     p->coll_array = (struct coll **)pExtra;<br>      pExtra += ROUND8(sizeof(struct coll **) * nCol);<br>-     p->coll_id_array = (uint32_t *) pExtra;<br>      pExtra += ROUND8(sizeof(uint32_t) * nCol);<br>      p->aiRowLogEst = (LogEst *) pExtra;<br>      pExtra += sizeof(LogEst) * (nCol + 1);<br>-     p->aiColumn = (i16 *) pExtra;<br>      pExtra += sizeof(i16) * nCol;<br>-     p->sort_order = (enum sort_order *) pExtra;<br>-     p->nColumn = nCol;<br>      *ppExtra = ((char *)p) + nByte;<br>   }<br>   return p;<br>@@ -2831,18 +2791,119 @@ addIndexToTable(Index * pIndex, Table * pTab)<br>   }<br> }<br> <br>-bool<br>-index_is_unique(Index *idx)<br>+void<br>+append(struct region *r, const char *str, size_t *total_sql_size)<br> {<br>-  assert(idx != NULL);<br>-  uint32_t space_id = SQLITE_PAGENO_TO_SPACEID(idx->tnum);<br>-  uint32_t index_id = SQLITE_PAGENO_TO_INDEXID(idx->tnum);<br>-  struct space *space = space_by_id(space_id);<br>-  assert(space != NULL);<br>-  struct index *tnt_index = space_index(space, index_id);<br>-  assert(tnt_index != NULL);<br>+  memcpy(region_alloc(r, strlen(str)), str, strlen(str));<br>+  *total_sql_size += strlen(str);<br>+}<br>+<br>+char *<br>+create_sql(const char *idx_name, struct space_def *space_def, ExprList *expr_list)<br>+{<br>+  struct region *r = &fiber()->gc;<br>+  size_t total_sql_size = 0;<br>+  append(r, "CREATE INDEX ", &total_sql_size);<br>+  append(r, idx_name, &total_sql_size);<br>+  append(r, " ON ", &total_sql_size);<br>+  append(r, space_def->name, &total_sql_size);<br>+  append(r, " (", &total_sql_size);<br>+<br>+  for (int i = 0; i < expr_list->nExpr; i++){<br>+     Expr *expr = expr_list->a[i].pExpr;<br>+     assert(expr->op == TK_COLLATE || expr->op == TK_COLUMN);<br>+     Expr *column_expr = sqlite3ExprSkipCollate(expr);<br>+     const char *name = space_def->fields[column_expr->iColumn].name;<br>+<br>+     if (expr->op == TK_COLLATE){<br>+        append(r, name, &total_sql_size);<br>+        append(r, " COLLATE ", &total_sql_size);<br>+        const char *coll_name = expr->u.zToken;<br>+        append(r, coll_name, &total_sql_size);<br>+        append(r, ", ", &total_sql_size);<br>+     } else {<br>+        append(r, name, &total_sql_size);<br>+        append(r, ", ", &total_sql_size);<br>+     }<br>+  }<br>+<br>+  memcpy(region_alloc(r, 1), "\0", 1);<br>+  total_sql_size += 1;<br>+  char *res = region_join(r, total_sql_size);<br>+<br>+  /*<br>+   * fix last ", " with ")\0"<br>+   */<br>+  res[strlen(res) - 2] = ')';<br>+  res[strlen(res) - 1] = '\0';<br>+  return res;<br>+}<br>+<br>+void<br>+set_index_def(Parse *parse, Index *index, Table *table, uint32_t iid,<br>+     const char *name, uint32_t name_len, int on_error,<br>+     ExprList *expr_list, u8 idx_type)<br>+{<br>+  struct space_def *space_def = table->def;<br>+  struct index_opts opts;<br>+  index_opts_create(&opts);<br>+  opts.is_unique = on_error != ON_CONFLICT_ACTION_NONE;<br>+<br>+  struct key_def *key_def = key_def_new(expr_list->nExpr);<br>+  if (key_def == NULL)<br>+     return;<br>+<br>+  for (int i = 0; i < expr_list->nExpr; i++) {<br>+     Expr *expr = expr_list->a[i].pExpr;<br>+     sql_resolve_self_reference(parse, table, NC_IdxExpr,<br>+              expr, 0);<br>+     if (parse->nErr > 0)<br>+        return;<br>+<br>+     Expr *column_expr = sqlite3ExprSkipCollate(expr);<br>+     if (column_expr->op != TK_COLUMN) {<br>+        sqlite3ErrorMsg(parse,<br>+              "functional indexes aren't supported "<br>+              "in the current version");<br>+        return;<br>+     }<br>+<br>+     uint32_t fieldno = column_expr->iColumn;<br>+<br>+     uint32_t coll_id;<br>+     struct coll *coll;<br>+     if (expr->op == TK_COLLATE)<br>+        coll = sql_get_coll_seq(parse, expr->u.zToken,<br>+                 &coll_id);<br>+     else<br>+        coll = sql_column_collation(space_def, fieldno,<br>+                 &coll_id);<br>+<br>+     if (sqlite3StrICmp(expr->u.zToken, "binary") != 0 &&<br>+         coll == NULL && expr->op == TK_COLLATE)<br>+        return;<br>+<br>+     /* Tarantool: DESC indexes are not supported so far.<br>+     * See gh-3016.<br>+     */<br>+     key_def_set_part(key_def, i, fieldno,<br>+           space_def->fields[fieldno].type,<br>+           space_def->fields[fieldno].nullable_action,<br>+           coll, coll_id, SORT_ORDER_ASC);<br>+  }<br>+<br>+  if (idx_type == SQLITE_IDXTYPE_APPDEF)<br>+     opts.sql = create_sql(name, table->def, expr_list);<br> <br>-  return tnt_index->def->opts.is_unique;<br>+  struct key_def *pk_key_def;<br>+  if (idx_type == SQLITE_IDXTYPE_APPDEF)<br>+     pk_key_def = table->pIndex->def->key_def;<br>+  else<br>+     pk_key_def = NULL;<br>+<br>+<br>+  index->def = index_def_new(space_def->id, iid, name, name_len,<br>+           TREE, &opts, key_def, pk_key_def);<br> }<br> <br> void<br>@@ -2853,12 +2914,11 @@ sql_create_index(struct Parse *parse, struct Token *token,<br> {<br>   Table *pTab = 0;   /* Table to be indexed */<br>   Index *pIndex = 0; /* The index to be created */<br>-  char *zName = 0;   /* Name of the index */<br>+  char *name = 0;    /* Name of the index */<br>   int nName;    /* Number of characters in zName */<br>-  int i, j;<br>+  int i;<br>   DbFixer sFix;     /* For assigning database names to pTable */<br>   sqlite3 *db = parse->db;<br>-  struct ExprList_item *col_listItem;    /* For looping over col_list */<br>   int nExtra = 0;       /* Space allocated for zExtra[] */<br>   char *zExtra = 0;  /* Extra space after the Index object */<br>   struct session *user_session = current_session();<br>@@ -2934,24 +2994,24 @@ sql_create_index(struct Parse *parse, struct Token *token,<br>    * our own name.<br>    */<br>   if (token) {<br>-     zName = sqlite3NameFromToken(db, token);<br>-     if (zName == 0)<br>+     name = sqlite3NameFromToken(db, token);<br>+     if (name == 0)<br>         goto exit_create_index;<br>      assert(token->z != 0);<br>      if (!db->init.busy) {<br>-        if (sqlite3HashFind(&db->pSchema->tblHash, zName) !=<br>+        if (sqlite3HashFind(&db->pSchema->tblHash, name) !=<br>             NULL) {<br>            sqlite3ErrorMsg(parse,<br>                  "there is already a table named %s",<br>-                 zName);<br>+                 name);<br>            goto exit_create_index;<br>         }<br>      }<br>-     if (sqlite3HashFind(&pTab->idxHash, zName) != NULL) {<br>+     if (sqlite3HashFind(&pTab->idxHash, name) != NULL) {<br>         if (!if_not_exist) {<br>            sqlite3ErrorMsg(parse,<br>                  "index %s.%s already exists",<br>-                 pTab->def->name, zName);<br>+                 pTab->def->name, name);<br>         } else {<br>            assert(!db->init.busy);<br>         }<br>@@ -2963,10 +3023,10 @@ sql_create_index(struct Parse *parse, struct Token *token,<br>      for (pLoop = pTab->pIndex, n = 1; pLoop;<br>           pLoop = pLoop->pNext, n++) {<br>      }<br>-     zName =<br>+     name =<br>          sqlite3MPrintf(db, "sqlite_autoindex_%s_%d", pTab->def->name,<br>               n);<br>-     if (zName == 0) {<br>+     if (name == 0) {<br>         goto exit_create_index;<br>      }<br>   }<br>@@ -3006,17 +3066,24 @@ sql_create_index(struct Parse *parse, struct Token *token,<br>   /*<br>    * Allocate the index structure.<br>    */<br>-  nName = sqlite3Strlen30(zName);<br>+  nName = sqlite3Strlen30(name);<br>+<br>+  if (nName > BOX_NAME_MAX) {<br>+     sqlite3ErrorMsg(parse,<br>+           "%s.%s exceeds indexes' names length limit",<br>+           pTab->def->name, name);<br>+     goto exit_create_index;<br>+  }<br>+<br>+  if (sqlite3CheckIdentifierName(parse, name) != SQLITE_OK)<br>+     goto exit_create_index;<br>+<br>   pIndex = sqlite3AllocateIndexObject(db, col_list->nExpr,<br>                   nName + nExtra + 1, &zExtra);<br>   if (db->mallocFailed) {<br>      goto exit_create_index;<br>   }<br>   assert(EIGHT_BYTE_ALIGNMENT(pIndex->aiRowLogEst));<br>-  assert(EIGHT_BYTE_ALIGNMENT(pIndex->coll_array));<br>-  pIndex->zName = zExtra;<br>-  zExtra += nName + 1;<br>-  memcpy(pIndex->zName, zName, nName + 1);<br>   pIndex->pTable = pTab;<br>   pIndex->onError = (u8) on_error;<br>   /*<br>@@ -3031,7 +3098,6 @@ sql_create_index(struct Parse *parse, struct Token *token,<br>      pIndex->idxType = idx_type;<br>   }<br>   pIndex->pSchema = db->pSchema;<br>-  pIndex->nColumn = col_list->nExpr;<br>   /* Tarantool have access to each column by any index */<br>   if (where) {<br>      sql_resolve_self_reference(parse, pTab, NC_PartIdx, where,<br>@@ -3040,60 +3106,25 @@ sql_create_index(struct Parse *parse, struct Token *token,<br>      where = NULL;<br>   }<br> <br>-  /* Analyze the list of expressions that form the terms of the index and<br>-   * report any errors.  In the common case where the expression is exactly<br>-   * a table column, store that column in aiColumn[].  For general expressions,<br>-   * populate pIndex->aColExpr and store XN_EXPR (-2) in aiColumn[].<br>-   *<br>+  /*<br>    * TODO: Issue a warning if two or more columns of the index are identical.<br>    * TODO: Issue a warning if the table primary key is used as part of the<br>    * index key.<br>    */<br>-  for (i = 0, col_listItem = col_list->a; i < col_list->nExpr;<br>-       i++, col_listItem++) {<br>-     Expr *pCExpr;  /* The i-th index expression */<br>-     sql_resolve_self_reference(parse, pTab, NC_IdxExpr,<br>-                 col_listItem->pExpr, NULL);<br>-     if (parse->nErr > 0)<br>-        goto exit_create_index;<br>-     pCExpr = sqlite3ExprSkipCollate(col_listItem->pExpr);<br>-     if (pCExpr->op != TK_COLUMN) {<br>-        sqlite3ErrorMsg(parse,<br>-              "functional indexes aren't supported "<br>-              "in the current version");<br>-        goto exit_create_index;<br>-     } else {<br>-        j = pCExpr->iColumn;<br>-        assert(j <= 0x7fff);<br>-        if (j < 0) {<br>-           j = pTab->iPKey;<br>-        }<br>-        pIndex->aiColumn[i] = (i16) j;<br>-     }<br>-     struct coll *coll;<br>-     uint32_t id;<br>-     if (col_listItem->pExpr->op == TK_COLLATE) {<br>-        const char *coll_name = col_listItem->pExpr->u.zToken;<br>-        coll = sql_get_coll_seq(parse, coll_name, &id);<br> <br>-        if (coll == NULL &&<br>-            sqlite3StrICmp(coll_name, "binary") != 0) {<br>-           goto exit_create_index;<br>-        }<br>-     } else if (j >= 0) {<br>-        coll = sql_column_collation(pTab->def, j, &id);<br>-     } else {<br>-        id = COLL_NONE;<br>-        coll = NULL;<br>-     }<br>-     pIndex->coll_array[i] = coll;<br>-     pIndex->coll_id_array[i] = id;<br>-<br>-     /* Tarantool: DESC indexes are not supported so far.<br>-      * See gh-3016.<br>-      */<br>-     pIndex->sort_order[i] = SORT_ORDER_ASC;<br>+  uint32_t max_iid = 0;<br>+  for (Index *index = pTab->pIndex; index; index = index->pNext) {<br>+     max_iid = (max_iid > index->def->iid) ? max_iid :<br>+           index->def->iid + 1;<br>   }<br>+<br>+  set_index_def(parse, pIndex, pTab, max_iid, name, nName, on_error,<br>+        col_list, idx_type);<br>+<br>+  if (pIndex->def == NULL ||<br>+      !index_def_is_valid(pIndex->def, pTab->def->name))<br>+        goto exit_create_index;<br>+<br>   if (pTab == parse->pNewTable) {<br>      /* This routine has been called to create an automatic index as a<br>       * result of a PRIMARY KEY or UNIQUE clause on a column definition, or<br>@@ -3118,25 +3149,24 @@ sql_create_index(struct Parse *parse, struct Token *token,<br>       */<br>      Index *pIdx;<br>      for (pIdx = pTab->pIndex; pIdx; pIdx = pIdx->pNext) {<br>-        int k;<br>+        uint32_t k;<br>         assert(IsUniqueIndex(pIdx));<br>         assert(pIdx->idxType != SQLITE_IDXTYPE_APPDEF);<br>         assert(IsUniqueIndex(pIndex));<br> <br>-        if (pIdx->nColumn != pIndex->nColumn)<br>+        if (pIdx->def->key_def->part_count !=<br>+              pIndex->def->key_def->part_count)<br>            continue;<br>-        for (k = 0; k < pIdx->nColumn; k++) {<br>-           assert(pIdx->aiColumn[k] >= 0);<br>-           if (pIdx->aiColumn[k] != pIndex->aiColumn[k])<br>+        for (k = 0; k < pIdx->def->key_def->part_count; k++) {<br>+           if (pIdx->def->key_def->parts[k].fieldno != pIndex->def->key_def->parts[k].fieldno)<br>               break;<br>            struct coll *coll1, *coll2;<br>-           uint32_t id;<br>-           coll1 = sql_index_collation(pIdx, k, &id);<br>-           coll2 = sql_index_collation(pIndex, k, &id);<br>+           coll1 = pIdx->def->key_def->parts[k].coll;<br>+           coll2 = pIndex->def->key_def->parts[k].coll;<br>            if (coll1 != coll2)<br>               break;<br>         }<br>-        if (k == pIdx->nColumn) {<br>+        if (k == pIdx->def->key_def->part_count) {<br>            if (pIdx->onError != pIndex->onError) {<br>               /* This constraint creates the same index as a previous<br>                * constraint specified somewhere in the CREATE TABLE statement.<br>@@ -3159,6 +3189,7 @@ sql_create_index(struct Parse *parse, struct Token *token,<br>            }<br>            if (idx_type == SQLITE_IDXTYPE_PRIMARYKEY)<br>               pIdx->idxType = idx_type;<br>+<br>            goto exit_create_index;<br>         }<br>      }<br>@@ -3170,7 +3201,7 @@ sql_create_index(struct Parse *parse, struct Token *token,<br>   assert(parse->nErr == 0);<br>   if (db->init.busy) {<br>      Index *p;<br>-     p = sqlite3HashInsert(&pTab->idxHash, pIndex->zName, pIndex);<br>+     p = sqlite3HashInsert(&pTab->idxHash, pIndex->def->name, pIndex);<br>      if (p) {<br>         assert(p == pIndex);   /* Malloc must have failed */<br>         sqlite3OomFault(db);<br>@@ -3268,28 +3299,7 @@ sql_create_index(struct Parse *parse, struct Token *token,<br>   sql_expr_delete(db, where, false);<br>   sql_expr_list_delete(db, col_list);<br>   sqlite3SrcListDelete(db, tbl_name);<br>-  sqlite3DbFree(db, zName);<br>-}<br>-<br>-/**<br>- * Return number of columns in given index.<br>- * If space is ephemeral, use internal<br>- * SQL structure to fetch the value.<br>- */<br>-uint32_t<br>-index_column_count(const Index *idx)<br>-{<br>-  assert(idx != NULL);<br>-  uint32_t space_id = SQLITE_PAGENO_TO_SPACEID(idx->tnum);<br>-  struct space *space = space_by_id(space_id);<br>-  /* It is impossible to find an ephemeral space by id. */<br>-  if (space == NULL)<br>-     return idx->nColumn;<br>-<br>-  uint32_t index_id = SQLITE_PAGENO_TO_INDEXID(idx->tnum);<br>-  struct index *index = space_index(space, index_id);<br>-  assert(index != NULL);<br>-  return index->def->key_def->part_count;<br>+  sqlite3DbFree(db, name);<br> }<br> <br> /** Return true if given index is unique and not nullable. */<br>@@ -3297,15 +3307,8 @@ bool<br> index_is_unique_not_null(const Index *idx)<br> {<br>   assert(idx != NULL);<br>-  uint32_t space_id = SQLITE_PAGENO_TO_SPACEID(idx->tnum);<br>-  struct space *space = space_by_id(space_id);<br>-  assert(space != NULL);<br>-<br>-  uint32_t index_id = SQLITE_PAGENO_TO_INDEXID(idx->tnum);<br>-  struct index *index = space_index(space, index_id);<br>-  assert(index != NULL);<br>-  return (index->def->opts.is_unique &&<br>-     !index->def->key_def->is_nullable);<br>+  assert(idx->def != NULL);<br>+  return (idx->def->key_def->is_nullable && idx->def->opts.is_unique);<br> }<br> <br> void<br>@@ -3933,18 +3936,18 @@ sqlite3UniqueConstraint(Parse * pParse,    /* Parsing context */<br>     )<br> {<br>   char *zErr;<br>-  int j;<br>+  uint32_t j;<br>   StrAccum errMsg;<br>   Table *pTab = pIdx->pTable;<br> <br>   sqlite3StrAccumInit(&errMsg, pParse->db, 0, 0, 200);<br>   if (pIdx->aColExpr) {<br>-     sqlite3XPrintf(&errMsg, "index '%q'", pIdx->zName);<br>+     sqlite3XPrintf(&errMsg, "index '%q'", pIdx->def->name);<br>   } else {<br>-     for (j = 0; j < pIdx->nColumn; j++) {<br>+     for (j = 0; j < pIdx->def->key_def->part_count; j++) {<br>         char *zCol;<br>-        assert(pIdx->aiColumn[j] >= 0);<br>-        zCol = pTab->def->fields[pIdx->aiColumn[j]].name;<br>+        uint32_t fieldno = pIdx->def->key_def->parts[j].fieldno;<br>+        zCol = pTab->def->fields[fieldno].name;<br>         if (j)<br>            sqlite3StrAccumAppend(&errMsg, ", ", 2);<br>         sqlite3XPrintf(&errMsg, "%s.%s", pTab->def->name, zCol);<br>@@ -3967,11 +3970,10 @@ static bool<br> collationMatch(struct coll *coll, struct Index *index)<br> {<br>   assert(coll != NULL);<br>-  for (int i = 0; i < index->nColumn; i++) {<br>-     uint32_t id;<br>-     struct coll *idx_coll = sql_index_collation(index, i, &id);<br>-     assert(idx_coll != 0 || index->aiColumn[i] < 0);<br>-     if (index->aiColumn[i] >= 0 && coll == idx_coll)<br>+  for (uint32_t i = 0; i < index->def->key_def->part_count; i++) {<br>+     struct coll *idx_coll = index->def->key_def->parts[i].coll;<br>+     assert(idx_coll != NULL);<br>+     if (coll == idx_coll)<br>         return true;<br>   }<br>   return false;<br>diff --git a/src/box/sql/delete.c b/src/box/sql/delete.c<br>index ddad54b3e..504738cd5 100644<br>--- a/src/box/sql/delete.c<br>+++ b/src/box/sql/delete.c<br>@@ -209,7 +209,7 @@ sql_table_delete_from(struct Parse *parse, struct SrcList *tab_list,<br>      } else {<br>         pk = sqlite3PrimaryKeyIndex(table);<br>         assert(pk != NULL);<br>-        pk_len = index_column_count(pk);<br>+        pk_len = pk->def->key_def->part_count;<br>         parse->nMem += pk_len;<br>         sqlite3VdbeAddOp2(v, OP_OpenTEphemeral, eph_cursor,<br>                 pk_len);<br>@@ -252,11 +252,11 @@ sql_table_delete_from(struct Parse *parse, struct SrcList *tab_list,<br>      /* Extract the primary key for the current row */<br>      if (!is_view) {<br>         for (int i = 0; i < pk_len; i++) {<br>-           assert(pk->aiColumn[i] >= 0);<br>            sqlite3ExprCodeGetColumnOfTable(v, table->def,<br>                        tab_cursor,<br>-                       pk-><br>-                       aiColumn[i],<br>+                       pk->def-><br>+                       key_def-><br>+                       parts[i].fieldno,<br>                        reg_pk + i);<br>         }<br>      } else {<br>@@ -326,7 +326,7 @@ sql_table_delete_from(struct Parse *parse, struct SrcList *tab_list,<br>         sqlite3VdbeAddOp3(v, OP_OpenWrite, tab_cursor,<br>                 table->tnum, space_ptr_reg);<br>         sql_vdbe_set_p4_key_def(parse, pk);<br>-        VdbeComment((v, "%s", pk->zName));<br>+        VdbeComment((v, "%s", pk->def->name));<br> <br>         if (one_pass == ONEPASS_MULTI)<br>            sqlite3VdbeJumpHere(v, iAddrOnce);<br>@@ -536,14 +536,14 @@ sql_generate_index_key(struct Parse *parse, struct Index *index, int cursor,<br>         *part_idx_label = 0;<br>      }<br>   }<br>-  int col_cnt = index_column_count(index);<br>+  int col_cnt = index->def->key_def->part_count;<br>   int reg_base = sqlite3GetTempRange(parse, col_cnt);<br>   if (prev != NULL && (reg_base != reg_prev ||<br>              prev->pPartIdxWhere != NULL))<br>      prev = NULL;<br>   for (int j = 0; j < col_cnt; j++) {<br>-     if (prev != NULL && prev->aiColumn[j] == index->aiColumn[j]<br>-         && prev->aiColumn[j] != XN_EXPR) {<br>+     if (prev->def->key_def->parts[j].fieldno ==<br>+         index->def->key_def->parts[j].fieldno && prev == NULL) {<br>         /*<br>          * This column was already computed by the<br>          * previous index.<br>diff --git a/src/box/sql/expr.c b/src/box/sql/expr.c<br>index 8866f6fed..93cd45470 100644<br>--- a/src/box/sql/expr.c<br>+++ b/src/box/sql/expr.c<br>@@ -2422,21 +2422,20 @@ sqlite3FindInIndex(Parse * pParse, /* Parsing context */<br>              pIdx = pIdx->pNext) {<br>            Bitmask colUsed; /* Columns of the index used */<br>            Bitmask mCol;  /* Mask for the current column */<br>-           if (pIdx->nColumn < nExpr)<br>+           if ((int)pIdx->def->key_def->part_count < nExpr)<br>               continue;<br>            /* Maximum nColumn is BMS-2, not BMS-1, so that we can compute<br>             * BITMASK(nExpr) without overflowing<br>             */<br>-           testcase(pIdx->nColumn == BMS - 2);<br>-           testcase(pIdx->nColumn == BMS - 1);<br>-           if (pIdx->nColumn >= BMS - 1)<br>+           testcase(pIdx->def->key_def->part_count == BMS - 2);<br>+           testcase(pIdx->def->key_def->part_count == BMS - 1);<br>+           if (pIdx->def->key_def->part_count >= BMS - 1)<br>               continue;<br>            if (mustBeUnique) {<br>-              if (pIdx->nColumn > nExpr<br>-                  || (pIdx->nColumn > nExpr<br>-                  && !index_is_unique(pIdx))) {<br>-                    continue;  /* This index is not unique over the IN RHS columns */<br>-              }<br>+              if ((int)pIdx->def->key_def->part_count > nExpr<br>+                  || !pIdx->def->opts.is_unique)<br>+                 /* This index is not unique over the IN RHS columns */<br>+                    continue;<br>            }<br> <br>            colUsed = 0;   /* Columns of index used so far */<br>@@ -2449,12 +2448,13 @@ sqlite3FindInIndex(Parse * pParse, /* Parsing context */<br>               int j;<br> <br>               for (j = 0; j < nExpr; j++) {<br>-                 if (pIdx->aiColumn[j] !=<br>-                     pRhs->iColumn) {<br>-                    continue;<br>-                 }<br>-                 struct coll *idx_coll;<br>-                 idx_coll = sql_index_collation(pIdx, j, &id);<br>+                 if ((int)pIdx->def->key_def-><br>+                    parts[j].fieldno<br>+                    != pRhs->iColumn)<br>+                       continue;<br>+<br>+                 struct coll *idx_coll =<br>+                    pIdx->def->key_def->parts[j].coll;<br>                  if (pReq != NULL &&<br>                      pReq != idx_coll) {<br>                     continue;<br>@@ -2483,12 +2483,12 @@ sqlite3FindInIndex(Parse * pParse, /* Parsing context */<br>                       0, 0, 0,<br>                       sqlite3MPrintf(db,<br>                       "USING INDEX %s FOR IN-OPERATOR",<br>-                      pIdx->zName),<br>+                      pIdx->def->name),<br>                       P4_DYNAMIC);<br>               emit_open_cursor(pParse, iTab,<br>                      pIdx->tnum);<br>               sql_vdbe_set_p4_key_def(pParse, pIdx);<br>-              VdbeComment((v, "%s", pIdx->zName));<br>+              VdbeComment((v, "%s", pIdx->def->name));<br>               assert(IN_INDEX_INDEX_DESC ==<br>                      IN_INDEX_INDEX_ASC + 1);<br>               eType = IN_INDEX_INDEX_ASC +<br>@@ -2515,7 +2515,7 @@ sqlite3FindInIndex(Parse * pParse,   /* Parsing context */<br>                     /* Tarantool: Check for null is performed on first key of the index.  */<br>                     sqlite3SetHasNullFlag(v,<br>                                 iTab,<br>-                                pIdx->aiColumn[0],<br>+                                pIdx->def->key_def->parts[0].fieldno,<br>                                 *prRhsHasNull);<br>                  }<br>               }<br>@@ -3146,12 +3146,13 @@ sqlite3ExprCodeIN(Parse * pParse,  /* Parsing and code generating context */<br>      struct Index *pk = sqlite3PrimaryKeyIndex(tab);<br>      assert(pk);<br> <br>+     uint32_t fieldno = pk->def->key_def->parts[0].fieldno;<br>      enum affinity_type affinity =<br>-        tab->def->fields[pk->aiColumn[0]].affinity;<br>-     if (pk->nColumn == 1<br>+        tab->def->fields[fieldno].affinity;<br>+     if (pk->def->key_def->part_count == 1<br>          && affinity == AFFINITY_INTEGER<br>-         && pk->aiColumn[0] < nVector) {<br>-        int reg_pk = rLhs + pk->aiColumn[0];<br>+         && (int) fieldno < nVector) {<br>+        int reg_pk = rLhs + (int)fieldno;<br>         sqlite3VdbeAddOp2(v, OP_MustBeInt, reg_pk, destIfFalse);<br>      }<br>   }<br>@@ -3483,17 +3484,9 @@ sqlite3ExprCodeLoadIndexColumn(Parse * pParse,  /* The parsing context */<br>                int regOut  /* Store the index column value in this register */<br>     )<br> {<br>-  i16 iTabCol = pIdx->aiColumn[iIdxCol];<br>-  if (iTabCol == XN_EXPR) {<br>-     assert(pIdx->aColExpr);<br>-     assert(pIdx->aColExpr->nExpr > iIdxCol);<br>-     pParse->iSelfTab = iTabCur;<br>-     sqlite3ExprCodeCopy(pParse, pIdx->aColExpr->a[iIdxCol].pExpr,<br>-               regOut);<br>-  } else {<br>-     sqlite3ExprCodeGetColumnOfTable(pParse->pVdbe, pIdx->pTable->def,<br>-                 iTabCur, iTabCol, regOut);<br>-  }<br>+  i16 iTabCol = pIdx->def->key_def->parts[iIdxCol].fieldno;<br>+  sqlite3ExprCodeGetColumnOfTable(pParse->pVdbe, pIdx->pTable->def,<br>+              iTabCur, iTabCol, regOut);<br> }<br> <br> void<br>diff --git a/src/box/sql/fkey.c b/src/box/sql/fkey.c<br>index 70ebef89f..878c6d0b3 100644<br>--- a/src/box/sql/fkey.c<br>+++ b/src/box/sql/fkey.c<br>@@ -256,8 +256,8 @@ sqlite3FkLocateIndex(Parse * pParse,   /* Parse context to store any error in */<br>   }<br> <br>   for (pIdx = pParent->pIndex; pIdx; pIdx = pIdx->pNext) {<br>-     int nIdxCol = index_column_count(pIdx);<br>-     if (nIdxCol == nCol && index_is_unique(pIdx)<br>+     int nIdxCol = pIdx->def->key_def->part_count;<br>+     if (nIdxCol == nCol && pIdx->def->opts.is_unique<br>          && pIdx->pPartIdxWhere == 0) {<br>         /* pIdx is a UNIQUE index (or a PRIMARY KEY) and has the right number<br>          * of columns. If each indexed column corresponds to a foreign key<br>@@ -287,7 +287,8 @@ sqlite3FkLocateIndex(Parse * pParse,   /* Parse context to store any error in */<br>             */<br>            int i, j;<br>            for (i = 0; i < nCol; i++) {<br>-              i16 iCol = pIdx->aiColumn[i];  /* Index of column in parent tbl */<br>+              i16 iCol = (int) pIdx->def->key_def->parts[i].fieldno; /* Index of column in parent tbl */<br>+<br>               char *zIdxCol; /* Name of indexed column */<br> <br>               if (iCol < 0)<br>@@ -303,8 +304,7 @@ sqlite3FkLocateIndex(Parse * pParse,   /* Parse context to store any error in */<br>                           iCol,<br>                           &id);<br>               struct coll *coll =<br>-                 sql_index_collation(pIdx, i,<br>-                           &id);<br>+                 pIdx->def->key_def->parts[i].coll;<br>               if (def_coll != coll)<br>                  break;<br> <br>@@ -464,13 +464,14 @@ fkLookupParent(Parse * pParse,   /* Parse context */<br>            for (i = 0; i < nCol; i++) {<br>               int iChild = aiCol[i] + 1 + regData;<br>               int iParent =<br>-                  pIdx->aiColumn[i] + 1 + regData;<br>-              assert(pIdx->aiColumn[i] >= 0);<br>+                 (int) pIdx->def->key_def->parts[i].fieldno<br>+                 + 1 + regData;<br>               assert(aiCol[i] != pTab->iPKey);<br>-              if (pIdx->aiColumn[i] == pTab->iPKey) {<br>+              if ((int)pIdx->def->key_def-><br>+                 parts[i].fieldno == pTab->iPKey)<br>                  /* The parent key is a composite key that includes the IPK column */<br>-                 iParent = regData;<br>-              }<br>+                    iParent = regData;<br>+<br>               sqlite3VdbeAddOp3(v, OP_Ne, iChild,<br>                       iJump, iParent);<br>               VdbeCoverage(v);<br>@@ -622,7 +623,7 @@ fkScanChildren(Parse * pParse, /* Parse context */<br>   Vdbe *v = sqlite3GetVdbe(pParse);<br> <br>   assert(pIdx == 0 || pIdx->pTable == pTab);<br>-  assert(pIdx == 0 || (int)index_column_count(pIdx) == pFKey->nCol);<br>+  assert(pIdx == 0 || (int) pIdx->def->key_def->part_count == pFKey->nCol);<br>   assert(pIdx != 0);<br> <br>   if (nIncr < 0) {<br>@@ -646,7 +647,7 @@ fkScanChildren(Parse * pParse, /* Parse context */<br>      i16 iCol;  /* Index of column in child table */<br>      const char *zCol;  /* Name of column in child table */<br> <br>-     iCol = pIdx ? pIdx->aiColumn[i] : -1;<br>+     iCol = pIdx ? pIdx->def->key_def->parts[i].fieldno : -1;<br>      pLeft = exprTableRegister(pParse, pTab, regData, iCol);<br>      iCol = aiCol ? aiCol[i] : pFKey->aCol[0].iFrom;<br>      assert(iCol >= 0);<br>@@ -671,10 +672,9 @@ fkScanChildren(Parse * pParse,    /* Parse context */<br>      Expr *pEq, *pAll = 0;<br>      Index *pPk = sqlite3PrimaryKeyIndex(pTab);<br>      assert(pIdx != 0);<br>-     int col_count = index_column_count(pPk);<br>+     int col_count = pPk->def->key_def->part_count;<br>      for (i = 0; i < col_count; i++) {<br>-        i16 iCol = pIdx->aiColumn[i];<br>-        assert(iCol >= 0);<br>+        i16 iCol = (int) pIdx->def->key_def->parts[i].fieldno;<br>         pLeft = exprTableRegister(pParse, pTab, regData, iCol);<br>         pRight =<br>            exprTableColumn(db, pTab->def,<br>@@ -992,7 +992,6 @@ sqlite3FkCheck(Parse * pParse, /* Parse context */<br>         if (aiCol[i] == pTab->iPKey) {<br>            aiCol[i] = -1;<br>         }<br>-        assert(pIdx == 0 || pIdx->aiColumn[i] >= 0);<br>      }<br> <br>      pParse->nTab++;<br>@@ -1126,10 +1125,9 @@ sqlite3FkOldmask(Parse * pParse,    /* Parse context */<br>         Index *pIdx = 0;<br>         sqlite3FkLocateIndex(pParse, pTab, p, &pIdx, 0);<br>         if (pIdx) {<br>-           int nIdxCol = index_column_count(pIdx);<br>+           int nIdxCol = pIdx->def->key_def->part_count;<br>            for (i = 0; i < nIdxCol; i++) {<br>-              assert(pIdx->aiColumn[i] >= 0);<br>-              mask |= COLUMN_MASK(pIdx->aiColumn[i]);<br>+              mask |= COLUMN_MASK(pIdx->def->key_def->parts[i].fieldno);<br>            }<br>         }<br>      }<br>@@ -1264,11 +1262,10 @@ fkActionTrigger(Parse * pParse,    /* Parse context */<br>                || (pTab->iPKey >= 0<br>               && pTab->iPKey <<br>                  (int)pTab->def->field_count));<br>-        assert(pIdx == 0 || pIdx->aiColumn[i] >= 0);<br>         sqlite3TokenInit(&tToCol,<br>                pTab->def->fields[pIdx ? pIdx-><br>-                     aiColumn[i] : pTab->iPKey].<br>-               name);<br>+                  def->key_def->parts[i].fieldno<br>+                 : pTab->iPKey].name);<br>         sqlite3TokenInit(&tFromCol,<br>                pFKey->pFrom->def->fields[<br>                  iFromCol].name);<br>diff --git a/src/box/sql/insert.c b/src/box/sql/insert.c<br>index 59c61c703..09bda843b 100644<br>--- a/src/box/sql/insert.c<br>+++ b/src/box/sql/insert.c<br>@@ -89,7 +89,7 @@ sqlite3IndexAffinityStr(sqlite3 * db, Index * pIdx)<br>       * up.<br>       */<br>      int n;<br>-     int nColumn = index_column_count(pIdx);<br>+     int nColumn = pIdx->def->key_def->part_count;<br>      pIdx->zColAff =<br>          (char *)sqlite3DbMallocRaw(0, nColumn + 1);<br>      if (!pIdx->zColAff) {<br>@@ -97,22 +97,8 @@ sqlite3IndexAffinityStr(sqlite3 * db, Index * pIdx)<br>         return 0;<br>      }<br>      for (n = 0; n < nColumn; n++) {<br>-        i16 x = pIdx->aiColumn[n];<br>-        if (x >= 0) {<br>-           char affinity = pIdx->pTable-><br>-              def->fields[x].affinity;<br>-           pIdx->zColAff[n] = affinity;<br>-        } else {<br>-           char aff;<br>-           assert(x == XN_EXPR);<br>-           assert(pIdx->aColExpr != 0);<br>-           aff =<br>-               sqlite3ExprAffinity(pIdx->aColExpr->a[n].<br>-                    pExpr);<br>-           if (aff == 0)<br>-              aff = AFFINITY_BLOB;<br>-           pIdx->zColAff[n] = aff;<br>-        }<br>+        i16 x = pIdx->def->key_def->parts[n].fieldno;<br>+        pIdx->zColAff[n] = pIdx->pTable->def->fields[x].affinity;<br>      }<br>      pIdx->zColAff[n] = 0;<br>   }<br>@@ -645,7 +631,7 @@ sqlite3Insert(Parse * pParse,  /* Parser context */<br>           pIdx = pIdx->pNext, i++) {<br>         assert(pIdx);<br>         aRegIdx[i] = ++pParse->nMem;<br>-        pParse->nMem += index_column_count(pIdx);<br>+        pParse->nMem += pIdx->def->key_def->part_count;<br>      }<br>   }<br> <br>@@ -1088,7 +1074,7 @@ sqlite3GenerateConstraintChecks(Parse * pParse,     /* The parser context */<br>   nCol = pTab->def->field_count;<br> <br>   pPk = sqlite3PrimaryKeyIndex(pTab);<br>-  nPkField = index_column_count(pPk);<br>+  nPkField = pPk->def->key_def->part_count;<br> <br>   /* Record that this module has started */<br>   VdbeModuleComment((v, "BEGIN: GenCnstCks(%d,%d,%d,%d,%d)",<br>@@ -1252,38 +1238,27 @@ sqlite3GenerateConstraintChecks(Parse * pParse,       /* The parser context */<br>       * the insert or update.  Store that record in the aRegIdx[ix] register<br>       */<br>      regIdx = aRegIdx[ix] + 1;<br>-     int nIdxCol = (int)index_column_count(pIdx);<br>+     int nIdxCol = pIdx->def->key_def->part_count;<br>      for (i = 0; i < nIdxCol; i++) {<br>-        int iField = pIdx->aiColumn[i];<br>+        int iField = (int) pIdx->def->key_def->parts[i].fieldno;<br>         int x;<br>-        if (iField == XN_EXPR) {<br>-           pParse->ckBase = regNewData + 1;<br>-           sqlite3ExprCodeCopy(pParse,<br>-                     pIdx->aColExpr->a[i].pExpr,<br>-                     regIdx + i);<br>-           pParse->ckBase = 0;<br>-           VdbeComment((v, "%s column %d", pIdx->zName,<br>-                   i));<br>-        } else {<br>-           /* OP_SCopy copies value in separate register,<br>-            * which later will be used by OP_NoConflict.<br>-            * But OP_NoConflict is necessary only in cases<br>-            * when bytecode is needed for proper UNIQUE<br>-            * constraint handling.<br>-            */<br>-           if (uniqueByteCodeNeeded) {<br>-              if (iField == pTab->iPKey)<br>-                 x = regNewData;<br>-              else<br>-                 x = iField + regNewData + 1;<br>-<br>-              assert(iField >= 0);<br>-              sqlite3VdbeAddOp2(v, OP_SCopy,<br>-                      x, regIdx + i);<br>-              VdbeComment((v, "%s",<br>-                      pTab->def->fields[<br>-                    iField].name));<br>-           }<br>+        /* OP_SCopy copies value in separate register,<br>+         * which later will be used by OP_NoConflict.<br>+         * But OP_NoConflict is necessary only in cases<br>+         * when bytecode is needed for proper UNIQUE<br>+         * constraint handling.<br>+         */<br>+        if (uniqueByteCodeNeeded) {<br>+           if (iField == pTab->iPKey)<br>+              x = regNewData;<br>+           else<br>+              x = iField + regNewData + 1;<br>+<br>+           assert(iField >= 0);<br>+           sqlite3VdbeAddOp2(v, OP_SCopy,<br>+                   x, regIdx + i);<br>+           VdbeComment((v, "%s",<br>+                   pTab->def->fields[iField].name));<br>         }<br>      }<br> <br>@@ -1293,8 +1268,12 @@ sqlite3GenerateConstraintChecks(Parse * pParse,    /* The parser context */<br>         /* If PK is marked as INTEGER, use it as strict type,<br>          * not as affinity. Emit code for type checking */<br>         if (nIdxCol == 1) {<br>-           reg_pk = regNewData + 1 + pIdx->aiColumn[0];<br>-           if (pTab->zColAff[pIdx->aiColumn[0]] ==<br>+           reg_pk = regNewData + 1 +<br>+               pIdx->def->key_def->parts[0].fieldno;<br>+<br>+           int fieldno = (int)pIdx->def->key_def-><br>+              parts[0].fieldno;<br>+           if (pTab->zColAff[fieldno] ==<br>                AFFINITY_INTEGER) {<br>               int skip_if_null = sqlite3VdbeMakeLabel(v);<br>               if ((pTab->tabFlags & TF_Autoincrement) != 0) {<br>@@ -1312,7 +1291,7 @@ sqlite3GenerateConstraintChecks(Parse * pParse,     /* The parser context */<br> <br>         sqlite3VdbeAddOp3(v, OP_MakeRecord, regNewData + 1,<br>               pTab->def->field_count, aRegIdx[ix]);<br>-        VdbeComment((v, "for %s", pIdx->zName));<br>+        VdbeComment((v, "for %s", pIdx->def->name));<br>      }<br> <br>      /* In an UPDATE operation, if this index is the PRIMARY KEY<br>@@ -1400,7 +1379,7 @@ sqlite3GenerateConstraintChecks(Parse * pParse,     /* The parser context */<br>      if (uniqueByteCodeNeeded) {<br>         sqlite3VdbeAddOp4Int(v, OP_NoConflict, iThisCur,<br>                    addrUniqueOk, regIdx,<br>-                   index_column_count(pIdx));<br>+                   pIdx->def->key_def->part_count);<br>      }<br>      VdbeCoverage(v);<br> <br>@@ -1410,19 +1389,18 @@ sqlite3GenerateConstraintChecks(Parse * pParse,       /* The parser context */<br>                         nPkField);<br>      if (isUpdate || on_error == ON_CONFLICT_ACTION_REPLACE) {<br>         int x;<br>-        int nPkCol = index_column_count(pPk);<br>+        int nPkCol = pPk->def->key_def->part_count;<br>         /* Extract the PRIMARY KEY from the end of the index entry and<br>          * store it in registers regR..regR+nPk-1<br>          */<br>         if (pIdx != pPk) {<br>            for (i = 0; i < nPkCol; i++) {<br>-              assert(pPk->aiColumn[i] >= 0);<br>-              x = pPk->aiColumn[i];<br>+              x = pPk->def->key_def->parts[i].fieldno;<br>               sqlite3VdbeAddOp3(v, OP_Column,<br>                       iThisCur, x, regR + i);<br>               VdbeComment((v, "%s.%s", pTab->def->name,<br>                  pTab->def->fields[<br>-                    pPk->aiColumn[i]].name));<br>+                    x].name));<br>            }<br>         }<br>         if (isUpdate && uniqueByteCodeNeeded) {<br>@@ -1440,10 +1418,11 @@ sqlite3GenerateConstraintChecks(Parse * pParse,       /* The parser context */<br>                     regIdx : regR);<br> <br>            for (i = 0; i < nPkCol; i++) {<br>-              uint32_t id;<br>-              char *p4 = (char *)sql_index_collation(pPk, i, &id);<br>-              x = pPk->aiColumn[i];<br>-              assert(x >= 0);<br>+              char *p4 = (char *) pPk->def->key_def->parts[i].coll;<br>+              x = pPk->def->key_def->parts[i].fieldno;<br>+              if (pPk->tnum==0) {<br>+                 x = -1;<br>+              }<br>               if (i == (nPkCol - 1)) {<br>                  addrJump = addrUniqueOk;<br>                  op = OP_Eq;<br>@@ -1620,8 +1599,8 @@ sqlite3OpenTableAndIndices(Parse * pParse,   /* Parsing context */<br>          IsPrimaryKeyIndex(pIdx) ||    /* Condition 2 */<br>          sqlite3FkReferences(pTab) ||   /* Condition 3 */<br>          /* Condition 4 */<br>-         (index_is_unique(pIdx) && pIdx->onError !=<br>-          ON_CONFLICT_ACTION_DEFAULT &&<br>+         (pIdx->def->opts.is_unique && pIdx->onError !=<br>+                   ON_CONFLICT_ACTION_DEFAULT &&<br>           /* Condition 4.1 */<br>           pIdx->onError != ON_CONFLICT_ACTION_ABORT) ||<br>           /* Condition 4.2 */<br>@@ -1639,7 +1618,7 @@ sqlite3OpenTableAndIndices(Parse * pParse,   /* Parsing context */<br>                    space_ptr_reg);<br>            sql_vdbe_set_p4_key_def(pParse, pIdx);<br>            sqlite3VdbeChangeP5(v, p5);<br>-           VdbeComment((v, "%s", pIdx->zName));<br>+           VdbeComment((v, "%s", pIdx->def->name));<br>         }<br>      }<br>   }<br>@@ -1676,8 +1655,8 @@ xferCompatibleIndex(Index * pDest, Index * pSrc)<br>   uint32_t i;<br>   assert(pDest && pSrc);<br>   assert(pDest->pTable != pSrc->pTable);<br>-  uint32_t nDestCol = index_column_count(pDest);<br>-  uint32_t nSrcCol = index_column_count(pSrc);<br>+  uint32_t nDestCol = pDest->def->key_def->part_count;<br>+  uint32_t nSrcCol = pSrc->def->key_def->part_count;<br>   if (nDestCol != nSrcCol) {<br>      return 0;  /* Different number of columns */<br>   }<br>@@ -1685,24 +1664,17 @@ xferCompatibleIndex(Index * pDest, Index * pSrc)<br>      return 0;  /* Different conflict resolution strategies */<br>   }<br>   for (i = 0; i < nSrcCol; i++) {<br>-     if (pSrc->aiColumn[i] != pDest->aiColumn[i]) {<br>+<br>+     if (pSrc->def->key_def->parts[i].fieldno !=<br>+         pDest->def->key_def->parts[i].fieldno) {<br>         return 0;  /* Different columns indexed */<br>      }<br>-     if (pSrc->aiColumn[i] == XN_EXPR) {<br>-        assert(pSrc->aColExpr != 0 && pDest->aColExpr != 0);<br>-        if (sqlite3ExprCompare(pSrc->aColExpr->a[i].pExpr,<br>-                     pDest->aColExpr->a[i].pExpr,<br>-                     -1) != 0) {<br>-           return 0;  /* Different expressions in the index */<br>-        }<br>-     }<br>      if (sql_index_column_sort_order(pSrc, i) !=<br>          sql_index_column_sort_order(pDest, i)) {<br>         return 0;  /* Different sort orders */<br>      }<br>-     uint32_t id;<br>-     if (sql_index_collation(pSrc, i, &id) !=<br>-         sql_index_collation(pDest, i, &id)) {<br>+     if (pSrc->def->key_def->parts[i].coll !=<br>+         pDest->def->key_def->parts[i].coll) {<br>         return 0;  /* Different collating sequences */<br>      }<br>   }<br>@@ -1876,7 +1848,7 @@ xferOptimization(Parse * pParse, /* Parser context */<br>      }<br>   }<br>   for (pDestIdx = pDest->pIndex; pDestIdx; pDestIdx = pDestIdx->pNext) {<br>-     if (index_is_unique(pDestIdx)) {<br>+     if (pDestIdx->def->opts.is_unique) {<br>         destHasUniqueIdx = 1;<br>      }<br>      for (pSrcIdx = pSrc->pIndex; pSrcIdx; pSrcIdx = pSrcIdx->pNext) {<br>@@ -1960,11 +1932,11 @@ xferOptimization(Parse * pParse,   /* Parser context */<br>      assert(pSrcIdx);<br>      emit_open_cursor(pParse, iSrc, pSrcIdx->tnum);<br>      sql_vdbe_set_p4_key_def(pParse, pSrcIdx);<br>-     VdbeComment((v, "%s", pSrcIdx->zName));<br>+     VdbeComment((v, "%s", pSrcIdx->def->name));<br>      emit_open_cursor(pParse, iDest, pDestIdx->tnum);<br>      sql_vdbe_set_p4_key_def(pParse, pDestIdx);<br>      sqlite3VdbeChangeP5(v, OPFLAG_BULKCSR);<br>-     VdbeComment((v, "%s", pDestIdx->zName));<br>+     VdbeComment((v, "%s", pDestIdx->def->name));<br>      addr1 = sqlite3VdbeAddOp2(v, OP_Rewind, iSrc, 0);<br>      VdbeCoverage(v);<br>      sqlite3VdbeAddOp2(v, OP_RowData, iSrc, regData);<br>diff --git a/src/box/sql/pragma.c b/src/box/sql/pragma.c<br>index 9dab5a7fd..1065489f8 100644<br>--- a/src/box/sql/pragma.c<br>+++ b/src/box/sql/pragma.c<br>@@ -370,7 +370,7 @@ sqlite3Pragma(Parse * pParse, Token * pId, /* First part of [schema.]id field */<br>                  for (k = 1;<br>                       k <=<br>                       (int)pTab->def->field_count<br>-                      && pPk->aiColumn[k - 1] !=<br>+                      && (int) pPk->def->key_def->parts[k - 1].fieldno !=<br>                       i; k++) {<br>                  }<br>               }<br>@@ -430,7 +430,7 @@ sqlite3Pragma(Parse * pParse, Token * pId, /* First part of [schema.]id field */<br>               size_t avg_tuple_size_idx =<br>                  sql_index_tuple_size(space, idx);<br>               sqlite3VdbeMultiLoad(v, 2, "sii",<br>-                         pIdx->zName,<br>+                         pIdx->def->name,<br>                          avg_tuple_size_idx,<br>                          index_field_tuple_est(pIdx, 0));<br>               sqlite3VdbeAddOp2(v, OP_ResultRow, 1,<br>@@ -459,11 +459,11 @@ sqlite3Pragma(Parse * pParse, Token * pId,   /* First part of [schema.]id field */<br>                   */<br>                  pParse->nMem = 3;<br>               }<br>-              mx = index_column_count(pIdx);<br>+              mx = pIdx->def->key_def->part_count;<br>               assert(pParse->nMem <=<br>                      pPragma->nPragCName);<br>               for (i = 0; i < mx; i++) {<br>-                 i16 cnum = pIdx->aiColumn[i];<br>+                 i16 cnum = (int) pIdx->def->key_def->parts[i].fieldno;<br>                  assert(pIdx->pTable);<br>                  sqlite3VdbeMultiLoad(v, 1,<br>                             "iis", i,<br>@@ -477,9 +477,10 @@ sqlite3Pragma(Parse * pParse, Token * pId,    /* First part of [schema.]id field */<br>                             name);<br>                  if (pPragma->iArg) {<br>                     const char *c_n;<br>-                    uint32_t id;<br>+                    uint32_t id =<br>+                       pIdx->def->key_def->parts[i].coll_id;<br>                     struct coll *coll =<br>-                       sql_index_collation(pIdx, i, &id);<br>+                       pIdx->def->key_def->parts[i].coll;<br>                     if (coll != NULL)<br>                        c_n = coll_by_id(id)->name;<br>                     else<br>@@ -519,10 +520,8 @@ sqlite3Pragma(Parse * pParse, Token * pId,    /* First part of [schema.]id field */<br>                      { "c", "u", "pk" };<br>                  sqlite3VdbeMultiLoad(v, 1,<br>                             "isisi", i,<br>-                            pIdx-><br>-                            zName,<br>-                            index_is_unique<br>-                            (pIdx),<br>+                            pIdx->def->name,<br>+                            pIdx->def->opts.is_unique,<br>                             azOrigin<br>                             [pIdx-><br>                              idxType],<br>diff --git a/src/box/sql/select.c b/src/box/sql/select.c<br>index 2aa35a114..2646a99c3 100644<br>--- a/src/box/sql/select.c<br>+++ b/src/box/sql/select.c<br>@@ -4291,7 +4291,7 @@ sqlite3IndexedByLookup(Parse * pParse, struct SrcList_item *pFrom)<br>      char *zIndexedBy = pFrom->u1.zIndexedBy;<br>      Index *pIdx;<br>      for (pIdx = pTab->pIndex;<br>-          pIdx && strcmp(pIdx->zName, zIndexedBy);<br>+          pIdx && strcmp(pIdx->def->name, zIndexedBy);<br>           pIdx = pIdx->pNext) ;<br>      if (!pIdx) {<br>         sqlite3ErrorMsg(pParse, "no such index: %s", zIndexedBy,<br>diff --git a/src/box/sql/sqliteInt.h b/src/box/sql/sqliteInt.h<br>index 01351a183..0680c2adb 100644<br>--- a/src/box/sql/sqliteInt.h<br>+++ b/src/box/sql/sqliteInt.h<br>@@ -2102,27 +2102,19 @@ struct UnpackedRecord {<br>  * program is executed). See convertToWithoutRowidTable() for details.<br>  */<br> struct Index {<br>-  char *zName;      /* Name of this index */<br>-  i16 *aiColumn;    /* Which columns are used by this index.  1st is 0 */<br>   LogEst *aiRowLogEst;   /* From ANALYZE: Est. rows selected by each column */<br>   Table *pTable;    /* The SQL table being indexed */<br>   char *zColAff;    /* String defining the affinity of each column */<br>   Index *pNext;     /* The next index associated with the same table */<br>   Schema *pSchema;   /* Schema containing this index */<br>-  /** Sorting order for each column. */<br>-  enum sort_order *sort_order;<br>-  /** Array of collation sequences for index. */<br>-  struct coll **coll_array;<br>-  /** Array of collation identifiers. */<br>-  uint32_t *coll_id_array;<br>   Expr *pPartIdxWhere;   /* WHERE clause for partial indices */<br>   ExprList *aColExpr;    /* Column expressions */<br>   int tnum;     /* DB Page containing root of this index */<br>-  u16 nColumn;      /* Number of columns stored in the index */<br>   u8 onError;       /* ON_CONFLICT_ACTION_ABORT, _IGNORE, _REPLACE,<br>             * or _NONE<br>             */<br>   unsigned idxType:2;    /* 1==UNIQUE, 2==PRIMARY KEY, 0==CREATE INDEX */<br>+  struct index_def *def;<br> };<br> <br> /**<br>@@ -3526,16 +3518,7 @@ void sqlite3AddCollateType(Parse *, Token *);<br>  */<br> struct coll *<br> sql_column_collation(struct space_def *def, uint32_t column, uint32_t *coll_id);<br>-/**<br>- * Return name of given column collation from index.<br>- *<br>- * @param idx Index which is used to fetch column.<br>- * @param column Number of column.<br>- * @param[out] coll_id Collation identifier.<br>- * @retval Pointer to collation.<br>- */<br>-struct coll *<br>-sql_index_collation(Index *idx, uint32_t column, uint32_t *id);<br>+<br> bool<br> space_is_view(Table *);<br> <br>@@ -3607,8 +3590,6 @@ void sqlite3SrcListAssignCursors(Parse *, SrcList *);<br> void sqlite3IdListDelete(sqlite3 *, IdList *);<br> void sqlite3SrcListDelete(sqlite3 *, SrcList *);<br> Index *sqlite3AllocateIndexObject(sqlite3 *, i16, int, char **);<br>-bool<br>-index_is_unique(Index *);<br> <br> /**<br>  * Create a new index for an SQL table.  name is the name of the<br>@@ -4293,8 +4274,6 @@ int sqlite3InvokeBusyHandler(BusyHandler *);<br> int<br> sql_analysis_load(struct sqlite3 *db);<br> <br>-uint32_t<br>-index_column_count(const Index *);<br> bool<br> index_is_unique_not_null(const Index *);<br> void sqlite3RegisterLikeFunctions(sqlite3 *, int);<br>diff --git a/src/box/sql/trigger.c b/src/box/sql/trigger.c<br>index e1126b2d2..ea3521133 100644<br>--- a/src/box/sql/trigger.c<br>+++ b/src/box/sql/trigger.c<br>@@ -872,8 +872,6 @@ codeRowTrigger(Parse * pParse, /* Current parse context */<br>   pSubParse->pToplevel = pTop;<br>   pSubParse->eTriggerOp = pTrigger->op;<br>   pSubParse->nQueryLoop = pParse->nQueryLoop;<br>-  struct region *region = &fiber()->gc;<br>-  pSubParse->region_initial_size = region_used(region);<br> <br>   v = sqlite3GetVdbe(pSubParse);<br>   if (v) {<br>diff --git a/src/box/sql/update.c b/src/box/sql/update.c<br>index 590aad28b..6545b3b06 100644<br>--- a/src/box/sql/update.c<br>+++ b/src/box/sql/update.c<br>@@ -237,14 +237,14 @@ sqlite3Update(Parse * pParse,       /* The parser context */<br>    */<br>   for (j = 0, pIdx = pTab->pIndex; pIdx; pIdx = pIdx->pNext, j++) {<br>      int reg;<br>-     int nIdxCol = index_column_count(pIdx);<br>+     int nIdxCol = pIdx->def->key_def->part_count;<br>      if (chngPk || hasFK || pIdx->pPartIdxWhere || pIdx == pPk) {<br>         reg = ++pParse->nMem;<br>         pParse->nMem += nIdxCol;<br>      } else {<br>         reg = 0;<br>         for (i = 0; i < nIdxCol; i++) {<br>-           i16 iIdxCol = pIdx->aiColumn[i];<br>+           i16 iIdxCol = pIdx->def->key_def->parts[i].fieldno;<br>            if (iIdxCol < 0 || aXRef[iIdxCol] >= 0) {<br>               reg = ++pParse->nMem;<br>               pParse->nMem += nIdxCol;<br>@@ -306,7 +306,7 @@ sqlite3Update(Parse * pParse,     /* The parser context */<br>      nPk = nKey;<br>   } else {<br>      assert(pPk != 0);<br>-     nPk = index_column_count(pPk);<br>+     nPk = pPk->def->key_def->part_count;<br>   }<br>   iPk = pParse->nMem + 1;<br>   pParse->nMem += nPk;<br>@@ -333,9 +333,9 @@ sqlite3Update(Parse * pParse,     /* The parser context */<br>      }<br>   } else {<br>      for (i = 0; i < nPk; i++) {<br>-        assert(pPk->aiColumn[i] >= 0);<br>         sqlite3ExprCodeGetColumnOfTable(v, pTab->def, iDataCur,<br>-                    pPk->aiColumn[i],<br>+                    pPk->def->key_def-><br>+                       parts[i].fieldno,<br>                     iPk + i);<br>      }<br>   }<br>diff --git a/src/box/sql/vdbemem.c b/src/box/sql/vdbemem.c<br>index f408b7701..7f9eeb71a 100644<br>--- a/src/box/sql/vdbemem.c<br>+++ b/src/box/sql/vdbemem.c<br>@@ -1087,7 +1087,7 @@ valueNew(sqlite3 * db, struct ValueNewStat4Ctx *p)<br>         Index *pIdx = p->pIdx; /* Index being probed */<br>         int nByte; /* Bytes of space to allocate */<br>         int i; /* Counter variable */<br>-        int nCol = index_column_count(pIdx);<br>+        int nCol = pIdx->def->key_def->part_count;<br> <br>         nByte = sizeof(Mem) * nCol +<br>            ROUND8(sizeof(UnpackedRecord));<br>diff --git a/src/box/sql/where.c b/src/box/sql/where.c<br>index e79164781..9dd4721ad 100644<br>--- a/src/box/sql/where.c<br>+++ b/src/box/sql/where.c<br>@@ -376,19 +376,21 @@ whereScanInit(WhereScan * pScan, /* The WhereScan object being initialized */<br>   pScan->is_column_seen = false;<br>   if (pIdx) {<br>      int j = iColumn;<br>-     iColumn = pIdx->aiColumn[j];<br>-     if (iColumn == XN_EXPR) {<br>-        pScan->pIdxExpr = pIdx->aColExpr->a[j].pExpr;<br>-     } else if (iColumn >= 0) {<br>+     iColumn = pIdx->def->key_def->parts[j].fieldno;<br>+     /*<br>+      * pIdx->tnum == 0 means that pIdx is a fake integer<br>+      * primary key index<br>+      */<br>+     if (pIdx->tnum == 0)<br>+        iColumn = -1;<br>+<br>+     if (iColumn >= 0) {<br>         char affinity =<br>            pIdx->pTable->def->fields[iColumn].affinity;<br>         pScan->idxaff = affinity;<br>-        uint32_t id;<br>-        pScan->coll = sql_index_collation(pIdx, j, &id);<br>+        pScan->coll = pIdx->def->key_def->parts[j].coll;<br>         pScan->is_column_seen = true;<br>      }<br>-  } else if (iColumn == XN_EXPR) {<br>-     return 0;<br>   }<br>   pScan->opMask = opMask;<br>   pScan->k = 0;<br>@@ -466,16 +468,16 @@ findIndexCol(Parse * pParse, /* Parse context */<br> {<br>   for (int i = 0; i < pList->nExpr; i++) {<br>      Expr *p = sqlite3ExprSkipCollate(pList->a[i].pExpr);<br>-     if (p->op == TK_COLUMN &&<br>-         p->iColumn == pIdx->aiColumn[iCol] &&<br>-         p->iTable == iBase) {<br>+     if (p->op == TK_COLUMN && p->iTable == iBase &&<br>+        p->iColumn == (int) pIdx->def->key_def-><br>+        parts[iCol].fieldno) {<br>         bool is_found;<br>         uint32_t id;<br>         struct coll *coll = sql_expr_coll(pParse,<br>                       pList->a[i].pExpr,<br>                       &is_found, &id);<br>         if (is_found &&<br>-            coll == sql_index_collation(pIdx, iCol, &id)) {<br>+            coll == pIdx->def->key_def->parts[iCol].coll) {<br>            return i;<br>         }<br>      }<br>@@ -484,27 +486,6 @@ findIndexCol(Parse * pParse,  /* Parse context */<br>   return -1;<br> }<br> <br>-/*<br>- * Return TRUE if the iCol-th column of index pIdx is NOT NULL<br>- */<br>-static int<br>-indexColumnNotNull(Index * pIdx, int iCol)<br>-{<br>-  int j;<br>-  assert(pIdx != 0);<br>-  assert(iCol >= 0 && iCol < (int)index_column_count(pIdx));<br>-  j = pIdx->aiColumn[iCol];<br>-  if (j >= 0) {<br>-     return !pIdx->pTable->def->fields[j].is_nullable;<br>-  } else if (j == (-1)) {<br>-     return 1;<br>-  } else {<br>-     assert(j == (-2));<br>-     return 0;  /* Assume an indexed expression can always yield a NULL */<br>-<br>-  }<br>-}<br>-<br> /*<br>  * Return true if the DISTINCT expression-list passed as the third argument<br>  * is redundant.<br>@@ -556,9 +537,9 @@ isDistinctRedundant(Parse * pParse,       /* Parsing context */<br>    *      contain a "col=X" term are subject to a NOT NULL constraint.<br>    */<br>   for (pIdx = pTab->pIndex; pIdx; pIdx = pIdx->pNext) {<br>-     if (!index_is_unique(pIdx))<br>+     if (!pIdx->def->opts.is_unique)<br>         continue;<br>-     int col_count = index_column_count(pIdx);<br>+     int col_count = pIdx->def->key_def->part_count;<br>      for (i = 0; i < col_count; i++) {<br>         if (0 ==<br>             sqlite3WhereFindTerm(pWC, iBase, i, ~(Bitmask) 0,<br>@@ -566,11 +547,12 @@ isDistinctRedundant(Parse * pParse,     /* Parsing context */<br>            if (findIndexCol<br>                (pParse, pDistinct, iBase, pIdx, i) < 0)<br>               break;<br>-           if (indexColumnNotNull(pIdx, i) == 0)<br>+           uint32_t j = pIdx->def->key_def->parts[i].fieldno;<br>+           if (!pIdx->pTable->def->fields[j].is_nullable == 0)<br>               break;<br>         }<br>      }<br>-     if (i == (int)index_column_count(pIdx)) {<br>+     if (i == (int) pIdx->def->key_def->part_count) {<br>         /* This index implies that the DISTINCT qualifier is redundant. */<br>         return 1;<br>      }<br>@@ -1107,7 +1089,7 @@ whereRangeAdjust(WhereTerm * pTerm, LogEst nNew)<br> char<br> sqlite3IndexColumnAffinity(sqlite3 * db, Index * pIdx, int iCol)<br> {<br>-  assert(iCol >= 0 && iCol < (int)index_column_count(pIdx));<br>+  assert(iCol >= 0 && iCol < (int) pIdx->def->key_def->part_count);<br>   if (!pIdx->zColAff) {<br>      if (sqlite3IndexAffinityStr(db, pIdx) == 0)<br>         return AFFINITY_BLOB;<br>@@ -1169,13 +1151,12 @@ whereRangeSkipScanEst(Parse * pParse,     /* Parsing & code generating context */<br>   int nUpper = index->def->opts.stat->sample_count + 1;<br>   int rc = SQLITE_OK;<br>   u8 aff = sqlite3IndexColumnAffinity(db, p, nEq);<br>-  uint32_t id;<br> <br>   sqlite3_value *p1 = 0; /* Value extracted from pLower */<br>   sqlite3_value *p2 = 0; /* Value extracted from pUpper */<br>   sqlite3_value *pVal = 0;   /* Value extracted from record */<br> <br>-  struct coll *pColl = sql_index_collation(p, nEq, &id);<br>+  struct coll *pColl = p->def->key_def->parts[nEq].coll;<br>   if (pLower) {<br>      rc = sqlite3Stat4ValueFromExpr(pParse, pLower->pExpr->pRight,<br>                      aff, &p1);<br>@@ -1521,7 +1502,7 @@ whereEqualScanEst(Parse * pParse,    /* Parsing & code generating context */<br>   int bOk;<br> <br>   assert(nEq >= 1);<br>-  assert(nEq <= (int)index_column_count(p));<br>+  assert(nEq <= (int) p->def->key_def->part_count);<br>   assert(pBuilder->nRecValid < nEq);<br> <br>   /* If values are not available for all fields of the index to the left<br>@@ -1542,7 +1523,7 @@ whereEqualScanEst(Parse * pParse,    /* Parsing & code generating context */<br> <br>   whereKeyStats(pParse, p, pRec, 0, a);<br>   WHERETRACE(0x10, ("equality scan regions %s(%d): %d\n",<br>-          p->zName, nEq - 1, (int)a[1]));<br>+        p->def->name, nEq - 1, (int)a[1]));<br>   *pnRow = a[1];<br> <br>   return rc;<br>@@ -1674,7 +1655,7 @@ whereLoopPrint(WhereLoop * p, WhereClause * pWC)<br>            pItem->zAlias ? pItem->zAlias : pTab->def->name);<br> #endif<br>   const char *zName;<br>-  if (p->pIndex && (zName = p->pIndex->zName) != 0) {<br>+  if (p->pIndex && (zName = p->pIndex->def->name) != 0) {<br>      if (strncmp(zName, "sqlite_autoindex_", 17) == 0) {<br>         int i = sqlite3Strlen30(zName) - 1;<br>         while (zName[i] != '_')<br>@@ -2236,7 +2217,7 @@ whereRangeVectorLen(Parse * pParse,  /* Parsing context */<br>   int nCmp = sqlite3ExprVectorSize(pTerm->pExpr->pLeft);<br>   int i;<br> <br>-  nCmp = MIN(nCmp, (int)(index_column_count(pIdx) - nEq));<br>+  nCmp = MIN(nCmp, (int)(pIdx->def->key_def->part_count - nEq));<br>   for (i = 1; i < nCmp; i++) {<br>      /* Test if comparison i of pTerm is compatible with column (i+nEq)<br>       * of the index. If not, exit the loop.<br>@@ -2259,7 +2240,7 @@ whereRangeVectorLen(Parse * pParse,  /* Parsing context */<br>       */<br>      if (pLhs->op != TK_COLUMN<br>          || pLhs->iTable != iCur<br>-         || pLhs->iColumn != pIdx->aiColumn[i + nEq]<br>+         || pLhs->iColumn != (int)pIdx->def->key_def->parts[i + nEq].fieldno<br>          || sql_index_column_sort_order(pIdx, i + nEq) !=<br>             sql_index_column_sort_order(pIdx, nEq)) {<br>         break;<br>@@ -2275,7 +2256,7 @@ whereRangeVectorLen(Parse * pParse,  /* Parsing context */<br>      pColl = sql_binary_compare_coll_seq(pParse, pLhs, pRhs, &id);<br>      if (pColl == 0)<br>         break;<br>-          if (sql_index_collation(pIdx, i + nEq, &id) != pColl)<br>+     if (pIdx->def->key_def->parts[(i + nEq)].coll != pColl)<br>         break;<br>   }<br>   return i;<br>@@ -2318,13 +2299,13 @@ whereLoopAddBtreeIndex(WhereLoopBuilder * pBuilder,    /* The WhereLoop factory */<br>   LogEst rSize;     /* Number of rows in the table */<br>   LogEst rLogSize;   /* Logarithm of table size */<br>   WhereTerm *pTop = 0, *pBtm = 0;    /* Top and bottom range constraints */<br>-  uint32_t nProbeCol = index_column_count(pProbe);<br>+  uint32_t nProbeCol = pProbe->def->key_def->part_count;<br> <br>   pNew = pBuilder->pNew;<br>   if (db->mallocFailed)<br>      return SQLITE_NOMEM_BKPT;<br>   WHERETRACE(0x800, ("BEGIN addBtreeIdx(%s), nEq=%d\n",<br>-           pProbe->zName, pNew->nEq));<br>+        pProbe->def->name, pNew->nEq));<br> <br>   assert((pNew->wsFlags & WHERE_TOP_LIMIT) == 0);<br>   if (pNew->wsFlags & WHERE_BTM_LIMIT) {<br>@@ -2374,8 +2355,9 @@ whereLoopAddBtreeIndex(WhereLoopBuilder * pBuilder,  /* The WhereLoop factory */<br>      LogEst nOutUnadjusted; /* nOut before IN() and WHERE adjustments */<br>      int nIn = 0;<br>      int nRecValid = pBuilder->nRecValid;<br>+     uint32_t j = pProbe->def->key_def->parts[saved_nEq].fieldno;<br>      if ((eOp == WO_ISNULL || (pTerm->wtFlags & TERM_VNULL) != 0)<br>-         && indexColumnNotNull(pProbe, saved_nEq)<br>+         && !pProbe->pTable->def->fields[j].is_nullable<br>          ) {<br>         continue;  /* ignore IS [NOT] NULL constraints on NOT NULL columns */<br>      }<br>@@ -2445,13 +2427,13 @@ whereLoopAddBtreeIndex(WhereLoopBuilder * pBuilder,    /* The WhereLoop factory */<br>                      */<br>         }<br>      } else if (eOp & WO_EQ) {<br>-        int iCol = pProbe->aiColumn[saved_nEq];<br>+        int iCol = pProbe->def->key_def->parts[saved_nEq].fieldno;<br>         pNew->wsFlags |= WHERE_COLUMN_EQ;<br>         assert(saved_nEq == pNew->nEq);<br>-        if ((iCol > 0 && nInMul == 0<br>-           && saved_nEq == nProbeCol - 1)<br>-            ) {<br>-           if (iCol >= 0 &&<br>+        if ((iCol > 0 && nInMul == 0 &&<br>+              saved_nEq == nProbeCol - 1)<br>+           ) {<br>+           if (pProbe->tnum != 0 &&<br>                !index_is_unique_not_null(pProbe)) {<br>               pNew->wsFlags |= WHERE_UNQ_WANTED;<br>            } else {<br>@@ -2514,8 +2496,7 @@ whereLoopAddBtreeIndex(WhereLoopBuilder * pBuilder,  /* The WhereLoop factory */<br>         assert(eOp & (WO_ISNULL | WO_EQ | WO_IN));<br> <br>         assert(pNew->nOut == saved_nOut);<br>-        if (pTerm->truthProb <= 0<br>-            && pProbe->aiColumn[saved_nEq] >= 0) {<br>+        if (pTerm->truthProb <= 0 && pProbe->tnum != 0 ) {<br>            assert((eOp & WO_IN) || nIn == 0);<br>            testcase(eOp & WO_IN);<br>            pNew->nOut += pTerm->truthProb;<br>@@ -2671,7 +2652,7 @@ whereLoopAddBtreeIndex(WhereLoopBuilder * pBuilder,  /* The WhereLoop factory */<br>   }<br> <br>   WHERETRACE(0x800, ("END addBtreeIdx(%s), nEq=%d, rc=%d\n",<br>-           pProbe->zName, saved_nEq, rc));<br>+        pProbe->def->name, saved_nEq, rc));<br>   return rc;<br> }<br> <br>@@ -2715,7 +2696,7 @@ indexMightHelpWithOrderBy(WhereLoopBuilder * pBuilder,<br>   ExprList *pOB;<br>   ExprList *aColExpr;<br>   int ii, jj;<br>-  int nIdxCol = index_column_count(pIndex);<br>+  int nIdxCol = pIndex->def->key_def->part_count;<br>   if (index_is_unordered(pIndex))<br>      return 0;<br>   if ((pOB = pBuilder->pWInfo->pOrderBy) == 0)<br>@@ -2726,13 +2707,12 @@ indexMightHelpWithOrderBy(WhereLoopBuilder * pBuilder,<br>         if (pExpr->iColumn < 0)<br>            return 1;<br>         for (jj = 0; jj < nIdxCol; jj++) {<br>-           if (pExpr->iColumn == pIndex->aiColumn[jj])<br>+           if (pExpr->iColumn == (int)<br>+               pIndex->def->key_def->parts[jj].fieldno)<br>               return 1;<br>         }<br>      } else if ((aColExpr = pIndex->aColExpr) != 0) {<br>         for (jj = 0; jj < nIdxCol; jj++) {<br>-           if (pIndex->aiColumn[jj] != XN_EXPR)<br>-              continue;<br>            if (sqlite3ExprCompare<br>                (pExpr, aColExpr->a[jj].pExpr,<br>                 iCursor) == 0) {<br>@@ -2815,7 +2795,6 @@ whereLoopAddBtree(WhereLoopBuilder * pBuilder,   /* WHERE clause information */<br>   Index *pProbe;    /* An index we are evaluating */<br>   Index sPk;    /* A fake index object for the primary key */<br>   LogEst aiRowEstPk[2];  /* The aiRowLogEst[] value for the sPk index */<br>-  i16 aiColumnPk = -1;   /* The aColumn[] value for the sPk index */<br>   SrcList *pTabList; /* The FROM clause */<br>   struct SrcList_item *pSrc; /* The FROM clause btree term to add */<br>   WhereLoop *pNew;   /* Template WhereLoop object */<br>@@ -2846,11 +2825,27 @@ whereLoopAddBtree(WhereLoopBuilder * pBuilder, /* WHERE clause information */<br>       */<br>      Index *pFirst; /* First of real indices on the table */<br>      memset(&sPk, 0, sizeof(Index));<br>-     sPk.nColumn = 1;<br>-     sPk.aiColumn = &aiColumnPk;<br>      sPk.aiRowLogEst = aiRowEstPk;<br>      sPk.onError = ON_CONFLICT_ACTION_REPLACE;<br>      sPk.pTable = pTab;<br>+<br>+     struct key_def *key_def = key_def_new(1);<br>+     if (key_def == NULL)<br>+        return SQLITE_ERROR;<br>+<br>+     key_def_set_part(key_def, 0, 0, pTab->def->fields[0].type,<br>+            ON_CONFLICT_ACTION_ABORT,<br>+            NULL, COLL_NONE, SORT_ORDER_ASC);<br>+<br>+     struct index_opts index_opts = index_opts_default;<br>+<br>+     sPk.def = index_def_new(pTab->def->id, 0, "primary",<br>+           sizeof("primary") - 1, TREE, &index_opts,<br>+           key_def, NULL);<br>+<br>+     if (sPk.def == NULL)<br>+        return SQLITE_ERROR;<br>+<br>      aiRowEstPk[0] = sql_space_tuple_log_count(pTab);<br>      aiRowEstPk[1] = 0;<br>      pFirst = pSrc->pTab->pIndex;<br>@@ -3325,8 +3320,8 @@ wherePathSatisfiesOrderBy(WhereInfo * pWInfo,    /* The WHERE clause */<br>               index_is_unordered(pIndex)) {<br>            return 0;<br>         } else {<br>-           nColumn = index_column_count(pIndex);<br>-           isOrderDistinct = index_is_unique(pIndex);<br>+           nColumn = pIndex->def->key_def->part_count;<br>+           isOrderDistinct = pIndex->def->opts.is_unique;<br>         }<br> <br>         /* Loop through all columns of the index and deal with the ones<br>@@ -3387,7 +3382,7 @@ wherePathSatisfiesOrderBy(WhereInfo * pWInfo,    /* The WHERE clause */<br>             * (revIdx) for the j-th column of the index.<br>             */<br>            if (pIndex) {<br>-              iColumn = pIndex->aiColumn[j];<br>+              iColumn = pIndex->def->key_def->parts[j].fieldno;<br>               revIdx = sql_index_column_sort_order(pIndex,<br>                                j);<br>               if (iColumn == pIndex->pTable->iPKey)<br>@@ -3442,8 +3437,7 @@ wherePathSatisfiesOrderBy(WhereInfo * pWInfo,    /* The WHERE clause */<br>                              pOrderBy->a[i].pExpr,<br>                              &is_found, &id);<br>                  struct coll *idx_coll =<br>-                    sql_index_collation(pIndex,<br>-                              j, &id);<br>+                    pIndex->def->key_def->parts[j].coll;<br>                  if (is_found &&<br>                      coll != idx_coll)<br>                     continue;<br>@@ -4105,9 +4099,9 @@ whereShortCut(WhereLoopBuilder * pBuilder)<br>   } else {<br>      for (pIdx = pTab->pIndex; pIdx; pIdx = pIdx->pNext) {<br>         int opMask;<br>-        int nIdxCol = index_column_count(pIdx);<br>+        int nIdxCol = pIdx->def->key_def->part_count;<br>         assert(pLoop->aLTermSpace == pLoop->aLTerm);<br>-        if (!index_is_unique(pIdx)<br>+        if (!pIdx->def->opts.is_unique<br>             || pIdx->pPartIdxWhere != 0<br>             || nIdxCol > ArraySize(pLoop->aLTermSpace)<br>             )<br>@@ -4650,7 +4644,7 @@ sqlite3WhereBegin(Parse * pParse,    /* The parser context */<br>               wctrlFlags & WHERE_ORDERBY_MIN) == 0) {<br>               sqlite3VdbeChangeP5(v, OPFLAG_SEEKEQ); /* Hint to COMDB2 */<br>            }<br>-           VdbeComment((v, "%s", pIx->zName));<br>+           VdbeComment((v, "%s", pIx->def->name));<br> #ifdef SQLITE_ENABLE_COLUMN_USED_MASK<br>            {<br>               u64 colUsed = 0;<br>@@ -4781,7 +4775,7 @@ sqlite3WhereEnd(WhereInfo * pWInfo)<br>      if (pLevel->addrSkip) {<br>         sqlite3VdbeGoto(v, pLevel->addrSkip);<br>         VdbeComment((v, "next skip-scan on %s",<br>-                pLoop->pIndex->zName));<br>+                pLoop->pIndex->def->name));<br>         sqlite3VdbeJumpHere(v, pLevel->addrSkip);<br>         sqlite3VdbeJumpHere(v, pLevel->addrSkip - 2);<br>      }<br>diff --git a/src/box/sql/wherecode.c b/src/box/sql/wherecode.c<br>index 09b267194..94aae9958 100644<br>--- a/src/box/sql/wherecode.c<br>+++ b/src/box/sql/wherecode.c<br>@@ -47,9 +47,8 @@<br> static const char *<br> explainIndexColumnName(Index * pIdx, int i)<br> {<br>-  i = pIdx->aiColumn[i];<br>-  if (i == XN_EXPR)<br>-     return "<expr>";<br>+//    i = pIdx->aiColumn[i];<br>+  i = pIdx->def->key_def->parts[i].fieldno;<br>   return pIdx->pTable->def->fields[i].name;<br> }<br> <br>@@ -222,7 +221,7 @@ sqlite3WhereExplainOneScan(Parse * pParse, /* Parse context */<br>         }<br>         if (zFmt) {<br>            sqlite3StrAccumAppend(&str, " USING ", 7);<br>-           sqlite3XPrintf(&str, zFmt, pIdx->zName);<br>+           sqlite3XPrintf(&str, zFmt, pIdx->def->name);<br>            explainIndexRange(&str, pLoop);<br>         }<br>      } else if ((flags & WHERE_IPK) != 0<br>@@ -708,7 +707,7 @@ codeAllEqualityTerms(Parse * pParse,   /* Parsing context */<br>      sqlite3VdbeAddOp1(v, (bRev ? OP_Last : OP_Rewind), iIdxCur);<br>      VdbeCoverageIf(v, bRev == 0);<br>      VdbeCoverageIf(v, bRev != 0);<br>-     VdbeComment((v, "begin skip-scan on %s", pIdx->zName));<br>+     VdbeComment((v, "begin skip-scan on %s", pIdx->def->name));<br>      j = sqlite3VdbeAddOp0(v, OP_Goto);<br>      pLevel->addrSkip =<br>          sqlite3VdbeAddOp4Int(v, (bRev ? OP_SeekLT : OP_SeekGT),<br>@@ -718,8 +717,7 @@ codeAllEqualityTerms(Parse * pParse,   /* Parsing context */<br>      sqlite3VdbeJumpHere(v, j);<br>      for (j = 0; j < nSkip; j++) {<br>         sqlite3VdbeAddOp3(v, OP_Column, iIdxCur,<br>-                pIdx->aiColumn[j], regBase + j);<br>-        testcase(pIdx->aiColumn[j] == XN_EXPR);<br>+                pIdx->def->key_def->parts[j].fieldno, regBase + j);<br>         VdbeComment((v, "%s", explainIndexColumnName(pIdx, j)));<br>      }<br>   }<br>@@ -1245,10 +1243,10 @@ sqlite3WhereCodeOneLoopStart(WhereInfo * pWInfo,   /* Complete information about t<br>      assert(pWInfo->pOrderBy == 0<br>             || pWInfo->pOrderBy->nExpr == 1<br>             || (pWInfo->wctrlFlags & WHERE_ORDERBY_MIN) == 0);<br>-     int nIdxCol = index_column_count(pIdx);<br>+     int nIdxCol = pIdx->def->key_def->part_count;<br>      if ((pWInfo->wctrlFlags & WHERE_ORDERBY_MIN) != 0<br>          && pWInfo->nOBSat > 0 && (nIdxCol > nEq)) {<br>-        j = pIdx->aiColumn[nEq];<br>+        j = pIdx->def->key_def->parts[nEq].fieldno;<br>         /* Allow seek for column with `NOT NULL` == false attribute.<br>          * If a column may contain NULL-s, the comparator installed<br>          * by Tarantool is prepared to seek using a NULL value.<br>@@ -1304,12 +1302,9 @@ sqlite3WhereCodeOneLoopStart(WhereInfo * pWInfo,    /* Complete information about t<br>         }<br> #endif<br>         if (pRangeStart == 0) {<br>-           j = pIdx->aiColumn[nEq];<br>-           if ((j >= 0 &&<br>-                pIdx->pTable->def->fields[j].is_nullable)||<br>-               j == XN_EXPR) {<br>+           j = pIdx->def->key_def->parts[nEq].fieldno;<br>+           if (pIdx->pTable->def->fields[j].is_nullable)<br>               bSeekPastNull = 1;<br>-           }<br>         }<br>      }<br>      assert(pRangeEnd == 0<br>@@ -1386,9 +1381,10 @@ sqlite3WhereCodeOneLoopStart(WhereInfo * pWInfo,    /* Complete information about t<br>      }<br>      struct Index *pk = sqlite3PrimaryKeyIndex(pIdx->pTable);<br>      assert(pk);<br>-     int nPkCol = index_column_count(pk);<br>+     int nPkCol = pk->def->key_def->part_count;<br>+     uint32_t zero_fieldno = pk->def->key_def->parts[0].fieldno;<br>      char affinity =<br>-        pIdx->pTable->def->fields[pk->aiColumn[0]].affinity;<br>+        pIdx->pTable->def->fields[zero_fieldno].affinity;<br>      if (nPkCol == 1 && affinity == AFFINITY_INTEGER) {<br>         /* Right now INTEGER PRIMARY KEY is the only option to<br>          * get Tarantool's INTEGER column type. Need special handling<br>@@ -1397,7 +1393,8 @@ sqlite3WhereCodeOneLoopStart(WhereInfo * pWInfo, /* Complete information about t<br>          */<br>         int limit = pRangeStart == NULL ? nEq : nEq + 1;<br>         for (int i = 0; i < limit; i++) {<br>-           if (pIdx->aiColumn[i] == pk->aiColumn[0]) {<br>+           if (pIdx->def->key_def->parts[i].fieldno ==<br>+               zero_fieldno) {<br>               /* Here: we know for sure that table has INTEGER<br>                  PRIMARY KEY, single column, and Index we're<br>                  trying to use for scan contains this column. */<br>@@ -1506,10 +1503,10 @@ sqlite3WhereCodeOneLoopStart(WhereInfo * pWInfo,   /* Complete information about t<br>         /* pIdx is a covering index.  No need to access the main table. */<br>      }  else if (iCur != iIdxCur) {<br>         Index *pPk = sqlite3PrimaryKeyIndex(pIdx->pTable);<br>-        int nPkCol = index_column_count(pPk);<br>+        int nPkCol = pPk->def->key_def->part_count;<br>         int iKeyReg = sqlite3GetTempRange(pParse, nPkCol);<br>         for (j = 0; j < nPkCol; j++) {<br>-           k = pPk->aiColumn[j];<br>+           k = pPk->def->key_def->parts[j].fieldno;<br>            sqlite3VdbeAddOp3(v, OP_Column, iIdxCur, k,<br>                    iKeyReg + j);<br>         }<br>@@ -1614,7 +1611,7 @@ sqlite3WhereCodeOneLoopStart(WhereInfo * pWInfo, /* Complete information about t<br>       */<br>      if ((pWInfo->wctrlFlags & WHERE_DUPLICATES_OK) == 0) {<br>         Index *pPk = sqlite3PrimaryKeyIndex(pTab);<br>-        int nPkCol = index_column_count(pPk);<br>+        int nPkCol = pPk->def->key_def->part_count;<br>         regRowset = pParse->nTab++;<br>         sqlite3VdbeAddOp2(v, OP_OpenTEphemeral,<br>                 regRowset, nPkCol);<br>@@ -1718,13 +1715,15 @@ sqlite3WhereCodeOneLoopStart(WhereInfo * pWInfo,   /* Complete information about t<br>                  int iSet =<br>                      ((ii == pOrWc->nTerm - 1) ? -1 : ii);<br>                  Index *pPk = sqlite3PrimaryKeyIndex (pTab);<br>-                 int nPk = index_column_count(pPk);<br>+                 int nPk = pPk->def->key_def->part_count;<br>                  int iPk;<br> <br>                  /* Read the PK into an array of temp registers. */<br>                  r = sqlite3GetTempRange(pParse, nPk);<br>                  for (iPk = 0; iPk < nPk; iPk++) {<br>-                    int iCol = pPk->aiColumn[iPk];<br>+                    int iCol = pPk->def-><br>+                       key_def-><br>+                       parts[iPk].fieldno;<br>                     sqlite3ExprCodeGetColumnToReg<br>                        (pParse, pTab->def,<br>                         iCol, iCur,<br>diff --git a/src/box/sql/whereexpr.c b/src/box/sql/whereexpr.c<br>index aa6d4524d..40e4e2577 100644<br>--- a/src/box/sql/whereexpr.c<br>+++ b/src/box/sql/whereexpr.c<br>@@ -903,7 +903,6 @@ exprMightBeIndexed(SrcList * pFrom,    /* The FROM clause */<br>         int *piColumn   /* Write the referenced table column number here */<br>     )<br> {<br>-  Index *pIdx;<br>   int i;<br>   int iCur;<br> <br>@@ -930,20 +929,6 @@ exprMightBeIndexed(SrcList * pFrom,   /* The FROM clause */<br>   for (i = 0; mPrereq > 1; i++, mPrereq >>= 1) {<br>   }<br>   iCur = pFrom->a[i].iCursor;<br>-  for (pIdx = pFrom->a[i].pTab->pIndex; pIdx; pIdx = pIdx->pNext) {<br>-     if (pIdx->aColExpr == 0)<br>-        continue;<br>-     for (i = 0; i < pIdx->nColumn; i++) {<br>-        if (pIdx->aiColumn[i] != XN_EXPR)<br>-           continue;<br>-        if (sqlite3ExprCompare<br>-            (pExpr, pIdx->aColExpr->a[i].pExpr, iCur) == 0) {<br>-           *piCur = iCur;<br>-           *piColumn = XN_EXPR;<br>-           return 1;<br>-        }<br>-     }<br>-  }<br>   return 0;<br> }<br> <br>-- <br>2.14.3 (Apple Git-98)</pre><br>-- <br>Ivan Koptelov<br><style></style>
</div></div>
            
        
                <base target="_self" href="https://e.mail.ru/">
        </div>

        
</div></BODY></HTML>