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